| 1 | Synergy Developer and Porting Guide | 
|---|
| 2 | =================================== | 
|---|
| 3 |  | 
|---|
| 4 | This document is under development. | 
|---|
| 5 |  | 
|---|
| 6 | Code Organization | 
|---|
| 7 | ----------------- | 
|---|
| 8 |  | 
|---|
| 9 | The synergy source code organization is: | 
|---|
| 10 |  | 
|---|
| 11 | .               -- root makefiles, some standard documentation | 
|---|
| 12 | cmd             -- program source code | 
|---|
| 13 | launcher      -- synergy launcher for Windows | 
|---|
| 14 | synergyc      -- synergy client | 
|---|
| 15 | synergys      -- synergy server | 
|---|
| 16 | config          -- stuff for autoconf/automake | 
|---|
| 17 | dist            -- files for creating distributions | 
|---|
| 18 | nullsoft      -- files for creating Nullsoft NSIS installer (Windows) | 
|---|
| 19 | rpm           -- files for creating RPMs | 
|---|
| 20 | doc             -- placeholder for documentation | 
|---|
| 21 | examples        -- example files | 
|---|
| 22 | lib             -- library source code | 
|---|
| 23 | arch          -- platform dependent utility library | 
|---|
| 24 | base          -- simple utilities | 
|---|
| 25 | client        -- synergy client library | 
|---|
| 26 | common        -- commonly needed header files | 
|---|
| 27 | io            -- I/O | 
|---|
| 28 | mt            -- multithreading | 
|---|
| 29 | net           -- networking | 
|---|
| 30 | platform      -- platform dependent display/window/event stuff | 
|---|
| 31 | server        -- synergy server library | 
|---|
| 32 | synergy       -- synergy shared client/server code library | 
|---|
| 33 |  | 
|---|
| 34 | Note how the utility code required by the programs is placed into | 
|---|
| 35 | separate library directories.  This makes the makefiles a little | 
|---|
| 36 | more awkward but makes for a cleaner organization.  The top level | 
|---|
| 37 | directory has only the standard documentation files and the files | 
|---|
| 38 | necessary to configure and build the rest of the project. | 
|---|
| 39 |  | 
|---|
| 40 |  | 
|---|
| 41 | Coding Style Guide | 
|---|
| 42 | ------------------ | 
|---|
| 43 |  | 
|---|
| 44 | Synergy uses many coding conventions.  Contributed code should | 
|---|
| 45 | following these guidelines. | 
|---|
| 46 |  | 
|---|
| 47 | - Symbol Naming | 
|---|
| 48 | Names always begin with a letter (never an underscore).  The first | 
|---|
| 49 | letter of interior names are always capitalized.  Acronyms should | 
|---|
| 50 | be all uppercase.  For example: myTextAsASCII. | 
|---|
| 51 |  | 
|---|
| 52 | Names come it two flavors:  leading capital and leading lowercase. | 
|---|
| 53 | The former have the first character capitalized and the latter | 
|---|
| 54 | don't.  In the following table, leading capital names are indicated | 
|---|
| 55 | by `Name' and leading lowercase names by `name'. | 
|---|
| 56 |  | 
|---|
| 57 | The naming convention for various things are: | 
|---|
| 58 |  | 
|---|
| 59 | * Exceptions       -- X + Name    XMyException | 
|---|
| 60 | * Interfaces       -- I + Name    IMyInterface | 
|---|
| 61 | * Template Classes -- T + Name    TMyTemplate<> | 
|---|
| 62 | * Other Classes    -- C + Name    CMyClass | 
|---|
| 63 | * Enumerations     -- E + Name    EMyEnumeration | 
|---|
| 64 | * Constants        -- k + Name    kMyConstant | 
|---|
| 65 | * Data Members     -- m_ + name   m_myDataMember | 
|---|
| 66 | * Methods          -- name        myMethod | 
|---|
| 67 | * Functions        -- name        myFunction | 
|---|
| 68 | * Variables        -- name        myVariable | 
|---|
| 69 |  | 
|---|
| 70 | Exceptions are types that get thrown and are generally derived | 
|---|
| 71 | (possibly indirectly) from XBase.  Interfaces are derived (possibly | 
|---|
| 72 | indirectly) from IInterface and have only pure virtual functions. | 
|---|
| 73 | Other classes are classes that aren't exceptions or interfaces. | 
|---|
| 74 | Constants include global constants and enumerants. | 
|---|
| 75 |  | 
|---|
| 76 | Method names should usually have the form `verbObject'.  For example: | 
|---|
| 77 | * isGameOn() | 
|---|
| 78 | * getBeer() | 
|---|
| 79 | * pressPowerButton() | 
|---|
| 80 | * setChannel() | 
|---|
| 81 | In general, use `get' and `set' to read and write state but use `is' | 
|---|
| 82 | to read boolean state.  Note that classes that contain only `is', | 
|---|
| 83 | `get', and `set' are probably plain old data;  you might want to | 
|---|
| 84 | consider using public data members only or, better, refactor your | 
|---|
| 85 | design to have classes that actually do something more than just | 
|---|
| 86 | hold data. | 
|---|
| 87 |  | 
|---|
| 88 | - File Naming | 
|---|
| 89 | Each class should have one source and one header file.  If the | 
|---|
| 90 | class is named `CMyClass' then the source file should be named | 
|---|
| 91 | `CMyClass.cpp' and the header file `CMyClass.h'. | 
|---|
| 92 |  | 
|---|
| 93 | Headers files not containing a class should have some meaningful | 
|---|
| 94 | name with a leading capital (e.g. `Version.h'). | 
|---|
| 95 |  | 
|---|
| 96 | Source files without a header file have a leading lowercase name. | 
|---|
| 97 | Only files containing the entry point for an application should | 
|---|
| 98 | lack a header file. | 
|---|
| 99 |  | 
|---|
| 100 | - Dependencies | 
|---|
| 101 | * No circular library dependencies | 
|---|
| 102 | Library dependencies form an acyclic graph.  Conceptually | 
|---|
| 103 | libraries can be arranged in layers where each library only | 
|---|
| 104 | references libraries in layers below it, not in the same layer | 
|---|
| 105 | or layers above it.  The makefiles build the lowest layer | 
|---|
| 106 | libraries first and work upwards. | 
|---|
| 107 |  | 
|---|
| 108 | * Avoid circular uses-a relationships | 
|---|
| 109 | When possible, design classes with one-way uses-a relationships | 
|---|
| 110 | and avoid cycles.  This makes it easier to understand the code. | 
|---|
| 111 | However, sometimes it's not always practical so it is permitted. | 
|---|
| 112 |  | 
|---|
| 113 | * Included files in headers | 
|---|
| 114 | Headers should #include only the necessary headers.  In | 
|---|
| 115 | particular, if a class is referenced in a header file only as a | 
|---|
| 116 | pointer or a reference then use `class COtherClass;' instead of | 
|---|
| 117 | `#include "COtherClass.h".' | 
|---|
| 118 |  | 
|---|
| 119 | * #include syntax | 
|---|
| 120 | Non-synergy header files must be included using angle brackets | 
|---|
| 121 | while synergy header files must be included using double quotes. | 
|---|
| 122 | #include "CSynergyHeader.h" | 
|---|
| 123 | #include <systemheader.h> | 
|---|
| 124 | The file name in a #include must not be a relative path unless | 
|---|
| 125 | it's a system header file and it's customary to use a relative | 
|---|
| 126 | path, e.g. `#include <sys/types.h>'.  Use compiler options to | 
|---|
| 127 | add necessary directories to the include search path. | 
|---|
| 128 |  | 
|---|
| 129 | * Included file ordering | 
|---|
| 130 | Files should be included in the following order: | 
|---|
| 131 | * Header for source file | 
|---|
| 132 | The first include for CMyClass.cpp must be CMyClass.h. | 
|---|
| 133 | * Other headers in directory, sorted alphabetically | 
|---|
| 134 | * Headers for each library, sorted alphabetically per library | 
|---|
| 135 | Include headers from the library closest in the dependency graph | 
|---|
| 136 | first, then the next farthest, etc.  Sort alphabetically within | 
|---|
| 137 | each library. | 
|---|
| 138 | * System headers | 
|---|
| 139 |  | 
|---|
| 140 | - C++ | 
|---|
| 141 | * C++ features | 
|---|
| 142 | Synergy uses the following more recent C++ features: | 
|---|
| 143 | * bool | 
|---|
| 144 | * templates | 
|---|
| 145 | * exceptions | 
|---|
| 146 | * mutable | 
|---|
| 147 | * new scoping rules | 
|---|
| 148 | * the standard C++ library | 
|---|
| 149 |  | 
|---|
| 150 | Do not use the following C++ features: | 
|---|
| 151 | * dynamic_cast | 
|---|
| 152 | * run time type information | 
|---|
| 153 | * namespaces and using (use std:: where necessary) | 
|---|
| 154 |  | 
|---|
| 155 | The new scoping rules say that the scope of a variable declared | 
|---|
| 156 | in a for statement is limited to the for loop.  For example: | 
|---|
| 157 |  | 
|---|
| 158 | for (int i = 0; i < 10; ++i) { | 
|---|
| 159 | // i is in scope here | 
|---|
| 160 | } | 
|---|
| 161 | // i is not in scope here | 
|---|
| 162 |  | 
|---|
| 163 | for (int i = -10; i < 0; ++i) { | 
|---|
| 164 | // an entirely new i is in scope here | 
|---|
| 165 | } | 
|---|
| 166 | // i is not in scope here | 
|---|
| 167 |  | 
|---|
| 168 | This is used routinely in synergy, but only in for loops.  There | 
|---|
| 169 | is a macro for `for' in lib/base/common.h when building under | 
|---|
| 170 | Microsoft Visual C++ that works around the fact that that compiler | 
|---|
| 171 | doesn't follow the new scoping rules.  Use the macro if your | 
|---|
| 172 | compiler uses the old scoping rules. | 
|---|
| 173 |  | 
|---|
| 174 | * Standard C++ library | 
|---|
| 175 | The standard C++ library containers should always be used in favor | 
|---|
| 176 | of custom containers wherever reasonable.  std::string is used | 
|---|
| 177 | throughout synergy but only as the CString typedef;  always use | 
|---|
| 178 | CString, never std::string except in the arch library.  Synergy | 
|---|
| 179 | avoids using auto_ptr due to some portability problems.  Synergy | 
|---|
| 180 | makes limited use of standard algorithms and streams but they can | 
|---|
| 181 | be freely used in new code. | 
|---|
| 182 |  | 
|---|
| 183 | * Limited multiple inheritance | 
|---|
| 184 | Classes should inherit implementation from at most one superclass. | 
|---|
| 185 | Inheriting implementation from multiple classes can have unpleasant | 
|---|
| 186 | consequences in C++ due to it's limited capabilities.  Classes can | 
|---|
| 187 | inherit     from any number of interface classes.  An interface class | 
|---|
| 188 | provides only pure virtual methods.  Synergy breaks this rule in | 
|---|
| 189 | IInterface which implements the virtual destructor for convenience. | 
|---|
| 190 |  | 
|---|
| 191 | * No globals | 
|---|
| 192 | Avoid global variables.  All global variables must be static, making | 
|---|
| 193 | it visible only with its source file.  Most uses of global variables | 
|---|
| 194 | are better served by static data members of a class.  Global | 
|---|
| 195 | constants are permitted in some circumstances. | 
|---|
| 196 |  | 
|---|
| 197 | Also avoid global functions.  Use public static member functions in | 
|---|
| 198 | a class instead. | 
|---|
| 199 |  | 
|---|
| 200 | These rules are violated by the main source file for each program | 
|---|
| 201 | (except that the globals are still static).  They could easily be | 
|---|
| 202 | rewritten to put all the variables and functions into a class but | 
|---|
| 203 | there's little to be gained by that. | 
|---|
| 204 |  | 
|---|
| 205 | * Private data only | 
|---|
| 206 | If a class is plain-old-data (i.e. it has no methods) all of its | 
|---|
| 207 | data members should be public.  Otherwise all of its data members | 
|---|
| 208 | should be private, not public or protected.  This makes it much | 
|---|
| 209 | easier to track the use of a member when reading code.  Protected | 
|---|
| 210 | data is not allowed because `protected' is a synonym for `public | 
|---|
| 211 | to my subclasses' and public data is a Bad Thing.  While it might | 
|---|
| 212 | seem okay in this limited situation, the situation is not at all | 
|---|
| 213 | limited since an arbitrary number of classes can be derived, | 
|---|
| 214 | directly or indirectly, from the class and any of those classes | 
|---|
| 215 | have full access to the protected data. | 
|---|
| 216 |  | 
|---|
| 217 | * Plain old data | 
|---|
| 218 | A class that merely contains data and doesn't perform operations | 
|---|
| 219 | on that data (other than reads and writes) is plain old data (POD). | 
|---|
| 220 | POD should have only public data members and non-copy constructors. | 
|---|
| 221 | It must not have any methods other than constructors, not even a | 
|---|
| 222 | destructor or assignment operators, nor protected or private data. | 
|---|
| 223 | Note that this definition of POD is not the definition used in the | 
|---|
| 224 | C++ standard, which limits the contained data types to types that | 
|---|
| 225 | have no constructors, destructors, or methods. | 
|---|
| 226 |  | 
|---|
| 227 | * Avoid using friend | 
|---|
| 228 | Avoid declaring friend functions or classes.  They're sometimes | 
|---|
| 229 | necessary for operator overloading.  If you find it necessary to | 
|---|
| 230 | add friends to some class C, consider creating a utility class U. | 
|---|
| 231 | A utility class is declared as the only friend of C and provides | 
|---|
| 232 | only static methods.  Each method forwards to a private method on | 
|---|
| 233 | an object of C type (passed as a parameter to the U's method). | 
|---|
| 234 | This makes maintenance easier since only U has friend access to C | 
|---|
| 235 | and finding any call to U is trivial (they're prefixed by U::). | 
|---|
| 236 |  | 
|---|
| 237 | * Don't test for NULL when using `delete' or `delete[]' | 
|---|
| 238 | It's unnecessary since delete does it anyway. | 
|---|
| 239 |  | 
|---|
| 240 | - Makefiles | 
|---|
| 241 | Automake's makefiles (named Makefile.am) have a few requirements: | 
|---|
| 242 | * Define the following macro at the top of the file: | 
|---|
| 243 | NULL = | 
|---|
| 244 | * Lists should have one item per line and end in $(NULL).  For | 
|---|
| 245 | example: | 
|---|
| 246 | EXTRA_DIST =     \ | 
|---|
| 247 | kiwi.txt     \ | 
|---|
| 248 | mango.cpp    \ | 
|---|
| 249 | papaya.h     \ | 
|---|
| 250 | $(NULL) | 
|---|
| 251 | Indentation must use tabs in a makefile.  Line continuations | 
|---|
| 252 | (backslashes) should be aligned using tabs. | 
|---|
| 253 | * Lists of files should be sorted alphabetically in groups (e..g | 
|---|
| 254 | source files, header files, then other files).  Lists of | 
|---|
| 255 | subdirectories must be in the desired build order. | 
|---|
| 256 |  | 
|---|
| 257 | - Source Formatting | 
|---|
| 258 | Every project has its own formatting style and no style satisfies | 
|---|
| 259 | everyone.  New code should be consistent with existing code: | 
|---|
| 260 |  | 
|---|
| 261 | * All files should include the copyright and license notice | 
|---|
| 262 | * Use tabs to indent | 
|---|
| 263 | * Tabs are 4 columns | 
|---|
| 264 | * Lines should not extend past the 80th column | 
|---|
| 265 | * Open braces ({) go on same line as introducing statement | 
|---|
| 266 | `for (i = 0; i < 10; ++i) {' not | 
|---|
| 267 | for (i = 0; i < 10; ++i) | 
|---|
| 268 | { | 
|---|
| 269 | * Close braces line up with introducing statement | 
|---|
| 270 | * Open brace for function is on a line by itself in first column | 
|---|
| 271 | * Close brace for function lines up with open brace | 
|---|
| 272 | * Always use braces on: if, else, for, while, do, switch | 
|---|
| 273 | * `else {' goes on its own line | 
|---|
| 274 | * Always explicitly test pointers against NULL | 
|---|
| 275 | e.g. `if (ptr == NULL)' not `if (ptr)' | 
|---|
| 276 | * Always explicitly test integral values against 0 | 
|---|
| 277 | e.g. `if (i == 0)' not `if (i)' | 
|---|
| 278 | * Put spaces around binary operators and after statements | 
|---|
| 279 | e.g. `if (a == b) {' not `if(a==b){' | 
|---|
| 280 | * C'tor initializers are one per line, indented one tab stop | 
|---|
| 281 | * Other indentation should follow existing practice | 
|---|
| 282 | * Use Qt style comments for extraction by doxygen (i.e. //! and /*!) | 
|---|
| 283 | * Mark incomplete or buggy code with `FIXME' | 
|---|
| 284 |  | 
|---|
| 285 | - Other | 
|---|
| 286 | * calls to LOG() should always be all on one line (even past column 80) | 
|---|
| 287 |  | 
|---|
| 288 |  | 
|---|
| 289 | Class Relationships | 
|---|
| 290 | ------------------- | 
|---|
| 291 |  | 
|---|
| 292 | The doxygen documentation can help in understanding the relationships | 
|---|
| 293 | between objects.  Use `make doxygen' in the top level directory to | 
|---|
| 294 | create the doxygen documentation into doc/doxygen/html.  You must have | 
|---|
| 295 | doxygen installed, of course. | 
|---|
| 296 |  | 
|---|
| 297 | FIXME -- high level overview of class relationships | 
|---|
| 298 |  | 
|---|
| 299 |  | 
|---|
| 300 | Portability | 
|---|
| 301 | ----------- | 
|---|
| 302 |  | 
|---|
| 303 | Synergy is mostly platform independent code but necessarily has | 
|---|
| 304 | platform dependent parts.  The mundane platform dependent parts | 
|---|
| 305 | come from the usual suspects:  networking, multithreading, file | 
|---|
| 306 | system, high resolution clocks, system logging, etc.  Porting | 
|---|
| 307 | these parts is relatively straightforward. | 
|---|
| 308 |  | 
|---|
| 309 | Synergy also has more esoteric platform dependent code.  The | 
|---|
| 310 | functions for low-level event interception and insertion, | 
|---|
| 311 | warping the cursor position, character to keyboard event | 
|---|
| 312 | translation, clipboard manipulation, and screen saver control | 
|---|
| 313 | are often obscure and poorly documented.  Unfortunately, these | 
|---|
| 314 | are exactly the functions synergy requires to do its magic. | 
|---|
| 315 |  | 
|---|
| 316 | Porting synergy to a new platform requires the following steps: | 
|---|
| 317 |  | 
|---|
| 318 | - Setting up the build | 
|---|
| 319 | - Adjusting lib/common/common.h | 
|---|
| 320 | - Implementing lib/arch | 
|---|
| 321 | - Implementing lib/platform | 
|---|
| 322 | - Tweaks | 
|---|
| 323 |  | 
|---|
| 324 | Setting up the build: | 
|---|
| 325 |  | 
|---|
| 326 | The first phase is simply to create the files necessary to build the | 
|---|
| 327 | other files.  On Unix, synergy uses autoconf/automake which produces | 
|---|
| 328 | a `configure' script that generates makefiles.  On Windows, synergy | 
|---|
| 329 | uses Visual C++ workspace and project files.  If you're porting to | 
|---|
| 330 | another Unix variant, you may need to adjust `configure.in', | 
|---|
| 331 | `acinclude.m4', and Unix flavor dependent code in lib/arch.  Note | 
|---|
| 332 | especially the SYSAPI_* and WINAPI_* macro definitions in | 
|---|
| 333 | ARCH_CFLAGS.  Exactly one of each must be defined.  It should also | 
|---|
| 334 | add AM_CONDITIONALs if a new SYSAPI_* or WINAPI_* was added. | 
|---|
| 335 |  | 
|---|
| 336 | Adjusting lib/common/common.h: | 
|---|
| 337 |  | 
|---|
| 338 | The lib/common/common.h header file is included directly or indirectly | 
|---|
| 339 | by every other file.  Its primary job is to include config.h, which | 
|---|
| 340 | defines macros depending on what the 'configure' script discovered | 
|---|
| 341 | about the system.  If the platform does not use the 'configure' script | 
|---|
| 342 | it must define the appropriate SYSAPI_* and WINAPI_* macro.  It may | 
|---|
| 343 | also do other platform specific setup. | 
|---|
| 344 |  | 
|---|
| 345 | Adjusting lib/common/BasicTypes.h: | 
|---|
| 346 |  | 
|---|
| 347 | No changes should be necessary in BasicTypes.h.  However, if the | 
|---|
| 348 | platform's system header files define SInt8, et al. you may need | 
|---|
| 349 | to adjust the typedefs to match the system's definitions. | 
|---|
| 350 |  | 
|---|
| 351 | Implementing lib/arch: | 
|---|
| 352 |  | 
|---|
| 353 | Much platform dependent code lives in lib/arch.  There are several | 
|---|
| 354 | interface classes there and they must all be implemented for each | 
|---|
| 355 | platform.  See the interface header files for more information. | 
|---|
| 356 |  | 
|---|
| 357 | Platforms requiring special functions should create a class named | 
|---|
| 358 | CArchMiscXXX where XXX is the platform name.  The class should have | 
|---|
| 359 | only static methods.  Clients can include the appropriate header | 
|---|
| 360 | file and make calls directly, surrounded by a suitable #ifdef/#endif. | 
|---|
| 361 |  | 
|---|
| 362 | If using automake, the Makefile.am should list the system specific | 
|---|
| 363 | files in a XXX_SOURCE_FILES macro where XXX matches the appropriate | 
|---|
| 364 | AM_CONDITIONAL symbol.  XXX_SOURCE_FILES must be added to EXTRA_DIST | 
|---|
| 365 | and the following added above the INCLUDES macro: | 
|---|
| 366 |  | 
|---|
| 367 | if XXX | 
|---|
| 368 | libarch_a_SOURCES =                     \ | 
|---|
| 369 | $(COMMON_SOURCE_FILES)  \ | 
|---|
| 370 | $(XXX_SOURCE_FILES)             \ | 
|---|
| 371 | $(NULL) | 
|---|
| 372 | endif | 
|---|
| 373 |  | 
|---|
| 374 | Implementing lib/platform: | 
|---|
| 375 |  | 
|---|
| 376 | Most of the remaining platform dependent code lives in lib/platform. | 
|---|
| 377 | The code there implements platform dependent window, clipboard, keyboard | 
|---|
| 378 | and screen saver handling.  If a platform is named XXX then the following | 
|---|
| 379 | classes should be derived and implemented: | 
|---|
| 380 |  | 
|---|
| 381 | * CXXXClipboard : IClipboard | 
|---|
| 382 | Provides clipboard operations.  Typically, this class will | 
|---|
| 383 | have helper classes for converting between various clipboard | 
|---|
| 384 | data formats. | 
|---|
| 385 |  | 
|---|
| 386 | * CXXXEventQueueBuffer : IEventQueueBuffer | 
|---|
| 387 | Provides operations for waiting for, posting and retrieving events. | 
|---|
| 388 | Also provides operations for creating and deleting timers. | 
|---|
| 389 |  | 
|---|
| 390 | * CXXXKeyState : CKeyState | 
|---|
| 391 | Provides operations for synthesizing key events and for mapping a | 
|---|
| 392 | key ID to a sequence of events to generate that key. | 
|---|
| 393 |  | 
|---|
| 394 | * CXXXScreen : IScreen, IPrimaryScreen, ISecondaryScreen, IPlatformScreen | 
|---|
| 395 | Provides screen operations. | 
|---|
| 396 |  | 
|---|
| 397 | * CXXXScreenSaver : IScreenSaver | 
|---|
| 398 | Provides screen saver operations. | 
|---|
| 399 |  | 
|---|
| 400 | If using automake, the Makefile.am should list the window system | 
|---|
| 401 | specific files in a XXX_SOURCE_FILES macro where XXX matches the | 
|---|
| 402 | appropriate AM_CONDITIONAL symbol.  XXX_SOURCE_FILES must be added | 
|---|
| 403 | to EXTRA_DIST and the following added above the INCLUDES macro: | 
|---|
| 404 |  | 
|---|
| 405 | if XXX | 
|---|
| 406 | libplatform_a_SOURCES = $(XXX_SOURCE_FILES) | 
|---|
| 407 | endif | 
|---|
| 408 |  | 
|---|
| 409 | Tweaks: | 
|---|
| 410 |  | 
|---|
| 411 | Finally, each platform typically requires various adjustments here | 
|---|
| 412 | and there.  In particular, synergyc.cpp and synergys.cpp usually | 
|---|
| 413 | require platform dependent code for the main entry point, parsing | 
|---|
| 414 | arguments, and reporting errors.  Also, some platforms may benefit | 
|---|
| 415 | from a graphical user interface front end.  These are generally | 
|---|
| 416 | not portable and synergy doesn't provide any infrastructure for | 
|---|
| 417 | the code common to any platform, though it may do so someday. | 
|---|
| 418 | There is, however, an implementation of a GUI front end for Windows | 
|---|
| 419 | that serves as an example. | 
|---|