diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a7ebc10 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +Synergy Authors +=============== + +Chris Schoeneman diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..7cbc437 --- /dev/null +++ b/BUGS @@ -0,0 +1,12 @@ +Known Bugs in Synergy +===================== + +View known bugs at: +http://sourceforge.net/tracker/?group_id=59275&atid=490467 + +Report bugs at: +http://sourceforge.net/tracker/?group_id=59275&atid=490467 + +When reporting bugs, please include the version of the operating +system you're using (on the server and relevant clients) and what +locales you use. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..895e589 --- /dev/null +++ b/COPYING @@ -0,0 +1,283 @@ +Synergy is copyright (C) 2002 Chris Schoeneman. +Synergy is distributed under the following license. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..38bd186 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7045 @@ +2003/08/24 14:52:02 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.14. + +---------- +2003/08/23 11:41:03 crs +lib/platform/CXWindowsClipboard.cpp + +Removed hack to work around apparent lesstif bug. I'm not seeing +the bug manifest itself and the hack breaks pasting into lesstif +programs that don't have the bug. Added DEBUG2 level logging of +the requestor window's properties in case some users run into +problems. + +---------- +2003/08/06 21:00:57 crs +lib/platform/CMSWindowsScreen.cpp + +Added more debugging log messages to clipboard handling. + +---------- +2003/08/06 21:00:17 crs +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h + +Fixed failure to treat log copied to clipboard as subject to +transfer to clients/server. This prevented copying the log +to the clipboard then pasting it on another screen. + +---------- +2003/08/06 20:59:01 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h + +Fixed failure to get non-default settings from advanced options +dialog in launcher when the dialog hadn't been opened. + +---------- +2003/08/06 20:57:41 crs +lib/arch/CArchLogWindows.cpp + +Fixed memory leak in win32 system log open() call. + +---------- +2003/08/06 20:56:52 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.13. + +---------- +2003/08/03 21:23:29 crs +lib/arch/CArchMultithreadWindows.cpp + +Fixed bugs in setting thread priority. + +---------- +2003/07/19 22:07:13 crs +configure.in +Version.h + +Changed version to 1.0.12. + +---------- +2003/07/19 17:22:06 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/synergyc.cpp +configure.in +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/common/Version.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Merge synergy 1.1 fixes into 1.0 branch. + +---------- +2003/07/19 17:19:26 crs + +Branched synergy 1.0 from 1.0.11. + +---------- +2003/07/17 21:16:58 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Fixed handling of a dead key followed by space on win32 and X11. +A dead key followed by space should convert the dead key to a +regular character. + +---------- +2003/07/16 22:38:43 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Fixed handling of some non-ASCII but directly mapped characters +on win32. The o, a, and u with diaeresis in the german keyboard +mapping are examples. + +---------- +2003/07/16 21:40:57 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Fixed handling of shift/ctrl/alt on special keys on win32 server. + +---------- +2003/07/13 20:42:11 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Fixed handling of some keystrokes on win32. Pressing a dead key +and then space should convert the dead key to a non-dead key but +previous the key was discarded. Fixed that but VkKeyScan() fails +in this case so added special case to fix that (assuming AltGr is +required). VkKeyScan() can return the wrong result for characters +that have more than one virtual key mapped to them. AltGr+9 (^) +on the French layout has this problem. Now detecting that problem +and using the current keyboard state to decide if AltGr is +required. + +---------- +2003/07/13 17:03:41 crs +cmd/synergyc/synergyc.cpp + +Forgot to remove --camp and --no-camp from brief usage message. + +---------- +2003/07/13 16:57:08 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Changed XSync() to XFlush() in X windows secondary screen. This +doesn't appear to have any negative consequences and may prevent +synergy from freezing when some X client (probably the window +manager) grabs the server. + +---------- +2003/07/12 17:57:31 crs +cmd/synergyc/synergyc.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CXWindowsScreen.cpp + +Prevent INFO level log messages when client is repeatedly trying +to connect. This prevents a log from filling up while the client +can't connect for no useful reason. Also removed --camp option +and cleaned up handling of client connection. Users must now use +--restart instead of --camp. + +---------- +2003/07/08 18:40:46 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h + +Changed windows server to release ctrl and alt keys when it's +sending a key that requires AltGr. That's because AltGr *is* +ctrl and alt but AltGr should be seen on clients as mode +switch without the ctrl and alt. I can't think of a better +way to do this other than to not send modifier keystrokes to +the clients at all. + +---------- +2003/07/05 17:06:18 crs +configure.in +lib/common/Version.h + +Change version to 1.0.11. Skipping version 1.0.10 because there +have been too many major changes since 1.0.8. A new experimental +release will provide a stable starting point for testing. + +---------- +2003/07/05 17:05:12 crs +lib/synergy/CSecondaryScreen.cpp + +Fix to avoid warping mouse until client successfully connects to +the server. + +---------- +2003/07/05 17:04:26 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h + +Keyboard fixes on win32. + +---------- +2003/07/05 17:04:06 crs +lib/server/CConfig.h + +Fix for new template syntax. + +---------- +2003/07/05 14:49:08 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp + +Minor X11 keyboard code cleanup. Also now handling KeyPress with +keycode == 0 generated by XFilterEvent() by using the keycode from +the previous KeyPress. + +---------- +2003/07/05 14:47:41 crs +lib/platform/CXWindowsScreen.cpp + +Compress sequential MappingNotify events into one. + +---------- +2003/07/01 19:35:28 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Rewrote key handling on X11 client. This should fix problems +with applying the incorrect shift and mode switch modifiers to +some keycodes, such as getting Pointer_EnableKeys when pressing +shift with NumLock enabled. + +---------- +2003/06/22 21:27:38 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h + +Added support for input methods. Only handling IMs that don't +need a precompose area or status area. This includes IMs that +do simple dead key composition. This only changes the server. +The client still does not decompose a character it cannot +generate directly into the keysyms to compose the character. + +---------- +2003/06/22 16:39:25 crs +lib/client/CServerProxy.cpp + +More fixes for X11 client keyboard handling. + +---------- +2003/06/22 16:39:02 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +More fixes for X11 client keyboard handling. + +---------- +2003/06/22 15:01:44 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Checkpoint for improving X11 client key handling. Should prevent +unintentional Pointer_EnableKeys (i.e. generating NumLock press +and release around a shift press). + +---------- +2003/06/08 22:20:01 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Another ctrl+alt+del checkpoint. + +---------- +2003/06/08 22:12:12 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +ctrl+alt+del emulation checkpoint. + +---------- +2003/06/08 16:31:52 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +More DEBUG2 level debugging of keyboard handling. + +---------- +2003/06/08 15:42:05 crs +lib/common/Makefile.am + +Added new file to Makefile. + +---------- +2003/06/02 20:07:16 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Fixed ctrl and alt keys on win32 clients. Was broken by a recent +fix to character handling. + +---------- +2003/06/02 20:06:20 crs +lib/client/CServerProxy.cpp + +Fixed errors in log strings. + +---------- +2003/06/02 20:06:03 crs +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.h +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/common/common.dsp +lib/common/stddeque.h + +Added menu item on win32 tray icon to copy the last 1000 lines from +the log to the clipboard. + +---------- +2003/05/26 09:50:35 crs +lib/platform/CXWindowsClipboard.cpp + +Added workaround for broken clipboard owners that report the +type of TARGETS as TARGETS instead of ATOM. + +---------- +2003/05/26 09:49:38 crs +lib/platform/CMSWindowsClipboard.cpp + +No longer installing clibboard format for plain text on windows nt +family because nt automatically converts to and from the unicode +format. This may fix text encoding errors when synergy puts +non-ascii text on the clipboard and other clients prefer CF_TEXT +to CF_UNICODE (which they should not because synergy lists +CF_UNICODE first). + +---------- +2003/05/26 09:46:52 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsScreen.cpp + +Fixed loss of ctrl+alt+del key releases when the Winlogin desktop +is accessible (was already fixed when inaccessible). This change +also ignores press and release of virtual key 0, which should never +happen but does according to one user. + +---------- +2003/05/21 21:22:14 crs +lib/arch/CArchMultithreadWindows.cpp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CSecondaryScreen.cpp + +Fixed unsigned compare against zero. Changed win32 priority to +maximum. + +---------- +2003/05/21 19:38:11 crs +lib/server/CServer.cpp +lib/server/CServer.h + +Made double tap require moving farther away from the tapped edge +before arming. This should reduce spurious double taps. + +---------- +2003/05/20 19:15:58 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h + +Attempt to improve key event synthesis. This change adds support +for dead keys and attempts to choose the correct code page for the +thread that will (probably) receive synthesized events. + +---------- +2003/05/20 19:14:40 crs +lib/platform/CSynergyHook.cpp + +Minor thread ID compare fix. + +---------- +2003/05/20 19:14:24 crs +lib/arch/CArchMultithreadWindows.cpp + +Reduced maximum priority in debug build. + +---------- +2003/05/17 20:58:27 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/IMSWindowsScreenEventHandler.h +lib/synergy/CPrimaryScreen.cpp + +Fixed getting locked to screen after ctrl+alt+del. Also fixed +cursor not being hidden on win32 server when on client screens +(which happened when using low-level hooks). + +---------- +2003/05/17 14:10:11 crs +INSTALL + +Added documentation for xtestIsXineramaUnaware option. + +---------- +2003/05/17 14:03:32 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Fixed previous fix. Was trying to avoid using XWarpPointer() when +warping on screen 0. That just doesn't work if screen 0 is not at +0,0. So now always use XWarpPointer() if there are multiple +xinerama screens and the appropriate option is enabled. + +---------- +2003/05/17 13:44:24 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Added workaround for when XTest is unaware of Xinerama. When that's +true, faking a mouse motion outside screen 0 is clamped onto screen 0. +When the workaround is enabled, we use XWarpPointer() instead of an +XTest fake motion. This isn't perfect but the only real fix requires +patching XTest. + +---------- +2003/05/17 12:48:32 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Added support for old versions of XF86keysym.h that are missing +some expected #defines. + +---------- +2003/05/10 17:27:05 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.8. + +---------- +2003/05/10 17:26:42 crs +INSTALL +README + +Updated documentation. + +---------- +2003/05/08 21:59:35 crs +lib/server/CServer.cpp + +Fixed jumping to same client screen. It was broken by an earlier +change (probably double tap). Jumping to the same server screen +worked correctly. + +---------- +2003/05/04 21:40:42 crs +configure.in +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/synergy/KeyTypes.h +lib/synergy/MouseTypes.h + +Added support for 4th and 5th (non-mouse-wheel) buttons and +"Internet" keyboard keys. + +---------- +2003/05/03 15:16:30 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added screen saver synchronization option to win32 launcher dialog. + +---------- +2003/05/03 14:54:03 crs +lib/synergy/CSecondaryScreen.cpp + +Removed accidental debugging code. + +---------- +2003/05/03 14:38:36 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/server/CConfig.cpp +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h +lib/synergy/OptionTypes.h + +Added global configuration option to disable screen saver +synchronization. + +---------- +2003/05/03 13:57:52 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Forgot to restore global auto-repeat configuration on exit. + +---------- +2003/05/03 13:50:06 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h + +Now warping mouse to center of screen when leaving client screens. +Some users requested this. Also, the hider window is mapped before +warping the mouse so the active window shouldn't change if the focus +policy is point-to-focus. Showing the window first can also reduce +the likelihood of seeing the cursor briefly in its hidden position. + +---------- +2003/05/03 13:28:21 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Now turning off auto-repeat when on an X11 client. This prevents +the server from auto-repeating fake events, which is undesired +since synergy will do the auto-repeating itself. This also +disables auto-repeat on any keys locally configured on X11 to not +auto-repeat. That's mainly to suppress auto-repeat on modifier +keys, which auto-repeat on win32 but not X11. + +---------- +2003/05/03 12:54:22 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h + +Fixed a few win32 keyboard/mouse problems. First, the mouse hook +now captures non-client area mouse messages. Previously, these +were ignored (because i forgot about them) and they caused all +kinds of problems because they weren't forwarded. For example, +clicking on a window border would cause the window to start +resizing when the mouse came back to the server screen. Moving +inside a title bar meant that the mouse wouldn't move on the +client screen. + +Second, because non-client messages are now handled, the full +screen transparent window is no longer necessary to capture +input so it's never displayed. (The window is still necessary +for clipboard ownership so it's still created.) No transparent +window means no screen flashing. It also means we don't have to +become the foreground and active window. This plays better with +apps that minimize or restore when they're no longer the +foreground application/active window. + +Third, fixed the low level keyboard hook to forward toggle key +updates, which it was neglecting to do. + +Finally, keyboard and mouse input is always forwarded from the hook +to the primary screen handler which then shadows the current key +and mouse button state. If we're using low level hooks then this +isn't really necessary and GetKeyState() always returns the right +info but without low level hooks it means we can just use the +shadow state. It also means we don't have to show our window in +order to get the system's key state table up to date, fixing the +screen flash when checking for the scroll lock state. + +---------- +2003/05/03 12:37:03 crs +lib/arch/CArchMultithreadWindows.cpp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CSecondaryScreen.cpp + +Boosted priority of main synergy threads to be very high (highest +realtime priority). After some testing it appears that anything +less than this can starve synergy in some circumstances, preventing +it from forwarding messages to clients. This is a rather risky +change since synergy can now virtually take over a system if it +behaves badly. This change only affects windows systems since +lib/arch of other platforms don't yet attempt to boost priority. + +---------- +2003/04/27 18:05:32 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/server/Makefile.am +lib/server/server.dsp + +Fixes to previous checkpoint. Non-ascii keys seem to work correctly. +Still not supporting key composition on X11. + +---------- +2003/04/27 17:01:14 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_1.cpp +lib/server/CClientProxy1_1.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CSecondaryScreen.h +lib/synergy/IClient.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/KeyTypes.h +lib/synergy/ProtocolTypes.h + +Checkpointing improved key handling. This change adds non-ASCII +key handling to win32 on both client and server. It also changes +the protocol and adds code to ensure every key pressed also gets +released and that that doesn't get confused when the KeyID for +the press is different from the KeyID of the release (or repeat). + +---------- +2003/04/24 20:11:38 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h + +Added KeySym <-> Unicode mappings. Changed code to use those +mappings to better support Unicode key events. + +---------- +2003/04/24 20:10:13 crs +cmd/Makefile.am + +Added exec.dsp to EXTRA_DIST. + +---------- +2003/04/16 20:59:25 crs +all.dsp +cmd/exec.dsp +lib/platform/makehook.dsp +lib/platform/synrgyhk.dsp +synergy.dsw + +Win32 project configuration fixes. + +---------- +2003/04/16 20:59:14 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp + +Minor win32 fixes. + +---------- +2003/04/16 20:05:00 crs +lib/server/CConfig.cpp + +Now allowing screen names with underscores. + +---------- +2003/04/14 22:16:21 crs +lib/platform/CXWindowsPrimaryScreen.cpp + +Fixed incorrect initialization of an XMotionEvent. + +---------- +2003/04/14 22:15:56 crs +configure.in +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added workaround for apparent Xinerama bug when warping the pointer. +This should allow synergy to be used on a system using Xinerama to +create a single logical screen from multiple physical screens. + +---------- +2003/04/13 18:14:01 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed problem with type casting void* to int. + +---------- +2003/04/13 17:13:27 crs +lib/platform/CXWindowsScreenSaver.cpp + +Removed periodic call to XForceScreenSaver() to prevent the built-in +screen saver from activating. It was unnecessary since the built-in +screen saver is disabled as appropriate; this call was just to +ensure that the screen saver wouldn't start if an external program +reactivated the screen saver after synergy disabled it. + +It's possible that this was causing screen flicker under gnome, though +i don't know why. It's also possible that periodically sending events +to xscreensaver is causing the flicker but removing that code is more +difficult because xscreensaver can't be disabled, only deactivated or +killed. + +---------- +2003/04/13 14:59:53 crs +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CSynergyHook.cpp + +Fixed several win32 bugs. First, synergy wasn't forwarding mouse +events to other hook functions, which broke some tools like objectbar. +Second, windows key processing was fixed. Previously pressing and +release the key would only send a press event, locking the user onto +the client window; also, the win32 server treated as a Meta modifier +instead of a Super modifier, which broke any use of it as any kind of +modifier key. Third, added hacks to support several key combinations +on windows 95/98/me that are treated specially by windows, including +Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any +combination using the windows key like Win+E and Win+F but not +Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which +only happened when using a synergy server on windows) has been fixed; +unfortunately the solution causes a lot of screen redraws for some +reason. Finally, there's been a fix to clipboard handling that may +or may not fix a problem where the clipboard would stop transferring +between systems after a little while. I can't be sure if it fixes +the problem because I can't reproduce the problem. + +---------- +2003/04/13 14:39:17 crs +cmd/launcher/launcher.rc + +Added mention of tray icon to launcher start message box. + +---------- +2003/03/26 21:03:58 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.6. + +---------- +2003/03/25 21:31:39 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CSynergyHook.cpp + +This should fix multimon support on win32. + +---------- +2003/03/22 11:49:23 crs +FAQ +INSTALL +PORTING +README +TODO + +Documentation updates. + +---------- +2003/03/22 11:49:13 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/CGlobalOptions.h +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added key modifier and heartbeat options to GUI. + +---------- +2003/03/21 19:34:08 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Oops, included a windows only header in non-windows builds. + +---------- +2003/03/21 19:16:37 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Added check for the screen saver actually being active before +entering the loop waiting for it to deactivate. The failure +to check was causing the screen saver code to kick in when +the screen saver timeout occurred, even if the screen saver +wasn't enabled (because Windows still sends the screen saver +activating message for no good reason when the screen saver +is disabled). + +---------- +2003/03/21 19:14:32 crs +lib/arch/CArchMiscWindows.cpp + +Fixed errors in merge causing infinite loops. + +---------- +2003/03/21 19:14:10 crs +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico + +Fixed icons. + +---------- +2003/03/21 19:13:15 crs +lib/platform/CXWindowsUtil.cpp + +Fixed getWindowProperty(). It wasn't catching all failure +cases correctly. + +---------- +2003/03/17 22:32:10 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h + +Added options and advanced options dialogs which should've been +part of an earlier checkin. Also now saving and restoring +options that aren't in the configuration file to/from the +registry. + +---------- +2003/03/17 22:32:01 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.cpp +lib/server/CServer.cpp +lib/synergy/CPrimaryScreen.h + +Added a log message why the user is locked to the screen. + +---------- +2003/03/17 22:31:59 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Added type casts to avoid warning. + +---------- +2003/03/16 17:40:57 crs +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h + +Fixed detection of screen saver shutdown on windows nt. + +---------- +2003/03/16 17:40:56 crs +lib/common/Makefile.am +lib/common/common.dsp +lib/common/stdbitset.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Made releaseKeys() only synthesize key releases for those keys +that synergy synthesized a press for, not keys that the user +is physically pressing. + +---------- +2003/03/16 17:40:47 crs +lib/platform/CSynergyHook.cpp + +Minor hook fixes. + +---------- +2003/03/16 17:40:25 crs +cmd/synergyc/synergyc.rc +cmd/synergys/synergys.rc + +Added resources missing from previous checkin. + +---------- +2003/03/13 20:24:45 crs +lib/platform/CXWindowsScreenSaver.cpp + +Moved comment to more relevant location. + +---------- +2003/03/13 19:20:55 crs +lib/platform/CXWindowsScreen.cpp + +Fixed double locking of mutex. + +---------- +2003/03/12 22:34:07 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/CGlobalOptions.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/Makefile.am +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/resource.h +cmd/synergyc/CClientTaskBarReceiver.cpp +cmd/synergyc/CClientTaskBarReceiver.h +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CXWindowsClientTaskBarReceiver.h +cmd/synergyc/Makefile.am +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergyc/tb_error.ico +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.h +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/CServerTaskBarReceiver.h +cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +cmd/synergys/CXWindowsServerTaskBarReceiver.h +cmd/synergys/Makefile.am +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +cmd/synergys/tb_error.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchImpl.cpp +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchTaskBarWindows.cpp +lib/arch/CArchTaskBarWindows.h +lib/arch/CArchTaskBarXWindows.cpp +lib/arch/CArchTaskBarXWindows.h +lib/arch/IArchMultithread.h +lib/arch/IArchTaskBar.h +lib/arch/IArchTaskBarReceiver.h +lib/arch/Makefile.am +lib/arch/arch.dsp +lib/base/CJobList.cpp +lib/base/CJobList.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/base/Makefile.am +lib/base/base.dsp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CTimerThread.cpp +lib/mt/CTimerThread.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h + +Added switch delay and double-tap options to win32 and added a +tray icon to the client and server that gives status feedback to +the user and allows the user to kill the app. + +---------- +2003/02/23 19:29:08 crs +lib/server/CConfig.cpp +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for a user option to require hitting the edge of a +screen twice within a specified amount of time in order to switch +screens. This can help prevent unintended switching. + +---------- +2003/02/22 21:53:25 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CPrimaryScreen.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/IScreenEventHandler.h +lib/synergy/OptionTypes.h + +Added support on X11 for a global option to delay switching screens +when the mouse reaches a jump zone. + +---------- +2003/02/22 16:41:03 crs +lib/server/CConfig.cpp +lib/server/CConfig.h + +Added global options to CConfig (needed for heartbeat option). + +---------- +2003/02/22 16:20:23 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CServer.cpp +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for heartbeat global option. + +---------- +2003/02/22 15:04:09 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.5. + +---------- +2003/02/22 15:03:31 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/synergy/KeyTypes.h +lib/synergy/OptionTypes.h + +Changes to support remapping modifier keys on clients. + +---------- +2003/02/17 12:44:37 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.3. + +---------- +2003/02/16 19:55:54 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Changed win32 client side cursor warping to be all relative motion +when not on the primary monitor. This should eliminate the flicker +between virtual display 0,0 and the correct position. While this +allows the user to confuse synergy by using the client's mouse, +synergy recovers quickly and easily from any confusion. + +---------- +2003/02/16 19:53:56 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Added hack to heuristically detect bogus mouse motion caused by +a race condition where the synergy server updates the mouse +position but the synergy hook later receives a mouse update from +before the position change (i.e. out of order). + +---------- +2003/02/16 19:51:46 crs +lib/platform/CSynergyHook.cpp + +Commented out an unnecessary hook and added a compile time +switch to disable grabbing of keyboard on win32 to facilitate +debugging. + +---------- +2003/02/16 19:50:36 crs +lib/platform/CMSWindowsScreen.cpp + +Changed heap to stack allocation in an oft-called function for +data that's never used outside the function. + +---------- +2003/02/16 19:49:44 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchMultithreadWindows.cpp +lib/base/CUnicode.cpp + +Fixed memory leaks. + +---------- +2003/02/12 20:59:25 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Fixed incorrect mouse button swapping on client screens. + +---------- +2003/02/12 20:59:08 crs +lib/arch/CArchDaemonWindows.cpp + +Fixed error in debug build on win32. + +---------- +2003/02/12 19:50:22 crs +lib/arch/vsnprintf.cpp + +Added a simple implementation of vsnprintf for unix platforms +without it using /dev/null, vfprintf(), and vsprintf(). + +---------- +2003/02/12 19:38:39 crs +lib/arch/CArchMiscWindows.h +lib/arch/XArch.h +lib/base/CLog.h +lib/base/CString.h +lib/base/ILogOutputter.h +lib/mt/CCondVar.h +lib/mt/CLock.h +lib/mt/CThread.h +lib/mt/CTimerThread.h + +Made sure every file includes common.h directly or indirectly. +Also made sure common.h is included before any system headers. + +---------- +2003/02/01 18:10:43 crs +FAQ +INSTALL +README +TODO + +Added info about using SSH for authentication and encryption. + +---------- +2003/01/29 22:16:40 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +To support keymaps with only upper (or lower) case keysyms we now +use Xlib to convert an unmatched keysym to upper and lower case and +use whichever, if any, is not the same as the original keysym. +This supports case conversion in any language that Xlib supports +it in. + +---------- +2003/01/29 19:32:25 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Applied patch from grmcdorman at users dot sourceforge dot net to +support keymaps that have only uppercase letters, which is the case +by default on the Sun X server (for US keyboards anyway). + +---------- +2003/01/25 13:39:26 crs +NEWS +configure.in +lib/common/Version.h + +Changed version number to 1.0.2. + +---------- +2003/01/25 13:34:51 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/server/CConfig.cpp +lib/server/CConfig.h + +Added ability to set screen options from the windows launch dialog. + +---------- +2003/01/25 13:34:17 crs +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkWinsock.cpp + +Added missing entry in a socket family table. This was a serious +bug and should've failed on all platforms but just happened to +work on linux and windows. + +---------- +2003/01/22 08:37:32 crs +NEWS +configure.in +lib/common/Version.h + +Changed version number to 1.0.1. + +---------- +2003/01/22 08:36:43 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h + +Fixed running as a service on Windows NT family. + +---------- +2003/01/18 14:36:19 crs +lib/arch/CArchSleepWindows.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CSynergyHook.cpp + +Fixed stupid errors introduced by last attempt to fix broken +mouse behavior on multimonitor windows systems. Those errors +broke synergy on all windows systems running as a server. +Also added an attempt to reduce the occasional jump that can +occur when switching screens when windows is the server. + +---------- +2003/01/18 14:31:54 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Was forcing modifier keys that have no effect on the keysym +lookup to be up when synthesizing key events. Now leaving +those modifiers in their current state. + +---------- +2003/01/18 10:49:13 crs +Makefile.am + +Added a dist-pkg target to put the binary distribution files into +a tar gzip file. This is to ease distribution of the binaries on +systems without a packaging system supported by synergy (which +currently supports only RPM). + +---------- +2003/01/16 21:28:15 crs +lib/server/CServer.cpp + +Fixed lookup of neighbor screens. The first problem was an old +code in a conditional for moving left that blew an assert verifying +that the mouse position was really on the screen if the neighbor +screen wasn't connected. + +After that was fixed there was another problem when one screen +linked to another which then linked (in the same direction) to +itself. If the latter screen wasn't connected then it'd get into +an infinite loop. + +---------- +2003/01/14 19:46:41 crs +lib/server/CServer.cpp + +Moved log message into conditionals so it only appears when the +conditions are true. + +---------- +2003/01/14 19:46:17 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CSynergyHook.cpp + +Another try at fixing broken mouse behavior when a windows system +has multiple monitors with 0,0 of the virtual desktop not at the +upper-left. + +---------- +2003/01/12 16:35:54 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added test of using the client's own name as the server name +with an appropriate error message. + +---------- +2003/01/12 16:08:45 crs +lib/server/CServer.cpp + +Now catching and ignoring errors when writing to a socket in those +cases where errors were not being caught, typically when responding +to some other socket or protocol error. + +---------- +2003/01/11 21:06:21 crs +acinclude.m4 +configure.in +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchSleepUnix.cpp +lib/arch/CMultibyteEmu.cpp +lib/common/Makefile.am + +Fixes to support FreeBSD and Darwin. + +---------- +2003/01/11 15:16:41 crs +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Synergy no longer tries to suppress the screen saver once it starts. +It was doing that already if started through synergy but not if +started by something outside of synergy. In particular, if you +use `xscreensaver-command --activate' synergy used to send fake +mouse motion events every 5 seconds to deactivate it. That's +unlikely to be what the user wanted, especially if the locking is +enabled since it would force the password dialog to appear. + +As before, it's recommended that client screens not use locking +because xscreensaver will not deactivate without getting a +password even if we make the request through a programmatic +interface. Presumably that's for security reasons but it makes +life harder for synergy. + +---------- +2003/01/11 14:01:44 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp + +Attempt to fix problems with multimon windows. The mouse position +reported by the synergy hook dll is in a space with 0,0 in the +upper-left which is not necessarily the same as the virtual desktop +space. So the windows primary screen now accounts for that. On +the secondary screen, mouse_event() doesn't seem to accept negative +coordinates even on the windows NT family, making monitors with +negative coordinates inaccessible via absolute moves. So if the +move will be to negative coordinates, use the windows 95 family +fallback of absolute moving to 0,0 then relative moving to the +final position. + +---------- +2003/01/08 22:17:44 crs +FAQ +INSTALL + +Added bit about configuring on Solaris, which requires some +options to find the X11 includes and libraries. + +---------- +2003/01/08 21:36:14 crs +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchNetworkBSD.cpp + +Portability fixes. Now builds on Linux 2.2 and 2.4 and solaris. +Also builds on i386, alpha, G3/G4, and sparc. + +---------- +2003/01/08 21:36:13 crs +lib/client/CClient.cpp +lib/platform/CXWindowsUtil.cpp + +Changed log level of two messages. Now won't spew about reading +window properties and will report connection failure at DEBUG +instead of DEBUG1. + +---------- +2003/01/08 21:36:10 crs +FAQ + +Added a FAQ entry for client being rejected. User probably didn't +start the server or told the client the wrong server host name. + +---------- +2003/01/07 21:47:27 crs +ChangeLog +configure.in +lib/common/Version.h + +Changed version number to 0.9.15. Added 0.9.15 log entries. + +---------- +2003/01/07 21:12:51 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Attempts to improve forcing synergy window to foreground. These +changes don't seem to improve the situation but don't seem to +hurt either. + +---------- +2003/01/07 21:11:54 crs +lib/platform/CSynergyHook.cpp + +Added low-level mouse hook to support mouse wheel on NT (>=SP3). +Thanks to karsten for the patch used as a starting point. + +---------- +2003/01/05 21:52:28 crs +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h + +Added missing files. + +---------- +2003/01/05 21:48:54 crs +PORTING +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +doc/doxygen.cfg.in +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.h +lib/arch/CArchDaemonNone.h +lib/arch/CArchDaemonUnix.h +lib/arch/CArchDaemonWindows.h +lib/arch/CArchFileUnix.h +lib/arch/CArchFileWindows.h +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.h +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/CArchSleepUnix.h +lib/arch/CArchSleepWindows.h +lib/arch/CArchStringUnix.cpp +lib/arch/CArchStringUnix.h +lib/arch/CArchStringWindows.cpp +lib/arch/CArchStringWindows.h +lib/arch/CArchTimeUnix.h +lib/arch/CArchTimeWindows.h +lib/arch/IArchConsole.h +lib/arch/IArchFile.h +lib/arch/IArchLog.h +lib/arch/IArchMultithread.h +lib/arch/IArchNetwork.h +lib/arch/IArchSleep.h +lib/arch/IArchString.h +lib/arch/IArchTime.h +lib/arch/Makefile.am +lib/arch/XArchImpl.h +lib/arch/arch.dsp +lib/base/CLog.cpp +lib/base/CUnicode.cpp +lib/base/XBase.cpp +lib/base/XBase.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/client/ISecondaryScreenFactory.h +lib/client/Makefile.am +lib/client/client.dsp +lib/http/XHTTP.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/server/IPrimaryScreenFactory.h +lib/server/Makefile.am +lib/server/server.dsp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CPrimaryScreen.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h +lib/synergy/IPrimaryScreenFactory.h +lib/synergy/ISecondaryScreenFactory.h +lib/synergy/Makefile.am +lib/synergy/libsynergy.dsp + +Moved CPrimaryScreen and CSecondaryScreen to the lib/synergy +and the platform specific implementations to lib/platform. +Added an lib/arch method to query the platform's native wide +character encoding and changed CUnicode to use it. All +platform dependent code is now in lib/arch, lib/platform, +and the programs under cmd. Also added more documentation. + +---------- +2003/01/04 22:01:32 crs +PORTING +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/synergyc/Makefile.am +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergys/Makefile.am +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +configure.in +lib/Makefile.am +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleUnix.cpp +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchDaemonNone.cpp +lib/arch/CArchDaemonNone.h +lib/arch/CArchDaemonUnix.cpp +lib/arch/CArchDaemonUnix.h +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchFileUnix.cpp +lib/arch/CArchFileUnix.h +lib/arch/CArchFileWindows.cpp +lib/arch/CArchFileWindows.h +lib/arch/CArchImpl.cpp +lib/arch/CArchLogUnix.cpp +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchLogWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/CArchSleepUnix.cpp +lib/arch/CArchSleepUnix.h +lib/arch/CArchSleepWindows.cpp +lib/arch/CArchSleepWindows.h +lib/arch/CArchStringUnix.cpp +lib/arch/CArchStringUnix.h +lib/arch/CArchStringWindows.cpp +lib/arch/CArchStringWindows.h +lib/arch/CArchTimeUnix.cpp +lib/arch/CArchTimeUnix.h +lib/arch/CArchTimeWindows.cpp +lib/arch/CArchTimeWindows.h +lib/arch/CMultibyte.cpp +lib/arch/CMultibyteEmu.cpp +lib/arch/CMultibyteOS.cpp +lib/arch/IArchConsole.h +lib/arch/IArchDaemon.h +lib/arch/IArchFile.h +lib/arch/IArchLog.h +lib/arch/IArchMultithread.h +lib/arch/IArchNetwork.h +lib/arch/IArchSleep.h +lib/arch/IArchString.h +lib/arch/IArchTime.h +lib/arch/Makefile.am +lib/arch/XArch.cpp +lib/arch/XArch.h +lib/arch/XArchImpl.h +lib/arch/XArchUnix.cpp +lib/arch/XArchUnix.h +lib/arch/XArchWindows.cpp +lib/arch/XArchWindows.h +lib/arch/arch.dsp +lib/arch/vsnprintf.cpp +lib/base/BasicTypes.h +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStopwatch.cpp +lib/base/CString.cpp +lib/base/CString.h +lib/base/CStringUtil.cpp +lib/base/CStringUtil.h +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/IInterface.h +lib/base/ILogOutputter.h +lib/base/Makefile.am +lib/base/Version.h +lib/base/XBase.cpp +lib/base/XBase.h +lib/base/base.dsp +lib/base/common.h +lib/base/stdfstream.h +lib/base/stdistream.h +lib/base/stdlist.h +lib/base/stdmap.h +lib/base/stdostream.h +lib/base/stdpost.h +lib/base/stdpre.h +lib/base/stdset.h +lib/base/stdsstream.h +lib/base/stdvector.h +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/Makefile.am +lib/client/client.dsp +lib/common/BasicTypes.h +lib/common/IInterface.h +lib/common/Makefile.am +lib/common/Version.h +lib/common/common.dsp +lib/common/common.h +lib/common/stdfstream.h +lib/common/stdistream.h +lib/common/stdlist.h +lib/common/stdmap.h +lib/common/stdostream.h +lib/common/stdpost.h +lib/common/stdpre.h +lib/common/stdset.h +lib/common/stdsstream.h +lib/common/stdstring.h +lib/common/stdvector.h +lib/http/CHTTPProtocol.h +lib/http/Makefile.am +lib/http/XHTTP.cpp +lib/http/http.dsp +lib/io/CUnicode.cpp +lib/io/CUnicode.h +lib/io/Makefile.am +lib/io/XIO.cpp +lib/io/XIO.h +lib/io/io.dsp +lib/mt/CCondVar.cpp +lib/mt/CCondVar.h +lib/mt/CMutex.cpp +lib/mt/CMutex.h +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CThreadRep.cpp +lib/mt/CThreadRep.h +lib/mt/CTimerThread.cpp +lib/mt/Makefile.am +lib/mt/XMT.cpp +lib/mt/XMT.h +lib/mt/XThread.h +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/Makefile.am +lib/net/XNetwork.cpp +lib/net/XNetwork.h +lib/net/XSocket.cpp +lib/net/XSocket.h +lib/net/net.dsp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CPlatform.cpp +lib/platform/CPlatform.h +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/CXWindowsClipboard.cpp +lib/platform/IPlatform.h +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CServer.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/Makefile.am +lib/server/server.dsp +lib/synergy/Makefile.am +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h +lib/synergy/XSynergy.cpp +lib/synergy/XSynergy.h +lib/synergy/libsynergy.dsp +synergy.dsw + +Refactored some platform dependent code into a new library, +lib/arch. This should make porting easier. Will probably +continue to refactor a little more, moving platform dependent +event handling stuff into lib/platform. + +---------- +2002/12/26 18:40:22 crs +FAQ + +More FAQs. + +---------- +2002/12/25 23:49:42 crs +BUGS +FAQ +INSTALL +README +TODO + +Documentation update. + +---------- +2002/12/25 22:56:09 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +Made synrgyhk.dll error messages less cryptic. + +---------- +2002/12/25 19:21:17 crs +NEWS +configure.in +lib/base/Version.h + +Changed version number to 0.9.14. Added NEWS item. + +---------- +2002/12/25 18:44:54 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreenSaver.cpp + +Improved handling of screen saver handling when windows 2k is +the client and the screen saver is password protected. It used +to immediately turn off the screen saver (unintentionally) in +that case. + +---------- +2002/12/25 10:35:59 crs +acinclude.m4 +config/config.guess +config/config.sub +configure.in +lib/base/common.h +lib/mt/CThreadRep.cpp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/platform/CUnixPlatform.cpp + +Changes to support building on solaris, irix, and darwin. Also +removed test for working fork (AC_FORK). + +---------- +2002/12/23 14:47:44 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h + +Added code to process set/reset options messages from server. + +---------- +2002/12/23 13:55:21 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/IClient.h +lib/synergy/Makefile.am +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for per-screen options in the configuration file +and sending those options to the appropriate client screens. +Currently, two options are supported: halfDuplexCapsLock and +halfDuplexNumLock mark the caps lock and num lock keys, +respectively, as being half-duplex. + +---------- +2002/12/22 14:51:41 crs +configure.in +doc/doxygen.cfg.in + +Doxygen config file now sets HAVE_DOT to YES only if dot is found +by configure. + +---------- +2002/12/15 22:39:59 crs +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +Now handling any number of pointer buttons. + +---------- +2002/12/15 22:17:18 crs +lib/server/CXWindowsPrimaryScreen.cpp + +Now ignoring half-duplex keys that are down when deciding if +the mouse is locked to the screen. We can't tell if a half- +duplex key is physically down and logically down just means +it's active so there's no point in letting it lock the mouse +to the screen. + +---------- +2002/12/15 22:14:49 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +Now restoring toggle key states on leaving a client screen to +their state when the screen was entered. Previously when +leaving a client screen the toggle keys kept their state so, +say, caps lock, would remain on. This was inconvenient if +you then used the client's keyboard directly. + +---------- +2002/12/15 20:00:52 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +Fixed loss of ctrl+alt when transmitted to non-windows platforms +from a windows server. Was converting ctrl+alt on windows to +mode switch on the server. No longer doing that; windows clients +will interpret ctrl+alt as AltGr and other clients will just see +ctrl+alt. Also made the right alt key mode switch on windows +servers in case the user wants to force a mode switch, but that +means the right alt key no longer acts as alt on clients. + +---------- +2002/12/15 19:58:41 crs +lib/platform/CMSWindowsScreen.cpp + +Fixed client not reconnecting when server dies bug. + +---------- +2002/12/15 19:57:28 crs +lib/client/CXWindowsSecondaryScreen.cpp + +Cleanup and changed some DEBUG1 messages to DEBUG2. + +---------- +2002/12/15 11:12:39 crs +doc/doxygen.cfg.in + +Enabled using dot and class diagrams. + +---------- +2002/11/05 19:23:05 crs +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Fixed bug in detecting screen saver activation. Was using || instead +of && in conditional. + +---------- +2002/11/03 18:09:28 crs +acinclude.m4 +configure.in +lib/io/CUnicode.h +lib/net/CNetwork.h +lib/platform/CUnixPlatform.cpp + +Merged fixes for building on MacOS X. It dies on one file with +an internal compiler error; building that file without +optimization works around the compiler bug. Sadly, synergy can +only interact with X windows, not native MacOS windows. + +---------- +2002/10/30 22:22:16 crs +acinclude.m4 + +Escaped quotes to satisfy older autoheader versions. + +---------- +2002/10/30 22:16:30 crs +lib/net/CNetwork.h +lib/net/CTCPSocket.cpp + +Fixed bugs in error handling in CTCPSocket; previously was not +handling read errors at all and error handling for writes was +never being used. Now the socket disconnects if a read or write +fails on the socket for any reason except EINTR. Also added + to includes in CNetwork.h because it's needed on +some platforms. + +---------- +2002/10/29 22:07:55 crs +all.dsp +lib/base/base.dsp +lib/client/client.dsp +lib/http/http.dsp +lib/io/CUnicode.cpp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/net.dsp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +Ported recent changes to win32 and fixed CRLF problems with project +files (most had CRCRCRLF). + +---------- +2002/10/28 22:49:21 crs +acinclude.m4 +configure.in +lib/mt/CThreadRep.cpp + +solaris configure and build fixes. without having solaris i +can only hope that these changes actually work. + +---------- +2002/10/22 22:35:13 crs +configure.in +lib/io/CUnicode.cpp + +Added workarounds for missing reentrant versions of wide char +to/from multi-byte conversion functions. + +---------- +2002/10/22 21:30:48 crs +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/Makefile.am +lib/io/CUnicode.cpp +lib/io/CUnicode.h +lib/io/Makefile.am +lib/platform/Makefile.am + +Moved CUnicode to lib/io. That's a reasonable place for it +that's after lib/mt. It needs to be after lib/mt in preparation +for supporting platforms without the reentrant wide char and +multi-byte functions. + +---------- +2002/10/20 22:39:54 crs +lib/client/CMSWindowsSecondaryScreen.cpp + +Fixed conditional to test for multimon to do nasty win32 mouse +positioning hack. Was doing hack if *not* a multiple monitor +system but should've been doing it if is *is* a multimon system. + +---------- +2002/10/20 22:36:24 crs +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp + +Replaced inet_addr() with inet_aton(), which is a better function +anyway but isn't implemented in winsock, removed use of INADDR_NONE +which some platforms don't define except on winsock which does +define it, and changed SOL_TCP to IPPROTO_TCP which should work on +more platforms. + +---------- +2002/10/17 21:37:41 crs +lib/platform/CXWindowsScreen.cpp + +Fixed CXWindowsScreen to force the event loop to wake up when +exitMainLoop() is called. + +---------- +2002/10/17 21:37:37 crs +lib/mt/CThreadRep.cpp + +Fixed CThreadRep to not raise a signal on the thread if it's +already dead. Otherwise the signal could propagate to the +parent thread (at least on linux threads) and cause havoc. + +---------- +2002/10/17 21:37:31 crs +lib/server/CServer.cpp +lib/server/CServer.h + +Changed server to fail with an error if in can't bind() the listen +socket for any reason other than it's in use. + +---------- +2002/10/17 20:56:28 crs +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp + +Changed non-reentrant network functions to be reentrant and +thread safe. + +---------- +2002/10/16 22:01:50 crs +acinclude.m4 +configure.in +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CTCPSocket.cpp +lib/platform/CXWindowsScreen.cpp + +Added support for using select() instead of poll(). + +---------- +2002/10/15 22:17:41 crs +lib/server/CConfig.cpp + +CConfig now accepts and discards \r at the end of a line. This +allows the unix server to read configuration files created on +microsoft windows platforms. + +---------- +2002/10/15 22:08:10 crs +cmd/synergys/synergys.cpp +lib/server/CConfig.cpp + +Fixed use of %s instead of %{1} in format() call. + +---------- +2002/10/15 22:01:41 crs +lib/client/CClient.cpp +lib/mt/CThreadRep.cpp +lib/mt/Makefile.am +lib/mt/XMT.cpp +lib/mt/XMT.h +lib/mt/XThread.h +lib/server/CServer.cpp + +Renamed XThreadUnavailable to XMTThreadUnavailable and derived it +from XBase so it can be caught normally. Changed client and server +to handle unavailable threads (in main loop, anyway). + +---------- +2002/10/15 21:35:12 crs +lib/mt/CThreadRep.cpp + +Workaround for pthread bug on RedHat 7.2 on multiprocessor +systems. + +---------- +2002/10/15 21:29:44 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/base/CLog.h +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CSecondaryScreen.cpp +lib/client/CServerProxy.cpp +lib/client/CXWindowsSecondaryScreen.cpp +lib/http/CHTTPProtocol.cpp +lib/mt/CMutex.cpp +lib/mt/CThread.cpp +lib/mt/CThreadRep.cpp +lib/mt/CTimerThread.cpp +lib/net/CNetwork.cpp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CWin32Platform.cpp +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsUtil.cpp +lib/server/CClientProxy1_0.cpp +lib/server/CHTTPServer.cpp +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryScreen.cpp +lib/server/CServer.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/synergy/CProtocolUtil.cpp + +Changed log() and logc() macros to LOG() and LOGC(), respectively. +This avoids a conflict with the standard math library log() +function. + +---------- +2002/09/14 21:31:35 crs +lib/base/XBase.cpp +lib/base/XBase.h + +removed std::exception from base class list of XBase. this +is a workaround for gcc 3.2 until everything necessary has +throw() specifiers. + +---------- +2002/09/14 20:56:50 crs +lib/server/CServer.cpp + +now logging bind failures as warnings. + +---------- +2002/09/14 20:56:28 crs +lib/base/XBase.cpp +lib/base/XBase.h +lib/io/XIO.cpp +lib/io/XIO.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPSocket.cpp +lib/net/XSocket.cpp +lib/net/XSocket.h + +added better network error message support. + +---------- +2002/09/14 12:07:02 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/synergy/KeyTypes.h + +Rewrote handling of key press on X11 client; it should be much +more robust now. Also added handling of Super modifier key and +changed windows keys to map to Super instead of Meta, which is +the default on my keyboard. + +---------- +2002/09/14 12:05:06 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added debug level combo box and version number to title bar of win32 +launcher. + +---------- +2002/09/14 12:03:43 crs +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc + +Fixed backend mode. Now reports log messages and, if any are +serious, shows a message box before exiting. + +---------- +2002/09/04 21:17:01 crs +NEWS +configure.in +lib/base/Version.h + +Changed version number to 0.9.11. Added NEWS item. + +---------- +2002/09/04 21:14:18 crs +lib/client/CMSWindowsSecondaryScreen.cpp + +now looking up SendEvent() using GetProcAddress() so win95 +systems can run the synergy client. + +---------- +2002/09/04 20:17:54 crs +lib/client/CXWindowsSecondaryScreen.cpp + +fixed bug that caused the wrong keycode to be used for most, +possibly all, keysyms. was reading past the end of an array +of keysyms. + +---------- +2002/09/02 17:36:25 crs +configure.in +lib/base/Version.h + +Changed version number to 0.9.10. + +---------- +2002/09/02 17:30:04 crs +BUGS +INSTALL +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/IPlatform.h +lib/server/CMSWindowsPrimaryScreen.cpp + +Fixed win32 config saving, keyboard mapping, and AltGr bugs. +Made extensive changes to the launcher to provide more control +over setting up auto-start and it now saves configuration to +the user's documents directory if auto-starting at login and +saves to the system directory if auto-starting at boot. +Replaced MapVirtualKey() with table lookup to work around that +function's lack of support for extended keyboard scan codes. +Added first cut at support for AltGr. + +---------- +2002/09/01 15:30:00 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/KeyTypes.h + +Added support for mode switch key to X11 screens. + +---------- +2002/09/01 10:31:10 crs +acinclude.m4 +configure.in +lib/base/stdsstream.h + +added more tests to autoconf. also now handling missing sstream +header in gcc 2.95 by including sstream header backported from v3. + +---------- +2002/09/01 09:28:54 crs +lib/platform/CXWindowsUtil.cpp + +lowered severity of some debug messages. + +---------- +2002/08/18 17:45:59 crs +configure.in +lib/base/Version.h + +Changed version number to 0.9.9. + +---------- +2002/08/18 17:40:10 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +fixed win32 deadlock. when a client disconnects the server will +warp the mouse to the primary screen. entering the primary +screen causes the primary screen's window to be hidden. the +deadlock occurs because hiding the window seems to post a +message then wait for it to be handled (or possibly it won't +send a message while a posted message is being handled). +thread A locks the mutex, warps the mouse, the hides the window. +thread B begins processing the mouse warp then tries to lock +the mutex. thread A is waiting on the event loop owned by B +while B is waiting on the mutex owned by A. this fix simply +hides the window asynchronously. however, there may be other +ways to cause a similar deadlock that have not been found. + +---------- +2002/08/18 17:35:10 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/server/CMSWindowsPrimaryScreen.cpp + +fixed PrintScrn handling; it was being changed to keypad multiply. + +---------- +2002/08/18 17:31:48 crs +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +no longer sending fake events for unmapped logical buttons. + +---------- +2002/08/11 22:43:07 crs +BUGS +INSTALL +NEWS +PORTING +README +cmd/Makefile.am +cmd/launcher/launcher.cpp +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.ico +cmd/synergy/synergy.rc +cmd/synergyc/Makefile.am +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergyc/synergyc.ico +cmd/synergyc/synergyc.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergy.ico +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +cmd/synergys/Makefile.am +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +cmd/synergys/synergys.ico +cmd/synergys/synergys.rc +configure.in +dist/rpm/synergy.spec.in +synergy.dsw + +Moved synergy client to cmd/synergyc and renamed it synergyc. +Moved synergy server to cmd/synergys and renamed it synergys. +Updated documentation to reflect that and the win32 launcher. + +---------- +2002/08/11 11:50:49 crs +Makefile.am +TODO + +added TODO file and top-level rule to make zip file of distribution +files. + +---------- +2002/08/03 11:50:07 crs +lib/mt/CCondVar.h + +removed pre-instantiation of templates in header file. + +---------- +2002/08/03 11:49:36 crs +all.dsp +cmd/Makefile.am +cmd/launcher/Makefile.am +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/launcher/synergy.ico +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.ico +cmd/synergy/synergy.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergy.ico +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +configure.in +lib/base/CString.cpp +lib/base/Version.h +lib/base/base.dsp +lib/client/client.dsp +lib/http/http.dsp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/CMSWindowsScreen.cpp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/server.dsp +lib/synergy/libsynergy.dsp +synergy.dsw + +added win32 launcher program. also changed VC++ dsp and dsw +files to binary form so \r\n aren't converted. added icons +to client and server apps on win32. + +---------- +2002/08/02 17:57:54 crs +Makefile.am +configure.in +dist/Makefile.am +dist/rpm/Makefile.am +dist/rpm/synergy.spec.in + +added build rule to create RPMs. + +---------- +2002/08/02 17:53:23 crs +AUTHORS +BUGS +FAQ +HISTORY +INSTALL +NEWS +PORTING +README + +minor documentation updates. added HISTORY and PORTING. + +---------- +2002/08/01 18:07:32 crs +AUTHORS +BUGS +COPYING +ChangeLog +FAQ +INSTALL +NEWS +README + +added files for release. + +---------- +2002/08/01 11:45:21 crs +configure.in +lib/base/CStopwatch.cpp +lib/mt/CCondVar.cpp +lib/platform/CUnixPlatform.cpp + +minor automake fixes. + +---------- +2002/07/31 17:40:36 crs +Makefile.am + +added simple rule to build doxygen. + +---------- +2002/07/31 17:40:21 crs +lib/synergy/XSynergy.h + +fixed comment. + +---------- +2002/07/31 17:35:43 crs +Makefile.am + +removed two programs from files to clean. + +---------- +2002/07/31 17:34:05 crs +Makefile.am +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergyd/Makefile.am +lib/Makefile.am +lib/base/Makefile.am +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am + +fixes to get vpath builds working (necessary for `make distcheck'). + +---------- +2002/07/31 16:57:26 crs +Makefile.am +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergy/synergy.cpp +cmd/synergyd/Makefile.am +cmd/synergyd/synergyd.cpp +configure.in +lib/Makefile.am +lib/base/Makefile.am +lib/base/Version.h +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.h +lib/synergy/Version.h + +Moved version header to base and it now uses VERSION macro +from config.h if available (which means version is now a +string, not three integers). Changed version to 1.0.0 and +protocol version to 1.0. And added MAINTAINERCLEANFILES +to makefiles to remove generated files. + +---------- +2002/07/31 16:27:06 crs +Makefile.am +cmd/synergy/Makefile.am +cmd/synergyd/Makefile.am +examples/synergy.linux.init +examples/synergyd.linux.init +lib/base/Makefile.am +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am +nodist/notes +notes + +added EXTRA_* files to get `make dist' doing the right thing. + +---------- +2002/07/31 16:24:45 crs +notes + +checkpoint notes. + +---------- +2002/07/31 13:56:59 crs +lib/base/CLog.cpp + +removed now unnecssary #define. + +---------- +2002/07/31 13:41:58 crs +lib/http/http.dsp +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/IMSWindowsScreenEventHandler.h + +okay, now the files should no longer be executable. + +---------- +2002/07/31 13:34:18 crs +lib/http/http.dsp +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/IMSWindowsScreenEventHandler.h +notes + +removed unintentional executable flag. + +---------- +2002/07/31 13:29:33 crs +notes + +checkpoint notes. + +---------- +2002/07/31 13:18:27 crs +README + +added comment about large motif clipboard items to README. + +---------- +2002/07/31 13:10:15 crs +README + +updated README. + +---------- +2002/07/31 12:40:41 crs +lib/platform/CSynergyHook.cpp +lib/platform/synrgyhk.dsp + +now building hook dll for release without linking in standard +C runtime. need C runtime for debug build for asserts. + +---------- +2002/07/31 12:39:34 crs +cmd/synergy/synergy.cpp +cmd/synergyd/synergyd.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsScreen.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/IClient.h +lib/synergy/IScreen.h +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h + +fixed problem with opening client and server. in some cases it +would fail to open in such a way that it could never succeed +but it'd never stop retrying. now terminating when open fails +such that it'll never succeed. + +---------- +2002/07/30 19:03:40 crs +lib/client/client.dsp +lib/io/io.dsp +lib/net/net.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +added new files to projects and added two project files that +should've been adding in change 530. + +---------- +2002/07/30 18:49:31 crs +lib/client/CServerProxy.cpp +lib/server/CClientProxy1_0.cpp +lib/synergy/ProtocolTypes.h + +made it so a negative kHeartRate disables heartbeats and set +kHeartRate to -1. + +---------- +2002/07/30 18:31:21 crs +lib/mt/CThreadRep.cpp +lib/mt/XThread.h + +moved exception definition to header file. + +---------- +2002/07/30 18:31:00 crs +cmd/synergy/synergy.cpp +cmd/synergyd/synergyd.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/ISecondaryScreenFactory.h +lib/client/Makefile.am +lib/io/IStreamFilterFactory.h +lib/io/Makefile.am +lib/net/CTCPSocketFactory.cpp +lib/net/CTCPSocketFactory.h +lib/net/ISocketFactory.h +lib/net/Makefile.am +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/IPrimaryScreenFactory.h +lib/server/Makefile.am +lib/synergy/CTCPSocketFactory.cpp +lib/synergy/CTCPSocketFactory.h +lib/synergy/ISocketFactory.h +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.h + +now using class factories to move some decisions from the libraries +into the application. + +---------- +2002/07/30 16:52:46 crs +Makefile.am +base/BasicTypes.h +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CLog.cpp +base/CLog.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.cpp +base/CString.h +base/CUnicode.cpp +base/CUnicode.h +base/IInterface.h +base/IJob.h +base/Makefile.am +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/base.dsp +base/common.h +base/stdfstream.h +base/stdistream.h +base/stdlist.h +base/stdmap.h +base/stdostream.h +base/stdpost.h +base/stdpre.h +base/stdset.h +base/stdsstream.h +base/stdvector.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile.am +client/client.cpp +client/client.dsp +client/client.rc +client/resource.h +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +configure.in +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/Makefile.am +http/XHTTP.cpp +http/XHTTP.h +http/http.dsp +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/Makefile.am +io/XIO.cpp +io/XIO.h +io/io.dsp +lib/Makefile.am +lib/base/BasicTypes.h +lib/base/CFunctionJob.cpp +lib/base/CFunctionJob.h +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStopwatch.cpp +lib/base/CStopwatch.h +lib/base/CString.cpp +lib/base/CString.h +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/IInterface.h +lib/base/IJob.h +lib/base/Makefile.am +lib/base/TMethodJob.h +lib/base/XBase.cpp +lib/base/XBase.h +lib/base/base.dsp +lib/base/common.h +lib/base/stdfstream.h +lib/base/stdistream.h +lib/base/stdlist.h +lib/base/stdmap.h +lib/base/stdostream.h +lib/base/stdpost.h +lib/base/stdpre.h +lib/base/stdset.h +lib/base/stdsstream.h +lib/base/stdvector.h +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/client/Makefile.am +lib/http/CHTTPProtocol.cpp +lib/http/CHTTPProtocol.h +lib/http/Makefile.am +lib/http/XHTTP.cpp +lib/http/XHTTP.h +lib/http/http.dsp +lib/io/CBufferedInputStream.cpp +lib/io/CBufferedInputStream.h +lib/io/CBufferedOutputStream.cpp +lib/io/CBufferedOutputStream.h +lib/io/CInputStreamFilter.cpp +lib/io/CInputStreamFilter.h +lib/io/COutputStreamFilter.cpp +lib/io/COutputStreamFilter.h +lib/io/CStreamBuffer.cpp +lib/io/CStreamBuffer.h +lib/io/IInputStream.h +lib/io/IOutputStream.h +lib/io/Makefile.am +lib/io/XIO.cpp +lib/io/XIO.h +lib/io/io.dsp +lib/mt/CCondVar.cpp +lib/mt/CCondVar.h +lib/mt/CLock.cpp +lib/mt/CLock.h +lib/mt/CMutex.cpp +lib/mt/CMutex.h +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CThreadRep.cpp +lib/mt/CThreadRep.h +lib/mt/CTimerThread.cpp +lib/mt/CTimerThread.h +lib/mt/Makefile.am +lib/mt/XThread.h +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/IDataSocket.h +lib/net/IListenSocket.h +lib/net/ISocket.h +lib/net/Makefile.am +lib/net/XNetwork.cpp +lib/net/XNetwork.h +lib/net/XSocket.cpp +lib/net/XSocket.h +lib/net/net.dsp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/CPlatform.cpp +lib/platform/CPlatform.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsClipboard.h +lib/platform/CXWindowsClipboardTextConverter.cpp +lib/platform/CXWindowsClipboardTextConverter.h +lib/platform/CXWindowsClipboardUCS2Converter.cpp +lib/platform/CXWindowsClipboardUCS2Converter.h +lib/platform/CXWindowsClipboardUTF8Converter.cpp +lib/platform/CXWindowsClipboardUTF8Converter.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h +lib/platform/IMSWindowsScreenEventHandler.h +lib/platform/IPlatform.h +lib/platform/Makefile.am +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CHTTPServer.cpp +lib/server/CHTTPServer.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/server/Makefile.am +lib/synergy/CClipboard.cpp +lib/synergy/CClipboard.h +lib/synergy/CInputPacketStream.cpp +lib/synergy/CInputPacketStream.h +lib/synergy/COutputPacketStream.cpp +lib/synergy/COutputPacketStream.h +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/CTCPSocketFactory.cpp +lib/synergy/CTCPSocketFactory.h +lib/synergy/ClipboardTypes.h +lib/synergy/IClient.h +lib/synergy/IClipboard.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/IScreen.h +lib/synergy/IScreenEventHandler.h +lib/synergy/IScreenReceiver.h +lib/synergy/IScreenSaver.h +lib/synergy/IServer.h +lib/synergy/ISocketFactory.h +lib/synergy/KeyTypes.h +lib/synergy/Makefile.am +lib/synergy/MouseTypes.h +lib/synergy/ProtocolTypes.h +lib/synergy/Version.h +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h +lib/synergy/XSynergy.cpp +lib/synergy/XSynergy.h +lib/synergy/libsynergy.dsp +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/Makefile.am +mt/XThread.h +mt/mt.dsp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile.am +net/XNetwork.cpp +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h +net/net.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.cpp +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.cpp +platform/CMSWindowsClipboardUTF16Converter.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CMSWindowsScreenSaver.cpp +platform/CMSWindowsScreenSaver.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CSynergyHook.cpp +platform/CSynergyHook.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.cpp +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.cpp +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.cpp +platform/CXWindowsClipboardUTF8Converter.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IMSWindowsScreenEventHandler.h +platform/IPlatform.h +platform/Makefile.am +platform/makehook.dsp +platform/platform.dsp +platform/synrgyhk.dsp +server/CClientProxy.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile.am +server/resource.h +server/server.cpp +server/server.dsp +server/server.rc +synergy.dsw +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/ClipboardTypes.h +synergy/IClient.h +synergy/IClipboard.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IScreenSaver.h +synergy/IServer.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/Makefile.am +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/Version.h +synergy/XScreen.cpp +synergy/XScreen.h +synergy/XSynergy.cpp +synergy/XSynergy.h +synergy/synergy.dsp + +Reorganized source tree. Moved client.cpp into cmd/synergy as +synergy.cpp and server.cpp into cmd/synergyd as synergyd.cpp. +Moved and renamed related files. Moved remaining source files +into lib/.... Modified and added makefiles as appropriate. +Result is that library files are under lib with each library +in its own directory and program files are under cmd with each +command in its own directory. + +---------- +2002/07/30 15:17:44 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/IClient.h + +Replaced method name `run' with `mainLoop', and `stop' and `quit' +with `exitMainLoop' in most places. + +---------- +2002/07/30 14:59:36 crs +client/CClient.h +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.h +server/CClientProxy.h +server/CClientProxy1_0.h +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.h +server/CPrimaryScreen.h +server/CServer.h +server/CXWindowsPrimaryScreen.h +synergy/IClient.h +synergy/IScreen.h +synergy/IScreenReceiver.h +synergy/IServer.h + +Added doxygen comments for all relevant headers in client and server. + +---------- +2002/07/29 17:03:55 crs +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.h +platform/CMSWindowsScreen.h +platform/CMSWindowsScreenSaver.h +platform/CPlatform.h +platform/CSynergyHook.h +platform/CUnixPlatform.h +platform/CWin32Platform.h +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.h +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.h +platform/IMSWindowsScreenEventHandler.h +platform/IPlatform.h + +Added doxygen comments for all relevant headers in platform. + +---------- +2002/07/29 16:07:26 crs +synergy/CClipboard.h +synergy/CInputPacketStream.h +synergy/COutputPacketStream.h +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.h +synergy/ClipboardTypes.h +synergy/IClient.h +synergy/IClipboard.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IScreenSaver.h +synergy/IServer.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/Version.h +synergy/XScreen.h +synergy/XSynergy.h + +Added doxygen comments for all relevant headers in synergy. + +---------- +2002/07/29 16:06:52 crs +platform/CMSWindowsScreen.cpp +server/CPrimaryClient.cpp + +moved try/catch block from CMSWindowsScreen to CPrimaryClient. +this means CMSWindowsScreen doesn't need to include XSynergy.h. + +---------- +2002/07/29 16:05:59 crs +doc/doxygen.cfg + +changed doxygen configuration. + +---------- +2002/07/28 19:06:52 crs +net/CNetwork.h +net/CNetworkAddress.h +net/CTCPListenSocket.h +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h + +Added doxygen comments for all relevant headers in net. + +---------- +2002/07/28 17:55:59 crs +http/CHTTPProtocol.h +http/XHTTP.cpp +http/XHTTP.h + +Added doxygen comments for all relevant headers in http. + +---------- +2002/07/28 17:25:13 crs +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.h +io/COutputStreamFilter.h +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/XIO.h + +Added doxygen comments for all relevant headers in io. + +---------- +2002/07/28 13:34:19 crs +mt/CCondVar.h +mt/CLock.h +mt/CMutex.h +mt/CThread.h +mt/CThreadRep.h +mt/CTimerThread.h +mt/XThread.h + +Added doxygen comments for all relevant headers in mt. + +---------- +2002/07/26 18:28:18 crs +base/CFunctionJob.h +base/CLog.h +base/CStopwatch.h +base/CString.h +base/CUnicode.h +base/IInterface.h +base/IJob.h +base/TMethodJob.h +base/XBase.h +base/common.h + +added doxygen comments for all relevant headers in base. + +---------- +2002/07/26 18:27:31 crs +platform/CXWindowsUtil.cpp + +fixed type mismatch (SInt32 vs int) in definition of +getWindowProperty(). + +---------- +2002/07/26 16:05:59 crs +doc/doxygen.cfg + +added configuration file for building doxygen documentation. +the code is not yet doxygen documented, though. + +---------- +2002/07/26 15:22:25 crs +platform/CXWindowsUtil.cpp + +now deleting property when so requested even if read failed. + +---------- +2002/07/25 18:08:00 crs +notes + +checkpoint. + +---------- +2002/07/25 17:58:01 crs +client/client.cpp +net/XSocket.cpp +server/server.cpp + +improved error messages for bad addresses. + +---------- +2002/07/25 17:52:40 crs +base/XBase.cpp +http/XHTTP.cpp +io/XIO.cpp +net/XNetwork.cpp +net/XSocket.cpp +server/CConfig.cpp +server/server.cpp +synergy/CProtocolUtil.cpp +synergy/XScreen.cpp +synergy/XSynergy.cpp + +made all getWhat() methods on exceptions consistent. they now +all use format() the same way. also changed format() to actually +do formatting. however, it doesn't try looking up formatting +strings by id, it just uses the fallback format string. + +---------- +2002/07/25 17:23:35 crs +base/CLog.cpp +base/CLog.h +base/CString.cpp +base/CString.h + +moved string formatting into CStringUtil from CLog and added +methods for format positional string arguments. + +---------- +2002/07/25 09:55:01 crs +platform/CXWindowsScreen.cpp + +added unix specific implementation of CXWindowsScreen::mainLoop() +that uses poll() to process events more efficiently. it won't +wake up nor sleep any more than necessary, unlike the platform +independent implementation that polls and sleeps. + +---------- +2002/07/25 09:23:24 crs +platform/CXWindowsClipboard.cpp + +finished INCR transfer changes. also made motifGetTime() return +icccmGetTime() because it seems motif does TIMESTAMP like ICCCM. + +---------- +2002/07/25 08:57:46 crs +platform/CXWindowsClipboard.cpp + +checkpoint. working on INCR transfers. + +---------- +2002/07/24 19:26:18 crs +platform/CMSWindowsScreen.cpp +platform/CWin32Platform.cpp +platform/CWin32Platform.h + +fixes for win32 due to changes in how s_restartable is handled. +the main change is that WM_QUIT now causes the thread to be +cancelled instead of mainLoop() just returning. this also +requires runDaemon() to call the run function in a new thread +each time it calls it because it could can cancelled. + +---------- +2002/07/24 19:24:21 crs +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardUTF16Converter.cpp + +fixes for win32 clipboard due to CUnicode nul terminator changes. + +---------- +2002/07/24 19:23:46 crs +base/CUnicode.cpp + +fixed an off-by-one error in UTF8ToText(). + +---------- +2002/07/24 17:39:52 crs +base/CUnicode.cpp + +fixed an off-by-one error in textToUTF8(). + +---------- +2002/07/24 17:30:32 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +fixed type of TARGETS target. + +---------- +2002/07/24 17:22:01 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardUTF16Converter.cpp + +made handling of nul terminators in CUnicode more sane. + +---------- +2002/07/24 17:07:52 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +some fixes for motif clipboard. still not handling incremental +transfer through root window property because not sure of the +protocol. + +---------- +2002/07/24 13:01:18 crs +client/CClient.cpp +client/client.cpp +net/CTCPSocket.cpp +net/XSocket.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsScreen.cpp +platform/CXWindowsUtil.cpp +platform/IPlatform.h +server/CPrimaryClient.cpp +server/CServer.cpp +server/server.cpp +synergy/ProtocolTypes.h +synergy/Version.h + +removed restart function from platform. no longer trying to +restart if the X server connection was lost; since synergy +is likely to be started by xdm or the user's xsession, it's +better for synergy to simply terminate when the connection +is lost. synergy will still restart due to other errors. +also fixed numerous other minor bugs and cleaned some stuff +up (like app error codes are now consistent and enumerated +in Version.h, for lack of a better place). and boosted +version and protocol numbers. + +---------- +2002/07/23 19:00:01 crs +notes + +checkpoint. + +---------- +2002/07/23 18:59:44 crs +platform/CMSWindowsClipboard.cpp + +fixed a bug in clipboard conversion (was using wrong converter or +no converter when one was available). + +---------- +2002/07/23 18:59:15 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp +synergy/KeyTypes.h + +converted win32 to use unicode based KeyID. + +---------- +2002/07/23 17:04:41 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/KeyTypes.h + +checkpoint. converting KeyID to use UTF-32 encoding instead of +X11 keysyms. + +---------- +2002/07/23 15:34:05 crs +synergy/CClipboard.cpp +synergy/IClipboard.h + +no longer attempting to unmarshall clipboard formats that aren't +known to the caller. if the client supports more formats than +the server then the server could get a clipboard format greater +than kNumFormats. with this change the server discards the +extra formats instead of crashing. + +---------- +2002/07/23 15:26:40 crs +base/CUnicode.cpp +base/CUnicode.h +base/base.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.cpp +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.cpp +platform/CMSWindowsClipboardUTF16Converter.h +platform/platform.dsp + +unicode clipboard changes for win32 plus some bug fixes. + +---------- +2002/07/23 12:35:36 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +removed unnecessary atoms from X clipboard object. + +---------- +2002/07/23 12:08:30 crs +base/CUnicode.cpp + +checkpoint. more CUnicode fixes. + +---------- +2002/07/23 11:51:13 crs +base/CUnicode.cpp + +checkpoint. fixed the other cases in the same function as the +previous checkin. also prevented the errors flag from getting +reset after the multibyte to wide character conversion. + +---------- +2002/07/23 11:42:54 crs +base/CUnicode.cpp + +checkpoint. fixed cases for mbrtowc (was using 1 and 2 instead +of -1 and -2). + +---------- +2002/07/23 11:36:18 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboardTextConverter.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/23 09:33:50 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard testing. + +---------- +2002/07/22 18:46:57 crs +base/CUnicode.cpp +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/22 18:17:21 crs +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/22 18:03:44 crs +platform/CXWindowsClipboard.cpp + +checkpoint. working on UTF8 clipboard transfer. + +---------- +2002/07/22 17:32:51 crs +base/CUnicode.cpp +base/CUnicode.h +base/Makefile.am +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.cpp +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.cpp +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.cpp +platform/CXWindowsClipboardUTF8Converter.h +platform/Makefile.am +synergy/IClipboard.h + +checkpoint. adding support for unicode in clipboard. + +---------- +2002/07/19 21:27:59 crs +README + +changed notes about how to startup configure synergy. it now +discourages using boot scripts, which can't handle X servers +requiring authorization, and suggests modifying xdm's Xsetup. + +---------- +2002/07/19 20:44:57 crs +examples/synergy.linux.init +examples/synergyd.linux.init + +updated init.d scripts to work with SuSE. however, it looks as +if they cannot be used on an X server using authentication +because the daemons they start are not authorized to connect to +the X server. X users should modify Xsetup or Xsession. + +---------- +2002/07/19 18:12:41 crs +server/CXWindowsPrimaryScreen.cpp + +formatting. + +---------- +2002/07/19 17:39:45 crs +server/CPrimaryScreen.cpp + +removed from previous change. + +---------- +2002/07/19 17:38:34 crs +server/CPrimaryScreen.cpp + +reordered operations to reduce cursor flashing when entering +primary screen. + +---------- +2002/07/18 17:03:10 crs +platform/CSynergyHook.cpp +server/CMSWindowsPrimaryScreen.cpp + +fixed handling of calling init() when a previous process did not +call cleanup(). if that process still appears to exist then the +init() fails. otherwise some cleanup is performed and the init() +proceeds. a synergy server started while another is running will +now exit immediately without interfering the original server. + +---------- +2002/07/18 17:00:48 crs +server/CServer.cpp +server/CServer.h + +now cancelling and waiting for the accept client thread before +cancelling any other threads. this prevents a race condition +where we disconnect a client but it reconnects before we +manage to shutdown. that might leave a thread running and +the connection won't be closed down properly. + +---------- +2002/07/18 16:58:08 crs +mt/CThreadRep.cpp +platform/CMSWindowsScreen.cpp + +changed waitForEvent() to handle a peculiar feature of +MsgWaitForMultipleObjects(): it will not return immediately +if an event already in the queue when it's called was already +in the queue during the last call to GetMessage()/PeekMessage(). +also now discarding screen saver events if there are any other +screen saver events in the queue already. this prevents these +events from piling up in the queue, which they'd do because we +sleep for 250ms when handling each one. + +---------- +2002/07/18 08:54:17 crs +synergy.dsw + +fixed incorrect paths to makehook and synrgyhk project files. + +---------- +2002/07/17 17:27:41 crs +platform/CMSWindowsScreen.cpp +platform/CSynergyHook.cpp +platform/CSynergyHook.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h + +attempt to fix stuttering when leaving win32 screen. seems to +work but will let testers make the final call. also fixed +desktop synchronization by setting a variable that was +mistakenly left unset. and tried to work around an apparent +bug in MsgWaitForMultipleObjects() that prevented the service +from closing down properly. start/pause/continue/stop +sequence still doesn't shut down correctly. start/pause/stop +and start/stop work fine. + +---------- +2002/07/17 17:24:44 crs +client/CClient.cpp + +removed unnecessary local variable. + +---------- +2002/07/16 19:07:15 crs +base/stdistream.h +io/CStreamBuffer.cpp + +fixes to get it compiling on .NET. + +---------- +2002/07/16 16:52:26 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CXWindowsScreen.cpp +platform/IMSWindowsScreenEventHandler.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IServer.h + +moved onError() method to IScreenReceiver from IPrimaryScreenReceiver. +also implemented onError in CClient which previously did not have +any way to handle display disconnection. + +---------- +2002/07/15 15:03:04 crs +platform/CSynergyHook.cpp +platform/synrgyhk.dsp + +completing previous checkin. + +---------- +2002/07/15 15:01:36 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.dsp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CSynergyHook.cpp +platform/CSynergyHook.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/IMSWindowsScreenEventHandler.h +platform/makehook.dsp +platform/platform.dsp +platform/synrgyhk.dsp +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.dsp +server/synrgyhk.dsp +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/ProtocolTypes.h +synergy/synergy.dsp + +checkpoint. refactored win32 code. had to edit and rename some +files so this is only a checkpoint. + +---------- +2002/07/13 22:00:38 crs +client/CClient.cpp +client/CClient.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/ISecondaryScreen.h +client/Makefile.am +client/client.cpp +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryScreen.h +server/Makefile.am +server/server.cpp +synergy/IClient.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IServer.h +synergy/Makefile.am + +checkpoint. still refactoring. merged common code from primary +screens into CPrimaryScreen and merged common code from secondary +screens into CSecondaryScreen. changed is-a relationship to a +has-a between the primary and secondary screen classes and the +generic platform dependent screen class to avoid multiple +inheritance of implementation. also standardized the interface +for those generic screen classes. adding a platform now involves +implementing simpler interfaces: IScreen for the generic screen, +IScreenEventHandler and some methods of CPrimaryScreen for the +primary screen, and IScreenEventHandler and some methods of +CSecondaryScreen for the secondary screen. did X11 platform +but not win32 platform. + +---------- +2002/07/12 20:41:23 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +refactoring. refactored stuff in client (with changes to server +as necessary). + +---------- +2002/07/11 18:58:49 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. making win32 and X primary screen code more similar +in order to share code later. + +---------- +2002/07/11 13:13:37 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CServerProxy.cpp +client/client.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryScreen.h +server/server.dsp +synergy/synergy.dsp + +applied refactoring to win32 code. + +---------- +2002/07/10 21:22:28 crs +client/CClient.cpp +client/CClient.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/ISecondaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreenReceiver.h +synergy/IServer.h +synergy/Makefile.am + +more refactoring. + +---------- +2002/07/10 20:18:32 crs +client/CClient.cpp +client/CClient.h +client/CServerProxy.cpp +client/CServerProxy.h +client/ISecondaryScreen.h +client/Makefile.am +client/client.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +synergy/IClient.h +synergy/ISecondaryScreen.h +synergy/Makefile.am + +refactored client code. it now uses IClient and IServer and +has a CServerProxy, making it's design similar to the server +code. + +---------- +2002/07/10 14:29:50 crs +synergy/IClient.h + +removed some obsolete comments. + +---------- +2002/07/10 14:15:17 crs +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryReceiver.h +server/Makefile.am +synergy/IServer.h +synergy/ProtocolTypes.h + +removed IPrimaryReceiver in favor of IServer, which required a few +minor changes to support IPrimaryReciever's functionality. this +does mean that the IPrimaryScreen class will be calling some +methods with dummy arguments. those are documented in +CPrimaryClient. + +---------- +2002/07/09 21:22:31 crs +acinclude.m4 +acsite.m4 +config/depcomp +config/install-sh +config/missing +config/mkinstalldirs +mt/CThread.cpp +mt/CThread.h +notes +platform/CXWindowsClipboard.cpp +server/CClientProxy.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryReceiver.h +server/IPrimaryScreen.h +server/IServerProtocol.h +server/Makefile.am +synergy/IClient.h +synergy/IServer.h +synergy/Makefile.am + +updated to new automake and refactored server stuff. the server +now speaks to the primary screen and secondary screens almost +everywhere the same way through an IClient interface; only +special primary screen calls are accessed through a different +interface, the CPrimaryClient interface. this simplifies the +server since it no longer needs to test whether the active screen +is the primary or a secondary in most cases. + +the server no longer speaks directly to the primary screen; all +that goes through the CPrimaryClient, which often just forwards +the call. the primary screen no longer speaks directly to the +server either, again going through the CPrimaryClient via a +IPrimaryReceiver interface. + +CServerProtocol classes have been replaced by CClientProxy +classes which are very similar. the name makes more sense +though. + +---------- +2002/07/09 17:31:45 crs +server/IPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/Makefile.am + +checkpoint. moved IPrimaryScreen.h. + +---------- +2002/07/07 15:15:34 crs +server/IServerProtocol.h +synergy/IServerProtocol.h + +moved IServerProtocol to server from synergy directory. + +---------- +2002/07/03 16:25:36 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +fixed spurious mouse motions when entering/leaving primary +screen on X11. + +---------- +2002/07/01 15:05:49 crs +server/CMSWindowsPrimaryScreen.cpp + +mistakenly removed mouse button checks when on secondary screens +from isLockedToScreen() in earlier checkin. + +---------- +2002/07/01 14:01:23 crs +README +notes + +checkpoint. + +---------- +2002/07/01 13:03:16 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h + +now synthesizing key release events for each pressed key when +the client screen is closed. this fixes the bug where the +client's keyboard was left with some keys logically pressed +when the client died (e.g. using ctrl+c on the client program +from the server's keyboard would leave the ctrl key logically +pressed). + +---------- +2002/07/01 13:01:16 crs +server/CServerProtocol1_0.cpp + +disabled removing client if no heartbeat is received. we don't +want that while testing because it might hide bugs. + +---------- +2002/07/01 13:00:12 crs +server/CMSWindowsPrimaryScreen.cpp + +fixed locking to screen on win32. was using GetKeyboardState() +to query keys but that doesn't give us up-to-date information. +now using GetAsyncKeyState() if on primary and m_keys if on +secondary. + +---------- +2002/07/01 12:58:52 crs +platform/CMSWindowsScreenSaver.cpp +platform/CMSWindowsScreenSaver.h + +added win32 screen saver class forgotten in previous checkins. + +---------- +2002/06/26 16:31:48 crs +client/CClient.cpp +http/CHTTPProtocol.cpp +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CInputStreamFilter.h +io/IInputStream.h +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/ProtocolTypes.h + +synergy hook DLL will now restart itself if a client tries to +init() it while it's already running. fixed an uninitialized +pointer bug in CServer and some cleanup-on-error code in +CMSWindowsPrimaryScreen. also added timeout to read() on +IInputStream and a heartbeat sent by clients so the server +can disconnect clients that are dead but never reset the TCP +connection. previously the server would keep these dead +clients around forever and if the user was locked on the +client screen for some reason then the server would have to +be rebooted (or the server would have to be killed via a +remote login). + +---------- +2002/06/26 13:48:08 crs +client/CClient.cpp +client/CClient.h + +client now compresses mouse motion events. this fixes slow +dragging on grace, possibly on win32 too. + +---------- +2002/06/26 13:31:06 crs +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h + +fixed getSize() to be non-blocking in CInputPacketStream. + +---------- +2002/06/26 12:44:52 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreenSaver.h + +fixed re-entrant calls to X bug. + +---------- +2002/06/23 23:24:22 crs +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h + +fixed handling of jumping to primary when screen saver starts +and back to secondary when it stops. also now redirecting +keyboard input to root window when screen saver starts; this +allows the user to type in the lock dialog and also effectively +discards any input used to deactivate the screen saver. + +---------- +2002/06/23 21:54:05 crs +notes + +checkpoint. + +---------- +2002/06/23 21:53:31 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/platform.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CSynergyHook.cpp +server/CSynergyHook.h + +win32 screen saver now handled. + +---------- +2002/06/23 21:48:33 crs +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h + +now disabling disable job timer when forcing screen saver +activation. previously the timer would deactivate the screen +saver shortly after activation. job timer is restored when +the screen saver is deactivated. + +---------- +2002/06/23 15:43:40 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.cpp +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile.am +synergy/IPrimaryScreen.h + +checkpoint screensaver changes. now handling xscreensaver +dying and restarting or starting after synergy does. also +now disabling the screen saver on the client. next step: +win32 support. + +---------- +2002/06/22 20:29:59 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h + +fixes to get xscreensaver integration working. + +---------- +2002/06/22 19:47:27 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +server/CXWindowsPrimaryScreen.cpp + +CXWindowsUtil::CErrorLock wasn't XSync()'ing the display before +installing and uninstalling the new error handler, causing +errors before the lock to be caught and errors during the lock +to not be caught. had to add Display* as argument to c'tor. + +---------- +2002/06/22 19:20:21 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/Makefile.am +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/IScreenSaver.h +synergy/ISecondaryScreen.h +synergy/Makefile.am + +checkpoint. adding screen saver support. only on X so far +and untested. also some known problems: not detecting an +xscreensaver started after us and not detecting built-in +screen saver activation (not sure if we can without using +ugly extensions). + +---------- +2002/06/22 17:31:24 crs +client/Makefile.am +http/Makefile.am +io/Makefile.am +mt/Makefile.am +net/Makefile.am +platform/Makefile.am +server/Makefile.am +synergy/Makefile.am + +added header files to _SOURCES. + +---------- +2002/06/22 13:55:45 crs +server/CXWindowsPrimaryScreen.cpp + +added comments. + +---------- +2002/06/22 12:09:49 crs +client/CXWindowsSecondaryScreen.cpp + +cleanup. + +---------- +2002/06/21 17:55:47 crs +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp + +cleaned up some minor bugs. + +---------- +2002/06/21 17:54:22 crs +net/CNetwork.cpp +net/CNetwork.h + +ported network changes to win32. + +---------- +2002/06/21 16:29:35 crs +net/CNetworkAddress.cpp + +now trying to convert hostname as a dot notation address before +trying name lookup. not all platforms will do this for us in +gethostbyname(). + +---------- +2002/06/21 16:19:08 crs +net/CNetwork.cpp +net/CNetwork.h +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp + +fixed CTCPSocket::connect() to allow cancellation. + +---------- +2002/06/21 15:18:01 crs +server/CServer.cpp +server/CServer.h + +some cleanup. also fixed a race condition when adding threads +to the thread list: the child thread would add itself to the +list which means there could be a time interval in the parent +where the child thread exists but isn't on the list. the +parent now does the adding and removing. + +---------- +2002/06/21 15:15:34 crs +platform/CUnixPlatform.cpp + +now blocking SIGINT and SIGTERM in restart function. the child +should handle the signal and terminate. then the restart +function will exit. + +---------- +2002/06/21 15:14:32 crs +mt/CThreadRep.cpp + +signal handler thread now dies when SIGABRT is raised. ignoring +SIGABRT in sigwait() seems to be a bug in the linux pthread +library. + +---------- +2002/06/20 16:27:49 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +fixed bug introduced by previous checkin. calling XCheckIfEvent() +multiple times is *not* the same as calling XIfEvent() because the +former will re-encounter events that it didn't process previously. +to make things simple it now pulls events off the queue and saves +them if not processed for selection transfer and puts them back +afterwards. + +---------- +2002/06/20 14:01:44 crs +platform/CXWindowsClipboard.cpp + +speeded up clipboard transfer by avoiding a selection request +when it wasn't necessary. (in particular, we were getting the +clipboard update time from the owner then emptying the clipboard, +so we didn't need to get the time. worse, most owners don't +support getting the time and we often timed out.) + +also fixed a multithread bug using the X display. we were using +a CThread to send an event after a timeout while we were waiting +in XIfEvent(). this necessarily involved two threads calling +into Xlib at once, which is not allowed. now using polling to +do the timeout because Xlib doesn't have a function to get +events with a timeout. + +---------- +2002/06/20 13:35:28 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +checkpoint. trying to fix a delay when sending clipboards on X. + +---------- +2002/06/20 11:13:37 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h + +added workaround for bug windows 98 (Me?) and multiple displays: +absolute mouse_event() moves don't work except for primary +display. + +---------- +2002/06/20 09:19:55 crs +server/CXWindowsPrimaryScreen.cpp + +work around for bug with mouse driver on lombard powerbook. + +---------- +2002/06/19 20:24:35 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +fixed bugs in mouse motion. wasn't taking care to capture all +motion events relative to the previous mouse position. for +example, if two mouse events arrive, the first at x+1,y and +the second at x+2,y, we used to compute deltas of 1,0 and 2,0 +instead of 1,0 and 1,0. that's fixed. also worked around a +bug (probably) in windows that caused a motion event after a +SetCursorPos() to be lost or reported one pixel off from the +correct position. now using mouse_event() which doesn't +have that problem. also fixed calculation of normalized +coordinates for mouse_event() when there are multiple +displays. + +---------- +2002/06/19 17:03:29 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/ProtocolTypes.h + +checkpoint. initial support for multiple displays on win32. + +---------- +2002/06/19 14:45:22 crs +client/Makefile.am +configure.in +server/Makefile.am + +fixed addition of X11 -L and -l options on link lines. + +---------- +2002/06/19 12:21:26 crs +configure.in +platform/CUnixPlatform.cpp + +checkpoint. automake changes for wait(). + +---------- +2002/06/19 11:58:48 crs +configure.in +http/CHTTPProtocol.cpp +platform/CUnixPlatform.cpp + +checkpoint. automake changes for reentrant functions. + +---------- +2002/06/19 11:23:49 crs +base/BasicTypes.h +base/CLog.cpp +base/CLog.h +base/CStopwatch.cpp +base/XBase.cpp +base/common.h +base/stdistream.h +base/stdostream.h +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +configure.in +mt/CCondVar.cpp +mt/CCondVar.h +mt/CMutex.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +net/CNetwork.cpp +net/CNetwork.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.h +platform/CXWindowsUtil.h +server/CServer.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/server.cpp +synergy/CProtocolUtil.h + +checkpoint. more conversion to automake. + +---------- +2002/06/19 08:23:08 crs +config/install-sh +config/missing +config/mkinstalldirs +configure.in +install-sh +missing +mkinstalldirs + +moved auxillary automake files into config directory. + +---------- +2002/06/18 19:47:52 crs +install-sh +missing +mkinstalldirs + +added automake required tools. + +---------- +2002/06/18 19:44:34 crs +Make-linux +Make-solaris +Makecommon +Makefile +Makefile.am +acsite.m4 +base/Makefile +base/Makefile.am +client/Makefile +client/Makefile.am +configure.in +http/Makefile +http/Makefile.am +io/Makefile +io/Makefile.am +mt/Makefile +mt/Makefile.am +net/Makefile +net/Makefile.am +platform/Makefile +platform/Makefile.am +server/Makefile +server/Makefile.am +synergy/Makefile +synergy/Makefile.am +tools/depconv + +started to convert to autoconf/automake. + +---------- +2002/06/18 18:34:55 crs +base/CLog.cpp + +now checking vsnprintf result against < 0 instead of == -1 +for portability. + +---------- +2002/06/18 18:33:59 crs +net/XSocket.cpp + +added FIXME to commented out code. + +---------- +2002/06/17 15:44:45 crs +README +client/client.cpp +server/server.cpp + +made command line parsing a little more sane with respect to +windows NT services. + +---------- +2002/06/17 14:10:25 crs +README + +updates + +---------- +2002/06/17 13:31:21 crs +base/CLog.cpp +base/CString.cpp +base/XBase.cpp +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +http/CHTTPProtocol.cpp +http/XHTTP.cpp +io/CBufferedInputStream.cpp +io/CBufferedOutputStream.cpp +io/CInputStreamFilter.cpp +io/COutputStreamFilter.cpp +io/CStreamBuffer.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp +net/CNetwork.cpp +net/CNetworkAddress.cpp +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +net/XNetwork.cpp +net/XSocket.cpp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsScreen.cpp +platform/CUnixPlatform.cpp +platform/CWin32Platform.cpp +platform/CXWindowsClipboard.cpp +platform/CXWindowsScreen.cpp +platform/CXWindowsUtil.cpp +server/CConfig.cpp +server/CHTTPServer.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServerProtocol.cpp +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/server.cpp +synergy/CClipboard.cpp +synergy/CInputPacketStream.cpp +synergy/COutputPacketStream.cpp +synergy/CProtocolUtil.cpp +synergy/XSynergy.cpp + +formatting changes. + +---------- +2002/06/17 12:02:26 crs +client/CClient.cpp +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CServer.cpp +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/ISocketFactory.h + +refactored ISocket into IDataSocket. the latter and IListenSocket +now derive from ISocket. + +---------- +2002/06/14 18:08:20 crs +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CLog.cpp +base/TMethodJob.h +base/common.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/client.cpp +io/CStreamBuffer.cpp +io/CStreamBuffer.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +platform/CMSWindowsScreen.cpp +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/IPlatform.h +server/server.cpp + +performance fixes on win32 plus clean up of some warnings. also +improved error messages when uninstalling service. + +---------- +2002/06/11 20:10:49 crs +README + +added a blurb about synrgyhk.dll and that the service manager +will look for the binary wherever it was when --install was +used. + +---------- +2002/06/11 20:09:59 crs +base/stdpre.h +platform/CMSWindowsScreen.h + +windows fixes needed for formatting changes. + +---------- +2002/06/11 18:33:03 crs +client/CXWindowsSecondaryScreen.cpp + +commented out half-duplex flags that should never have been +uncommented. + +---------- +2002/06/11 18:31:06 crs +server/CServer.cpp + +fixed bug with switching screens on primary when there's no +link in that direction (it would assert). introduced bug +when adding support for wrapping. now ignores attempts to +move in a direction with no link. + +---------- +2002/06/11 18:30:08 crs +platform/CUnixPlatform.cpp + +added missing #include . + +---------- +2002/06/10 22:06:45 crs +base/BasicTypes.h +base/CFunctionJob.cpp +base/CLog.cpp +base/CLog.h +base/CString.cpp +base/CString.h +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/common.h +base/stdistream.h +base/stdostream.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/XHTTP.cpp +http/XHTTP.h +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/COutputStreamFilter.cpp +io/CStreamBuffer.cpp +io/IInputStream.h +io/IOutputStream.h +io/XIO.cpp +io/XIO.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/XThread.h +net/CNetwork.cpp +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile +net/XNetwork.cpp +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CPlatform.cpp +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IPlatform.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/COutputPacketStream.cpp +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.cpp +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/XScreen.cpp +synergy/XSynergy.cpp + +indentation and other formatting changes. also cleaned up +#includes. + +---------- +2002/06/10 16:49:46 crs +base/CLog.cpp +base/CStopwatch.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThreadRep.h +net/CNetwork.cpp +net/CNetwork.h +platform/CMSWindowsClipboard.h +platform/CMSWindowsScreen.h +platform/CWin32Platform.h +server/CSynergyHook.h +server/synrgyhk.dsp + +win32 changes. now including windows.h with WIN32_LEAN_AND_MEAN +to avoid including some stuff we don't want (like winsock). + +---------- +2002/06/10 11:09:02 crs +README + +fixes. + +---------- +2002/06/10 11:08:02 crs +README + +updates. + +---------- +2002/06/10 11:00:55 crs +README +examples/synergy.conf +examples/synergy.linux.init +examples/synergyd.linux.init + +added example files and a README. + +---------- +2002/06/10 10:08:36 crs +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp + +now allowing a screen to be its own neighbor to allow wrapping. +also no longer warping mouse to 0,0 when setting server screen +info. that was causing the mouse to jump if the server screen +had itself as its left or top neighbor (directly or indirectly) +once a screen could be its own neighbor. + +---------- +2002/06/10 09:49:21 crs +server/CConfig.cpp + +fixed stripping of comments from configuration streams. + +---------- +2002/06/10 09:49:03 crs +client/client.cpp +server/server.cpp + +changed "permitted" to "supported" in error messages. + +---------- +2002/06/09 23:08:18 crs +client/CClient.cpp + +no longer camps if the server sends an error message. + +---------- +2002/06/09 22:20:28 crs +client/CClient.cpp +client/CClient.h +client/client.cpp + +added support for camping, i.e. repeatly trying to connect to the +server until we succeed. + +---------- +2002/06/09 22:20:01 crs +mt/CTimerThread.cpp +mt/CTimerThread.h + +CTimerThread now allows zero and negative timeouts. a negative +timeout never times out and CTimerThread is a no-op. + +---------- +2002/06/09 18:03:32 crs +platform/CXWindowsScreen.cpp + +now using ":0.0" as the display if DISPLAY isn't set. + +---------- +2002/06/09 18:00:03 crs +notes + +checkpoint. + +---------- +2002/06/09 17:59:32 crs +client/client.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp + +added command line option to choose the screen name. also now +using the hostname as the default name. this is on both client +and server. + +---------- +2002/06/09 17:35:28 crs +mt/CThreadRep.cpp + +added FIXME comment. + +---------- +2002/06/09 17:21:33 crs +server/CServer.cpp + +fixed problem with setConfig(). if the new config didn't +include a screen that was already connected under an alias +then that screen wouldn't be disconnected and removed from +the screen list until the screen voluntarily disconnected. +at that time removeConnection() would assert because the +screen name would not be found. now using the canonical +name in the protocol object as well as CServer. this +allows setConfig() to always detect removed screens and +disconnect them. + +---------- +2002/06/09 16:53:57 crs +platform/CUnixPlatform.cpp + +now exits instead of restarting if child dies due to an +unexpected signal. + +---------- +2002/06/09 16:53:25 crs +client/client.cpp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/XSocket.cpp +net/XSocket.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp +synergy/ProtocolTypes.h + +added command line and configuration file arguments to choose +the address and port to listen on or connect to. changed the +default port and put it in ProtocolTypes.h. the HTTP port is +now no longer opened unless the --http argument is supplied +or the config file includes it. + +---------- +2002/06/08 23:24:40 crs +server/CConfig.cpp +server/CConfig.h +server/CServer.cpp +server/server.cpp + +added aliases to configuration. an alias is another name for +a screen. it's expected that the server will want to accept +a given client under several names (e.g. the hostname and the +FQDN). + +---------- +2002/06/08 21:48:16 crs +notes + +checkpoint. + +---------- +2002/06/08 21:48:00 crs +all.dsp +base/CLog.cpp +base/CLog.h +base/base.dsp +base/common.h +base/stdpre.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +http/http.dsp +io/io.dsp +mt/CThreadRep.cpp +mt/mt.dsp +net/net.dsp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/IPlatform.h +platform/platform.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy/IPrimaryScreen.h +synergy/synergy.dsp + +win32 changes. changed names of binaries. added support for +running as (and installing/installing) a service. added +support for multiple desktops (NT only, 95 doesn't support +multiple desktops). + +---------- +2002/06/04 12:26:23 crs +Makefile +client/Makefile +client/client.cpp +client/client.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IPlatform.h +platform/Makefile +server/Makefile +server/server.cpp +server/server.dsp +synergy.dsw +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h +synergy/Makefile +synergy/synergy.dsp +test.cpp + +refactored some common platform dependent stuff into a new +library: platform. also removed test.cpp. + +---------- +2002/06/04 11:06:26 crs +client/CClient.cpp +client/client.cpp +server/CServer.cpp +server/CServerProtocol.cpp +server/server.cpp +synergy/ProtocolTypes.h +synergy/Version.h + +added command line parsing, restartability, and daemonizing to +client. broke win32 stuff though. also moved version and +copyright constants into a new file and renamed protocol +version constants. + +---------- +2002/06/04 11:03:34 crs +base/CLog.cpp + +fixed delete bug in printt -- when skipping file and line the +deleted pointer was wrong. + +---------- +2002/06/04 11:02:33 crs +synergy/CXWindowsClipboard.cpp + +fixed timeout when getting selection -- forgot to set flag to +terminate event loop. + +---------- +2002/06/03 18:53:18 crs +base/CLog.cpp +base/CLog.h +server/CConfig.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/CXWindowsScreen.cpp +synergy/IPrimaryScreen.h + +changes to add command line arguments. also added automatic +restarting and daemonizing on unix. daemon sends log messages +to syslog. unix now reads config file from file named on +command line; if no command line arg then uses effective +user's config file and if that's not there it finally tries +/etc/synergy.conf. if there are no screens configured then +one is added for the primary screen. broke some startup +stuff on win32. + +also now timing out if X primary screen can't grab the mouse +and keyboard. the server will just give up trying to switch +screens. the grabs will fail is some other app has a grab +and won't release it. note that kdm grabs the keyboard for +the duration that the login window is displayed, effectively +disabling synergy. + +---------- +2002/06/03 16:36:45 crs +base/CLog.cpp +base/CLog.h + +added a method to set the filter given a priority string (instead +of a number). fixed a comment related to what those priority +strings are. added a CLOG_PRINT priority which is never filtered +and suppresses the trace info and the priority level message. +it's intended as a way to output a message through the logger +without getting extra output. + +---------- +2002/06/03 16:34:22 crs +base/CString.cpp +base/CString.h +base/Makefile +base/base.dsp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h + +moved case insensitive comparison utility functions into CString +from CHTTPProtocol. + +---------- +2002/06/03 13:45:30 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/Makefile +synergy/XScreen.cpp +synergy/XScreen.h +synergy/synergy.dsp + +added better handling of X server disconnecting unexpectedly. +the apps still exit but they do it in a mostly controlled +manner. in particular, the server threads except the one +processing primary screen events will terminate gracefully. +this will be important should the server ever allow HTTP +clients to rewrite the configuration file. + +note that X makes it effectively impossible to continue once +the X server disconnects. even if it didn't it would be +difficult for synergy to recover. users will have to add +synergy to the X display manager's startup script if they +expect the server to be restarted. alternatively, we could +add code to fork synergy at startup; the child would do +the normal work while the parent would simply wait for the +child to exit and restart it. + +---------- +2002/06/02 23:07:57 crs +net/CTCPListenSocket.cpp + +shortened poll() timeout. + +---------- +2002/06/02 22:57:50 crs +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +net/CTCPSocket.cpp + +changed buffered output stream to wait() when flush()ing instead +of polling/sleeping. changed CTCPSocket to not use thread +cancellation but to instead use m_connected to exit the thread. +also shortened poll timeout. + +---------- +2002/06/02 21:35:20 crs +synergy/CMSWindowsScreen.cpp +synergy/CXWindowsScreen.cpp + +make sleep shorter in poll/sleep getEvent() loops. + +---------- +2002/06/02 21:03:38 crs +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsUtil.cpp + +removed poll/sleep code to improve performance. + +---------- +2002/06/02 19:04:24 crs +client/CXWindowsSecondaryScreen.cpp + +now ignores key if there's no key mapped for a required modifier. +was asserting (on the wrong expression). + +---------- +2002/06/02 18:49:35 crs +client/CClient.cpp +client/client.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +server/CServer.cpp +server/server.cpp + +added SIGINT and SIGTERM handling to unix client and server. +either signal causes the main thread to be cancelled. added +necessary code to make main thread cancellation clean up +nicely. + +---------- +2002/06/02 13:34:35 crs +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +server/CHTTPServer.cpp +server/CHTTPServer.h + +added a maximum request size to CHTTPProtocol so we can bail +on clients that cause us to use too much memory. also put +methods in CHTTPRequest to get/set headers and changed the +data structure used to store them. fixed a couple of other +miscellaneous bugs in CHTTPProtocol.cpp. + +---------- +2002/06/02 11:49:46 crs +mt/CCondVar.h +server/CServer.cpp +server/CServer.h + +now limiting number of simultaneous HTTP requests being handled +at once. this is to prevent denial of service. + +---------- +2002/06/01 19:26:11 crs +base/CLog.cpp +base/CLog.h +base/CString.h +base/XBase.h +base/base.dsp +base/common.h +base/stdfstream.h +base/stdistream.h +base/stdlist.h +base/stdmap.h +base/stdostream.h +base/stdpost.h +base/stdpre.h +base/stdset.h +base/stdsstream.h +base/stdvector.h +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/XHTTP.cpp +http/http.dsp +io/CStreamBuffer.h +io/io.dsp +mt/mt.dsp +net/net.dsp +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy.dsw +synergy/CXWindowsClipboard.h +synergy/IPrimaryScreen.h +synergy/synergy.dsp + +fixes, mainly for windows. first, had to add a notification from +CServer to the primary screen when the configuration changes so it +can make necessary adjustments (the win32 primary screen must tell +the hook dll about the new jump zones). + +changed includes of some std c++ library files to go through +our own include files. these wrap the include with stuff to +keep vc++ quiet when compiling at warning level 4, which is +what it does now. it also works around missing and + on g++2.96. + +added missing std:: where necessary. g++ doesn't really support +namespaces so it lets references without the namespace slip +through. + +added workaround or fix. not sure if istringstream::str(string) +should reset eofbit. it does on g++ but does not on vc++. +added clear() after str() so it works either way. + +added low-level keyboard hook to win32. if available (it's only +available on NT SP3 and up) it allows us to catch and handle +alt+tab, alt+esc, ctrl+esc, and windows key hot keys. i think +that leaves only ctrl+alt+del and accessibility functions +uncaught on those systems. + +---------- +2002/06/01 10:52:02 crs +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp + +added mutex to all public methods that didn't already have it. +fixed two blown assertions. first, if user tried to switch to +a client that had connected but hadn't yet sent the first info +message it would assert on the zero size screen. second, if +the primary screen was handling a mouse motion on behalf of a +secondary screen when that secondary screen disconnected then +an assert would blow because the primary screen would call +onMouseMoveSecondary() but m_protocol on the active screen is +NULL because disconnecting the active secondary screen caused +the mouse to jump to the primary screen. + +---------- +2002/05/31 18:35:53 crs +server/CConfig.h + +changed iterator to use iterator_traits directly instead of +std::iterator to support the old STL on grace. + +---------- +2002/05/31 18:18:29 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h + +server now rejects clients that are not in the configuration. +added a protocol message to indicate this. + +---------- +2002/05/31 18:09:43 crs +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp + +fixed setConfig() to disconnect secondary screens that aren't +in the new configuration. + +---------- +2002/05/31 18:08:08 crs +server/CConfig.cpp +server/CConfig.h + +made isScreen() a const method. + +---------- +2002/05/31 17:32:26 crs +server/CConfig.cpp +server/CConfig.h +server/server.cpp + +added I/O for configuration files and changed the server to use +an external file for its configuration (was hard coding a config +for testing). + +---------- +2002/05/31 14:44:54 crs +server/CConfig.cpp +server/CScreenMap.cpp +server/Makefile +server/server.dsp + +finished renaming CScreenMap to CConfig. + +---------- +2002/05/31 14:43:23 crs +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CScreenMap.cpp +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/server.cpp + +checkpoint. changed CScreenMap to CConfig. must still change +CScreenMap.cpp to CConfig.cpp. + +---------- +2002/05/31 14:34:16 crs +server/CConfig.h +server/CHTTPServer.cpp +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.h +server/CSynergyHook.cpp +server/server.cpp + +checkpoint. renamed CScreenMap.h to CConfig.h. will be +changing CScreenMap to CConfig everywhere. + +---------- +2002/05/31 14:25:26 crs +base/CLog.cpp +base/CLog.h +client/client.cpp +server/server.cpp + +added methods to CLog for getting the outputter, getting and +setting the priority filter, and added code for thread safety. +added code to apps to enable thread safety in CLog. + +---------- +2002/05/30 16:13:16 crs +Makefile +base/common.h +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/Makefile +http/XHTTP.cpp +http/XHTTP.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/Makefile + +added basic support for an embedded HTTP server. server +currently supports editing the screen map but changing +the map won't behave correctly if there are connected +screens. + +---------- +2002/05/30 16:11:59 crs +net/CTCPSocket.cpp +net/CTCPSocket.h + +fixed bug in closing down a socket. + +---------- +2002/05/27 18:55:51 crs +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h + +updated win32 clipboard to match new model. + +---------- +2002/05/27 18:35:14 crs +notes + +checkpoint + +---------- +2002/05/27 18:30:13 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h + +removed getEventMask() from primary screen. added a class to +CXWindowsUtil that installs/uninstalls an X error hander. +using that in primary screen, clipboard, and util to ensure +that certain errors don't kill the app. + +---------- +2002/05/27 18:28:06 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +removed getEventMask() and fixed some comments. also now using +toggle key states in updateModifiers(). + +---------- +2002/05/27 17:05:34 crs +synergy/CXWindowsClipboard.cpp + +changed lesstif hack to only apply to the CLIPBOARD selection. +apprently the PRIMARY selection must follow the ICCCM protocol +correctly. + +---------- +2002/05/27 16:51:07 crs +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h + +added missing files from previous submit. + +---------- +2002/05/27 16:22:59 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/Makefile + +checkpoint. changed clipboard model. the clipboard can only +be accessed now between open()/close(). ownership of the +clipboard is asserted via the empty() method. this parallels +the win32 model (but the win32 code hasn't been updated yet). + +refactored X11 clipboard code. moved the bulk of it into +CXWindowsClipboard and moved some comment event handling into +CXWindowsScreen. changed how requests are processed into a +hopefully easier to understand model. added support for getting +clipboard from and sending clipboard to motif (or at least +lesstif) clients. sending to lesstif required a hack to work +around an apparent bug in lesstif. + +---------- +2002/05/24 17:54:34 crs +notes + +checkpoint + +---------- +2002/05/24 17:54:28 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +added screen locking support to win32. added support for +resolution changing (only semi-supported on X because that +has no means for resizing screen anyway). also fixed some +clipboard problems on win32. + +---------- +2002/05/24 14:37:12 crs +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h + +added support for locking to a screen when the sroll lock is +toggled on or when any key or button is pressed. fully +implemented on X but stubbed out for now on win32. + +---------- +2002/05/23 18:35:15 crs +notes + +checkpoint + +---------- +2002/05/23 18:35:08 crs +server/CMSWindowsPrimaryScreen.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h + +added support for mouse wheel on win32. + +---------- +2002/05/23 15:50:38 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp + +added support for mouse wheel on X. + +---------- +2002/05/23 15:00:39 crs +server/server.cpp + +added a third screen to hard coded map for testing purposes. + +---------- +2002/05/23 15:00:13 crs +server/CServer.cpp + +fixed log message. + +---------- +2002/05/23 14:56:03 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +server/CServerProtocol.cpp +server/CServerProtocol1_0.cpp +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h + +server no longer asserts when a client connects with a name that's +already in use by another client. also added reporting of errors +from the server to clients so clients can report meaningful +messages to users. + +---------- +2002/05/23 14:04:43 crs +notes + +checkpoint + +---------- +2002/05/23 14:04:35 crs +net/CNetwork.h +synergy/CXWindowsScreen.h + +changed structs to classes. there should be no more structs now. + +---------- +2002/05/22 17:09:08 crs +notes + +checkpoint. + +---------- +2002/05/22 17:08:37 crs +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CProtocolUtil.cpp + +removed unnecessary call in screen class, added logging calls +in clipboard class, and added another cast in protocol util +to avoid warning on win32. + +---------- +2002/05/22 17:05:26 crs +server/CSynergyHook.cpp + +now letting some key events filter through. this allows the +keyboard lights to track toggle changes. however, it also +seems to let through keyboard events that shouldn't get +through. + +---------- +2002/05/22 17:02:58 crs +server/CScreenMap.cpp + +fixed incorrect for-loop over directions conditional. + +---------- +2002/05/22 17:01:17 crs +base/CLog.cpp +base/CLog.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h + +win32 changes. replaced log dialog hack with a windows console +window. now attaching thread input queues as necessary. shifted +code around so toggling toggle keys is immediately reflected by +secondary screen's keyboard. now setting extended key flag for +keys that need it. fixed handling of shift + caps-lock. added +handling of keys that should distinguish between left and right +but don't. fixed get/set of active window on leave/enter of +primary screen. replaced 1x1 primary window with a full screen +window to work around a problem with losing key events. changed +calculation of mouse move deltas. + +---------- +2002/05/22 16:56:06 crs +net/CTCPListenSocket.cpp + +fixed type of socket handle (from int to CNetwork::Socket). + +---------- +2002/05/22 16:55:19 crs +mt/CTimerThread.cpp + +removed blank line. + +---------- +2002/05/22 16:55:05 crs +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp + +changed un-inlined code to avoid bogus VC++ level 4 warnings. +added support for more win32 thread priorities. + +---------- +2002/05/22 16:51:59 crs +client/client.cpp + +fixed parameter type for socket port. + +---------- +2002/05/22 16:43:14 crs +base/common.h + +changed set of disabled win32 warnings. + +---------- +2002/05/22 16:42:48 crs +client/CClient.cpp + +fixed NULL dereference. + +---------- +2002/05/22 16:41:24 crs +base/common.h + +changed set of disabled win32 warnings. + +---------- +2002/05/22 16:40:51 crs +base/CLog.cpp +base/CLog.h + +replaced logging dialog hack with a windows console window. + +---------- +2002/05/22 16:40:38 crs +client/CClient.cpp + +fixed NULL dereference. + +---------- +2002/05/05 23:37:12 crs +net/CTCPSocket.cpp + +removed setting send buffer to zero size. it just reduced +performance. + +---------- +2002/05/05 19:52:03 crs +client/CXWindowsSecondaryScreen.cpp + +replaced True/False with true/false when assigning to m_repeat. +also should now work if the first element of a modifier +keymapping is 0. that won't normally be the case but xmodmap +was doing weird things on grace. if the first element is 0 +it'll try the second element. if that's also zero then that +modifier will be ignored. + +---------- +2002/05/05 19:38:09 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp + +fixes for win32 keyboard. + +---------- +2002/05/04 19:43:20 crs +client/CXWindowsSecondaryScreen.cpp + +fixed caps-lock handling. + +---------- +2002/05/04 18:33:48 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. added half duplex for num lock. + +---------- +2002/05/04 18:31:54 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +checkpoint. fixing up handling of half-duplex num-lock. + +---------- +2002/05/04 18:09:02 crs +client/CXWindowsSecondaryScreen.cpp + +checkpoint. changed when toggle keys toggle (now always on +release). must see if this works. + +---------- +2002/05/04 18:08:22 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +net/CTCPSocket.cpp +server/CMSWindowsPrimaryScreen.cpp + +Fixes for win32 key handling. + +---------- +2002/05/04 11:23:11 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of shift + caps-lock. those two modifiers should +cancel out if the keysym is subject to case conversion, but not +otherwise. also added logging of key lookup code. + +---------- +2002/05/03 12:23:48 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of shift+tab on a system that can map ISO_Left_Tab. +now tries to map ISO_Left_Tab without shift first then falls back +to Tab (note that if ISO_Left_Tab can be mapped but requires a +modifier then the modifier will be added). also changed attempt +to map ISO_Left_Tab as a backup to Tab to request the shift +modifier whether or not the primary screen requested it. + +---------- +2002/05/03 12:14:55 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of ISO_Left_Tab when that is not mapped to a +keycode by mapping it to tab with shift pressed. + +---------- +2002/05/03 11:49:30 crs +client/CXWindowsSecondaryScreen.cpp + +removed attempt to make release/press of a repeating key use +the same server time. was getting what appears to be deadlock +but not sure why. + +---------- +2002/05/03 11:26:44 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. made changes to support key autorepeats on X. + +---------- +2002/05/02 11:44:21 crs +synergy/COutputPacketStream.cpp + +Indentation change. + +---------- +2002/05/02 11:43:52 crs +io/CStreamBuffer.cpp +net/CTCPSocket.cpp + +Fixed bug in stream buffer that could cause data to be +inserted out of order. Also removed unnecessary limit +on writes to the TCP socket. + +---------- +2002/05/02 11:33:34 crs +io/CStreamBuffer.cpp + +checkpoint debugging of stream buffer. + +---------- +2002/05/01 16:30:20 crs +synergy/CXWindowsScreen.cpp + +Was trying to avoid sending clipboard if timestamp wasn't +changed but clipboard owners may not update that timestamp +when the selection is changed. Disabled the timestamp check. + +---------- +2002/05/01 16:17:57 crs +server/CServer.cpp +synergy/CXWindowsScreen.cpp + +Added more checks to avoid sending unchanged clipboard data. +Still takes too long to query the clipboard owner for info +(maybe 1/10th second) but not sure why or if that can be +improved. + +---------- +2002/05/01 15:31:47 crs +Make-linux +net/CNetwork.h +net/CTCPSocket.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. turned off nagle and send buffering. also +added test to skip clipboard conversion if a previous +conversion from that owner failed. + +---------- +2002/05/01 14:36:52 crs +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp + +Fixed uninitialized variable when computing toggle mask. Also +reduced priority of some mouse motion log messages. + +---------- +2002/05/01 14:35:55 crs +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/net.dsp + +removed obsolete files. + +---------- +2002/04/30 18:30:05 crs +client/CXWindowsSecondaryScreen.cpp + +added fallback for missing numpad movement keys (if there's no +mapping for those keys then the non-keypad versions are tried). + +---------- +2002/04/30 17:48:11 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +checkpoint. now sending toggle modifier state when entering +a screen. this allows the secondary screen to set it's +modifier state to match the primary screen's state. this is +not strictly necessary since each keystroke should adjust the +modifier state as needed to get the right result. + +---------- +2002/04/30 16:25:29 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +Added logging and handling of "half-duplex" caps-lock key. + +---------- +2002/04/30 16:23:30 crs +Make-linux +Make-solaris + +Changed name for auto-generated dependency files from +Makedepend to .depend. + +---------- +2002/04/30 16:23:03 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServerProtocol1_0.cpp +server/server.rc +synergy/CClipboard.cpp +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/synergy.dsp + +Fixes to get win32 client and server up to date. + +---------- +2002/04/29 14:40:01 crs +base/CFunctionJob.h +base/CLog.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.h +base/IInterface.h +base/IJob.h +base/TMethodJob.h +base/XBase.h +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +io/CBufferedInputStream.h +io/CBufferedOutputStream.h +io/CInputStreamFilter.h +io/COutputStreamFilter.h +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/XIO.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.h +mt/XThread.h +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.h +net/CSocketOutputStream.h +net/CSocketStreamBuffer.h +net/CTCPListenSocket.h +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/XNetwork.h +net/XSocket.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.h +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.h +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/XSynergy.h + +Indentation changes. + +---------- +2002/04/29 14:25:24 crs +client/CClient.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp + +Added some validation of protocol message parameters. + +---------- +2002/04/29 14:12:48 crs +synergy/CXWindowsScreen.cpp + +Shortened timeout on waiting for clipboard response. + +---------- +2002/04/29 14:08:48 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +Made event selection a little more robust. Also fixed failure +to marshall clipboard data when updating primary clipboards. + +---------- +2002/04/29 13:49:56 crs +client/CXWindowsSecondaryScreen.cpp + +Added missing event mask. + +---------- +2002/04/29 13:31:44 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +checkpoint. changed protocol to better handle clipboards. now +sending a sequence number with enter messages. screens use that +sequence number in clipboard grab and data messages. the server +uses the sequence number to order messages across clients. also +changed secondary screens to send clipboard updates on leaving +(or when grab occurs when not active) instead of on a query from +the server. primary effectively does the same. the query +message has been removed. + +---------- +2002/04/29 11:58:17 crs +client/CClient.cpp + +changed logging levels. + +---------- +2002/04/28 00:46:15 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +Clipboard improvements. Still not working right. Nedit +doesn't work at all but at least now there's a timeout to +prevent synergy from hanging waiting on a reply. + +---------- +2002/04/27 18:49:03 crs +base/CLog.cpp +base/CLog.h +client/CClient.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp +net/CNetwork.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CProtocolUtil.cpp +synergy/CXWindowsScreen.cpp + +Added more debug levels and moved some annoying debug messages +to those levels. Default log level is now DEBUG for debug +builds and INFO for release builds. + +---------- +2002/04/27 18:06:40 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CProtocolUtil.cpp +synergy/CXWindowsScreen.cpp +synergy/ProtocolTypes.h + +checkpoint. changed CProtocolUtil::readf() to store 1 and 2 +byte integers into pointers to 1 and 2 byte integers. was +always assuming pointers to 4 byte integers. + +---------- +2002/04/27 14:19:53 crs +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/ClipboardTypes.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +Added support for multiple clipboards. This is mainly to +support both PRIMARY and CLIPBOARD selections on X windows. + +---------- +2002/04/27 14:19:19 crs +Makecommon + +set TARGETS macro to BIN and LIB targets. + +---------- +2002/04/26 20:15:59 crs +notes + +updated + +---------- +2002/04/26 20:14:46 crs +client/CXWindowsSecondaryScreen.cpp + +Fixed caps-lock and num-lock behavior. It seems to work okay +now but did notice one problem: when powerbook is primary and +num-lock is on the keypad works fine until shift is pressed +(and released); after that the keypad only works while the +shift key is down. + +---------- +2002/04/26 20:12:55 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +Added hack to handle "half-duplex" caps-lock key on powerbook. +That key only reports press when pressed and released when +caps-lock is activated and only reports release when pressed +and released when caps-lock is deactivated. I don't know of a +way to detect this behavior so it may have to be configured by +the user. The code assumes normal behavior; will have to add +code to set the flag (perhaps from a user configuration). + +---------- +2002/04/26 17:38:01 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/KeyTypes.h + +changed processing of key events in X. secondary screen now +activates/deactivates modifiers as necessary to get a keycode +interpreted as the expected keysym. still some work and +testing to do on this. + +---------- +2002/04/25 10:44:01 crs +notes + +Added notes on keyboard handling. + +---------- +2002/04/25 10:43:53 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +added handling for DestroyNotify of clipboard requestors. + +---------- +2001/11/26 22:36:51 crs +synergy/CXWindowsScreen.cpp + +checkpoint. improvements to clipboard transfer on X windows. +not detecting a change to clipboard when synergy window isn't +the owner (since there's no event for this; we'll have to +check when we leave the screen i guess). large transfers +don't seem to work. + +---------- +2001/11/26 22:09:53 crs +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. testing clipboard transfer on X windows. + +---------- +2001/11/25 22:20:41 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. implementing clipboard owner in x windows. + +---------- +2001/11/25 18:44:13 crs +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h + +fixed function signature. + +---------- +2001/11/25 18:42:13 crs +Make-linux +Make-solaris +Makecommon +client/Makefile +server/Makefile + +executables are now built into a common area on unix (and they +already were on win32). + +---------- +2001/11/25 18:32:41 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +net/CNetwork.cpp +notes +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy.dsw +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/Makefile +synergy/ProtocolTypes.h +synergy/synergy.dsp + +added platform independent clipboard transfer stuff +clipboard owner support (MS windows done, X windows partial) +added key transfer on ms windows +mutex fixes in CClient (had race conditions) +faster debug output in ms windows +changed temporary screen name to "secondary" +network fixes on ms windows (poll returned wrong result) +fixed transparent cursor on ms windows + +---------- +2001/11/19 00:33:36 crs +Make-linux +all.dsp +base/BasicTypes.h +base/CLog.cpp +base/CLog.h +base/XBase.cpp +base/base.dsp +base/common.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile +client/client.cpp +client/client.dsp +client/client.rc +client/resource.h +io/io.dsp +mt/CCondVar.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/mt.dsp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/Makefile +net/XNetwork.cpp +net/XNetwork.h +net/net.dsp +notes +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile +server/makehook.dsp +server/resource.h +server/server.cpp +server/server.dsp +server/server.rc +server/synrgyhk.dsp +synergy.dsw +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/synergy.dsp + +checkpoint. merging win32 code. server on X is currently broken +and client probably is. + +---------- +2001/11/18 23:14:28 crs +Makefile +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile +client/client.cpp +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile +server/server.cpp +synergy/CClient.cpp +synergy/CClient.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/Makefile +synergy/client.cpp +synergy/server.cpp + +moved client and server files into their own respective +directories. + +---------- +2001/11/13 23:34:12 crs +synergy/CServer.cpp +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/Makefile + +added preliminary support for getting the X selection. + +---------- +2001/11/11 21:27:36 crs +synergy/CServer.cpp + +fixed clamping when mapping to a different screen when beyond +bottom or right of source screen. + +---------- +2001/11/11 21:15:30 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/Makefile + +factored common X windows screen stuff into a common base class. + +---------- +2001/11/10 22:28:37 crs +notes + +updated notes. + +---------- +2001/11/10 22:28:30 crs +Makefile + +added main app directory to build. + +---------- +2001/10/25 22:17:17 crs +io/CBufferedInputStream.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThreadRep.cpp +net/CNetworkAddress.cpp +net/CSocketInputStream.cpp +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsSecondaryScreen.cpp + +removed unnecessary global scoping operators. + +---------- +2001/10/25 22:09:27 crs +synergy/CXWindowsSecondaryScreen.cpp + +changed hider window to move underneath mouse when leaving the +screen. this makes it so if the mouse is moved locally, it'll +reappear where it was last seen. + +---------- +2001/10/25 21:40:29 crs +synergy/CClient.cpp +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h + +changed some method names and removed warpCursor() from +secondary screen interface. + +---------- +2001/10/24 23:29:29 crs +synergy/CServer.cpp +synergy/CServer.h + +now handling disconnect of secondary screen that has the cursor +by jumping back to the primary screen (without trying to notify +the now disconnected secondary screen). also fixed blown assert +in mapPosition(). + +---------- +2001/10/24 22:33:24 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h + +made calls to X thread safe. + +---------- +2001/10/23 22:45:59 crs +notes + +more notes. + +---------- +2001/10/23 22:41:46 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h + +added cursor hiding. + +---------- +2001/10/23 21:23:29 crs +base/CLog.cpp + +can now filter logging by level. + +---------- +2001/10/23 21:13:08 crs +synergy/CServer.cpp + +fixed blown assert trying to find neighbor when there was none. + +---------- +2001/10/21 00:21:21 crs +synergy/CClient.cpp + +fixed handling of stream ownership. + +---------- +2001/10/21 00:21:02 crs +io/CBufferedInputStream.cpp +io/CBufferedOutputStream.cpp +mt/CThreadRep.cpp +net/CTCPSocket.cpp +net/CTCPSocket.h +synergy/CServer.cpp +synergy/server.cpp + +fixed bugs in handling streams. + +---------- +2001/10/20 20:43:31 crs +Make-linux +mt/CThreadRep.cpp + +threading fixes. had sigmask set in wrong place. was setting +m_exit flag potentially after the object had been destroyed. +most importantly, RTTI must be enabled on PPC to avoid SIGILL. + +---------- +2001/10/14 19:16:54 crs +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp + +some debugging code. + +---------- +2001/10/14 18:29:43 crs +base/CLog.h +mt/CMutex.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +synergy/CClient.cpp +synergy/CClient.h +synergy/CProtocolUtil.cpp +synergy/CServerProtocol1_0.cpp +synergy/client.cpp +synergy/server.cpp + +fixed timeout bug in CThreadRep::wait() (negative timeout wouldn't +wait forever). also fixed early return from sleep due to signal. +now forcing client to initialize CThread to ensure global mutex +gets initialized before threads are used. + +---------- +2001/10/14 16:58:01 crs +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h + +removed exception specifications. thread exceptions weren't +being listed and they'd have to be added to every one. just +doesn't seem worth the trouble. + +---------- +2001/10/14 14:56:06 crs +synergy/CProtocolUtil.cpp + +stupid bug fixes. writef() used the wrong variable as the number +of bytes to write. readf() forgot to prepare the va_list. + +---------- +2001/10/14 14:38:45 crs +base/CLog.cpp +base/CLog.h + +forgot to add the logger files. + +---------- +2001/10/14 14:37:41 crs +Make-linux +base/Makefile +synergy/CClient.cpp +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServerProtocol1_0.cpp +synergy/CXWindowsPrimaryScreen.cpp + +added logging facility and added a bunch of log messages. + +---------- +2001/10/08 19:24:46 crs +Makefile +notes +synergy/CClient.cpp +synergy/CClient.h +synergy/CServer.cpp +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h +synergy/Makefile +synergy/client.cpp +synergy/server.cpp + +checkpoint. first cut of client and server apps. not tested +yet but they compile and *should* work as is. + +---------- +2001/10/06 14:18:01 crs +Make-linux +Makefile + +updated old files to new implementation + +---------- +2001/10/06 14:13:28 crs +BasicTypes.h +CClient.cpp +CClient.h +CEvent.h +CEventQueue.cpp +CEventQueue.h +CMessageSocket.cpp +CMessageSocket.h +CProtocol.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CServer.h +CSocket.cpp +CSocket.h +CSocketFactory.cpp +CSocketFactory.h +CString.h +CTrace.cpp +CTrace.h +CUnixEventQueue.cpp +CUnixEventQueue.h +CUnixTCPSocket.cpp +CUnixTCPSocket.h +CUnixXScreen.cpp +CUnixXScreen.h +CXScreen.cpp +CXScreen.h +IClient.h +IClipboard.h +IEventQueue.h +IJob.h +IScreen.h +IServer.h +ISocket.h +KeyTypes.h +Make-linux +Make-solaris +Makecommon +Makefile +MouseTypes.h +TMethodJob.h +XBase.cpp +XBase.h +XSocket.h +base/BasicTypes.h +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.h +base/IInterface.h +base/IJob.h +base/Makefile +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/common.h +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/Makefile +io/XIO.cpp +io/XIO.h +main.cpp +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/Makefile +mt/XThread.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile +net/XSocket.cpp +net/XSocket.h +notes +synergy/CClient.cpp +synergy/CClient.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/Makefile +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h +test.cpp + +Started over. + +---------- +2001/05/14 21:14:49 crs +MouseTypes.h + +flipped order of buttons to match default X setup. + +---------- +2001/05/14 21:14:25 crs +CClient.cpp +CEvent.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CXScreen.cpp +CXScreen.h +IScreen.h +KeyTypes.h + +added other mouse and key event handling to CXScreen. key repeat +isn't implemented and modifier masks are ignored. modifier masks +are new; they indicate the modifier key (shift, ctrl, etc) state +at the time of the key event. + +---------- +2001/05/13 12:43:16 crs +CUnixTCPSocket.cpp +CUnixTCPSocket.h + +more fixes to reduce latency. nagle agorithm doesn't seem to +stay off on a socket on linux because a connection clearly +doesn't send data as often as possible. will have to implement +a UDP socket to reduce overhead and avoid these delays. wanted +to do that anyway. + +---------- +2001/05/13 12:21:11 crs +CUnixTCPSocket.cpp +CXScreen.cpp + +fixes to avoid update delays. + +---------- +2001/05/13 12:07:32 crs +CMessageSocket.cpp + +fixed bug in read() that miscalculated the message length. + +---------- +2001/05/13 11:40:29 crs +BasicTypes.h +CClient.cpp +CClient.h +CEvent.h +CEventQueue.cpp +CEventQueue.h +CMessageSocket.cpp +CMessageSocket.h +CProtocol.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CServer.h +CSocket.cpp +CSocket.h +CSocketFactory.cpp +CSocketFactory.h +CString.h +CTrace.cpp +CTrace.h +CUnixEventQueue.cpp +CUnixEventQueue.h +CUnixTCPSocket.cpp +CUnixTCPSocket.h +CUnixXScreen.cpp +CUnixXScreen.h +CXScreen.cpp +CXScreen.h +IClient.h +IClipboard.h +IEventQueue.h +IJob.h +IScreen.h +IServer.h +ISocket.h +KeyTypes.h +Make-linux +Makefile +MouseTypes.h +TMethodJob.h +XBase.cpp +XBase.h +XSocket.h +main.cpp +tools/depconv + +initial revision of synergy. currently semi-supports X windows +on unix, but client screens don't simulate events other than +mouse move. also not supporting clipboard at all yet and the +main app is just a temporary framework to test with. must +clean up protocol and communication. + +---------- diff --git a/FAQ b/FAQ new file mode 100644 index 0000000..0962f02 --- /dev/null +++ b/FAQ @@ -0,0 +1,187 @@ +Synergy Frequently Asked Questions +================================== + +Questions +--------- +1. Why doesn't ctrl+alt+del work on secondary screens? +2. Can the server and client be using different operating systems? +3. What's the difference between synergy and x2x, x2vnc, etc? +4. What does "Cannot initialize hook library" mean? +5. What security/encryption does synergy provide? +6. What should I call my screens in the configuration? +7. Why do my CapsLock and NumLock keys act funny? +8. Can synergy share the display in addition to the mouse and keyboard? +9. Can synergy do drag and drop between computers? +10. Do AltGr or Mode-Switch work? +11. Why isn't synergy ported to platform XYZ? +12. My client can't connect. What's wrong? +13. Linking fails on Solaris. What's wrong? +14. The screen saver never starts. Why not? +15. I can't switch screens anymore for no apparent reason. Why? + +Answers +------- +1. Why doesn't ctrl+alt+del work on secondary screens? + + Synergy isn't able to capture ctrl+alt+del on PC compatible + systems because it's handled completely differently than + other keystrokes. However, when the mouse is on a client + screen, pressing ctrl+alt+pause will simulate ctrl+alt+del + on the client. (A client running on Windows NT, 2000, or XP + must be running as a service for this to work.) + +2. Can the server and client be using different operating systems? + + Yes. The synergy network protocol is platform neutral so + synergy doesn't care what operating systems are running on + the server and clients. + +3. What's the difference between synergy and x2x, x2vnc, etc? + + Unlike x2x, synergy supports any number of computers and + it doesn't require X on Microsoft Windows platforms. It + also has more advanced clipboard support and synchronizes + screensavers. x2vnc is also limited to two computers, + requires the separate vnc package, and is really only + appropriate for using an X system to control a non-X system. + However, the right tool for the job is whatever tool works + best for you. + +4. What does "Cannot initialize hook library" mean? + + This error can occur on a synergy server running on a + Microsoft Windows operating system. It means that synergy + is already running or possibly was not shut down properly. + If it's running then first end the synergy task. If it's + not then try logging off and back on or rebooting then + starting synergy again. + +5. What security/encryption does synergy provide? + + Synergy provides no built-in encryption or authentication. + Given that, synergy should not be used on or over any untrusted + network, especially the Internet. It's generally fine for home + networks. Future versions may provide built-in encryption and + authentication. + + Strong encryption and authentication is available through SSH + (secure shell). Run the SSH daemon (i.e. server) on the same + computer that you run the synergy server. It requires no + special configuration to support synergy. On each synergy + client system, run SSH with port forwarding: + + ssh -f -N -L 24800::24800 + + where is the name of the SSH/synergy server. + Once ssh authenticates itself, start the synergy client + normally except use `localhost' or `127.0.0.1' as the server's + address. SSH will then encrypt all communication on behalf of + synergy. Authentication is handled by the SSH authentication. + + A free implementation of SSH for Linux and many Unix systems + called OpenSSH is available from http://www.openssh.com/. For + Windows there's a port of OpenSSH using Cygwin + (http://www.cygwin.com/). + +6. What should I call my screens in the configuration? + + You can use any unique name in the configuration file for each + screen but it's easiest to use the hostname of the computer. + That's the computer name not including the domain. For example, + a computer with the fully qualified domain name `xyz.foo.com' has + the hostname `xyz'. There should also be an alias for `xyz' to + `xyz.foo.com'. If you don't use the computer's hostname, you + have to tell synergy the name of the screen using a command line + option, or the startup dialog on Windows. + +7. Why do my CapsLock and NumLock keys act funny? + + Some systems treat the Caps-Lock and Num-Lock keys differently + than all the others. Whereas most keys report going down when + physically pressed and going up when physically released, on + these systems the Caps-Lock and Num-Lock keys report going down + when being activated and going up when being deactivated. That + is, when you press and release, say, Caps-Lock to activate it, it + only reports going down, and when you press and release to + deactivate it, it only reports going up. This confuses synergy. + + You can solve the problem by changing your configuration file. + In the screens section, following each screen that has the + problem, add either or both of these lines as appropriate: + + halfDuplexCapsLock = true + halfDuplexNumLock = true + + Then restart synergy on the server. + +8. Can synergy share the display in addition to the mouse and keyboard? + + No. Synergy is a KM solution not a KVM (keyboard, video, mouse) + solution. However, future versions will probably support KVM. + Hopefully, this will make synergy suitable for managing large + numbers of headless servers. + +9. Can synergy do drag and drop between computers? + + No. That's a very cool idea and it'll be explored. However, it's + also clearly difficult and may take a long time to implement. + +10. Does AltGr/Mode-Switch work? + + Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. + That includes support for most (all?) European keyboard layouts. + All systems should be using the same keyboard layout, though, for + all characters to work. (Any character missing from a client's + layout cannot be generated by synergy.) + +11. Why isn't synergy ported to platform XYZ? + + Probably because the developers don't have access to platform XYZ + and/or are unfamiliar with development on XYZ. Also, synergy has + inherently non-portable aspects so there's a not insignificant + effort involved in porting. + +12. My client can't connect. What's wrong? + + A common mistake when starting the client is to give the wrong + server host name. The last synergyc command line option (Unix) + or the "Server Host Name" edit field (Windows) should be the + host name (or IP address) of the server *not* the client's host + name. If you get the error "connection failed: cannot connect + socket" followed by "the attempt to connect was forcefully + rejected" or "connection refused" then the server isn't started, + can't bind the address, or the client is connecting to the wrong + host name/address or port. + +13. Linking fails on Solaris. What's wrong? + + Did you add `--x-includes=/usr/openwin/include + --x-libraries=/usr/openwin/lib' (without the linebreak) to the + `configure' command line? Solaris puts the X11 includes and + libraries in an unusual place and the above lets synergy find + them. + +14. The screen saver never starts. Why not? + + If the synergy server is on X Windows then the screen saver will + not start while the mouse is on a client screen. This is a + consequence of how X Windows, synergy and xscreensaver work. + +15. I can't switch screens anymore for no apparent reason. Why? + + This sometimes happens but all the causes aren't yet known. One + known cause is if the synergy server in running on Windows + 95/98/Me and a 16-bit application is in the foreground when the + screen saver starts. Windows fails to notify synergy that the + screen saver has started in this situation (which is a bug in + Windows, not synergy) and synergy may intercept some keyboard + input and divert it to a client when the screen saver is running. + As a result, it's possible for the server system to believe a key + is pressed when it really isn't. Typically, it's the return key + and simply tapping it will allow synergy to switch screens again. + + If this problem happens to you, try tapping the enter key to see + if that solves the problem. If not, you can try running with + debug logging (--debug DEBUG) and synergy will report exactly why + it refuses to switch screens. If it claims a key is down then + try tapping that key and see if that solves the problem. diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..64c306d --- /dev/null +++ b/HISTORY @@ -0,0 +1,16 @@ +History of Synergy +================== + +The first incarnation of synergy was CosmoSynergy, created by +Richard Lee and Adam Feder then at Cosmo Software, Inc., a +subsidiary of SGI (nee Silicon Graphics, Inc.), at the end of +1996. They wrote it, and Chris Schoeneman contributed, to +solve a problem: most of the engineers in Cosmo Software had +both an Irix and a Windows box on their desks and switchboxes +were expensive and annoying. CosmoSynergy was a great success +but Cosmo Software declined to productize it and the company +was later closed. + +Synergy is a from-scratch reimplementation of CosmoSynergy. +It provides most of the features of the original and adds a +few improvements. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..f33265a --- /dev/null +++ b/INSTALL @@ -0,0 +1,575 @@ +Synergy Installation Instructions +================================= + +Prerequisites for building +-------------------------- + +To build synergy from the sources you'll need the following: + + Windows: + * VC++ 6.0 or up + + Unix: + * gcc 2.95 (or up) + * X11R4 or up headers and libraries + +In this document, "Unix" means any of the supported Unix or Unix-like +(e.g. Linux) operating systems. + + +Configuring the build +--------------------- + +This step is only necessary when building on Unix. + +To configure the build for your platform use the configure script: + + ./configure + +For a list of options to configure use: + + ./configure --help + +On Solaris you may need to use: + + ./configure --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib + +so synergy can find the X11 includes and libraries. + + +Building +-------- + +Windows: + Start VC++ and open `synergy.dsw'. Set the active configuration + (Build | Set Active Configuration) to `All - Debug' or `All - Release' + then build. Binaries are built into ./Debug or ./Release. + +Unix: + Simply enter: + + make + + This will build the client and server and leave them in their + respective source directories. + + +Installing +---------- + +Windows: + There is no support for creating an installer for synergy or installing + the files in a particular location. Instead, just copy the following + files from the Debug or Release directory to a directory you choose + (perhaps under the Program Files directory): + + * synergy.exe + * synergyc.exe + * synergys.exe + * synrgyhk.dll + +Unix: + make install + + will install the client and server into /usr/local/bin unless you + specified a different directory when you ran configure. + +See `Starting Automatically on ...' below for details on how to have +synergy start up automatically when the computer starts. + + +Running on Windows +------------------ + +Double click `synergy' on the server computer. The server is the +computer who's mouse and keyboard will be shared. This brings up a +dialog that lets you configure the server then test out the +configuration or start the server. + +First configure the server. Click the `Server' radio button + + * Click the `Server' radio button + * Click `Add' to add the server to the `Screens' list + * Enter the name of server (the computer name is recommended) + * Enter other names the server is known by + * Click OK + * Use `Add' to add your other computers + * Using a computer's name as its screen name is recommended + * Choose desired screen options on the `Add' dialog + * Use the controls under `Layout' to link screens together + * Click (once) on the server's name in the `Screens' list + * Choose the screen to the left of the server + * Use `---' if there is no screen to the left of the server + * Choose the screens to the right, above and below the server + * Repeat the above steps for all the other screens + * Use `Options...' to set desired options + * If the server's screen name is not the server's computer name: + * Click `Advanced...' + * Enter the server's screen name next to `Screen Name' + * Click `OK' + * Click `Test' + +The server will start and you'll see a console window with log messages +telling you about synergy's progress. If an error occurs you'll get one +or more dialog boxes telling you what the errors are; read the errors +to determine the problem then correct them and try `Test' again. + +Now that the server is running, you'll need to start a client. On any +client computer, double click `synergy'. Of course, you'll need to +have installed the four files listed under `Installing' above on the +client computer. Then configure the client: + + * Click the `Client' radio button + * Enter the server's computer name in `Server Host Name' + * Do not use any of the server's screen names, unless one of those + is also the computer name + * If the client's screen name is not the client's computer name: + * Click `Advanced...' + * Enter the client's screen name next to `Screen Name' + * Click `OK' + * Click `Test' + +If all goes well, the client connects to the server successfully and +the mouse and keyboard are shared. If an error occurs you'll get one +or more dialog boxes telling you what the errors are; read the errors +to determine the problem then correct them and try `Test' again. When +everything is working correctly, install the software on the other +client computers (if any) and repeat the steps for configuring the +client on each. + +Once the clients and server are working you can stop the clients and +server by clicking the `Stop' button on each computer or by right +clicking on the tray icon (by the clock in the task bar) and choosing +`Quit'. Then click `Start' on the server computer then on each of +the clients. Synergy will start and the dialog window will close. +You can stop synergy or check on its status using the tray icon. + +See `Starting Automatically on Windows' below for configuring synergy +to start automatically when the computer starts. + + +Configuring the Server on Unix +------------------------------ + +The synergy server requires configuration. The configuration file is a +plain text file broken into sections. Each section has the form: + + section: + + end + +Comments are introduced by `#' and continue to the end of the line. +The file can have the following sections. The `screens' section must +appear before the `links' and `aliases' sections. + + * screens + is a list of screen names, one name per line, each + followed by a colon. Names are arbitrary strings but they + must be unique. The hostname of each computer is recommended. + There must be a screen name for the server and each client. + Each screen can specify a number of options. Options have the + form `name = value' and a listed one per line after the screen + name. + + Example: + + section: screens + moe: + larry: + halfDuplexCapsLock = true + halfDuplexNumLock = true + curly: + meta = alt + end + + This declares three screens named: moe, larry, and curly. + Screen `larry' has half-duplex caps lock and num lock keys + (see below) and screen `curly' converts the meta modifier key + to the alt key. + + Screen can have the following options: + + halfDuplexCapsLock = {true|false} + This computer has a caps lock key that doesn't report a + press and a release event when the user presses it but + instead reports a press event when it's turned on and a + release event when it's turned off. If caps lock acts + strangely on all screens then you may need this option + on the server screen. If it acts strangely on one + screen then that screen may need the option. + + halfDuplexNumLock = {true|false} + This is identical to halfDuplexCapsLock except it + applies to the num lock key. + + xtestIsXineramaUnaware = {true|false} + This option works around a bug in the XTest extension + when used in combination with Xinerama. It affects + X11 clients only. Not all versions of the XTest + extension are aware of the Xinerama extension. As a + result, they do not move the mouse correctly when + using multiple Xinerama screens. This option is + currently true by default. If you know your XTest + extension is Xinerama aware then set this option to + false. + + shift = {shift|ctrl|alt|meta|super|none} + ctrl = {shift|ctrl|alt|meta|super|none} + alt = {shift|ctrl|alt|meta|super|none} + meta = {shift|ctrl|alt|meta|super|none} + super = {shift|ctrl|alt|meta|super|none} + Map a modifier key pressed on the server's keyboard to + a different modifier on this client. This option only + has an effect on a client screen; it's accepted and + ignored on the server screen. + + You can map, say, the shift key to shift (the default), + ctrl, alt, meta, super or nothing. Normally, you + wouldn't remap shift or ctrl. You might, however, have + an X11 server with meta bound to the Alt keys. To use + this server effectively with a windows client, which + doesn't use meta but uses alt extensively, you'll want + the windows client to map meta to alt (using `meta = + alt'). + + * links + is a list of screen names just like in the `screens' + section except each screen is followed by a list of links, + one per line. Each link has the form ` = + '. A link indicates which screen is adjacent in the + given direction. + + + Example: + + section: links + moe: + right = larry + up = curly + larry: + left = moe + up = curly + curly: + down = larry + end + + This indicates that screen `larry' is to the right of screen + `moe' (so moving the cursor off the right edge of moe would + make it appear at the left edge of larry), `curly' is above + 'moe', `moe' is to the left of `larry', `curly' is above + `larry', and `larry' is below `curly'. Note that links do + not have to be symmetrical; moving up from moe then down + from curly lands the cursor on larry. + + * aliases + is a list of screen names just like in the `screens' + section except each screen is followed by a list of aliases, + one per line *not* followed by a colon. An alias is a + screen name and must be unique. During screen name lookup + each alias is equivalent to the screen name it aliases. So + a client can connect using its canonical screen name or any + of its aliases. + + Example: + + section: aliases + larry: + larry.stooges.com + curly: + shemp + end + + Screen `larry' is also known as `larry.stooges.com' and can + connect as either name. Screen `curly' is also known as + `shemp'. (Hey, it's just an example.) + + * options + is a list of lines of the form `name = value'. These + set the global options. + + Example: + + section: options + heatbeat = 5000 + switchDelay = 500 + end + + You can use the following options: + + heartbeat = N + The server will expect each client to send a message no + less than every N milliseconds. If no message arrives + from a client within 3N seconds the server forces that + client to disconnect. + + If synergy fails to detect clients disconnecting while + the server is sleeping or vice versa, try using this + option. + + switchDelay = N + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it stays on the edge for N + milliseconds. This helps prevent unintentional + switching when working near the edge of a screen. + + switchDoubleTap = N + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it's moved away from the edge + and then back to the edge within N milliseconds. With + the option you have to quickly tap the edge twice to + switch. This helps prevent unintentional switching + when working near the edge of a screen. + + screenSaverSync = {true|false} + If set to false then synergy won't synchronize screen + savers. Client screen savers will start according to + their individual configurations. The server screen + saver won't start if there is input, even if that input + is directed toward a client screen. + + You can use both the switchDelay and switchDoubleTap options at + the same time. Synergy will switch when either requirement is + satisfied. + +The synergy server will try certain pathnames to load the configuration +file if the user doesn't specify a path using the `--config' command +line option. `synergys --help' reports those pathnames. + + +Running the Server on Unix +-------------------------- + +Run the server on the computer that has the keyboard and mouse to +be shared. You must have prepared a configuration file before +starting the server. The server should be started before the +clients but that's not required. + +Run the synergy server on the server system using the following +command line: + + synergys -f --config + +Replace with the path to the configuration file. +The `-f' option causes synergys to run in the foreground. This is +recommended until you've verified that the configuration works. +If you didn't include the system's hostname in the configuration +file (either as a screen name or an alias) then you'll have to add +`--name ' to the command line, where is +a name in the configuration file. You can use `synergys --help' +for a list of command line options. + +See `Starting Automatically on Unix' below for running synergy +automatically when the X server starts. + + +Running the Client on Unix +-------------------------- + +Run the client on all computers that aren't the server using the +following command line: + + synergyc -f --no-camp + +Replace with the hostname or address of the +server system. The `-f' option causes synergy to run in the +foreground. The `--no-camp' prevents synergy from retrying to +connect to the server until it succeeds. Both are recommended +until you've verified that the configuration works. If you +didn't include the system's hostname in the configuration file +(either as a screen name or an alias) then you'll have to add +`--name ' to the command line, where +is a name in the configuration file. + +The client should quickly report `connected to server'. If it +does not but doesn't print an error and exit immediately then +it's trying to connect to the server but cannot. It will time +out in 30 seconds and exit (use ctrl+c to exit earlier). You +should check that the server is running and is reachable over +the network and try again. + +If the client fails and exits it should print an error describing +the problem. Here are typical problems and possible solutions: + + * failed to open screen: + check permission to open the X display; + check that the DISPLAY environment variable is set. + * already connected: + check that the synergy client isn't already running. + * refused client: + add client to the server's configuration file. + * connection failed: + check ; + the server cannot open the desired port, stop the + program using that port (24800) and restart the + server. + +Once all the clients are running, try moving the mouse to each +screen. Be sure to check all the configured links. + +See `Starting Automatically on Unix' below for running synergy +automatically when the X server starts. + + +Starting Automatically on Windows +--------------------------------- + +When all the clients work you're ready to have synergy start +automatically each time the system (re)starts. Click `Stop' on all +the clients then on the server'. Now click the `Configure...' button +by the text `Automatic Startup'. The `Auto Start' dialog will pop up. +If an error occurs then correct the problem and click `Configure' +again. + +On the `Auto Start' dialog you'll configure synergy to start +automatically when the computer starts or when you log in. You can +also configure synergy to not start automatically. You can only +start synergy automatically when the computer starts if you have +sufficient access rights. The dialog will let you know if you have +sufficient permission. + +If synergy is already configured to automatically start then there +will be two `Uninstall' buttons, at most one of which is enabled. +Click the enabled button, if any, to configure synergy to not start +automatically. + +If synergy is not configured to start automatically then there will +be two `Install' buttons. If you have sufficient permission to +have synergy start automatically when the computer does then the +`Install' button in the `When Computer Starts' box will be enabled. +Click it to have synergy start for all users when the computer starts. +In this case, synergy will be available during the login screen. +Otherwise, click the `Install' button in the `When You Log In' box +to have synergy automatically start when you log in. + + +Starting Automatically on Unix +------------------------------ + +Synergy requires an X server. That means a server must be +running and synergy must be authorized to connect to that server. +It's best to have the display manager start synergy. You'll need +the necessary (probably root) permission to modify the display +manager configuration files. If you don't have that permission +you can start synergy after logging in via the .xsession file. + +To have the display manager start synergy, edit the Xsetup script. +The location of this file depends on your installation. It might +be /etc/X11/xdm/Xsetup. Near the end of the file but before +anyplace the script calls exit, start the client with something +like: + + /usr/bin/killall synergyc + /usr/local/bin/synergyc [] + + must not include `-f' or `--no-daemon'. Change the +paths as necessary. It's important to make sure no old copies +of synergy are running so they can't interfere with the new one. + +To start the server use something like: + + /usr/bin/killall synergys + /usr/local/bin/synergys [] --config + +Again, must not include `-f' or `--no-daemon'. If +the configuration pathname is one of the default locations then +you don't need the `--config' option. + +Note that some display managers (xdm and kdm, but not gdm) grab +the keyboard and do not release it until the user logs in, for +security reasons. This prevents a synergy server from sharing +the mouse and keyboard until the user logs in. It doesn't +prevent a synergy client from synthesizing mouse and keyboard +input, though. + + +Network Security +---------------- + +Synergy has no built-in support for encryption or authentication. +The server accepts connections from any computer. The server and +clients send all data unencrypted which means the clipboard and +mouse and keyboard events (e.g. typed passwords) are easily +examined by anyone listening on the network. Therefore, do not +run synergy on untrusted networks except as follows. + +You can use SSH (secure shell) to provide strong authentication +and encryption to synergy without modifying either SSH or synergy. +On Linux and Unix a free implementation of SSH called OpenSSH is +available at http://www.openssh.com/. On Windows you can use the +Cygwin version of OpenSSH. + +First, install the SSH server (sshd) on the computer running the +synergy server. Next, install the SSH client (ssh) on each +synergy client computer. Start the SSH and synergy servers +normally. Then, for each client, start the SSH client with port +forwarding: + + ssh -f -N -L 24800::24800 + +where is the name or address of the SSH and +synergy server host. 24800 is the default synergy port; replace +it with whichever port you use if you don't use the default. Once +ssh authenticates with the server, start the synergy client as +usual except use `localhost' or `127.0.0.1' for the server +address. Synergy will then pass all communication through SSH +which encrypts it, passes it over the network, decrypts it, and +hands it back to synergy. Authentication is provided by SSH's +authentication. + + + +Common Command Line Options +--------------------------- + -d, --debug use debugging level + --daemon run as a daemon (Unix) or background (Windows) + -f, --no-daemon run in the foreground + -n, --name use instead of the hostname + --restart automatically restart on failures + -1, --no-restart do not restart on failure + -h, --help print help and exit + --version print version information and exit + +Debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, +INFO, DEBUG, DEBUG1, and DEBUG2. Only messages at or above the given +level are logged. Messages are logged to a terminal window when +running in the foreground. Unix logs messages to syslog when running +as a daemon. The Windows NT family logs messages to the event log +when running as a service. The Windows 95 family shows FATAL log +messages in a message box and others in a terminal window when running +as a service. + +The `--name' option lets the client or server use a name other than +its hostname for its screen. This name is used when checking the +configuration. + +Neither the client nor server will automatically restart if an error +occurs that is sure to happen every time. For example, the server +will exit immediately if it can't find itself in the configuration. +On X11 both the client and server will also terminate if the +connection to the X server is lost. Since xdm will normally restart +the X server and synergy, this is the correct behavior. + + +Server Command Line Options +--------------------------- + -a, --address
listen for connections on the given address + -c, --config read configuration from + +
has one of the following forms: + + : + : + is a hostname or address of a network interface on the +server system. is a port number from 1 to 65535. +defaults to the system's hostname and defaults to 24800. + + +Client Command Line Options +--------------------------- + --camp retry connection to server until successful + --no-camp try connection to server only once +
address of server + +see the "server command line options" for a description of
+but note that there is no default though there is a +default . diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..edf9fec --- /dev/null +++ b/Makefile.am @@ -0,0 +1,107 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = . +VDEPTH = ./$(VPATH) + +SUBDIRS = \ + lib \ + cmd \ + dist \ + $(NULL) + +EXTRA_DIST = \ + BUGS \ + FAQ \ + HISTORY \ + PORTING \ + TODO \ + all.dsp \ + synergy.dsw \ + doc/doxygen.cfg.in \ + examples/synergy.conf \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + aclocal.m4 \ + config.h \ + config.h.in \ + config.log \ + config.status \ + configure \ + stamp-h.in \ + stamp-h1 \ + doc/doxygen.cfg \ + doc/doxygen/html/* \ + $(NULL) + +PKG_FILES = \ + AUTHORS \ + BUGS \ + COPYING \ + ChangeLog \ + FAQ \ + HISTORY \ + INSTALL \ + NEWS \ + README \ + TODO \ + cmd/synergyc/synergyc \ + cmd/synergys/synergys \ + examples/synergy.conf \ + $(NULL) +PKG_PROG_FILES = \ + synergyc \ + synergys \ + $(NULL) + +# build doxygen documentation +doxygen: + doxygen doc/doxygen.cfg + +# build RPMs +RPMTOPDIR=/var/tmp/@PACKAGE@-@VERSION@ +dist-rpm: dist + rm -rf $(RPMTOPDIR) + mkdir $(RPMTOPDIR) + (cd $(RPMTOPDIR); mkdir BUILD SOURCES SPECS SRPMS RPMS) + cp @PACKAGE@-@VERSION@.tar.gz $(RPMTOPDIR)/SOURCES + rpm --define '_topdir $(RPMTOPDIR)' -ba dist/rpm/synergy.spec && \ + mv -f $(RPMTOPDIR)/SRPMS/*.rpm . && \ + mv -f $(RPMTOPDIR)/RPMS/*/*.rpm . && \ + rm -rf $(RPMTOPDIR) + +# build zip +# FIXME -- have automake generate this rule for us +dist-zip: distdir + zip -r $(distdir).zip $(distdir) + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) + +# build binary package. owner/group of packaged files will be +# owner/group of user running make. +PKGTOPDIR=/var/tmp/@PACKAGE@-@VERSION@ +dist-pkg: all + rm -rf $(PKGTOPDIR) + mkdir $(PKGTOPDIR) + mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + cp $(PKG_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \ + chmod 644 *; \ + chmod 755 $(PKG_PROG_FILES); \ + strip $(PKG_PROG_FILES) ) + type=`uname -s -m | tr '[A-Z] ' '[a-z].'`; \ + (cd $(PKGTOPDIR); tar cf - @PACKAGE@-@VERSION@ | \ + gzip - ) > @PACKAGE@-@VERSION@-1.$${type}.tar.gz && \ + rm -rf $(PKGTOPDIR) diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..5e99167 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,520 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = . +VDEPTH = ./$(VPATH) + +SUBDIRS = \ + lib \ + cmd \ + dist \ + $(NULL) + + +EXTRA_DIST = \ + BUGS \ + FAQ \ + HISTORY \ + PORTING \ + TODO \ + all.dsp \ + synergy.dsw \ + doc/doxygen.cfg.in \ + examples/synergy.conf \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + aclocal.m4 \ + config.h \ + config.h.in \ + config.log \ + config.status \ + configure \ + stamp-h.in \ + stamp-h1 \ + doc/doxygen.cfg \ + doc/doxygen/html/* \ + $(NULL) + + +PKG_FILES = \ + AUTHORS \ + BUGS \ + COPYING \ + ChangeLog \ + FAQ \ + HISTORY \ + INSTALL \ + NEWS \ + README \ + TODO \ + cmd/synergyc/synergyc \ + cmd/synergys/synergys \ + examples/synergy.conf \ + $(NULL) + +PKG_PROG_FILES = \ + synergyc \ + synergys \ + $(NULL) + + +# build RPMs +RPMTOPDIR = /var/tmp/@PACKAGE@-@VERSION@ + +# build binary package. owner/group of packaged files will be +# owner/group of user running make. +PKGTOPDIR = /var/tmp/@PACKAGE@-@VERSION@ +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = doc/doxygen.cfg +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = README ./stamp-h.in AUTHORS COPYING ChangeLog INSTALL \ + Makefile.am Makefile.in NEWS TODO acinclude.m4 aclocal.m4 \ + config.h.in config/config.guess config/config.sub \ + config/depcomp config/install-sh config/missing \ + config/mkinstalldirs configure configure.in +DIST_SUBDIRS = $(SUBDIRS) +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$@ $(SHELL) ./config.status + +$(top_builddir)/config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck +$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) + cd $(srcdir) && $(AUTOCONF) + +$(ACLOCAL_M4): configure.in acinclude.m4 + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +config.h: stamp-h + @if test ! -f $@; then \ + rm -f stamp-h; \ + $(MAKE) stamp-h; \ + else :; fi +stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h stamp-hT + @echo timestamp > stamp-hT 2> /dev/null + cd $(top_builddir) \ + && CONFIG_FILES= CONFIG_HEADERS=config.h \ + $(SHELL) ./config.status + @mv stamp-hT stamp-h +$(srcdir)/config.h.in: $(srcdir)/./stamp-h.in + @if test ! -f $@; then \ + rm -f $(srcdir)/./stamp-h.in; \ + $(MAKE) $(srcdir)/./stamp-h.in; \ + else :; fi +$(srcdir)/./stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) + @rm -f $(srcdir)/./stamp-h.in $(srcdir)/./stamp-h.inT + @echo timestamp > $(srcdir)/./stamp-h.inT 2> /dev/null + cd $(top_srcdir) && $(AUTOHEADER) + @mv $(srcdir)/./stamp-h.inT $(srcdir)/./stamp-h.in + +distclean-hdr: + -rm -f config.h +doc/doxygen.cfg: $(top_builddir)/config.status $(top_srcdir)/doc/doxygen.cfg.in + cd $(top_builddir) && CONFIG_FILES=$@ CONFIG_HEADERS= CONFIG_LINKS= $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = . +# Avoid unsightly `./'. +distdir = $(PACKAGE)-$(VERSION) + +GZIP_ENV = --best + +distdir: $(DISTFILES) + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) + mkdir $(distdir) + $(mkinstalldirs) $(distdir)/config $(distdir)/dist/rpm $(distdir)/doc $(distdir)/examples + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + -chmod -R a+w $(distdir) > /dev/null 2>&1; rm -rf $(distdir) + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(AMTAR) xf - + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/=build + mkdir $(distdir)/=inst + chmod a-w $(distdir) + dc_install_base=`CDPATH=: && cd $(distdir)/=inst && pwd` \ + && cd $(distdir)/=build \ + && ../configure --srcdir=.. --prefix=$$dc_install_base \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && (test `find $$dc_install_base -type f -print | wc -l` -le 1 \ + || (echo "Error: files left after uninstall" 1>&2; \ + exit 1) ) \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && $(MAKE) $(AM_MAKEFLAGS) distclean \ + && rm -f $(distdir).tar.gz \ + && (test `find . -type f -print | wc -l` -eq 0 \ + || (echo "Error: files left after distclean" 1>&2; \ + exit 1) ) + -chmod -R a+w $(distdir) > /dev/null 2>&1; rm -rf $(distdir) + @echo "$(distdir).tar.gz is ready for distribution" | \ + sed 'h;s/./=/g;p;x;p;x' +check-am: all-am +check: check-recursive +all-am: Makefile config.h +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +dist-all: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) +distclean: distclean-recursive + -rm -f config.status config.cache config.log +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive dist dist-all distcheck distclean \ + distclean-generic distclean-hdr distclean-recursive \ + distclean-tags distdir dvi dvi-am dvi-recursive info info-am \ + info-recursive install install-am install-data install-data-am \ + install-data-recursive install-exec install-exec-am \ + install-exec-recursive install-info install-info-am \ + install-info-recursive install-man install-recursive \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am installdirs-recursive maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive mostlyclean \ + mostlyclean-generic mostlyclean-recursive tags tags-recursive \ + uninstall uninstall-am uninstall-info-am \ + uninstall-info-recursive uninstall-recursive + + +# build doxygen documentation +doxygen: + doxygen doc/doxygen.cfg +dist-rpm: dist + rm -rf $(RPMTOPDIR) + mkdir $(RPMTOPDIR) + (cd $(RPMTOPDIR); mkdir BUILD SOURCES SPECS SRPMS RPMS) + cp @PACKAGE@-@VERSION@.tar.gz $(RPMTOPDIR)/SOURCES + rpm --define '_topdir $(RPMTOPDIR)' -ba dist/rpm/synergy.spec && \ + mv -f $(RPMTOPDIR)/SRPMS/*.rpm . && \ + mv -f $(RPMTOPDIR)/RPMS/*/*.rpm . && \ + rm -rf $(RPMTOPDIR) + +# build zip +# FIXME -- have automake generate this rule for us +dist-zip: distdir + zip -r $(distdir).zip $(distdir) + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) +dist-pkg: all + rm -rf $(PKGTOPDIR) + mkdir $(PKGTOPDIR) + mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + cp $(PKG_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \ + chmod 644 *; \ + chmod 755 $(PKG_PROG_FILES); \ + strip $(PKG_PROG_FILES) ) + type=`uname -s -m | tr '[A-Z] ' '[a-z].'`; \ + (cd $(PKGTOPDIR); tar cf - @PACKAGE@-@VERSION@ | \ + gzip - ) > @PACKAGE@-@VERSION@-1.$${type}.tar.gz && \ + rm -rf $(PKGTOPDIR) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..109501c --- /dev/null +++ b/NEWS @@ -0,0 +1,180 @@ +Synergy News +============ + +* Aug-24-2003 - Synergy 1.0.14 released + + Made following changes: + * Fixed bugs in setting win32 process/thread priority + * Fixed resource leak in opening win32 system log + * Fixed win32 launcher not getting non-default advanced options + * Synergy log copied to clipboard now transferred to other screens + * Hack to work around lesstif clipboard removed (fixes pasting on X) + +* Jul-20-2003 - Synergy 1.0.12 released + + This release finally completes support for non-ASCII characters, + fully supporting most (all?) European keyboard layouts including + dead key composition. This release includes changes from several + experimental versions (1.0.9, 1.0.11, 1.1.0, 1.1.1, 1.1.2, and + 1.1.3). + + Made following changes: + * Added non-ASCII support to win32 and X11 + * Added dead key support to win32 and X11 + * Fixed AltGr handling + * Added ctrl+alt+del simulation using ctrl+alt+pause + * Fixed loss of key event when user releases ctrl+alt+del + * Fixed incorrect synthesis of pointer-keys event on X11 + * Fixed Xinerama support + * Made some clipboard fixes on win32 and X11 + * Add tray icon menu item to copy log to clipboard + * Fixed mouse warping on unconnected client + * Stopped unconnected client from filling up event logs + +* May-10-2003 - Synergy 1.0.8 released + + Made following changes: + * Fixed hook forwarding (fixing interaction with objectbar) + * Fixed "Windows" key handling and added support Win+E, Win+F, etc + * Added win 95/98/me support for Alt+Tab, Alt+Esc, Ctrl+Esc + * Fixed scroll lock locking to server screen + * Fixed screen flashing on X11 and Windows + * Fixed compile problem on 64 bit systems + * Fixed Xinerama support + * Now allowing screen names that include underscores + * Improved non-ASCII key handling on Windows + * Fixed lagginess + * Fixed failure to capture all mouse input on Windows + * Fixed auto-repeat bugs on X11 + * Added option to disable screen saver synchronization + * Added support for 4th and 5th mouse buttons on Windows + * Added support for "Internet" and "Multimedia" keys + * Fixed jumping from client to itself (mouse wrapping) + +* Mar-27-2003 - Synergy 1.0.6 released + + Made following changes: + * Added tray icon on win32 + * Fixed multi-monitor support on win32 + * Fixed win32 screen saver detection on NT/2k/XP + * Added per-screen options to remap modifier keys + * Added global options for restricting screen jumping + * Added global option for detecting unresponsive clients + * Added more logging for why screen jump won't happen + * Fixed problem sending the CLIPBOARD to motif/lesstif apps + * Win32 launcher now remembers non-config-file state + +* Feb-18-2003 - Synergy 1.0.3 released + + Made following changes: + * Support for X11 keymaps with only uppercase letters + * Fixed memory leaks + * Added documentation on using synergy with SSH + * Fixed unnecessary left-handed mouse button swapping + * Fixed debug build error on win32 + * Reduced frequency of large cursor jumps when leaving win32 server + * Changed cursor motion on win32 multimon to relative moves only + + +* Jan-25-2003 - Synergy 1.0.2 released + + Made following changes: + * Fixed out-of-bounds array lookup in the BSD and Windows network code + * Added ability to set screen options from Windows launch dialog + + +* Jan-22-2003 - Synergy 1.0.1 released + + Made following changes: + * Fixed running as a service on Windows NT family + + +* Jan-20-2003 - Synergy 1.0.0 released + + Made following changes: + * Refactored to centralize platform dependent code + * Added support for mouse wheel on Windows NT (SP3 and up) + * Portability improvements + * Added more documentation + * Fixes for working with xscreensaver + * Fixes for circular screen links + + This release has been tested on Linux and Windows. It builds and + is believed to run on Solaris and FreeBSD. It is believed to + build and run on Irix and AIX. It builds but does not work on + MacOS X. + + +* Dec-25-2002 - Synergy 0.9.14 released + + Made following changes: + * Fixed solaris compile problems (untested) + * Fixed irix compile problems (untested) + * Fixed windows client not reconnecting when server dies bug + * Fixed loss of ctrl+alt from windows server to non-windows clients + * Fixed handling of password protected windows client screen saver + * Now handling any number of pointer buttons on X11 + * Toggle key states now restored when leaving clients + * Added support for per-screen config options + * Added config options for half-duplex toggle keys on X11 + * Enabled class diagrams in doxygen documentation + + +* Nov-05-2002 - Synergy 0.9.13 released + + Made following changes: + * Fixed solaris compile problems (untested) + * Fixed MacOS X compile problems (semi-functional) + * Fixed gcc-3.2 compile problems + * Fixed some thread startup and shutdown bugs + * Server now quits if bind() fails with an error other than in use + * Fixed bug in moving mouse on Win98 without multiple monitors + * Fixed bug in handling TCP socket errors on read and write + * Fixed spurious screen saver activation on X11 + * Unix platforms can now read Win32 configuration files + * Minor error reporting fixes + + +* Sep-14-2002 - Synergy 0.9.12 released + + Made following changes: + * Win32 was not reporting log messages properly when run from synergy.exe + * Network error messages weren't reporting useful information + * Synergy won't build on gcc 3.2; added workaround for known problem + * X11 wasn't handling some keys/key combinations correctly + * Added option to change logging level when testing from synergy.exe + + +* Sep-04-2002 - Synergy 0.9.11 released + + Fixed following bugs: + * Worked around missing SendInput() on windows 95/NT 4 prior to SP3 + * Fixed keyboard mapping on X11 synergy client + + +* Sep-02-2002 - Synergy 0.9.10 released + + Fixed following bugs: + * The Pause/Break and KP_Enter buttons were not working correctly on windows + * Configuration options were being lost on windows after a reboot + * Added support for AltGr/ModeSwitch keys + * Added support for auto-start on windows when not administrator + * Improved autoconf + * Added workaround for lack of sstream header on g++ 2.95. + + +* Aug-18-2002 - Synergy 0.9.9 released + + Fixed three bugs: + * The PrintScrn button was not working correctly on windows + * The Win32 server could hang when a client disconnected + * Using the mouse wheel could hang the X server + + +* Aug-11-2002 - Synergy 0.9.8 released + + Supports any number of clients under Linux or Windows 95 or NT4 + or later. Includes mouse and keyboard sharing, clipboard + synchronization and screen saver synchronization. Supports ASCII + keystrokes, 5 button mouse with wheel, and Unicode text clipboard + format. diff --git a/PORTING b/PORTING new file mode 100644 index 0000000..3281822 --- /dev/null +++ b/PORTING @@ -0,0 +1,394 @@ +Synergy Developer and Porting Guide +=================================== + +This document is under development. + +Code Organization +----------------- + +The synergy source code organization is: + +. -- root makefiles, some standard documentation +cmd -- program source code + launcher -- synergy launcher for Windows + synergyc -- synergy client + synergys -- synergy server +config -- stuff for autoconf/automake +dist -- files for creating distributions + rpm -- files for creating RPMs +doc -- placeholder for documentation +examples -- example files +lib -- library source code + arch -- platform dependent utility library + base -- simple utilities + client -- synergy client library + common -- commonly needed header files + http -- simple http tools + io -- I/O + mt -- multithreading + net -- networking + platform -- platform dependent display/window/event stuff + server -- synergy server library + synergy -- synergy shared client/server code library + +Note how the utility code required by the programs is placed into +separate library directories. This makes the makefiles a little +more awkward but makes for a cleaner organization. The top level +directory has only the standard documentation files and the files +necessary to configure and build the rest of the project. + + +Coding Style Guide +------------------ + +Synergy uses many coding conventions. Contributed code should +following these guidelines. + +- Symbol Naming + Names always begin with a letter (never an underscore). The first + letter of interior names are always capitalized. Acronyms should + be all uppercase. For example: myTextAsASCII. + + Names come it two flavors: leading capital and leading lowercase. + The former have the first character capitalized and the latter + don't. In the following table, leading capital names are indicated + by `Name' and leading lowercase names by `name'. + + The naming convention for various things are: + + * Exceptions -- X + Name XMyException + * Interfaces -- I + Name IMyInterface + * Template Classes -- T + Name TMyTemplate<> + * Other Classes -- C + Name CMyClass + * Enumerations -- E + Name EMyEnumeration + * Constants -- k + Name kMyConstant + * Data Members -- m_ + name m_myDataMember + * Methods -- name myMethod + * Functions -- name myFunction + * Variables -- name myVariable + + Exceptions are types that get thrown and are generally derived + (possibly indirectly) from XBase. Interfaces are derived (possibly + indirectly) from IInterface and have only pure virtual functions. + Other classes are classes that aren't exceptions or interfaces. + Constants include global constants and enumerants. + + Method names should usually have the form `verbObject'. For example: + * isGameOn() + * getBeer() + * pressPowerButton() + * setChannel() + In general, use `get' and `set' to read and write state but use `is' + to read boolean state. Note that classes that contain only `is', + `get', and `set' are probably plain old data; you might want to + consider using public data members only or, better, refactor your + design to have classes that actually do something more than just + hold data. + +- File Naming + Each class should have one source and one header file. If the + class is named `CMyClass' then the source file should be named + `CMyClass.cpp' and the header file `CMyClass.h'. + + Headers files not containing a class should have some meaningful + name with a leading capital (e.g. `Version.h'). + + Source files without a header file have a leading lowercase name. + Only files containing the entry point for an application should + lack a header file. + +- Dependencies + * No circular library dependencies + Library dependencies form an acyclic graph. Conceptually + libraries can be arranged in layers where each library only + references libraries in layers below it, not in the same layer + or layers above it. The makefiles build the lowest layer + libraries first and work upwards. + + * Avoid circular uses-a relationships + When possible, design classes with one-way uses-a relationships + and avoid cycles. This makes it easier to understand the code. + However, sometimes it's not always practical so it is permitted. + + * Included files in headers + Headers should #include only the necessary headers. In + particular, if a class is referenced in a header file only as a + pointer or a reference then use `class COtherClass;' instead of + `#include "COtherClass.h".' + + * #include syntax + Non-synergy header files must be included using angle brackets + while synergy header files must be included using double quotes. + #include "CSynergyHeader.h" + #include + The file name in a #include must not be a relative path unless + it's a system header file and it's customary to use a relative + path, e.g. `#include '. Use compiler options to + add necessary directories to the include search path. + + * Included file ordering + Files should be included in the following order: + * Header for source file + The first include for CMyClass.cpp must be CMyClass.h. + * Other headers in directory, sorted alphabetically + * Headers for each library, sorted alphabetically per library + Include headers from the library closest in the dependency graph + first, then the next farthest, etc. Sort alphabetically within + each library. + * System headers + +- C++ + * C++ features + Synergy uses the following more recent C++ features: + * bool + * templates + * exceptions + * mutable + * new scoping rules + * the standard C++ library + + Do not use the following C++ features: + * dynamic_cast + * run time type information + * namespaces and using (use std:: where necessary) + + The new scoping rules say that the scope of a variable declared + in a for statement is limited to the for loop. For example: + + for (int i = 0; i < 10; ++i) { + // i is in scope here + } + // i is not in scope here + + for (int i = -10; i < 0; ++i) { + // an entirely new i is in scope here + } + // i is not in scope here + + This is used routinely in synergy, but only in for loops. There + is a macro for `for' in lib/base/common.h when building under + Microsoft Visual C++ that works around the fact that that compiler + doesn't follow the new scoping rules. Use the macro if your + compiler uses the old scoping rules. + + * Standard C++ library + The standard C++ library containers should always be used in favor + of custom containers wherever reasonable. std::string is used + throughout synergy but only as the CString typedef; always use + CString, never std::string except in the arch library. Synergy + avoids using auto_ptr due to some portability problems. Synergy + makes limited use of standard algorithms and streams but they can + be freely used in new code. + + * Limited multiple inheritance + Classes should inherit implementation from at most one superclass. + Inheriting implementation from multiple classes can have unpleasant + consequences in C++ due to it's limited capabilities. Classes can + inherit from any number of interface classes. An interface class + provides only pure virtual methods. Synergy breaks this rule in + IInterface which implements the virtual destructor for convenience. + + * No globals + Avoid global variables. All global variables must be static, making + it visible only with its source file. Most uses of global variables + are better served by static data members of a class. Global + constants are permitted in some circumstances. + + Also avoid global functions. Use public static member functions in + a class instead. + + These rules are violated by the main source file for each program + (except that the globals are still static). They could easily be + rewritten to put all the variables and functions into a class but + there's little to be gained by that. + + * Private data only + If a class is plain-old-data (i.e. it has no methods) all of its + data members should be public. Otherwise all of its data members + should be private, not public or protected. This makes it much + easier to track the use of a member when reading code. Protected + data is not allowed because `protected' is a synonym for `public + to my subclasses' and public data is a Bad Thing. While it might + seem okay in this limited situation, the situation is not at all + limited since an arbitrary number of classes can be derived, + directly or indirectly, from the class and any of those classes + have full access to the protected data. + + * Plain old data + A class that merely contains data and doesn't perform operations + on that data (other than reads and writes) is plain old data (POD). + POD should have only public data members and non-copy constructors. + It must not have any methods other than constructors, not even a + destructor or assignment operators, nor protected or private data. + Note that this definition of POD is not the definition used in the + C++ standard, which limits the contained data types to types that + have no constructors, destructors, or methods. + + * Avoid using friend + Avoid declaring friend functions or classes. They're sometimes + necessary for operator overloading. If you find it necessary to + add friends to some class C, consider creating a utility class U. + A utility class is declared as the only friend of C and provides + only static methods. Each method forwards to a private method on + an object of C type (passed as a parameter to the U's method). + This makes maintenance easier since only U has friend access to C + and finding any call to U is trivial (they're prefixed by U::). + + * Don't test for NULL when using `delete' or `delete[]' + It's unnecessary since delete does it anyway. + +- Makefiles + Automake's makefiles (named Makefile.am) have a few requirements: + * Define the following macros at the top of the file: + NULL = + DEPTH = + VDEPTH = ./$(VPATH)/$(DEPTH) + is `..', `../..', `../../..', etc, + whichever references the top directory of the synergy tree. For + example, for a subdirectory of the top level use `..', for a + subdirectory of a subdirectory of the top level use `../..'. + * Lists should have one item per line and end in $(NULL). For + example: + EXTRA_DIST = \ + kiwi.txt \ + mango.cpp \ + papaya.h \ + $(NULL) + Indentation must use tabs in a makefile. Line continuations + (backslashes) should be aligned using tabs. + * Lists of files should be sorted alphabetically. Lists of + subdirectories must be in the desired build order. + +- Source Formatting + Every project has its own formatting style and no style satisfies + everyone. New code should be consistent with existing code: + + * All files should include the copyright and license notice + * Use tabs to indent + * Tabs are 4 columns + * Lines should not extend past the 80th column + * Open braces ({) go on same line as introducing statement + `for (i = 0; i < 10; ++i) {' not + for (i = 0; i < 10; ++i) + { + * Close braces line up with introducing statement + * Open brace for function is on a line by itself in first column + * Close brace for function lines up with open brace + * Always use braces on: if, else, for, while, do, switch + * `else {' goes on its own line + * Always explicitly test pointers against NULL + e.g. `if (ptr == NULL)' not `if (ptr)' + * Always explicitly test integral values against 0 + e.g. `if (i == 0)' not `if (i)' + * Put spaces around binary operators and after statements + e.g. `if (a == b) {' not `if(a==b){' + * C'tor initializers are one per line, indented one tab stop + * Other indentation should follow existing practice + * Use Qt style comments for extraction by doxygen (i.e. //! and /*!) + * Mark incomplete or buggy code with `FIXME' + +- Other + * calls to LOG() should always be all on one line (even past column 80) + + +Class Relationships +------------------- + +The doxygen documentation can help in understanding the relationships +between objects. Use `make doxygen' in the top level directory to +create the doxygen documentation into doc/doxygen/html. You must have +doxygen installed, of course. + +FIXME -- high level overview of class relationships + + +Portability +----------- + +Synergy is mostly platform independent code but necessarily has +platform dependent parts. The mundane platform dependent parts +come from the usual suspects: networking, multithreading, file +system, high resolution clocks, system logging, etc. Porting +these parts is relatively straightforward. + +Synergy also has more esoteric platform dependent code. The +functions for low-level event interception and insertion, +warping the cursor position, character to keyboard event +translation, clipboard manipulation, and screen saver control +are often obscure and poorly documented. Unfortunately, these +are exactly the functions synergy requires to do its magic. + +Porting synergy to a new platform requires the following steps: + +- Setting up the build +- Adjusting lib/common/common.h +- Implementing lib/arch +- Implementing lib/platform +- Tweaks + +Setting up the build: + +The first phase is simply to create the files necessary to build the +other files. On Unix, synergy uses autoconf/automake which produces +a `configure' script that generates makefiles. On Windows, synergy +uses Visual C++ workspace and project files. If you're porting to +another Unix variant, you may need to adjust `configure.in', +`acinclude.m4', and Unix flavor dependent code in lib/arch. + +Adjusting lib/common/common.h: + +The lib/common/common.h header file is included directly or indirectly +by every other file. It prepares some platform dependent macros for +integer sizes and defines a macro for conveniently testing which +platform we're building on. That macro is named *_LIKE (e.g. UNIX_LIKE) +and has the value `1'. Exactly one *_LIKE macro must be defined by +common.h. + +Implementing lib/arch: + +Much platform dependent code lives in lib/arch. There are several +interface classes there and they must all be implemented for each +platform. See the interface header files for more information. + +Platforms requiring special functions should create a class named +CArchMiscXXX where XXX is the platform name. The class should have +only static methods. Clients can include the appropriate header +file and make calls directly, surrounded by a suitable #ifdef/#endif. + +Implementing lib/platform: + +Most of the remaining platform dependent code lives in lib/platform. +The code there implements platform dependent window, clipboard, and +screen saver handling. If a platform is named XXX then the following +classes should be derived and implemented: + + * CXXXClipboard : IClipboard + Provides clipboard operations. Typically, this class will + have helper classes for converting between various clipboard + data formats. + + * CXXXScreen : IScreen + Provide screen operations common to the server and clients. + The CXXXPrimaryScreen and CXXXSecondaryScreen classes use a + CXXXScreen. + + * CXXXPrimaryScreen : CPrimaryScreen, IScreenEventHandler + Provides server screen operations. + + * CXXXSecondaryScreen : CSecondaryScreen, IScreenEventHandler + Provides client screen operations. + + * CXXXScreenSaver : IScreenSaver + Provides screen saver operations. + +Tweaks: + +Finally, each platform typically requires various adjustments here +and there. In particular, synergyc.cpp and synergys.cpp usually +require platform dependent code for the main entry point, parsing +arguments, and reporting errors. Also, some platforms may benefit +from a graphical user interface front end. These are generally +not portable and synergy doesn't provide any infrastructure for +the code common to any platform, though it may do so someday. +There is, however, an implementation of a GUI front end for Windows +that serves as an example. diff --git a/README b/README new file mode 100644 index 0000000..072c9f2 --- /dev/null +++ b/README @@ -0,0 +1,267 @@ +Synergy +======= + +synergy: [noun] a mutually advantageous conjunction of distinct elements + +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own display. + +Redirecting the mouse and keyboard is as simple as moving the mouse +off the edge of your screen. Synergy also merges the clipboards of +all the systems into one, allowing cut-and-paste between systems. +Furthermore, it synchronizes screen savers so they all start and stop +together and, if screen locking is enabled, only one screen requires +a password to unlock them all. + +Synergy is open source and released under the GNU Public License (GPL). + +The synergy home page is: +http://synergy2.sourceforge.net/ + +The synergy project page is: +http://sourceforge.net/projects/synergy2/ + +Report bugs to: +http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 + + +Please see the following files for more information: +AUTHORS -- The list of synergy's authors +BUGS -- A list of known bugs and limitations +COPYING -- The license synergy is release under +FAQ -- Frequently asked questions about synergy +HISTORY -- A brief history of synergy +INSTALL -- Detailed build and installation instructions +NEWS -- News about the synergy project +PORTING -- Porting guide for developers +TODO -- List of things to add to synergy + + +System Requirements +------------------- + +* All operating systems: + keyboard, + mouse, + TCP/IP networking; + +* Microsoft Windows 95, Windows 98, Windows Me (the Windows 96 family); + +* Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); + +* Unix: + X Windows version 11 revision 4 or up with the XTEST extension + (use `xdpyinfo | grep XTEST' to check for XTEST). + +In this document, "Unix" means any of the following: Linux, Solaris, +Irix. Synergy may compile and run on other Unix variants, too. Patches +for other platforms are welcome (including patches that package binaries); +See the contact information available off of the synergy home page or use +the patch page on sourceforge. + + +Installation +------------ + +See INSTALL for detailed build and installation instructions and for +more information on configuring synergy. + + +Quick Start +----------- +Synergy lets you use one keyboard and mouse across multiple computers. +To do so it requires that all the computers are connected to each other +via TCP/IP networking. Most systems come with this installed. + +The first step is to pick which keyboard and mouse you want to share. +The computer with that keyboard and mouse is called the "primary +screen" and it runs the synergy server. All of the other computers +are "secondary screens" and run the synergy client. The Windows NT +family, starting with NT 4 with service pack 3, and Unix are the best +choices. The Windows version provides a convenient GUI for +configuration. + +Second, you install the software. Choose the appropriate package +and install it. On Windows you should unzip the files into the +`Program Files' directory; all the files will be put into a new +directory named `Synergy' under `Program Files'. You must install +the software on all the computers that will share the mouse and +keyboard. + +Third, you configure and start the server. + Windows + ------- + Run `synergy' by double clicking on it. This brings up a dialog. + Configure the server: + + * Click the `Server' radio button + * Click `Add' to add the server to the `Screens' list + * Enter the name of server (the computer name is recommended) + * Enter other names the server is known by + * Click OK + * Use `Add' to add your other computers + * Using a computer's name as its screen name is recommended + * Use the controls under `Layout' to link screens together + * Click (once) on the server's name in the `Screens' list + * Choose the screen to the left of the server + * Use `---' if there is no screen to the left of the server + * Choose the screens to the right, above and below the server + * Repeat the above steps for all the other screens + * Use `Options...' to set desired options. + * If the server's screen name is not the server's computer name: + * Click `Advanced...' + * Enter the server's screen name next to `Screen Name' + * Click `OK' + + Now click `Test'. The server will start and you'll see a console window + with log messages telling you about synergy's progress. If an error + occurs you'll get a dialog box telling you synergy is about to quit; + read the log messages to determine the problem then correct it and try + `Test' again. + + Unix + ---- + Create a text file named synergy.conf with the following: + + section: screens + : + : + end + section: links + : + right = + : + left = + end + + Replace each occurrence of `' with the host name of the + primary screen computer and `' with the host name of a + secondary screen computer. In the above example, is to + the right of and is to the left of . + If necessary you should replace `right' and `left' with `left', + `right', 'up', or `down'. If you have more than two computers + you can add those too: add each computer's host name in the + `screens' section and add the appropriate links. + + Now start the server. Normally synergy wants to run "in the + background." It detaches from the terminal and doesn't have a + visible window, effectively disappearing from view. Until you're + sure your configuration works you should start synergy "in the + foreground" using the `-f' command line option: + + synergys -f --config synergy.conf + + Check the reported messages for errors. Use ctrl+c to stop synergy, + correct any problems, and start it again. + +Finally, start the clients. + Windows + ------- + Run `synergy' on the client by double clicking on it. Configure the + client: + + * Click the `Client' radio button + * Enter the server's computer name in `Server Host Name' + * Do not use any of the server's screen names, unless one of those + is also the computer name + * If the client's screen name is not the client's computer name: + * Click `Advanced...' + * Enter the client's screen name next to `Screen Name' + * Click `OK' + * Click `Test' + + If an error occurs you'll get a dialog box telling you synergy is + about to quit; read the log messages to determine the problem then + correct it and try `Test' again. + + Unix + ---- + + To start a client, enter the following: + + synergyc -f + + where `' is replaced by the name of the computer + running the synergy server. If an error is reported use ctrl+c to + stop synergy, fix the error, and try again. + +Both the client and server should immediately report the connection +or an error. If successful, you should now be able to move the +mouse off the appropriate edge of your server's screen and have it +appear on the client's screen. If you're running the synery server +on Windows 95, 98, or Me then make sure the synergy log window is +not the active window; just click on another window, like synergy's +`Running Test...' window, if it is. Use the mouse and keyboard +normally except use the edge of the screens to jump to other screens. +You can also cut-and-paste across computers. Currently, only text +transfers between computers. Start the remaining clients. + +Once the configuration is verified, see the instructions in INSTALL +under `Starting Automatically on ...' for details on running synergy +in the background and on starting synergy automatically when you start +your computers. + + +Tips and Tricks +--------------- +* Be aware that not all keystrokes can be handled by synergy. In + particular, ctrl+alt+del is not handled. However, synergy can + convert ctrl+alt+pause into ctrl+alt+del on the client side. + (Synergy must be installed as a service on the client for this to + work on the Windows NT family.) Some non-standard keys may not + work, especially "multimedia" buttons, though several are + correctly handled. + +* A screen can be its own neighbor. That allows a screen to "wrap". + For example, if a configuration linked the left and right sides of + a screen to itself then moving off the left of the screen would put + the mouse at the right of the screen and vice versa. + +* You cannot switch screens when a key or mouse button is pressed. + +* You cannot switch screens when the scroll lock it toggled on. Use + this to prevent unintentional switching. + +* Turn off mouse driven virtual desktop switching on X windows. It + will interfere with synergy. Use keyboard shortcuts instead. + +* Synergy's screen saver synchronization works best with xscreensaver + under X windows. Synergy works better with xscreensaver if it is + using one of the screen saver extensions. Prior to xscreensaver 4.0 + you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' + command line options to enable an extension (assuming your server has + the extension). Starting with 4.0 you must enable the corresponding + option in your .xscreensaver file. + +* Synergy automatically converts newlines in clipboard text (Unix + expects \n to end each line while Windows expects \r\n). + +* Clients can be started and stopped at any time. When a screen is + not connected, the mouse will jump over that screen as if the mouse + had moved all the way across it and jumped to the next screen. + +* A client's keyboard and mouse are fully functional while synergy is + running. You can use them in case synergy locks up. + +* Strong authentication and encryption is available by using SSH. See + the INSTALL file for more information. Synergy does not otherwise + provide secure communications and it should not be used on or over + untrusted networks. + +* Synergy doesn't work if a 16-bit Windows application has the focus + on Windows 95/98/Me. This is due to limitations of Windows. One + commonly used 16-bit application is the command prompt (command.exe) + and this includes synergy's log window when running in test mode. + + +Bug Reports +----------- + +Synergy is being improved all the time but we can only fix problems +that we know about. Please let us know of any problems you encounter, +including confusing or unhelpful documentation. File reports at: + + http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 diff --git a/TODO b/TODO new file mode 100644 index 0000000..78b3b80 --- /dev/null +++ b/TODO @@ -0,0 +1,61 @@ +Synergy To Do List +================== + +Things to do to synergy, in no particular order: + +* Provide GUI configuration + + There's a GUI tool on win32 but no other platforms. Also, it'd be + nice if the tool allowed users to drag screen icons around to set + the links between them (though this assumes links are symmetrical + and synergy supports asymmetrical links). + +* Provide taskbar feedback + + There's a tray icon on win32 for checking synergy's current status + and to quit synergy. It'd be nice to have something similar on + X11. + +* Port to other platforms + + Most desired is MacOS X. + +* Write man/html pages + +* Provide a win32 installer/uninstaller + + Synergy doesn't have any special needs so even just unzipping is + satisfactory, but a proper installer would be nice. And, more + importantly, it should provide an uninstaller. + +* Add more clipboard formats + + Synergy currently supports only text on the clipboard. It should + support more formats, such as images and sound. For each format, + some canonical type must be chosen. For text, that's UTF-8 with + \n for newlines. For images, it might be BMP or PNG. Whatever it + is it should losslessly support any type it might be converted to. + The type is converted to each platform's native type. For example, + BMP for images on win32. + +Then there are major new features: + +* Provide a KVM mode + + In this mode synergy would share the monitor in addition to the + keyboard and mouse. + +* Support for limited drag and drop between systems + +* Support for (virtual) terminals on unix + + This would be useful in KVM mode to administer several remote + headless systems that you don't want running X just so synergy + can work. + +* Configurable keys + + This includes shortcuts to jump to different screens, always + directing certain keystrokes to the same system, never sending + certain keystrokes to some systems, and remapping keys on the + server to other keys on the clients. diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..2ebdc27 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,515 @@ +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + +AC_DEFUN([ACX_CHECK_SOCKLEN_T], [ + AC_MSG_CHECKING([for socklen_t]) + AC_TRY_COMPILE([ + #include + #include + ], + [socklen_t len;],[acx_socklen_t_ok=yes],[acx_socklen_t_ok=no]) + AC_MSG_RESULT($acx_socklen_t_ok) + if test x"$acx_socklen_t_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_SOCKLEN_T,1,[Define if your compiler defines socklen_t.]),[$1]) + : + else + acx_socklen_t_ok=no + $2 + fi +])dnl ACX_CHECK_SOCKLEN_T + +AC_DEFUN([ACX_CHECK_CXX], [ + AC_MSG_CHECKING([if g++ defines correct C++ macro]) + AC_TRY_COMPILE(, [ + #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS) + #error wrong macro + #endif],[acx_cxx_macro_ok=yes],[acx_cxx_macro_ok=no]) + AC_MSG_RESULT($acx_cxx_macro_ok) + if test x"$acx_cxx_macro_ok" = xyes; then + SYNERGY_CXXFLAGS="" + else + SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS" + fi +])dnl ACX_CHECK_CXX + +AC_DEFUN([ACX_CHECK_CXX_BOOL], [ + AC_MSG_CHECKING([for bool support]) + AC_TRY_COMPILE(, [bool t = true, f = false;], + [acx_cxx_bool_ok=yes],[acx_cxx_bool_ok=no]) + AC_MSG_RESULT($acx_cxx_bool_ok) + if test x"$acx_cxx_bool_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_BOOL,1,[Define if your compiler has bool support.]),[$1]) + : + else + acx_cxx_bool_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_BOOL + +AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [ + AC_MSG_CHECKING([for exception support]) + AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}], + [acx_cxx_exception_ok=yes],[acx_cxx_exception_ok=no]) + AC_MSG_RESULT($acx_cxx_exception_ok) + if test x"$acx_cxx_exception_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_EXCEPTIONS,1,[Define if your compiler has exceptions support.]),[$1]) + : + else + acx_cxx_exception_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_EXCEPTIONS + +AC_DEFUN([ACX_CHECK_CXX_CASTS], [ + AC_MSG_CHECKING([for C++ cast support]) + AC_TRY_COMPILE(, [const char* f="a";const_cast(f); + reinterpret_cast(f);static_cast(4.5);], + [acx_cxx_cast_ok=yes],[acx_cxx_cast_ok=no]) + AC_MSG_RESULT($acx_cxx_cast_ok) + if test x"$acx_cxx_cast_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_CASTS,1,[Define if your compiler has C++ cast support.]),[$1]) + : + else + acx_cxx_cast_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_CASTS + +AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [ + AC_MSG_CHECKING([for mutable support]) + AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}}; + A a;a.f();],[acx_cxx_mutable_ok=yes],[acx_cxx_mutable_ok=no]) + AC_MSG_RESULT($acx_cxx_mutable_ok) + if test x"$acx_cxx_mutable_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_MUTABLE,1,[Define if your compiler has mutable support.]),[$1]) + : + else + acx_cxx_mutable_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_MUTABLE + +AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ + AC_MSG_CHECKING([for C++ standard library]) + AC_TRY_LINK([#include ], [std::set a; a.insert(3);], + [acx_cxx_stdlib_ok=yes],[acx_cxx_stdlib_ok=no]) + AC_MSG_RESULT($acx_cxx_stdlib_ok) + if test x"$acx_cxx_stdlib_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_STDLIB,1,[Define if your compiler has standard C++ library support.]),[$1]) + : + else + acx_cxx_stdlib_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_STDLIB + +AC_DEFUN([ACX_CHECK_GETPWUID_R], [ + AC_MSG_CHECKING([for working getpwuid_r]) + AC_TRY_LINK([#include ], + [char buffer[4096]; struct passwd pwd, *pwdp; + getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp);], + acx_getpwuid_r_ok=yes, acx_getpwuid_r_ok=no) + AC_MSG_RESULT($acx_getpwuid_r_ok) + if test x"$acx_getpwuid_r_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_GETPWUID_R,1,[Define if you have a working \`getpwuid_r\' function.]),[$1]) + : + else + acx_getpwuid_r_ok=no + $2 + fi +])dnl ACX_CHECK_GETPWUID_R + +AC_DEFUN([ACX_CHECK_POLL], [ + AC_MSG_CHECKING([for poll]) + AC_TRY_LINK([#include ], + [struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], + acx_poll_ok=yes, acx_poll_ok=no) + AC_MSG_RESULT($acx_poll_ok) + if test x"$acx_poll_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the \`poll\' function.]),[$1]) + : + else + acx_poll_ok=no + $2 + fi +])dnl ACX_CHECK_POLL + +dnl See if we need extra libraries for nanosleep +AC_DEFUN([ACX_CHECK_NANOSLEEP], [ + acx_nanosleep_ok=no + acx_nanosleep_list="" + + dnl check if user has set NANOSLEEP_LIBS + save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS" + if test x"$NANOSLEEP_LIBS" != x; then + acx_nanosleep_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl nanosleep. `none' should appear first. + acx_nanosleep_list="none $acx_nanosleep_list rt" + for flag in $acx_nanosleep_list; do + case $flag in + none) + AC_MSG_CHECKING([for nanosleep]) + NANOSLEEP_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for nanosleep in $save_user_NANOSLEEP_LIBS]) + NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS" + ;; + + *) + AC_MSG_CHECKING([for nanosleep in -l$flag]) + NANOSLEEP_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$NANOSLEEP_LIBS $LIBS" + AC_TRY_LINK([#include ], + [struct timespec t = { 1, 1000 }; nanosleep(&t, NULL);], + acx_nanosleep_ok=yes, acx_nanosleep_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_nanosleep_ok) + if test x"$acx_nanosleep_ok" = xyes; then + break; + fi + NANOSLEEP_LIBS="" + done + + AC_SUBST(NANOSLEEP_LIBS) + + # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: + if test x"$acx_nanosleep_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the \`nanosleep\' function.]),[$1]) + : + else + acx_nanosleep_ok=no + $2 + fi +])dnl ACX_CHECK_NANOSLEEP + +dnl See if we need extra libraries for inet_aton +AC_DEFUN([ACX_CHECK_INET_ATON], [ + acx_inet_aton_ok=no + acx_inet_aton_list="" + + dnl check if user has set INET_ATON_LIBS + save_user_INET_ATON_LIBS="$INET_ATON_LIBS" + if test x"$INET_ATON_LIBS" != x; then + acx_inet_aton_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl inet_aton. `none' should appear first. + acx_inet_aton_list="none $acx_inet_aton_list resolv" + for flag in $acx_inet_aton_list; do + case $flag in + none) + AC_MSG_CHECKING([for inet_aton]) + INET_ATON_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for inet_aton in $save_user_INET_ATON_LIBS]) + INET_ATON_LIBS="$save_user_INET_ATON_LIBS" + ;; + + *) + AC_MSG_CHECKING([for inet_aton in -l$flag]) + INET_ATON_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$INET_ATON_LIBS $LIBS" + AC_TRY_LINK([#include + #include + #include + #include ], + [struct in_addr addr; inet_aton("foo.bar", &addr);], + acx_inet_aton_ok=yes, acx_inet_aton_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_inet_aton_ok) + if test x"$acx_inet_aton_ok" = xyes; then + break; + fi + INET_ATON_LIBS="" + done + + AC_SUBST(INET_ATON_LIBS) +])dnl ACX_CHECK_INET_ATON + +dnl The following macros are from http://www.gnu.org/software/ac-archive/ +dnl which distributes them under the following license: +dnl +dnl Every Autoconf macro presented on this web site is free software; you can +dnl redistribute it and/or modify it under the terms of the GNU General +dnl Public License as published by the Free Software Foundation; either +dnl version 2, or (at your option) any later version. +dnl +dnl They are distributed in the hope that they will be useful, but WITHOUT +dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +dnl more details. (You should have received a copy of the GNU General Public +dnl License along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place -- Suite 330, Boston, MA 02111-1307, +dnl USA.) +dnl +dnl As a special exception, the Free Software Foundation gives unlimited +dnl permission to copy, distribute and modify the configure scripts that are +dnl the output of Autoconf. You need not follow the terms of the GNU General +dnl Public License when using or distributing such scripts, even though +dnl portions of the text of Autoconf appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that constitutes +dnl the Autoconf program. +dnl +dnl Certain portions of the Autoconf source text are designed to be copied +dnl (in certain cases, depending on the input) into the output of Autoconf. +dnl We call these the "data" portions. The rest of the Autoconf source text +dnl consists of comments plus executable code that decides which of the data +dnl portions to output in any given case. We call these comments and +dnl executable code the "non-data" portions. Autoconf never copies any of the +dnl non-data portions into its output. +dnl +dnl This special exception to the GPL applies to versions of Autoconf +dnl released by the Free Software Foundation. When you make and distribute a +dnl modified version of Autoconf, you may extend this special exception to +dnl the GPL to apply to your modified version as well, *unless* your modified +dnl version has the potential to copy into its output some of the text that +dnl was the non-data portion of the version that you started with. (In other +dnl words, unless your change moves or copies text from the non-data portions +dnl to the data portions.) If your modification has such potential, you must +dnl delete any notice of this special exception to the GPL from your modified +dnl version + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + alpha*-osf*) flag="-D_REENTRANT";; + *solaris*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + # Detect POSIX sigwait() + AC_MSG_CHECKING([for POSIX sigwait]) + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=yes, ok=unknown) + if test x"$ok" = xunknown; then + save_CXXFLAGS2="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) + CXXFLAGS="$save_CXXFLAGS2" + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" != xno; then + AC_DEFINE(HAVE_POSIX_SIGWAIT,1,[Define if you have a POSIX \`sigwait\' function.]) + if test x"$ok" != xyes; then + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi + fi + + # Detect pthread signal functions + AC_MSG_CHECKING([for pthread signal functions]) + AC_TRY_LINK([#include + #include ], + [pthread_kill(pthread_self(), SIGTERM);], + ok=yes, ok=no) + AC_MSG_RESULT(${ok}) + if test x"$ok" = xyes; then + AC_DEFINE(HAVE_PTHREAD_SIGNAL,1,[Define if you have \`pthread_sigmask\' and \`pthread_kill\' functions.]) + fi + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +])dnl ACX_PTHREAD diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..7b27d1e --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1076 @@ +# aclocal.m4 generated automatically by aclocal 1.5 + +# Copyright 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + +AC_DEFUN([ACX_CHECK_SOCKLEN_T], [ + AC_MSG_CHECKING([for socklen_t]) + AC_TRY_COMPILE([ + #include + #include + ], + [socklen_t len;],[acx_socklen_t_ok=yes],[acx_socklen_t_ok=no]) + AC_MSG_RESULT($acx_socklen_t_ok) + if test x"$acx_socklen_t_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_SOCKLEN_T,1,[Define if your compiler defines socklen_t.]),[$1]) + : + else + acx_socklen_t_ok=no + $2 + fi +])dnl ACX_CHECK_SOCKLEN_T + +AC_DEFUN([ACX_CHECK_CXX], [ + AC_MSG_CHECKING([if g++ defines correct C++ macro]) + AC_TRY_COMPILE(, [ + #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS) + #error wrong macro + #endif],[acx_cxx_macro_ok=yes],[acx_cxx_macro_ok=no]) + AC_MSG_RESULT($acx_cxx_macro_ok) + if test x"$acx_cxx_macro_ok" = xyes; then + SYNERGY_CXXFLAGS="" + else + SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS" + fi +])dnl ACX_CHECK_CXX + +AC_DEFUN([ACX_CHECK_CXX_BOOL], [ + AC_MSG_CHECKING([for bool support]) + AC_TRY_COMPILE(, [bool t = true, f = false;], + [acx_cxx_bool_ok=yes],[acx_cxx_bool_ok=no]) + AC_MSG_RESULT($acx_cxx_bool_ok) + if test x"$acx_cxx_bool_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_BOOL,1,[Define if your compiler has bool support.]),[$1]) + : + else + acx_cxx_bool_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_BOOL + +AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [ + AC_MSG_CHECKING([for exception support]) + AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}], + [acx_cxx_exception_ok=yes],[acx_cxx_exception_ok=no]) + AC_MSG_RESULT($acx_cxx_exception_ok) + if test x"$acx_cxx_exception_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_EXCEPTIONS,1,[Define if your compiler has exceptions support.]),[$1]) + : + else + acx_cxx_exception_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_EXCEPTIONS + +AC_DEFUN([ACX_CHECK_CXX_CASTS], [ + AC_MSG_CHECKING([for C++ cast support]) + AC_TRY_COMPILE(, [const char* f="a";const_cast(f); + reinterpret_cast(f);static_cast(4.5);], + [acx_cxx_cast_ok=yes],[acx_cxx_cast_ok=no]) + AC_MSG_RESULT($acx_cxx_cast_ok) + if test x"$acx_cxx_cast_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_CASTS,1,[Define if your compiler has C++ cast support.]),[$1]) + : + else + acx_cxx_cast_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_CASTS + +AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [ + AC_MSG_CHECKING([for mutable support]) + AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}}; + A a;a.f();],[acx_cxx_mutable_ok=yes],[acx_cxx_mutable_ok=no]) + AC_MSG_RESULT($acx_cxx_mutable_ok) + if test x"$acx_cxx_mutable_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_MUTABLE,1,[Define if your compiler has mutable support.]),[$1]) + : + else + acx_cxx_mutable_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_MUTABLE + +AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ + AC_MSG_CHECKING([for C++ standard library]) + AC_TRY_LINK([#include ], [std::set a; a.insert(3);], + [acx_cxx_stdlib_ok=yes],[acx_cxx_stdlib_ok=no]) + AC_MSG_RESULT($acx_cxx_stdlib_ok) + if test x"$acx_cxx_stdlib_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_STDLIB,1,[Define if your compiler has standard C++ library support.]),[$1]) + : + else + acx_cxx_stdlib_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_STDLIB + +AC_DEFUN([ACX_CHECK_GETPWUID_R], [ + AC_MSG_CHECKING([for working getpwuid_r]) + AC_TRY_LINK([#include ], + [char buffer[4096]; struct passwd pwd, *pwdp; + getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp);], + acx_getpwuid_r_ok=yes, acx_getpwuid_r_ok=no) + AC_MSG_RESULT($acx_getpwuid_r_ok) + if test x"$acx_getpwuid_r_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_GETPWUID_R,1,[Define if you have a working \`getpwuid_r\' function.]),[$1]) + : + else + acx_getpwuid_r_ok=no + $2 + fi +])dnl ACX_CHECK_GETPWUID_R + +AC_DEFUN([ACX_CHECK_POLL], [ + AC_MSG_CHECKING([for poll]) + AC_TRY_LINK([#include ], + [struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], + acx_poll_ok=yes, acx_poll_ok=no) + AC_MSG_RESULT($acx_poll_ok) + if test x"$acx_poll_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the \`poll\' function.]),[$1]) + : + else + acx_poll_ok=no + $2 + fi +])dnl ACX_CHECK_POLL + +dnl See if we need extra libraries for nanosleep +AC_DEFUN([ACX_CHECK_NANOSLEEP], [ + acx_nanosleep_ok=no + acx_nanosleep_list="" + + dnl check if user has set NANOSLEEP_LIBS + save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS" + if test x"$NANOSLEEP_LIBS" != x; then + acx_nanosleep_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl nanosleep. `none' should appear first. + acx_nanosleep_list="none $acx_nanosleep_list rt" + for flag in $acx_nanosleep_list; do + case $flag in + none) + AC_MSG_CHECKING([for nanosleep]) + NANOSLEEP_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for nanosleep in $save_user_NANOSLEEP_LIBS]) + NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS" + ;; + + *) + AC_MSG_CHECKING([for nanosleep in -l$flag]) + NANOSLEEP_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$NANOSLEEP_LIBS $LIBS" + AC_TRY_LINK([#include ], + [struct timespec t = { 1, 1000 }; nanosleep(&t, NULL);], + acx_nanosleep_ok=yes, acx_nanosleep_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_nanosleep_ok) + if test x"$acx_nanosleep_ok" = xyes; then + break; + fi + NANOSLEEP_LIBS="" + done + + AC_SUBST(NANOSLEEP_LIBS) + + # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: + if test x"$acx_nanosleep_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the \`nanosleep\' function.]),[$1]) + : + else + acx_nanosleep_ok=no + $2 + fi +])dnl ACX_CHECK_NANOSLEEP + +dnl See if we need extra libraries for inet_aton +AC_DEFUN([ACX_CHECK_INET_ATON], [ + acx_inet_aton_ok=no + acx_inet_aton_list="" + + dnl check if user has set INET_ATON_LIBS + save_user_INET_ATON_LIBS="$INET_ATON_LIBS" + if test x"$INET_ATON_LIBS" != x; then + acx_inet_aton_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl inet_aton. `none' should appear first. + acx_inet_aton_list="none $acx_inet_aton_list resolv" + for flag in $acx_inet_aton_list; do + case $flag in + none) + AC_MSG_CHECKING([for inet_aton]) + INET_ATON_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for inet_aton in $save_user_INET_ATON_LIBS]) + INET_ATON_LIBS="$save_user_INET_ATON_LIBS" + ;; + + *) + AC_MSG_CHECKING([for inet_aton in -l$flag]) + INET_ATON_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$INET_ATON_LIBS $LIBS" + AC_TRY_LINK([#include + #include + #include + #include ], + [struct in_addr addr; inet_aton("foo.bar", &addr);], + acx_inet_aton_ok=yes, acx_inet_aton_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_inet_aton_ok) + if test x"$acx_inet_aton_ok" = xyes; then + break; + fi + INET_ATON_LIBS="" + done + + AC_SUBST(INET_ATON_LIBS) +])dnl ACX_CHECK_INET_ATON + +dnl The following macros are from http://www.gnu.org/software/ac-archive/ +dnl which distributes them under the following license: +dnl +dnl Every Autoconf macro presented on this web site is free software; you can +dnl redistribute it and/or modify it under the terms of the GNU General +dnl Public License as published by the Free Software Foundation; either +dnl version 2, or (at your option) any later version. +dnl +dnl They are distributed in the hope that they will be useful, but WITHOUT +dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +dnl more details. (You should have received a copy of the GNU General Public +dnl License along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place -- Suite 330, Boston, MA 02111-1307, +dnl USA.) +dnl +dnl As a special exception, the Free Software Foundation gives unlimited +dnl permission to copy, distribute and modify the configure scripts that are +dnl the output of Autoconf. You need not follow the terms of the GNU General +dnl Public License when using or distributing such scripts, even though +dnl portions of the text of Autoconf appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that constitutes +dnl the Autoconf program. +dnl +dnl Certain portions of the Autoconf source text are designed to be copied +dnl (in certain cases, depending on the input) into the output of Autoconf. +dnl We call these the "data" portions. The rest of the Autoconf source text +dnl consists of comments plus executable code that decides which of the data +dnl portions to output in any given case. We call these comments and +dnl executable code the "non-data" portions. Autoconf never copies any of the +dnl non-data portions into its output. +dnl +dnl This special exception to the GPL applies to versions of Autoconf +dnl released by the Free Software Foundation. When you make and distribute a +dnl modified version of Autoconf, you may extend this special exception to +dnl the GPL to apply to your modified version as well, *unless* your modified +dnl version has the potential to copy into its output some of the text that +dnl was the non-data portion of the version that you started with. (In other +dnl words, unless your change moves or copies text from the non-data portions +dnl to the data portions.) If your modification has such potential, you must +dnl delete any notice of this special exception to the GPL from your modified +dnl version + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + alpha*-osf*) flag="-D_REENTRANT";; + *solaris*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + # Detect POSIX sigwait() + AC_MSG_CHECKING([for POSIX sigwait]) + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=yes, ok=unknown) + if test x"$ok" = xunknown; then + save_CXXFLAGS2="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) + CXXFLAGS="$save_CXXFLAGS2" + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" != xno; then + AC_DEFINE(HAVE_POSIX_SIGWAIT,1,[Define if you have a POSIX \`sigwait\' function.]) + if test x"$ok" != xyes; then + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi + fi + + # Detect pthread signal functions + AC_MSG_CHECKING([for pthread signal functions]) + AC_TRY_LINK([#include + #include ], + [pthread_kill(pthread_self(), SIGTERM);], + ok=yes, ok=no) + AC_MSG_RESULT(${ok}) + if test x"$ok" = xyes; then + AC_DEFINE(HAVE_PTHREAD_SIGNAL,1,[Define if you have \`pthread_sigmask\' and \`pthread_kill\' functions.]) + fi + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +])dnl ACX_PTHREAD + +# Do all the work for Automake. This macro actually does too much -- +# some checks are only needed if your package does certain things. +# But this isn't really a big deal. + +# serial 5 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# We require 2.13 because we rely on SHELL being computed by configure. +AC_PREREQ([2.13]) + +# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) +# ----------------------------------------------------------- +# If MACRO-NAME is provided do IF-PROVIDED, else IF-NOT-PROVIDED. +# The purpose of this macro is to provide the user with a means to +# check macros which are provided without letting her know how the +# information is coded. +# If this macro is not defined by Autoconf, define it here. +ifdef([AC_PROVIDE_IFELSE], + [], + [define([AC_PROVIDE_IFELSE], + [ifdef([AC_PROVIDE_$1], + [$2], [$3])])]) + + +# AM_INIT_AUTOMAKE(PACKAGE,VERSION, [NO-DEFINE]) +# ---------------------------------------------- +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run \"make distclean\" there first]) +fi + +# Define the identity of the package. +PACKAGE=$1 +AC_SUBST(PACKAGE)dnl +VERSION=$2 +AC_SUBST(VERSION)dnl +ifelse([$3],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) +AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])]) + +# Autoconf 2.50 wants to disallow AM_ names. We explicitly allow +# the ones we care about. +ifdef([m4_pattern_allow], + [m4_pattern_allow([^AM_[A-Z]+FLAGS])])dnl + +# Autoconf 2.50 always computes EXEEXT. However we need to be +# compatible with 2.13, for now. So we always define EXEEXT, but we +# don't compute it. +AC_SUBST(EXEEXT) +# Similar for OBJEXT -- only we only use OBJEXT if the user actually +# requests that it be used. This is a bit dumb. +: ${OBJEXT=o} +AC_SUBST(OBJEXT) + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_MISSING_PROG(AMTAR, tar) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl +AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_PROVIDE_IFELSE([AC_PROG_][CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_][CC], + defn([AC_PROG_][CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_][CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_][CXX], + defn([AC_PROG_][CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) + +# +# Check to make sure that the build environment is sane. +# + +# serial 3 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + + +# serial 2 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + am_backtick='`' + AC_MSG_WARN([${am_backtick}missing' script is too old or missing]) +fi +]) + +# AM_AUX_DIR_EXPAND + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], [ +# expand $ac_aux_dir to an absolute path +am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd` +]) + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# serial 4 -*- Autoconf -*- + + + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + + +# _AM_DEPENDENCIES(NAME) +# --------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX" or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'] + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + echo '#include "conftest.h"' > conftest.c + echo 'int i;' > conftest.h + echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=conftest.c object=conftest.o \ + depfile=conftest.Po tmpdepfile=conftest.TPo \ + $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && + grep conftest.h conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +$1DEPMODE="depmode=$am_cv_$1_dependencies_compiler_type" +AC_SUBST([$1DEPMODE]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[rm -f .deps 2>/dev/null +mkdir .deps 2>/dev/null +if test -d .deps; then + DEPDIR=.deps +else + # MS-DOS does not allow filenames that begin with a dot. + DEPDIR=_deps +fi +rmdir .deps 2>/dev/null +AC_SUBST(DEPDIR) +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking Speeds up one-time builds + --enable-dependency-tracking Do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +pushdef([subst], defn([AC_SUBST])) +subst(AMDEPBACKSLASH) +popdef([subst]) +]) + +# Generate code to set up dependency tracking. +# This macro should only be invoked once -- use via AC_REQUIRE. +# Usage: +# AM_OUTPUT_DEPENDENCY_COMMANDS + +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],[ +AC_OUTPUT_COMMANDS([ +test x"$AMDEP_TRUE" != x"" || +for mf in $CONFIG_FILES; do + case "$mf" in + Makefile) dirpart=.;; + */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;; + *) continue;; + esac + grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`echo "$file" | sed -e 's|/[^/]*$||'` + $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1 + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +], [AMDEP_TRUE="$AMDEP_TRUE" +ac_aux_dir="$ac_aux_dir"])]) + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +doit: + @echo done +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include='#' +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote='"' + _am_result=BSD + fi +fi +AC_SUBST(am__include) +AC_SUBST(am__quote) +AC_MSG_RESULT($_am_result) +rm -f confinc confmf +]) + +# serial 3 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +# +# FIXME: Once using 2.50, use this: +# m4_match([$1], [^TRUE\|FALSE$], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_DEFUN([AM_CONDITIONAL], +[ifelse([$1], [TRUE], + [errprint(__file__:__line__: [$0: invalid condition: $1 +])dnl +m4exit(1)])dnl +ifelse([$1], [FALSE], + [errprint(__file__:__line__: [$0: invalid condition: $1 +])dnl +m4exit(1)])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi]) + +# Like AC_CONFIG_HEADER, but automatically create stamp file. + +# serial 3 + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. We must strip everything past the first ":", +# and everything past the last "/". + +AC_PREREQ([2.12]) + +AC_DEFUN([AM_CONFIG_HEADER], +[ifdef([AC_FOREACH],dnl + [dnl init our file count if it isn't already + m4_ifndef([_AM_Config_Header_Index], m4_define([_AM_Config_Header_Index], [0])) + dnl prepare to store our destination file list for use in config.status + AC_FOREACH([_AM_File], [$1], + [m4_pushdef([_AM_Dest], m4_patsubst(_AM_File, [:.*])) + m4_define([_AM_Config_Header_Index], m4_incr(_AM_Config_Header_Index)) + dnl and add it to the list of files AC keeps track of, along + dnl with our hook + AC_CONFIG_HEADERS(_AM_File, +dnl COMMANDS, [, INIT-CMDS] +[# update the timestamp +echo timestamp >"AS_ESCAPE(_AM_DIRNAME(]_AM_Dest[))/stamp-h]_AM_Config_Header_Index[" +][$2]m4_ifval([$3], [, [$3]]))dnl AC_CONFIG_HEADERS + m4_popdef([_AM_Dest])])],dnl +[AC_CONFIG_HEADER([$1]) + AC_OUTPUT_COMMANDS( + ifelse(patsubst([$1], [[^ ]], []), + [], + [test -z "$CONFIG_HEADERS" || echo timestamp >dnl + patsubst([$1], [^\([^:]*/\)?.*], [\1])stamp-h]),dnl +[am_indx=1 +for am_file in $1; do + case " \$CONFIG_HEADERS " in + *" \$am_file "*) + am_dir=\`echo \$am_file |sed 's%:.*%%;s%[^/]*\$%%'\` + if test -n "\$am_dir"; then + am_tmpdir=\`echo \$am_dir |sed 's%^\(/*\).*\$%\1%'\` + for am_subdir in \`echo \$am_dir |sed 's%/% %'\`; do + am_tmpdir=\$am_tmpdir\$am_subdir/ + if test ! -d \$am_tmpdir; then + mkdir \$am_tmpdir + fi + done + fi + echo timestamp > "\$am_dir"stamp-h\$am_indx + ;; + esac + am_indx=\`expr \$am_indx + 1\` +done]) +])]) # AM_CONFIG_HEADER + +# _AM_DIRNAME(PATH) +# ----------------- +# Like AS_DIRNAME, only do it during macro expansion +AC_DEFUN([_AM_DIRNAME], + [m4_if(m4_regexp([$1], [^.*[^/]//*[^/][^/]*/*$]), -1, + m4_if(m4_regexp([$1], [^//\([^/]\|$\)]), -1, + m4_if(m4_regexp([$1], [^/.*]), -1, + [.], + m4_patsubst([$1], [^\(/\).*], [\1])), + m4_patsubst([$1], [^\(//\)\([^/].*\|$\)], [\1])), + m4_patsubst([$1], [^\(.*[^/]\)//*[^/][^/]*/*$], [\1]))[]dnl +]) # _AM_DIRNAME + diff --git a/all.dsp b/all.dsp new file mode 100644 index 0000000..83d50f6 --- /dev/null +++ b/all.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=all - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "all.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "all.mak" CFG="all - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "all - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "all - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "all - Win32 Release" +# Name "all - Win32 Debug" +# End Target +# End Project diff --git a/cmd/Makefile.am b/cmd/Makefile.am new file mode 100644 index 0000000..3d02899 --- /dev/null +++ b/cmd/Makefile.am @@ -0,0 +1,30 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + launcher \ + synergyc \ + synergys \ + $(NULL) + +EXTRA_DIST = \ + exec.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/cmd/Makefile.in b/cmd/Makefile.in new file mode 100644 index 0000000..7b24794 --- /dev/null +++ b/cmd/Makefile.in @@ -0,0 +1,351 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + launcher \ + synergyc \ + synergys \ + $(NULL) + + +EXTRA_DIST = \ + exec.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = cmd +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/exec.dsp b/cmd/exec.dsp new file mode 100644 index 0000000..10237f6 --- /dev/null +++ b/cmd/exec.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="exec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=exec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "exec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "exec.mak" CFG="exec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "exec - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "exec - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "exec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "exec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "exec - Win32 Release" +# Name "exec - Win32 Debug" +# End Target +# End Project diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp new file mode 100644 index 0000000..bd4009b --- /dev/null +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -0,0 +1,227 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "CAdvancedOptions.h" +#include "LaunchUtil.h" +#include "resource.h" + +// +// CAdvancedOptions +// + +CAdvancedOptions* CAdvancedOptions::s_singleton = NULL; + +CAdvancedOptions::CAdvancedOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config), + m_isClient(false), + m_screenName(ARCH->getHostName()), + m_port(kDefaultPort) +{ + assert(s_singleton == NULL); + s_singleton = this; + init(); +} + +CAdvancedOptions::~CAdvancedOptions() +{ + s_singleton = NULL; +} + +void +CAdvancedOptions::doModal(bool isClient) +{ + // save state + m_isClient = isClient; + + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADVANCED_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +CString +CAdvancedOptions::getScreenName() const +{ + return m_screenName; +} + +int +CAdvancedOptions::getPort() const +{ + return m_port; +} + +CString +CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const +{ + CString cmdLine; + + // screen name + if (!m_screenName.empty()) { + cmdLine += " --name "; + cmdLine += m_screenName; + } + + // port + char portString[20]; + sprintf(portString, "%d", m_port); + if (isClient) { + cmdLine += " "; + cmdLine += serverName; + cmdLine += ":"; + cmdLine += portString; + } + else { + cmdLine += " --address :"; + cmdLine += portString; + } + + return cmdLine; +} + +void +CAdvancedOptions::init() +{ + // get values from registry + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + DWORD newPort = CArchMiscWindows::readValueInt(key, "port"); + CString newName = CArchMiscWindows::readValueString(key, "name"); + if (newPort != 0) { + m_port = static_cast(newPort); + } + if (!newName.empty()) { + m_screenName = newName; + } + CArchMiscWindows::closeKey(key); + } +} + +void +CAdvancedOptions::doInit(HWND hwnd) +{ + // set values in GUI + HWND child; + char buffer[20]; + sprintf(buffer, "%d", m_port); + child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + + child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_screenName.c_str()); +} + +bool +CAdvancedOptions::save(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); + CString name = getWindowText(child); + if (!m_config->isValidScreenName(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return false; + } + if (!m_isClient && !m_config->isScreen(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return false; + } + + // get and verify port + child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT); + CString portString = getWindowText(child); + int port = atoi(portString.c_str()); + if (port < 1 || port > 65535) { + CString defaultPortString = CStringUtil::print("%d", kDefaultPort); + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_PORT).c_str(), + portString.c_str(), + defaultPortString.c_str())); + SetFocus(child); + return false; + } + + // save state + m_screenName = name; + m_port = port; + + // save values to registry + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + CArchMiscWindows::setValue(key, "port", m_port); + CArchMiscWindows::setValue(key, "name", m_screenName); + CArchMiscWindows::closeKey(key); + } + + return true; +} + +void +CAdvancedOptions::setDefaults(HWND hwnd) +{ + // restore defaults + m_screenName = ARCH->getHostName(); + m_port = kDefaultPort; + + // update GUI + doInit(hwnd); +} + +BOOL +CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_ADVANCED_DEFAULTS: + setDefaults(hwnd); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAdvancedOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CAdvancedOptions.h b/cmd/launcher/CAdvancedOptions.h new file mode 100644 index 0000000..c86bf38 --- /dev/null +++ b/cmd/launcher/CAdvancedOptions.h @@ -0,0 +1,76 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CADVANCEDOPTIONS_H +#define CADVANCEDOPTIONS_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Advanced options dialog for Microsoft Windows launcher +class CAdvancedOptions { +public: + CAdvancedOptions(HWND parent, CConfig*); + ~CAdvancedOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(bool isClient); + + //@} + //! @name accessors + //@{ + + //! Get the screen name + CString getScreenName() const; + + //! Get the port + int getPort() const; + + //! Convert options to command line string + CString getCommandLine(bool isClient, + const CString& serverName) const; + + //@} + +private: + void init(); + void doInit(HWND hwnd); + bool save(HWND hwnd); + void setDefaults(HWND hwnd); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAdvancedOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + bool m_isClient; + CString m_screenName; + int m_port; +}; + +#endif diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp new file mode 100644 index 0000000..1391063 --- /dev/null +++ b/cmd/launcher/CAutoStart.cpp @@ -0,0 +1,288 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CLog.h" +#include "ILogOutputter.h" +#include "CArch.h" +#include "CStringUtil.h" +#include "XArch.h" +#include "CAutoStart.h" +#include "LaunchUtil.h" +#include "resource.h" + +#define CLIENT_DAEMON_NAME "Synergy Client" +#define SERVER_DAEMON_NAME "Synergy Server" +#define CLIENT_DAEMON_INFO "Shares this system's mouse and keyboard with others." +#define SERVER_DAEMON_INFO "Shares this system's mouse and keyboard with others." + +// +// CAutoStartOutputter +// +// This class detects a message above a certain level and saves it +// + +class CAutoStartOutputter : public ILogOutputter { +public: + CAutoStartOutputter(CString* msg) : m_msg(msg) { } + virtual ~CAutoStartOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } + +private: + CString* m_msg; +}; + +bool +CAutoStartOutputter::write(ELevel level, const char* message) +{ + if (level <= CLog::kERROR) { + *m_msg = message; + } + return false; +} + + +// +// CAutoStart +// + +CAutoStart* CAutoStart::s_singleton = NULL; + +CAutoStart::CAutoStart(HWND parent, CConfig* config, const CString& cmdLine) : + m_parent(parent), + m_config(config), + m_isServer(config != NULL), + m_cmdLine(cmdLine), + m_name((config != NULL) ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME), + m_userConfigSaved(false) + +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CAutoStart::~CAutoStart() +{ + s_singleton = NULL; +} + +void +CAutoStart::doModal() +{ + // install our log outputter + CLOG->insert(new CAutoStartOutputter(&m_errorMessage)); + + // reset saved flag + m_userConfigSaved = false; + + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART), + m_parent, dlgProc, (LPARAM)this); + + // remove log outputter + CLOG->pop_front(); +} + +bool +CAutoStart::wasUserConfigSaved() const +{ + return m_userConfigSaved; +} + +void +CAutoStart::update() +{ + // get installation state + const bool installedSystem = ARCH->isDaemonInstalled( + m_name.c_str(), true); + const bool installedUser = ARCH->isDaemonInstalled( + m_name.c_str(), false); + + // get user's permissions + const bool canInstallSystem = ARCH->canInstallDaemon( + m_name.c_str(), true); + const bool canInstallUser = ARCH->canInstallDaemon( + m_name.c_str(), false); + + // update messages + CString msg, label; + if (canInstallSystem) { + msg = getString(IDS_AUTOSTART_PERMISSION_SYSTEM); + } + else if (canInstallUser) { + msg = getString(IDS_AUTOSTART_PERMISSION_USER); + } + else { + msg = getString(IDS_AUTOSTART_PERMISSION_NONE); + } + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_PERMISSION_MSG), msg); + if (installedSystem) { + msg = getString(IDS_AUTOSTART_INSTALLED_SYSTEM); + label = getString(IDS_UNINSTALL_LABEL); + } + else if (installedUser) { + msg = getString(IDS_AUTOSTART_INSTALLED_USER); + label = getString(IDS_UNINSTALL_LABEL); + } + else { + msg = getString(IDS_AUTOSTART_INSTALLED_NONE); + label = getString(IDS_INSTALL_LABEL); + } + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALLED_MSG), msg); + + // update buttons + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM), label); + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER), label); + if (installedSystem) { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, false); + m_install = false; + } + else if (installedUser) { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, false); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser); + m_install = false; + } + else { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser); + m_install = true; + } +} + +bool +CAutoStart::onInstall(bool allUsers) +{ + if (!m_install) { + return onUninstall(allUsers); + } + + // try saving configuration. if we can't then don't try + // installing the daemon. + if (m_config != NULL) { + if (!saveConfig(*m_config, allUsers)) { + showError(m_hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return false; + } + + // note if we've saved the user's configuration + if (!allUsers) { + m_userConfigSaved = true; + } + } + + // get the app path + CString appPath = getAppPath(m_isServer ? SERVER_APP : CLIENT_APP); + + // clear error message + m_errorMessage = ""; + + // install + try { + ARCH->installDaemon(m_name.c_str(), + m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, + appPath.c_str(), m_cmdLine.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), + getString(allUsers ? + IDS_INSTALLED_SYSTEM : + IDS_INSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { + if (m_errorMessage.empty()) { + m_errorMessage = CStringUtil::format( + getString(IDS_INSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); + } + showError(m_hwnd, m_errorMessage); + return false; + } +} + +bool +CAutoStart::onUninstall(bool allUsers) +{ + // clear error message + m_errorMessage = ""; + + // uninstall + try { + ARCH->uninstallDaemon(m_name.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE), + getString(allUsers ? + IDS_UNINSTALLED_SYSTEM : + IDS_UNINSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { + if (m_errorMessage.empty()) { + m_errorMessage = CStringUtil::format( + getString(IDS_UNINSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); + } + showError(m_hwnd, m_errorMessage); + return false; + } +} + +BOOL +CAutoStart::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + // save our hwnd + m_hwnd = hwnd; + + // update the controls + update(); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_AUTOSTART_INSTALL_SYSTEM: + onInstall(true); + update(); + return TRUE; + + case IDC_AUTOSTART_INSTALL_USER: + onInstall(false); + update(); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + m_hwnd = NULL; + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAutoStart::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h new file mode 100644 index 0000000..e931530 --- /dev/null +++ b/cmd/launcher/CAutoStart.h @@ -0,0 +1,78 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CAUTOSTART_H +#define CAUTOSTART_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Auto start dialog for Microsoft Windows launcher +class CAutoStart { +public: + // if config == NULL then it's assumed we're installing/uninstalling + // the client, otherwise the server. + CAutoStart(HWND parent, CConfig* config, const CString& cmdLine); + ~CAutoStart(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + //! Test if user configuration was saved + /*! + Returns true if the user's configuration (as opposed to the system-wide + configuration) was saved successfully while in doModal(). + */ + bool wasUserConfigSaved() const; + + //@} + +private: + void update(); + bool onInstall(bool allUsers); + bool onUninstall(bool allUsers); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAutoStart* s_singleton; + + HWND m_parent; + CConfig* m_config; + bool m_isServer; + CString m_cmdLine; + CString m_name; + HWND m_hwnd; + bool m_install; + CString m_errorMessage; + bool m_userConfigSaved; +}; + +#endif diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp new file mode 100644 index 0000000..8a44643 --- /dev/null +++ b/cmd/launcher/CGlobalOptions.cpp @@ -0,0 +1,259 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CGlobalOptions.h" +#include "LaunchUtil.h" +#include "resource.h" + +static const int s_defaultDelay = 250; +static const int s_defaultHeartbeat = 5000; + +// +// CGlobalOptions +// + +CGlobalOptions* CGlobalOptions::s_singleton = NULL; + +CGlobalOptions::CGlobalOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config), + m_delayTime(s_defaultDelay), + m_twoTapTime(s_defaultDelay), + m_heartbeatTime(s_defaultHeartbeat) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CGlobalOptions::~CGlobalOptions() +{ + s_singleton = NULL; +} + +void +CGlobalOptions::doModal() +{ + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_GLOBAL_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +void +CGlobalOptions::init(HWND hwnd) +{ + HWND child; + char buffer[30]; + + // reset options + sprintf(buffer, "%d", m_delayTime); + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + setWindowText(child, buffer); + sprintf(buffer, "%d", m_twoTapTime); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + setWindowText(child, buffer); + sprintf(buffer, "%d", m_heartbeatTime); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + setWindowText(child, buffer); + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + setItemChecked(child, true); + + // get the global options + const CConfig::CScreenOptions* options = m_config->getOptions(""); + if (options != NULL) { + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + const OptionID id = index->first; + const OptionValue value = index->second; + if (id == kOptionScreenSwitchDelay) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + setWindowText(child, buffer); + } + } + else if (id == kOptionScreenSwitchTwoTap) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + setWindowText(child, buffer); + } + } + else if (id == kOptionHeartbeat) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + setWindowText(child, buffer); + } + } + else if (id == kOptionScreenSaverSync) { + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + setItemChecked(child, (value != 0)); + } + } + } +} + +bool +CGlobalOptions::save(HWND hwnd) +{ + HWND child; + int newDelayTime = 0; + int newTwoTapTime = 0; + int newHeartbeatTime = 0; + + // get requested options + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + newDelayTime = getTime(hwnd, child, true); + if (newDelayTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + newDelayTime = getTime(hwnd, child, false); + if (newDelayTime == 0) { + newDelayTime = s_defaultDelay; + } + } + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + newTwoTapTime = getTime(hwnd, child, true); + if (newTwoTapTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + newTwoTapTime = getTime(hwnd, child, false); + if (newTwoTapTime == 0) { + newTwoTapTime = s_defaultDelay; + } + } + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + newHeartbeatTime = getTime(hwnd, child, true); + if (newHeartbeatTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + newHeartbeatTime = getTime(hwnd, child, false); + if (newHeartbeatTime == 0) { + newHeartbeatTime = s_defaultHeartbeat; + } + } + + // remove existing config options + m_config->removeOption("", kOptionScreenSwitchDelay); + m_config->removeOption("", kOptionScreenSwitchTwoTap); + m_config->removeOption("", kOptionHeartbeat); + m_config->removeOption("", kOptionScreenSaverSync); + + // add requested options + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionScreenSwitchDelay, newDelayTime); + } + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionScreenSwitchTwoTap, newTwoTapTime); + } + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionHeartbeat, newHeartbeatTime); + } + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + if (!isItemChecked(child)) { + m_config->addOption("", kOptionScreenSaverSync, 0); + } + + // save last values + m_delayTime = newDelayTime; + m_twoTapTime = newTwoTapTime; + m_heartbeatTime = newHeartbeatTime; + return true; +} + +int +CGlobalOptions::getTime(HWND hwnd, HWND child, bool reportError) +{ + CString valueString = getWindowText(child); + int value = atoi(valueString.c_str()); + if (value < 1) { + if (reportError) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_TIME).c_str(), + valueString.c_str())); + SetFocus(child); + } + return 0; + } + return value; +} + +BOOL +CGlobalOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CGlobalOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CGlobalOptions.h b/cmd/launcher/CGlobalOptions.h new file mode 100644 index 0000000..f04f1ba --- /dev/null +++ b/cmd/launcher/CGlobalOptions.h @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CGLOBALOPTIONS_H +#define CGLOBALOPTIONS_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Global options dialog for Microsoft Windows launcher +class CGlobalOptions { +public: + CGlobalOptions(HWND parent, CConfig*); + ~CGlobalOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + + //@} + +private: + void init(HWND hwnd); + bool save(HWND hwnd); + + int getTime(HWND hwnd, HWND child, bool reportError); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CGlobalOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + int m_delayTime; + int m_twoTapTime; + int m_heartbeatTime; +}; + +#endif diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp new file mode 100644 index 0000000..46c8ef9 --- /dev/null +++ b/cmd/launcher/LaunchUtil.cpp @@ -0,0 +1,228 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "LaunchUtil.h" +#include "CArch.h" +#include "resource.h" +#include "stdfstream.h" + +#define CONFIG_NAME "synergy.sgc" + +CString +getString(DWORD id) +{ + char buffer[1024]; + buffer[0] = '\0'; + LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0])); + return buffer; +} + +CString +getErrorString(DWORD error) +{ + char* buffer; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&buffer, + 0, + NULL) == 0) { + return getString(IDS_ERROR); + } + else { + CString result(buffer); + LocalFree(buffer); + return result; + } +} + +void +showError(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_ERROR); + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +void +askOkay(HWND hwnd, const CString& title, const CString& msg) +{ + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +bool +askVerify(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_VERIFY); + int result = MessageBox(hwnd, msg.c_str(), + title.c_str(), MB_OKCANCEL | MB_APPLMODAL); + return (result == IDOK); +} + +void +setWindowText(HWND hwnd, const CString& msg) +{ + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)msg.c_str()); +} + +CString +getWindowText(HWND hwnd) +{ + LRESULT size = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0); + char* buffer = new char[size + 1]; + SendMessage(hwnd, WM_GETTEXT, size + 1, (LPARAM)buffer); + buffer[size] = '\0'; + CString result(buffer); + delete[] buffer; + return result; +} + +HWND +getItem(HWND hwnd, int id) +{ + return GetDlgItem(hwnd, id); +} + +void +enableItem(HWND hwnd, int id, bool enabled) +{ + EnableWindow(GetDlgItem(hwnd, id), enabled); +} + +void +setItemChecked(HWND hwnd, bool checked) +{ + SendMessage(hwnd, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0); +} + +bool +isItemChecked(HWND hwnd) +{ + return (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED); +} + +CString +getAppPath(const CString& appName) +{ + // prepare path to app + char myPathname[MAX_PATH]; + GetModuleFileName(s_instance, myPathname, MAX_PATH); + const char* myBasename = ARCH->getBasename(myPathname); + CString appPath = CString(myPathname, myBasename - myPathname); + appPath += appName; + return appPath; +} + +static +bool +loadConfig(const CString& pathname, CConfig& config) +{ + try { + std::ifstream stream(pathname.c_str()); + if (stream) { + stream >> config; + return true; + } + } + catch (...) { + // ignore + } + return false; +} + +bool +loadConfig(CConfig& config) +{ + // load configuration + bool configLoaded = false; + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + // try loading the user's configuration + path = ARCH->concatPath(path, CONFIG_NAME); + if (loadConfig(path, config)) { + configLoaded = true; + } + else { + // try the system-wide config file + path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + if (loadConfig(path, config)) { + configLoaded = true; + } + } + } + } + return configLoaded; +} + +static +bool +saveConfig(const CString& pathname, const CConfig& config) +{ + try { + std::ofstream stream(pathname.c_str()); + if (stream) { + stream << config; + return !!stream; + } + } + catch (...) { + // ignore + } + return false; +} + +bool +saveConfig(const CConfig& config, bool sysOnly) +{ + // try saving the user's configuration + if (!sysOnly) { + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + } + + // try the system-wide config file + else { + CString path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + } + + return false; +} + +const TCHAR* const* +getSettingsPath() +{ + static const TCHAR* s_keyNames[] = { + TEXT("Software"), + TEXT("Synergy"), + TEXT("Synergy"), + NULL + }; + return s_keyNames; +} diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h new file mode 100644 index 0000000..4888926 --- /dev/null +++ b/cmd/launcher/LaunchUtil.h @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LAUNCHUTIL_H +#define LAUNCHUTIL_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +#define CLIENT_APP "synergyc.exe" +#define SERVER_APP "synergys.exe" + +class CConfig; + +// client must define this and set it before calling any function here +extern HINSTANCE s_instance; + +CString getString(DWORD id); +CString getErrorString(DWORD error); + +void showError(HWND hwnd, const CString& msg); +void askOkay(HWND hwnd, const CString& title, + const CString& msg); +bool askVerify(HWND hwnd, const CString& msg); + +void setWindowText(HWND hwnd, const CString& msg); +CString getWindowText(HWND hwnd); + +HWND getItem(HWND hwnd, int id); +void enableItem(HWND hwnd, int id, bool enabled); + +void setItemChecked(HWND, bool); +bool isItemChecked(HWND); + +CString getAppPath(const CString& appName); + +bool loadConfig(CConfig& config); +bool saveConfig(const CConfig& config, bool sysOnly); + +const TCHAR* const* getSettingsPath(); + +#endif diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am new file mode 100644 index 0000000..782d771 --- /dev/null +++ b/cmd/launcher/Makefile.am @@ -0,0 +1,36 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CAdvancedOptions.cpp \ + CAdvancedOptions.h \ + CAutoStart.cpp \ + CAutoStart.h \ + CGlobalOptions.cpp \ + CGlobalOptions.h \ + LaunchUtil.cpp \ + LaunchUtil.h \ + launcher.cpp \ + launcher.dsp \ + launcher.rc \ + resource.h \ + synergy.ico \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/cmd/launcher/Makefile.in b/cmd/launcher/Makefile.in new file mode 100644 index 0000000..f9e5317 --- /dev/null +++ b/cmd/launcher/Makefile.in @@ -0,0 +1,239 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CAdvancedOptions.cpp \ + CAdvancedOptions.h \ + CAutoStart.cpp \ + CAutoStart.h \ + CGlobalOptions.cpp \ + CGlobalOptions.h \ + LaunchUtil.cpp \ + LaunchUtil.h \ + launcher.cpp \ + launcher.dsp \ + launcher.rc \ + resource.h \ + synergy.ico \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = cmd/launcher +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = +DIST_COMMON = Makefile.am Makefile.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/launcher/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: +tags: TAGS +TAGS: + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp new file mode 100644 index 0000000..0c4b459 --- /dev/null +++ b/cmd/launcher/launcher.cpp @@ -0,0 +1,1211 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "KeyTypes.h" +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "CLog.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "Version.h" +#include "stdvector.h" +#include "resource.h" + +// these must come after the above because it includes windows.h +#include "LaunchUtil.h" +#include "CAutoStart.h" +#include "CGlobalOptions.h" +#include "CAdvancedOptions.h" + +#define CONFIG_NAME "synergy.sgc" +#define CLIENT_APP "synergyc.exe" +#define SERVER_APP "synergys.exe" + +typedef std::vector CStringList; + +class CScreenInfo { +public: + CString m_screen; + CStringList m_aliases; + CConfig::CScreenOptions m_options; +}; + +class CChildWaitInfo { +public: + HWND m_dialog; + HANDLE m_child; + DWORD m_childID; + HANDLE m_ready; + HANDLE m_stop; +}; + +struct CModifierInfo { +public: + int m_ctrlID; + const char* m_name; + KeyModifierID m_modifierID; + OptionID m_optionID; +}; + +static const CModifierInfo s_modifiers[] = { + { IDC_ADD_MOD_SHIFT, "Shift", + kKeyModifierIDShift, kOptionModifierMapForShift }, + { IDC_ADD_MOD_CTRL, "Ctrl", + kKeyModifierIDControl, kOptionModifierMapForControl }, + { IDC_ADD_MOD_ALT, "Alt", + kKeyModifierIDAlt, kOptionModifierMapForAlt }, + { IDC_ADD_MOD_META, "Meta", + kKeyModifierIDMeta, kOptionModifierMapForMeta }, + { IDC_ADD_MOD_SUPER, "Super", + kKeyModifierIDSuper, kOptionModifierMapForSuper } +}; + +static const KeyModifierID baseModifier = kKeyModifierIDShift; + +static const char* s_debugName[][2] = { + { TEXT("Error"), "ERROR" }, + { TEXT("Warning"), "WARNING" }, + { TEXT("Note"), "NOTE" }, + { TEXT("Info"), "INFO" }, + { TEXT("Debug"), "DEBUG" }, + { TEXT("Debug1"), "DEBUG1" }, + { TEXT("Debug2"), "DEBUG2" } +}; +static const int s_defaultDebug = 3; // INFO + +HINSTANCE s_instance = NULL; + +static CGlobalOptions* s_globalOptions = NULL; +static CAdvancedOptions* s_advancedOptions = NULL; + +static const TCHAR* s_mainClass = TEXT("GoSynergy"); +static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); + +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + CConfig m_config; + CConfig m_oldConfig; + CStringList m_screens; +}; + +CArgs* CArgs::s_instance = NULL; + + +static +BOOL CALLBACK +addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +static +void +tokenize(CStringList& tokens, const CString& src) +{ + // find first non-whitespace + CString::size_type x = src.find_first_not_of(" \t\r\n"); + if (x == CString::npos) { + return; + } + + // find next whitespace + do { + CString::size_type y = src.find_first_of(" \t\r\n", x); + if (y == CString::npos) { + y = src.size(); + } + tokens.push_back(src.substr(x, y - x)); + x = src.find_first_not_of(" \t\r\n", y); + } while (x != CString::npos); +} + +static +bool +isNameInList(const CStringList& names, const CString& name) +{ + for (CStringList::const_iterator index = names.begin(); + index != names.end(); ++index) { + if (CStringUtil::CaselessCmp::equal(name, *index)) { + return true; + } + } + return false; +} + +static +bool +isClientChecked(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); + return isItemChecked(child); +} + +static +void +enableSaveControls(HWND hwnd) +{ + enableItem(hwnd, IDC_MAIN_SAVE, ARG->m_config != ARG->m_oldConfig); +} + +static +void +enableScreensControls(HWND hwnd) +{ + // decide if edit and remove buttons should be enabled + bool client = isClientChecked(hwnd); + bool screenSelected = false; + if (!client) { + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + if (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR) { + screenSelected = true; + } + } + + // enable/disable controls + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST, !client); + enableItem(hwnd, IDC_MAIN_SERVER_ADD_BUTTON, !client); + enableItem(hwnd, IDC_MAIN_SERVER_EDIT_BUTTON, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_REMOVE_BUTTON, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_LAYOUT_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_LEFT_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_TOP_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_LABEL, screenSelected); +} + +static +void +enableMainWindowControls(HWND hwnd) +{ + bool client = isClientChecked(hwnd); + enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client); + enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client); + enableScreensControls(hwnd); + enableSaveControls(hwnd); +} + +static +void +updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) +{ + // remove all neighbors from combo box + SendMessage(hwnd, CB_RESETCONTENT, 0, 0); + + // add all screens to combo box + if (!screen.empty()) { + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++index) { + SendMessage(hwnd, CB_INSERTSTRING, + (WPARAM)-1, (LPARAM)index->c_str()); + } + } + + // add empty neighbor to combo box + SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)TEXT("---")); + + // select neighbor in combo box + LRESULT index = 0; + if (!screen.empty()) { + const CString& neighbor = ARG->m_config.getNeighbor(screen, direction); + if (!neighbor.empty()) { + index = SendMessage(hwnd, CB_FINDSTRINGEXACT, + 0, (LPARAM)neighbor.c_str()); + if (index == LB_ERR) { + index = 0; + } + } + } + SendMessage(hwnd, CB_SETCURSEL, index, 0); +} + +static +void +updateNeighbors(HWND hwnd) +{ + // get selected screen name or empty string if no selection + CString screen; + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + screen = ARG->m_screens[index]; + } + + // set neighbor combo boxes + child = getItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO); + updateNeighbor(child, screen, kLeft); + child = getItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO); + updateNeighbor(child, screen, kRight); + child = getItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO); + updateNeighbor(child, screen, kTop); + child = getItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO); + updateNeighbor(child, screen, kBottom); +} + +static +void +addScreen(HWND hwnd) +{ + // empty screen info + CScreenInfo info; + + // run dialog + if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + hwnd, addDlgProc, (LPARAM)&info) != 0) { + // get current number of screens + UInt32 i = ARG->m_screens.size(); + + // add screen to list control + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + CString item = CStringUtil::print("%d. %s", + i + 1, info.m_screen.c_str()); + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); + + // add screen to screen list + ARG->m_screens.push_back(info.m_screen); + + // add screen to config + ARG->m_config.addScreen(info.m_screen); + + // add aliases to config + for (CStringList::const_iterator index = info.m_aliases.begin(); + index != info.m_aliases.end(); ++index) { + ARG->m_config.addAlias(info.m_screen, *index); + } + + // set options + ARG->m_config.removeOptions(info.m_screen); + for (CConfig::CScreenOptions::const_iterator + index = info.m_options.begin(); + index != info.m_options.end(); ++index) { + ARG->m_config.addOption(info.m_screen, index->first, index->second); + } + + // update neighbors + updateNeighbors(hwnd); + enableScreensControls(hwnd); + enableSaveControls(hwnd); + } +} + +static +void +editScreen(HWND hwnd) +{ + // get selected list item + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // fill in screen info + CScreenInfo info; + info.m_screen = ARG->m_screens[index]; + for (CConfig::all_const_iterator index = ARG->m_config.beginAll(); + index != ARG->m_config.endAll(); ++index) { + if (CStringUtil::CaselessCmp::equal(index->second, info.m_screen) && + !CStringUtil::CaselessCmp::equal(index->second, index->first)) { + info.m_aliases.push_back(index->first); + } + } + const CConfig::CScreenOptions* options = + ARG->m_config.getOptions(info.m_screen); + if (options != NULL) { + info.m_options = *options; + } + + // save current info + CScreenInfo oldInfo = info; + + // run dialog + if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + hwnd, addDlgProc, (LPARAM)&info) != 0) { + // replace screen + ARG->m_screens[index] = info.m_screen; + + // remove old aliases + for (CStringList::const_iterator index = oldInfo.m_aliases.begin(); + index != oldInfo.m_aliases.end(); ++index) { + ARG->m_config.removeAlias(*index); + } + + // replace name + ARG->m_config.renameScreen(oldInfo.m_screen, info.m_screen); + + // add new aliases + for (CStringList::const_iterator index = info.m_aliases.begin(); + index != info.m_aliases.end(); ++index) { + ARG->m_config.addAlias(info.m_screen, *index); + } + + // set options + ARG->m_config.removeOptions(info.m_screen); + for (CConfig::CScreenOptions::const_iterator + index = info.m_options.begin(); + index != info.m_options.end(); ++index) { + ARG->m_config.addOption(info.m_screen, index->first, index->second); + } + + // update list + CString item = CStringUtil::print("%d. %s", + index + 1, info.m_screen.c_str()); + SendMessage(child, LB_DELETESTRING, index, 0); + SendMessage(child, LB_INSERTSTRING, index, + (LPARAM)item.c_str()); + SendMessage(child, LB_SETCURSEL, index, 0); + + // update neighbors + updateNeighbors(hwnd); + enableSaveControls(hwnd); + } +} + +static +void +removeScreen(HWND hwnd) +{ + // get selected list item + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // get screen name + CString name = ARG->m_screens[index]; + + // remove screen from list control + SendMessage(child, LB_DELETESTRING, index, 0); + + // remove screen from screen list + ARG->m_screens.erase(&ARG->m_screens[index]); + + // remove screen from config (this also removes aliases) + ARG->m_config.removeScreen(name); + + // update neighbors + updateNeighbors(hwnd); + enableScreensControls(hwnd); + enableSaveControls(hwnd); +} + +static +void +changeNeighbor(HWND hwnd, HWND combo, EDirection direction) +{ + // get selected screen + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // get screen name + CString screen = ARG->m_screens[index]; + + // get selected neighbor + index = SendMessage(combo, CB_GETCURSEL, 0, 0); + + // remove old connection + ARG->m_config.disconnect(screen, direction); + + // add new connection + if (index != LB_ERR && index != 0) { + LRESULT size = SendMessage(combo, CB_GETLBTEXTLEN, index, 0); + char* neighbor = new char[size + 1]; + SendMessage(combo, CB_GETLBTEXT, index, (LPARAM)neighbor); + ARG->m_config.connect(screen, direction, CString(neighbor)); + delete[] neighbor; + } + + enableSaveControls(hwnd); +} + +static +bool +execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo) +{ + // prepare startup info + STARTUPINFO startup; + startup.cb = sizeof(startup); + startup.lpReserved = NULL; + startup.lpDesktop = NULL; + startup.lpTitle = NULL; + startup.dwX = (DWORD)CW_USEDEFAULT; + startup.dwY = (DWORD)CW_USEDEFAULT; + startup.dwXSize = (DWORD)CW_USEDEFAULT; + startup.dwYSize = (DWORD)CW_USEDEFAULT; + startup.dwXCountChars = 0; + startup.dwYCountChars = 0; + startup.dwFillAttribute = 0; + startup.dwFlags = STARTF_FORCEONFEEDBACK; + startup.wShowWindow = SW_SHOWDEFAULT; + startup.cbReserved2 = 0; + startup.lpReserved2 = NULL; + startup.hStdInput = NULL; + startup.hStdOutput = NULL; + startup.hStdError = NULL; + + // prepare path to app + CString appPath = getAppPath(app); + + // put path to app in command line + CString commandLine = "\""; + commandLine += appPath; + commandLine += "\" "; + commandLine += cmdLine; + + // start child + if (CreateProcess(NULL, (char*)commandLine.c_str(), + NULL, + NULL, + FALSE, + CREATE_DEFAULT_ERROR_MODE | + CREATE_NEW_PROCESS_GROUP | + NORMAL_PRIORITY_CLASS, + NULL, + NULL, + &startup, + procInfo) == 0) { + return false; + } + else { + return true; + } +} + +static +CString +getCommandLine(HWND hwnd, bool testing) +{ + CString cmdLine; + + // add constant testing args + if (testing) { + cmdLine += " -z --no-restart --no-daemon"; + } + + // get the server name + CString server; + bool isClient = isClientChecked(hwnd); + if (isClient) { + // check server name + HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + server = getWindowText(child); + if (!ARG->m_config.isValidScreenName(server)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SERVER_NAME).c_str(), + server.c_str())); + SetFocus(child); + return CString(); + } + + // compare server name to local host. a common error + // is to provide the client's name for the server. we + // don't bother to check the addresses though that'd be + // more accurate. + if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SERVER_IS_CLIENT).c_str(), + server.c_str())); + SetFocus(child); + return CString(); + } + } + + // debug level. always include this. + if (true) { + HWND child = getItem(hwnd, IDC_MAIN_DEBUG); + DWORD debug = SendMessage(child, CB_GETCURSEL, 0, 0); + cmdLine += " --debug "; + cmdLine += s_debugName[debug][1]; + } + + // add advanced options + cmdLine += s_advancedOptions->getCommandLine(isClient, server); + + return cmdLine; +} + +static +HANDLE +launchApp(HWND hwnd, bool testing, DWORD* threadID) +{ + // decide if client or server + const bool isClient = isClientChecked(hwnd); + const char* app = isClient ? CLIENT_APP : SERVER_APP; + + // prepare command line + CString cmdLine = getCommandLine(hwnd, testing); + if (cmdLine.empty()) { + return NULL; + } + + // start child + PROCESS_INFORMATION procInfo; + if (!execApp(app, cmdLine, &procInfo)) { + showError(hwnd, CStringUtil::format( + getString(IDS_STARTUP_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return NULL; + } + + // don't need process handle + CloseHandle(procInfo.hProcess); + + // save thread ID if desired + if (threadID != NULL) { + *threadID = procInfo.dwThreadId; + } + + // return thread handle + return procInfo.hThread; +} + +static +BOOL CALLBACK +waitDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // only one wait dialog at a time! + static CChildWaitInfo* info = NULL; + + switch (message) { + case WM_INITDIALOG: + // save info pointer + info = reinterpret_cast(lParam); + + // save hwnd + info->m_dialog = hwnd; + + // signal ready + SetEvent(info->m_ready); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + case IDOK: + // signal stop + SetEvent(info->m_stop); + + // done + EndDialog(hwnd, 0); + return TRUE; + } + } + + return FALSE; +} + +static +DWORD WINAPI +waitForChildThread(LPVOID vinfo) +{ + CChildWaitInfo* info = reinterpret_cast(vinfo); + + // wait for ready + WaitForSingleObject(info->m_ready, INFINITE); + + // wait for thread to complete or stop event + HANDLE handles[2]; + handles[0] = info->m_child; + handles[1] = info->m_stop; + DWORD n = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + + // if stop was raised then terminate child and wait for it + if (n == WAIT_OBJECT_0 + 1) { + PostThreadMessage(info->m_childID, WM_QUIT, 0, 0); + WaitForSingleObject(info->m_child, INFINITE); + } + + // otherwise post IDOK to dialog box + else { + PostMessage(info->m_dialog, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0); + } + + return 0; +} + +static +void +waitForChild(HWND hwnd, HANDLE thread, DWORD threadID) +{ + // prepare info for child wait dialog and thread + CChildWaitInfo info; + info.m_dialog = NULL; + info.m_child = thread; + info.m_childID = threadID; + info.m_ready = CreateEvent(NULL, TRUE, FALSE, NULL); + info.m_stop = CreateEvent(NULL, TRUE, FALSE, NULL); + + // create a thread to wait on the child thread and event + DWORD id; + HANDLE waiter = CreateThread(NULL, 0, &waitForChildThread, &info,0, &id); + + // do dialog that let's the user terminate the test + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_WAIT), hwnd, + waitDlgProc, (LPARAM)&info); + + // force the waiter thread to finish and wait for it + SetEvent(info.m_ready); + SetEvent(info.m_stop); + WaitForSingleObject(waiter, INFINITE); + + // clean up + CloseHandle(waiter); + CloseHandle(info.m_ready); + CloseHandle(info.m_stop); +} + +static +void +initMainWindow(HWND hwnd) +{ + // append version number to title + CString titleFormat = getString(IDS_TITLE); + setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION)); + + // load configuration + bool configLoaded = loadConfig(ARG->m_config); + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + + // get settings from registry + bool isServer = configLoaded; + int debugLevel = s_defaultDebug; + CString server; + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + if (isServer && CArchMiscWindows::hasValue(key, "isServer")) { + isServer = (CArchMiscWindows::readValueInt(key, "isServer") != 0); + } + if (CArchMiscWindows::hasValue(key, "debug")) { + debugLevel = static_cast( + CArchMiscWindows::readValueInt(key, "debug")); + if (debugLevel < 0) { + debugLevel = 0; + } + else if (debugLevel > CLog::kDEBUG2) { + debugLevel = CLog::kDEBUG2; + } + } + server = CArchMiscWindows::readValueString(key, "server"); + CArchMiscWindows::closeKey(key); + } + + // choose client/server radio buttons + HWND child; + child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); + setItemChecked(child, !isServer); + child = getItem(hwnd, IDC_MAIN_SERVER_RADIO); + setItemChecked(child, isServer); + + // set server name + child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + setWindowText(child, server); + + // if config is loaded then initialize server controls + if (configLoaded) { + int i = 1; + child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++i, ++index) { + ARG->m_screens.push_back(*index); + CString item = CStringUtil::print("%d. %s", i, index->c_str()); + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); + } + } + + // debug level + child = getItem(hwnd, IDC_MAIN_DEBUG); + for (unsigned int i = 0; i < sizeof(s_debugName) / + sizeof(s_debugName[0]); ++i) { + SendMessage(child, CB_ADDSTRING, 0, (LPARAM)s_debugName[i][0]); + } + SendMessage(child, CB_SETCURSEL, debugLevel, 0); + + // update neighbor combo boxes + enableMainWindowControls(hwnd); + updateNeighbors(hwnd); +} + +static +void +saveMainWindow(HWND hwnd) +{ + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + HWND child; + child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + CArchMiscWindows::setValue(key, "server", getWindowText(child)); + child = getItem(hwnd, IDC_MAIN_DEBUG); + CArchMiscWindows::setValue(key, "debug", + SendMessage(child, CB_GETCURSEL, 0, 0)); + CArchMiscWindows::setValue(key, "isServer", + isClientChecked(hwnd) ? 0 : 1); + CArchMiscWindows::closeKey(key); + } +} + +static +BOOL CALLBACK +addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // only one add dialog at a time! + static CScreenInfo* info = NULL; + + switch (message) { + case WM_INITDIALOG: { + info = (CScreenInfo*)lParam; + + // set title + CString title; + if (info->m_screen.empty()) { + title = getString(IDS_ADD_SCREEN); + } + else { + title = CStringUtil::format( + getString(IDS_EDIT_SCREEN).c_str(), + info->m_screen.c_str()); + } + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)title.c_str()); + + // fill in screen name + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)info->m_screen.c_str()); + + // fill in aliases + CString aliases; + for (CStringList::const_iterator index = info->m_aliases.begin(); + index != info->m_aliases.end(); ++index) { + if (!aliases.empty()) { + aliases += "\r\n"; + } + aliases += *index; + } + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); + + // set options + CConfig::CScreenOptions::const_iterator index; + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + index = info->m_options.find(kOptionHalfDuplexCapsLock); + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + index = info->m_options.find(kOptionHalfDuplexNumLock); + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); + + // modifier options + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + + // fill in options + for (UInt32 j = 0; j < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++j) { + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)s_modifiers[j].m_name); + } + + // choose current value + index = info->m_options.find(s_modifiers[i].m_optionID); + KeyModifierID id = s_modifiers[i].m_modifierID; + if (index != info->m_options.end()) { + id = index->second; + } + SendMessage(child, CB_SETCURSEL, id - baseModifier, 0); + } + + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: { + CString newName; + CStringList newAliases; + + // extract name and aliases + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + newName = getWindowText(child); + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + tokenize(newAliases, getWindowText(child)); + + // name must be valid + if (!ARG->m_config.isValidScreenName(newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + newName.c_str())); + return TRUE; + } + + // aliases must be valid + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (!ARG->m_config.isValidScreenName(*index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + index->c_str())); + return TRUE; + } + } + + // new name may not be in the new alias list + if (isNameInList(newAliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SCREEN_NAME_IS_ALIAS).c_str(), + newName.c_str())); + return TRUE; + } + + // name must not exist in config but allow same name. also + // allow name if it exists in the old alias list but not the + // new one. + if (ARG->m_config.isScreen(newName) && + !CStringUtil::CaselessCmp::equal(newName, info->m_screen) && + !isNameInList(info->m_aliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + newName.c_str())); + return TRUE; + } + + // aliases must not exist in config but allow same aliases and + // allow an alias to be the old name. + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (ARG->m_config.isScreen(*index) && + !CStringUtil::CaselessCmp::equal(*index, info->m_screen) && + !isNameInList(info->m_aliases, *index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + index->c_str())); + return TRUE; + } + } + + // save name data + info->m_screen = newName; + info->m_aliases = newAliases; + + // save options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + if (isItemChecked(child)) { + info->m_options[kOptionHalfDuplexCapsLock] = 1; + } + else { + info->m_options.erase(kOptionHalfDuplexCapsLock); + } + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + if (isItemChecked(child)) { + info->m_options[kOptionHalfDuplexNumLock] = 1; + } + else { + info->m_options.erase(kOptionHalfDuplexNumLock); + } + + // save modifier options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + KeyModifierID id = static_cast( + SendMessage(child, CB_GETCURSEL, 0, 0) + + baseModifier); + if (id != s_modifiers[i].m_modifierID) { + info->m_options[s_modifiers[i].m_optionID] = id; + } + else { + info->m_options.erase(s_modifiers[i].m_optionID); + } + } + + // success + EndDialog(hwnd, 1); + info = NULL; + return TRUE; + } + + case IDCANCEL: + EndDialog(hwnd, 0); + info = NULL; + return TRUE; + } + + default: + break; + } + + return FALSE; +} + +static +LRESULT CALLBACK +mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + // test for unsaved data + if (ARG->m_config != ARG->m_oldConfig) { + if (!askVerify(hwnd, getString(IDS_UNSAVED_DATA_REALLY_QUIT))) { + return 0; + } + } + + // quit + PostQuitMessage(0); + return 0; + + case IDOK: + case IDC_MAIN_TEST: { + // note if testing + const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST); + + // save data + if (ARG->m_config != ARG->m_oldConfig) { + if (!saveConfig(ARG->m_config, false)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return 0; + } + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + } + + // launch child app + DWORD threadID; + HANDLE thread = launchApp(hwnd, testing, &threadID); + if (thread == NULL) { + return 0; + } + + // handle child program + if (testing) { + // wait for process to stop, allowing the user to kill it + waitForChild(hwnd, thread, threadID); + + // clean up + CloseHandle(thread); + } + else { + // don't need thread handle + CloseHandle(thread); + + // notify of success + askOkay(hwnd, getString(IDS_STARTED_TITLE), + getString(IDS_STARTED)); + + // quit + PostQuitMessage(0); + } + return 0; + } + + case IDC_MAIN_AUTOSTART: { + // construct command line + CString cmdLine = getCommandLine(hwnd, false); + if (!cmdLine.empty()) { + // run dialog + CAutoStart autoStart(hwnd, + isClientChecked(hwnd) ? NULL : &ARG->m_config, + cmdLine); + autoStart.doModal(); + if (autoStart.wasUserConfigSaved()) { + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + } + } + return 0; + } + + case IDC_MAIN_SAVE: + if (!saveConfig(ARG->m_config, false)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + } + else { + ARG->m_oldConfig = ARG->m_config; + enableSaveControls(hwnd); + } + return 0; + + case IDC_MAIN_CLIENT_RADIO: + case IDC_MAIN_SERVER_RADIO: + enableMainWindowControls(hwnd); + return 0; + + case IDC_MAIN_SERVER_ADD_BUTTON: + addScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_EDIT_BUTTON: + editScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_REMOVE_BUTTON: + removeScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_SCREENS_LIST: + if (HIWORD(wParam) == LBN_SELCHANGE) { + enableScreensControls(hwnd); + updateNeighbors(hwnd); + } + else if (HIWORD(wParam) == LBN_DBLCLK) { + editScreen(hwnd); + return 0; + } + break; + + case IDC_MAIN_SERVER_LEFT_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kLeft); + return 0; + } + break; + + case IDC_MAIN_SERVER_RIGHT_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kRight); + return 0; + } + break; + + case IDC_MAIN_SERVER_TOP_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kTop); + return 0; + } + break; + + case IDC_MAIN_SERVER_BOTTOM_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kBottom); + return 0; + } + break; + + case IDC_MAIN_OPTIONS: + s_globalOptions->doModal(); + enableSaveControls(hwnd); + break; + + case IDC_MAIN_ADVANCED: + s_advancedOptions->doModal(isClientChecked(hwnd)); + enableSaveControls(hwnd); + break; + } + + default: + break; + } + return DefDlgProc(hwnd, message, wParam, lParam); +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) +{ + CArch arch(instance); + CLOG; + CArgs args; + + s_instance = instance; + + // register main window (dialog) class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_HREDRAW | CS_VREDRAW; + classInfo.lpfnWndProc = &mainWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = DLGWINDOWEXTRA; + classInfo.hInstance = instance; + classInfo.hIcon = (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED); + classInfo.hCursor = LoadCursor(NULL, IDC_ARROW); + classInfo.hbrBackground = reinterpret_cast(COLOR_3DFACE + 1); + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = s_mainClass; + classInfo.hIconSm = (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED); + RegisterClassEx(&classInfo); + + // create main window + HWND mainWindow = CreateDialog(s_instance, + MAKEINTRESOURCE(IDD_MAIN), 0, NULL); + + // prep windows + initMainWindow(mainWindow); + s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config); + s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config); + + // show window + ShowWindow(mainWindow, nCmdShow); + + // main loop + MSG msg; + bool done = false; + do { + switch (GetMessage(&msg, NULL, 0, 0)) { + case -1: + // error + break; + + case 0: + // quit + done = true; + break; + + default: + if (!IsDialogMessage(mainWindow, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + break; + } + } while (!done); + + // save values to registry + saveMainWindow(mainWindow); + + return msg.wParam; +} diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp new file mode 100644 index 0000000..fecdcf8 --- /dev/null +++ b/cmd/launcher/launcher.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="launcher" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=launcher - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "launcher.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "launcher.mak" CFG="launcher - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "launcher - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "launcher - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "launcher - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergy.exe" +# SUBTRACT LINK32 /map + +!ELSEIF "$(CFG)" == "launcher - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../Debug/synergy.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "launcher - Win32 Release" +# Name "launcher - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CAdvancedOptions.cpp +# End Source File +# Begin Source File + +SOURCE=.\CAutoStart.cpp +# End Source File +# Begin Source File + +SOURCE=.\CGlobalOptions.cpp +# End Source File +# Begin Source File + +SOURCE=.\launcher.cpp +# End Source File +# Begin Source File + +SOURCE=.\launcher.rc +# End Source File +# Begin Source File + +SOURCE=.\LaunchUtil.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CAdvancedOptions.h +# End Source File +# Begin Source File + +SOURCE=.\CAutoStart.h +# End Source File +# Begin Source File + +SOURCE=.\CGlobalOptions.h +# End Source File +# Begin Source File + +SOURCE=.\LaunchUtil.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergy.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc new file mode 100644 index 0000000..a8bf8c7 --- /dev/null +++ b/cmd/launcher/launcher.rc @@ -0,0 +1,367 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 262 +STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +CLASS "GoSynergy" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Choose to start either the Client or Server and provide the requested information. Then click Test to check your settings or Start to save your settings and start Synergy.", + IDC_STATIC,7,7,286,19 + GROUPBOX "",IDC_STATIC,7,29,286,31 + GROUPBOX "",IDC_STATIC,7,67,286,103 + GROUPBOX "Options",IDC_STATIC,7,177,286,56 + CONTROL "&Client",IDC_MAIN_CLIENT_RADIO,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 + CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", + BS_AUTORADIOBUTTON,11,67,37,10 + LTEXT "Server &Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL, + 12,41,61,8 + EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,79,39,106,12, + ES_AUTOHSCROLL + LTEXT "&Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 + LISTBOX IDC_MAIN_SERVER_SCREENS_LIST,12,91,106,36, + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 + PUSHBUTTON "&Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 + PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 + LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 + LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 + COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,111,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 + COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,112,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 + COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,112,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 + COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,112,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,191,50,14 + PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,68,191,50,14 + LTEXT "Automatic Startup:",IDC_STATIC,138,193,59,8 + PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,202,191,50,14 + LTEXT "Lo&gging Level:",IDC_STATIC,12,216,48,8 + COMBOBOX IDC_MAIN_DEBUG,68,213,61,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 + DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 + PUSHBUTTON "Start",IDOK,187,241,50,14 + PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 +END + +IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 236 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Add Screen" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Screen Name:",IDC_STATIC,7,9,46,8 + EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,79,7,106,12,ES_AUTOHSCROLL + LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 + EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + GROUPBOX "Options",IDC_STATIC,7,72,178,64 + LTEXT "If your Caps Lock or Num Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", + IDC_STATIC,13,82,165,25 + CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 + CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 + GROUPBOX "Modifiers",IDC_STATIC,7,139,178,65 + LTEXT "Shift",IDC_STATIC,13,155,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,152,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Ctrl",IDC_STATIC,13,170,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,168,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Alt",IDC_STATIC,13,186,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,184,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Meta",IDC_STATIC,101,154,17,8 + COMBOBOX IDC_ADD_MOD_META,125,152,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Super",IDC_STATIC,101,170,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,168,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,79,215,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,215,50,14 +END + +IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Running Test..." +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Stop",IDOK,129,33,50,14 + LTEXT "Running synergy. Press Stop to end the test.", + IDC_STATIC,7,7,172,15 +END + +IDD_AUTOSTART DIALOG DISCARDABLE 0, 0, 195, 189 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Auto Start" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDCANCEL,138,168,50,14 + LTEXT "Synergy can be configured to start automatically when you log in. If you have sufficient access rights, you can instead configure synergy to start automatically when your computer starts.", + IDC_STATIC,7,7,181,33 + LTEXT "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself.", + IDC_AUTOSTART_PERMISSION_MSG,7,69,181,17 + LTEXT "Synergy is configured to start automatically when the system starts.", + IDC_AUTOSTART_INSTALLED_MSG,7,93,181,17 + GROUPBOX "When &You Log In",IDC_STATIC,7,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_USER,23,133,50,14 + GROUPBOX "When &Computer Starts",IDC_STATIC,104,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_SYSTEM,119,134,50,14 + LTEXT "Synergy can be configured to start automatically when the computer starts or when you log in but not both.", + IDC_STATIC,7,43,181,17 +END + +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 233 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "It's easy to unintentionally switch screens when the pointer is near a screen's edge. Synergy can prevent switching until certain conditions are met to reduce unintentional switching.", + IDC_STATIC,7,7,191,26 + LTEXT "Synergy can wait to switch until the cursor has been at a screen's edge for some amount of time.", + IDC_STATIC,7,37,193,16 + CONTROL "Switch after waiting",IDC_GLOBAL_DELAY_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,59,77,10 + EDITTEXT IDC_GLOBAL_DELAY_TIME,112,58,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,60,10,8 + LTEXT "Synergy can switch only when the cursor hits a screen edge twice within some amount of time.", + IDC_STATIC,7,77,193,16 + CONTROL "Switch on double tap within",IDC_GLOBAL_TWO_TAP_CHECK, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,99,103,10 + EDITTEXT IDC_GLOBAL_TWO_TAP_TIME,112,98,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,100,10,8 + LTEXT "Synergy can periodically check that clients are still alive and connected. Use this only if synergy doesn't detect when clients disconnect.", + IDC_STATIC,7,122,193,24 + CONTROL "Check clients every",IDC_GLOBAL_HEARTBEAT_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,153,78,10 + EDITTEXT IDC_GLOBAL_HEARTBEAT_TIME,112,152,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,154,10,8 + LTEXT "Synergy can synchronize screen savers across all screens.", + IDC_STATIC,7,176,193,8 + CONTROL "Synchronize screen savers",IDC_GLOBAL_SCREENSAVER_SYNC, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,192,101,10 + DEFPUSHBUTTON "OK",IDOK,94,212,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,212,50,14 +END + +IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 133 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Advanced Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Synergy normally uses this computer's name as its screen name. Enter another name here if you want to use a different screen name.", + IDC_STATIC,7,7,216,19 + LTEXT "Screen &Name:",IDC_STATIC,7,34,46,8 + EDITTEXT IDC_ADVANCED_NAME_EDIT,63,32,106,12,ES_AUTOHSCROLL + LTEXT "Synergy normally uses a particular network port number. Enter an alternative port here. (The server and all clients must use the same port number.)", + IDC_STATIC,7,56,216,26 + LTEXT "&Port:",IDC_STATIC,7,90,16,8 + EDITTEXT IDC_ADVANCED_PORT_EDIT,63,88,40,12,ES_AUTOHSCROLL | + ES_NUMBER + PUSHBUTTON "&Defaults",IDC_ADVANCED_DEFAULTS,7,112,50,14 + DEFPUSHBUTTON "OK",IDOK,118,112,50,14 + PUSHBUTTON "Cancel",IDCANCEL,173,112,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 255 + END + + IDD_ADD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 185 + TOPMARGIN, 7 + BOTTOMMARGIN, 229 + END + + IDD_WAIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 47 + END + + IDD_AUTOSTART, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 188 + TOPMARGIN, 7 + BOTTOMMARGIN, 182 + END + + IDD_GLOBAL_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 226 + END + + IDD_ADVANCED_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 223 + TOPMARGIN, 7 + BOTTOMMARGIN, 126 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergy.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_ERROR "Error" + IDS_INVALID_SCREEN_NAME "Screen name `%{1}' is invalid." + IDS_DUPLICATE_SCREEN_NAME "The screen name `%{1}' is already being used." + IDS_SCREEN_NAME_IS_ALIAS "A name may not be an alias of itself." + IDS_VERIFY "Confirm" + IDS_UNSAVED_DATA_REALLY_QUIT "You have unsaved changes. Really quit?" + IDS_UNKNOWN_SCREEN_NAME "The screen name `%{1}' is not in the layout." + IDS_INVALID_PORT "The port `%{1}' is invalid. It must be between 1 and 65535 inclusive. %{2} is the standard port." + IDS_SAVE_FAILED "Failed to save configuration: %{1}" + IDS_STARTUP_FAILED "Failed to start synergy: %{1}" + IDS_STARTED_TITLE "Started" + IDS_STARTED "Synergy was successfully started. Use the task manager or tray icon to terminate it." + IDS_UNINSTALL_TITLE "Removed Auto-Start" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_AUTOSTART_PERMISSION_SYSTEM + "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." + IDS_AUTOSTART_PERMISSION_USER + "You have sufficient access rights to install and uninstall Auto Start for just yourself." + IDS_AUTOSTART_PERMISSION_NONE + "You do not have sufficient access rights to install or uninstall Auto Start." + IDS_AUTOSTART_INSTALLED_SYSTEM + "Synergy is configured to start automatically when the system starts." + IDS_AUTOSTART_INSTALLED_USER + "Synergy is configured to start automatically when you log in." + IDS_AUTOSTART_INSTALLED_NONE + "Synergy is not configured to start automatically." + IDS_INSTALL_LABEL "Install" + IDS_UNINSTALL_LABEL "Uninstall" + IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}." + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}." + IDS_INSTALL_TITLE "Installed Auto-Start" + IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." + IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_UNINSTALLED_SYSTEM "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer." + IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in." + IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." + IDS_TITLE "Synergy - Version %{1}" + IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." + IDS_ADD_SCREEN "Add Screen" + IDS_EDIT_SCREEN "Edit Screen %{1}" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h new file mode 100644 index 0000000..2885b2f --- /dev/null +++ b/cmd/launcher/resource.h @@ -0,0 +1,113 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by launcher.rc +// +#define IDS_ERROR 1 +#define IDS_INVALID_SCREEN_NAME 2 +#define IDS_DUPLICATE_SCREEN_NAME 3 +#define IDS_SCREEN_NAME_IS_ALIAS 4 +#define IDS_VERIFY 5 +#define IDS_UNSAVED_DATA_REALLY_QUIT 6 +#define IDS_UNKNOWN_SCREEN_NAME 7 +#define IDS_INVALID_PORT 8 +#define IDS_SAVE_FAILED 9 +#define IDS_STARTUP_FAILED 10 +#define IDS_STARTED_TITLE 11 +#define IDS_STARTED 12 +#define IDS_INSTALL_FAILED 13 +#define IDS_UNINSTALL_TITLE 14 +#define IDS_UNINSTALLED 15 +#define IDS_UNINSTALL_FAILED 16 +#define IDS_CLIENT 17 +#define IDS_SERVER 18 +#define IDS_AUTOSTART_PERMISSION_SYSTEM 19 +#define IDS_AUTOSTART_PERMISSION_USER 20 +#define IDS_AUTOSTART_PERMISSION_NONE 21 +#define IDS_AUTOSTART_INSTALLED_SYSTEM 22 +#define IDS_AUTOSTART_INSTALLED_USER 23 +#define IDS_AUTOSTART_INSTALLED_NONE 24 +#define IDS_INSTALL_LABEL 25 +#define IDS_UNINSTALL_LABEL 26 +#define IDS_INSTALL_GENERIC_ERROR 27 +#define IDS_UNINSTALL_GENERIC_ERROR 28 +#define IDS_INSTALL_TITLE 29 +#define IDS_INSTALLED_SYSTEM 30 +#define IDS_INSTALLED_USER 31 +#define IDS_UNINSTALLED_SYSTEM 32 +#define IDS_UNINSTALLED_USER 33 +#define IDS_INVALID_SERVER_NAME 34 +#define IDS_TITLE 35 +#define IDS_SERVER_IS_CLIENT 36 +#define IDS_ADD_SCREEN 37 +#define IDS_EDIT_SCREEN 38 +#define IDS_INVALID_TIME 39 +#define IDD_MAIN 101 +#define IDD_ADD 102 +#define IDD_WAIT 103 +#define IDI_SYNERGY 104 +#define IDD_AUTOSTART 105 +#define IDD_ADVANCED_OPTIONS 106 +#define IDD_GLOBAL_OPTIONS 107 +#define IDC_MAIN_CLIENT_RADIO 1000 +#define IDC_MAIN_SERVER_RADIO 1001 +#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 +#define IDC_MAIN_ADVANCED_NAME_EDIT 1006 +#define IDC_MAIN_ADVANCED_PORT_EDIT 1008 +#define IDC_MAIN_TEST 1009 +#define IDC_MAIN_SAVE 1010 +#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1011 +#define IDC_MAIN_SERVER_SCREENS_LIST 1012 +#define IDC_MAIN_SERVER_SCREENS_LABEL 1013 +#define IDC_MAIN_SERVER_LAYOUT_LABEL 1014 +#define IDC_MAIN_SERVER_ADD_BUTTON 1018 +#define IDC_MAIN_SERVER_EDIT_BUTTON 1019 +#define IDC_ADD_SCREEN_NAME_EDIT 1020 +#define IDC_MAIN_SERVER_REMOVE_BUTTON 1020 +#define IDC_ADD_ALIASES_EDIT 1021 +#define IDC_MAIN_SERVER_OPTIONS_BUTTON 1021 +#define IDC_MAIN_SERVER_LEFT_COMBO 1022 +#define IDC_MAIN_SERVER_RIGHT_COMBO 1023 +#define IDC_MAIN_SERVER_TOP_COMBO 1024 +#define IDC_MAIN_SERVER_BOTTOM_COMBO 1025 +#define IDC_MAIN_SERVER_LEFT_LABEL 1026 +#define IDC_MAIN_SERVER_RIGHT_LABEL 1027 +#define IDC_MAIN_SERVER_TOP_LABEL 1028 +#define IDC_MAIN_SERVER_BOTTOM_LABEL 1029 +#define IDC_MAIN_UNINSTALL 1030 +#define IDC_AUTOSTART_INSTALLED_MSG 1031 +#define IDC_AUTOSTART_PERMISSION_MSG 1032 +#define IDC_AUTOSTART_INSTALL_USER 1033 +#define IDC_AUTOSTART_INSTALL_SYSTEM 1034 +#define IDC_MAIN_AUTOSTART 1035 +#define IDC_MAIN_OPTIONS 1036 +#define IDC_ADD_HD_CAPS_CHECK 1037 +#define IDC_MAIN_ADVANCED 1037 +#define IDC_ADD_HD_NUM_CHECK 1038 +#define IDC_ADVANCED_NAME_EDIT 1038 +#define IDC_ADVANCED_PORT_EDIT 1039 +#define IDC_MAIN_DEBUG 1040 +#define IDC_GLOBAL_DELAY_CHECK 1041 +#define IDC_GLOBAL_DELAY_TIME 1042 +#define IDC_GLOBAL_TWO_TAP_CHECK 1043 +#define IDC_ADD_MOD_SHIFT 1043 +#define IDC_GLOBAL_TWO_TAP_TIME 1044 +#define IDC_ADD_MOD_CTRL 1044 +#define IDC_ADD_MOD_ALT 1045 +#define IDC_GLOBAL_HEARTBEAT_CHECK 1045 +#define IDC_ADD_MOD_META 1046 +#define IDC_GLOBAL_HEARTBEAT_TIME 1046 +#define IDC_ADD_MOD_SUPER 1047 +#define IDC_GLOBAL_SCREENSAVER_SYNC 1047 +#define IDC_ADVANCED_DEFAULTS 1049 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 110 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1052 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/launcher/synergy.ico b/cmd/launcher/synergy.ico new file mode 100644 index 0000000..39e0ff8 Binary files /dev/null and b/cmd/launcher/synergy.ico differ diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp new file mode 100644 index 0000000..9c60c5b --- /dev/null +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -0,0 +1,163 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientTaskBarReceiver.h" +#include "CClient.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CClientTaskBarReceiver +// + +CClientTaskBarReceiver::CClientTaskBarReceiver() : + m_quit(NULL), + m_state(kNotRunning), + m_client(NULL) +{ + // create a job for getting notification when the client's + // status changes. + m_job = new TMethodJob(this, + &CClientTaskBarReceiver::statusChanged, NULL); +} + +CClientTaskBarReceiver::~CClientTaskBarReceiver() +{ + if (m_client != NULL) { + m_client->removeStatusJob(m_job); + } + delete m_job; + delete m_quit; +} + +void +CClientTaskBarReceiver::setClient(CClient* client) +{ + { + CLock lock(&m_mutex); + if (m_client != client) { + if (m_client != NULL) { + m_client->removeStatusJob(m_job); + } + m_client = client; + if (m_client != NULL) { + m_client->addStatusJob(m_job); + } + } + } + ARCH->updateReceiver(this); +} + +void +CClientTaskBarReceiver::setState(EState state) +{ + { + CLock lock(&m_mutex); + m_state = state; + } + ARCH->updateReceiver(this); +} + +void +CClientTaskBarReceiver::setQuitJob(IJob* job) +{ + CLock lock(&m_mutex); + delete m_quit; + m_quit = job; +} + +CClientTaskBarReceiver::EState +CClientTaskBarReceiver::getState() const +{ + return m_state; +} + +CClient* +CClientTaskBarReceiver::getClient() const +{ + return m_client; +} + +void +CClientTaskBarReceiver::lock() const +{ + m_mutex.lock(); +} + +void +CClientTaskBarReceiver::unlock() const +{ + m_mutex.unlock(); +} + +std::string +CClientTaskBarReceiver::getToolTip() const +{ + switch (m_state) { + case kNotRunning: + return "Synergy: Not running"; + + case kNotWorking: + return CString("Synergy: ") + m_errorMessage; + + case kNotConnected: + return "Synergy: Waiting for clients"; + + case kConnected: + return "Synergy: Connected"; + + default: + return ""; + } +} + +void +CClientTaskBarReceiver::quit() +{ + if (m_quit != NULL) { + m_quit->run(); + } +} + +void +CClientTaskBarReceiver::onStatusChanged() +{ + // do nothing +} + +void +CClientTaskBarReceiver::statusChanged(void*) +{ + // update our status + switch (m_client->getStatus(&m_errorMessage)) { + case CClient::kNotRunning: + setState(kNotRunning); + break; + + case CClient::kRunning: + setState(kConnected); + break; + + case CClient::kError: + setState(kNotWorking); + break; + + default: + break; + } + + // let subclasses have a go + onStatusChanged(); +} diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h new file mode 100644 index 0000000..aa52b9d --- /dev/null +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTTASKBARRECEIVER_H +#define CCLIENTTASKBARRECEIVER_H + +#include "CMutex.h" +#include "CString.h" +#include "IArchTaskBarReceiver.h" + +class CClient; +class IJob; + +//! Implementation of IArchTaskBarReceiver for the synergy server +class CClientTaskBarReceiver : public IArchTaskBarReceiver { +public: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnected, + kMaxState + }; + + CClientTaskBarReceiver(); + virtual ~CClientTaskBarReceiver(); + + //! @name manipulators + //@{ + + //! Set server + /*! + Sets the server. The receiver will query state from this server. + */ + void setClient(CClient*); + + //! Set state + /*! + Sets the current server state. + */ + void setState(EState); + + //! Set the quit job that causes the server to quit + /*! + Set the job that causes the server to quit. + */ + void setQuitJob(IJob* adopted); + + //@} + //! @name accessors + //@{ + + //! Get state + /*! + Returns the current server state. The receiver is not locked + by this call; the caller must do the locking. + */ + EState getState() const; + + //! Get server + /*! + Returns the server set by \c setClient(). + */ + CClient* getClient() const; + + //@} + + // IArchTaskBarReceiver overrides + virtual void showStatus() = 0; + virtual void runMenu(int x, int y) = 0; + virtual void primaryAction() = 0; + virtual void lock() const; + virtual void unlock() const; + virtual const Icon getIcon() const = 0; + virtual std::string getToolTip() const; + +protected: + void quit(); + + //! Status change notification + /*! + Called when status changes. The default implementation does + nothing. + */ + virtual void onStatusChanged(); + +private: + void statusChanged(void*); + +private: + CMutex m_mutex; + IJob* m_quit; + EState m_state; + CClient* m_client; + IJob* m_job; + CString m_errorMessage; +}; + +#endif diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp new file mode 100644 index 0000000..3bfd581 --- /dev/null +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -0,0 +1,310 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClientTaskBarReceiver.h" +#include "CClient.h" +#include "CMSWindowsClipboard.h" +#include "LogOutputters.h" +#include "BasicTypes.h" +#include "CArch.h" +#include "CArchTaskBarWindows.h" +#include "resource.h" + +static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = +{ + IDI_TASKBAR_NOT_RUNNING, + IDI_TASKBAR_NOT_WORKING, + IDI_TASKBAR_NOT_CONNECTED, + IDI_TASKBAR_CONNECTED +}; + +// +// CMSWindowsClientTaskBarReceiver +// + +CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( + HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : + CClientTaskBarReceiver(), + m_appInstance(appInstance), + m_window(NULL), + m_logBuffer(logBuffer) +{ + for (UInt32 i = 0; i < kMaxState; ++i) { + m_icon[i] = loadIcon(g_stateToIconID[i]); + } + m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); + + // don't create the window yet. we'll create it on demand. this + // has the side benefit of being created in the thread used for + // the task bar. that's good because it means the existence of + // the window won't prevent changing the main thread's desktop. + + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CMSWindowsClientTaskBarReceiver::~CMSWindowsClientTaskBarReceiver() +{ + ARCH->removeReceiver(this); + for (UInt32 i = 0; i < kMaxState; ++i) { + deleteIcon(m_icon[i]); + } + DestroyMenu(m_menu); + destroyWindow(); +} + +void +CMSWindowsClientTaskBarReceiver::showStatus() +{ + // create the window + createWindow(); + + // lock self while getting status + lock(); + + // get the current status + std::string status = getToolTip(); + + // done getting status + unlock(); + + // update dialog + HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); + + if (!IsWindowVisible(m_window)) { + // position it by the mouse + POINT cursorPos; + GetCursorPos(&cursorPos); + RECT windowRect; + GetWindowRect(m_window, &windowRect); + int x = cursorPos.x; + int y = cursorPos.y; + int fw = GetSystemMetrics(SM_CXDLGFRAME); + int fh = GetSystemMetrics(SM_CYDLGFRAME); + int ww = windowRect.right - windowRect.left; + int wh = windowRect.bottom - windowRect.top; + int sw = GetSystemMetrics(SM_CXFULLSCREEN); + int sh = GetSystemMetrics(SM_CYFULLSCREEN); + if (fw < 1) { + fw = 1; + } + if (fh < 1) { + fh = 1; + } + if (x + ww - fw > sw) { + x -= ww - fw; + } + else { + x -= fw; + } + if (x < 0) { + x = 0; + } + if (y + wh - fh > sh) { + y -= wh - fh; + } + else { + y -= fh; + } + if (y < 0) { + y = 0; + } + SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh, + SWP_SHOWWINDOW); + } +} + +void +CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) +{ + // do popup menu. we need a window to pass to TrackPopupMenu(). + // the SetForegroundWindow() and SendMessage() calls around + // TrackPopupMenu() are to get the menu to be dismissed when + // another window gets activated and are just one of those + // win32 weirdnesses. + createWindow(); + SetForegroundWindow(m_window); + HMENU menu = GetSubMenu(m_menu, 0); + SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + int n = TrackPopupMenu(menu, + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_LEFTBUTTON | + TPM_RIGHTBUTTON, + x, y, 0, m_window, NULL); + SendMessage(m_window, WM_NULL, 0, 0); + + // perform the requested operation + switch (n) { + case IDC_TASKBAR_STATUS: + showStatus(); + break; + + case IDC_TASKBAR_LOG: + copyLog(); + break; + + case IDC_TASKBAR_QUIT: + quit(); + break; + } +} + +void +CMSWindowsClientTaskBarReceiver::primaryAction() +{ + showStatus(); +} + +const IArchTaskBarReceiver::Icon +CMSWindowsClientTaskBarReceiver::getIcon() const +{ + return reinterpret_cast(m_icon[getState()]); +} + +void +CMSWindowsClientTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.emptyUnowned(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + +void +CMSWindowsClientTaskBarReceiver::onStatusChanged() +{ + if (IsWindowVisible(m_window)) { + showStatus(); + } +} + +HICON +CMSWindowsClientTaskBarReceiver::loadIcon(UINT id) +{ + HANDLE icon = LoadImage(m_appInstance, + MAKEINTRESOURCE(id), + IMAGE_ICON, + 0, 0, + LR_DEFAULTCOLOR); + return reinterpret_cast(icon); +} + +void +CMSWindowsClientTaskBarReceiver::deleteIcon(HICON icon) +{ + if (icon != NULL) { + DestroyIcon(icon); + } +} + +void +CMSWindowsClientTaskBarReceiver::createWindow() +{ + // ignore if already created + if (m_window != NULL) { + return; + } + + // get the status dialog + m_window = CreateDialogParam(m_appInstance, + MAKEINTRESOURCE(IDD_TASKBAR_STATUS), + NULL, + &CMSWindowsClientTaskBarReceiver::staticDlgProc, + reinterpret_cast( + reinterpret_cast(this))); + + // window should appear on top of everything, including (especially) + // the task bar. + DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + SetWindowLong(m_window, GWL_EXSTYLE, style); + + // tell the task bar about this dialog + CArchTaskBarWindows::addDialog(m_window); +} + +void +CMSWindowsClientTaskBarReceiver::destroyWindow() +{ + if (m_window != NULL) { + CArchTaskBarWindows::removeDialog(m_window); + DestroyWindow(m_window); + m_window = NULL; + } +} + +BOOL +CMSWindowsClientTaskBarReceiver::dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_INITDIALOG: + // use default focus + return TRUE; + + case WM_ACTIVATE: + // hide when another window is activated + if (LOWORD(wParam) == WA_INACTIVE) { + ShowWindow(hwnd, SW_HIDE); + } + break; + } + return FALSE; +} + +BOOL CALLBACK +CMSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_INITDIALOG, extract the CMSWindowsClientTaskBarReceiver* + // and put it in the extra window data then forward the call. + CMSWindowsClientTaskBarReceiver* self = NULL; + if (msg == WM_INITDIALOG) { + self = reinterpret_cast( + reinterpret_cast(lParam)); + SetWindowLong(hwnd, GWL_USERDATA, lParam); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, GWL_USERDATA); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->dlgProc(hwnd, msg, wParam, lParam); + } + else { + return (msg == WM_INITDIALOG) ? TRUE : FALSE; + } +} diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h new file mode 100644 index 0000000..56a3f16 --- /dev/null +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIENTTASKBARRECEIVER_H +#define CMSWINDOWSCLIENTTASKBARRECEIVER_H + +#define WIN32_LEAN_AND_MEAN + +#include "CClientTaskBarReceiver.h" +#include + +class CBufferedLogOutputter; + +//! Implementation of CClientTaskBarReceiver for Microsoft Windows +class CMSWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { +public: + CMSWindowsClientTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); + virtual ~CMSWindowsClientTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + void copyLog() const; + + // CClientTaskBarReceiver overrides + virtual void onStatusChanged(); + +private: + HICON loadIcon(UINT); + void deleteIcon(HICON); + void createWindow(); + void destroyWindow(); + + BOOL dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK + staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + +private: + HINSTANCE m_appInstance; + HWND m_window; + HMENU m_menu; + HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; +}; + +#endif diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp new file mode 100644 index 0000000..f60585e --- /dev/null +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClientTaskBarReceiver.h" +#include "CArch.h" + +// +// CXWindowsClientTaskBarReceiver +// + +CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver() +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CXWindowsClientTaskBarReceiver::~CXWindowsClientTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +CXWindowsClientTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +CXWindowsClientTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +CXWindowsClientTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +CXWindowsClientTaskBarReceiver::getIcon() const +{ + return NULL; +} + +void +CXWindowsClientTaskBarReceiver::onStatusChanged() +{ + // do nothing +} diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h new file mode 100644 index 0000000..e6028ce --- /dev/null +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIENTTASKBARRECEIVER_H +#define CXWINDOWSCLIENTTASKBARRECEIVER_H + +#include "CClientTaskBarReceiver.h" + +//! Implementation of CClientTaskBarReceiver for X Windows +class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { +public: + CXWindowsClientTaskBarReceiver(); + virtual ~CXWindowsClientTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CClientTaskBarReceiver overrides + virtual void onStatusChanged(); +}; + +#endif diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am new file mode 100644 index 0000000..6b2510c --- /dev/null +++ b/cmd/synergyc/Makefile.am @@ -0,0 +1,69 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsClientTaskBarReceiver.cpp \ + CMSWindowsClientTaskBarReceiver.h \ + resource.h \ + synergyc.dsp \ + synergyc.ico \ + synergyc.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +bin_PROGRAMS = synergyc +synergyc_SOURCES = \ + CClientTaskBarReceiver.cpp \ + CClientTaskBarReceiver.h \ + CXWindowsClientTaskBarReceiver.cpp \ + CXWindowsClientTaskBarReceiver.h \ + synergyc.cpp \ + $(NULL) +synergyc_LDADD = \ + $(DEPTH)/lib/client/libclient.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/client \ + $(NULL) diff --git a/cmd/synergyc/Makefile.in b/cmd/synergyc/Makefile.in new file mode 100644 index 0000000..78f4ef9 --- /dev/null +++ b/cmd/synergyc/Makefile.in @@ -0,0 +1,397 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsClientTaskBarReceiver.cpp \ + CMSWindowsClientTaskBarReceiver.h \ + resource.h \ + synergyc.dsp \ + synergyc.ico \ + synergyc.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +bin_PROGRAMS = synergyc +synergyc_SOURCES = \ + CClientTaskBarReceiver.cpp \ + CClientTaskBarReceiver.h \ + CXWindowsClientTaskBarReceiver.cpp \ + CXWindowsClientTaskBarReceiver.h \ + synergyc.cpp \ + $(NULL) + +synergyc_LDADD = \ + $(DEPTH)/lib/client/libclient.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/client \ + $(NULL) + +subdir = cmd/synergyc +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +bin_PROGRAMS = synergyc$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) + +am_synergyc_OBJECTS = CClientTaskBarReceiver.$(OBJEXT) \ + CXWindowsClientTaskBarReceiver.$(OBJEXT) synergyc.$(OBJEXT) +synergyc_OBJECTS = $(am_synergyc_OBJECTS) +synergyc_DEPENDENCIES = $(DEPTH)/lib/client/libclient.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/io/libio.a $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a $(DEPTH)/lib/arch/libarch.a +synergyc_LDFLAGS = + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClientTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClientTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/synergyc.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(synergyc_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(synergyc_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/synergyc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ + rm -f $(DESTDIR)$(bindir)/$$f; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +synergyc$(EXEEXT): $(synergyc_OBJECTS) $(synergyc_DEPENDENCIES) + @rm -f synergyc$(EXEEXT) + $(CXXLINK) $(synergyc_LDFLAGS) $(synergyc_OBJECTS) $(synergyc_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClientTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/synergyc.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic distclean distclean-compile distclean-depend \ + distclean-generic distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h new file mode 100644 index 0000000..1da7860 --- /dev/null +++ b/cmd/synergyc/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergyc.rc +// +#define IDS_FAILED 1 +#define IDI_SYNERGY 101 +#define IDI_TASKBAR_NOT_RUNNING 102 +#define IDI_TASKBAR_NOT_WORKING 103 +#define IDI_TASKBAR_NOT_CONNECTED 104 +#define IDI_TASKBAR_CONNECTED 105 +#define IDR_TASKBAR 107 +#define IDD_TASKBAR_STATUS 108 +#define IDC_TASKBAR_STATUS_STATUS 1000 +#define IDC_TASKBAR_QUIT 40003 +#define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp new file mode 100644 index 0000000..ce2dca0 --- /dev/null +++ b/cmd/synergyc/synergyc.cpp @@ -0,0 +1,739 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClient.h" +#include "ISecondaryScreenFactory.h" +#include "ProtocolTypes.h" +#include "Version.h" +#include "XScreen.h" +#include "CNetworkAddress.h" +#include "CTCPSocketFactory.h" +#include "XSocket.h" +#include "CCondVar.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "XThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "LogOutputters.h" +#include "CString.h" +#include "CArch.h" +#include + +#define DAEMON_RUNNING(running_) +#if WINDOWS_LIKE +#include "CMSWindowsScreen.h" +#include "CMSWindowsSecondaryScreen.h" +#include "CArchMiscWindows.h" +#include "CMSWindowsClientTaskBarReceiver.h" +#include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) +#elif UNIX_LIKE +#include "CXWindowsSecondaryScreen.h" +#include "CXWindowsClientTaskBarReceiver.h" +#endif + +// platform dependent name of a daemon +#if WINDOWS_LIKE +#define DAEMON_NAME "Synergy Client" +#elif UNIX_LIKE +#define DAEMON_NAME "synergyc" +#endif + +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_serverAddress; +}; + +CArgs* CArgs::s_instance = NULL; + + +// +// platform dependent factories +// + +//! Factory for creating secondary screens +/*! +Objects of this type create secondary screens appropriate for the +platform. +*/ +class CSecondaryScreenFactory : public ISecondaryScreenFactory { +public: + CSecondaryScreenFactory() { } + virtual ~CSecondaryScreenFactory() { } + + // ISecondaryScreenFactory overrides + virtual CSecondaryScreen* + create(IScreenReceiver*); +}; + +CSecondaryScreen* +CSecondaryScreenFactory::create(IScreenReceiver* receiver) +{ +#if WINDOWS_LIKE + return new CMSWindowsSecondaryScreen(receiver); +#elif UNIX_LIKE + return new CXWindowsSecondaryScreen(receiver); +#endif +} + + +//! CQuitJob +/*! +A job that cancels a given thread. +*/ +class CQuitJob : public IJob { +public: + CQuitJob(const CThread& thread); + ~CQuitJob(); + + // IJob overrides + virtual void run(); + +private: + CThread m_thread; +}; + +CQuitJob::CQuitJob(const CThread& thread) : + m_thread(thread) +{ + // do nothing +} + +CQuitJob::~CQuitJob() +{ + // do nothing +} + +void +CQuitJob::run() +{ + m_thread.cancel(); +} + + +// +// platform independent main +// + +static CClient* s_client = NULL; +static CClientTaskBarReceiver* s_taskBarReceiver = NULL; + +static +int +realMain(void) +{ + int result = kExitSuccess; + do { + bool opened = false; + bool locked = true; + try { + // create client + s_client = new CClient(ARG->m_name); + s_client->setAddress(ARG->m_serverAddress); + s_client->setScreenFactory(new CSecondaryScreenFactory); + s_client->setSocketFactory(new CTCPSocketFactory); + s_client->setStreamFilterFactory(NULL); + + // open client + try { + s_taskBarReceiver->setClient(s_client); + s_client->open(); + opened = true; + + // run client + DAEMON_RUNNING(true); + locked = false; + s_client->mainLoop(); + locked = true; + DAEMON_RUNNING(false); + + // get client status + if (s_client->wasRejected()) { + // try again later. we don't want to bother + // the server very often if it doesn't want us. + throw XScreenUnavailable(60.0); + } + + // clean up +#define FINALLY do { \ + if (!locked) { \ + DAEMON_RUNNING(false); \ + locked = true; \ + } \ + if (opened) { \ + s_client->close(); \ + } \ + s_taskBarReceiver->setClient(NULL); \ + delete s_client; \ + s_client = NULL; \ + } while (false) + FINALLY; + } + catch (XScreenUnavailable& e) { + // wait before retrying if we're going to retry + if (ARG->m_restartable) { + LOG((CLOG_DEBUG "waiting %.0f seconds to retry", e.getRetryTime())); + ARCH->sleep(e.getRetryTime()); + } + else { + result = kExitFailed; + } + FINALLY; + } + catch (XThread&) { + FINALLY; + throw; + } + catch (...) { + // don't try to restart and fail + ARG->m_restartable = false; + result = kExitFailed; + FINALLY; + } +#undef FINALLY + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed: %s", e.what())); + } + catch (XThread&) { + // terminated + ARG->m_restartable = false; + result = kExitTerminated; + } + } while (ARG->m_restartable); + + return result; +} + +static +void +realMainEntry(void* vresult) +{ + *reinterpret_cast(vresult) = realMain(); +} + +static +int +runMainInThread(void) +{ + int result = 0; + CThread appThread(new CFunctionJob(&realMainEntry, &result)); + try { +#if WINDOWS_LIKE + MSG msg; + while (appThread.waitForEvent(-1.0) == CThread::kEvent) { + // check for a quit event + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + CThread::getCurrentThread().cancel(); + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +#else + appThread.wait(-1.0); +#endif + return result; + } + catch (XThread&) { + appThread.cancel(); + appThread.wait(-1.0); + throw; + } +} + + +// +// command line parsing +// + +#define BYE "\nTry `%s --help' for more information." + +static void (*bye)(int) = &exit; + +static +void +version() +{ + LOG((CLOG_PRINT +"%s %s, protocol version %d.%d\n" +"%s", + ARG->m_pname, + kVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); +} + +static +void +help() +{ + LOG((CLOG_PRINT +"Usage: %s" +" [--daemon|--no-daemon]" +" [--debug ]" +" [--name ]" +" [--restart|--no-restart]" +" \n" +"\n" +"Start the synergy mouse/keyboard sharing server.\n" +"\n" +" -d, --debug filter out log messages with priorty below level.\n" +" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" +" DEBUG, DEBUG1, DEBUG2.\n" +" -f, --no-daemon run the client in the foreground.\n" +"* --daemon run the client as a daemon.\n" +" -n, --name use screen-name instead the hostname to identify\n" +" ourself to the server.\n" +" -1, --no-restart do not try to restart the client if it fails for\n" +" some reason.\n" +"* --restart restart the client automatically if it fails.\n" +" -h, --help display this help and exit.\n" +" --version display version information and exit.\n" +"\n" +"* marks defaults.\n" +"\n" +"The server address is of the form: [][:]. The hostname\n" +"must be the address or hostname of the server. The port overrides the\n" +"default port, %d.\n" +"\n" +"Where log messages go depends on the platform and whether or not the\n" +"client is running as a daemon.", + ARG->m_pname, kDefaultPort)); + +} + +static +bool +isArg(int argi, int argc, const char* const* argv, + const char* name1, const char* name2, + int minRequiredParameters = 0) +{ + if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || + (name2 != NULL && strcmp(argv[argi], name2) == 0)) { + // match. check args left. + if (argi + minRequiredParameters >= argc) { + LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + ARG->m_pname, argv[argi], ARG->m_pname)); + bye(kExitArgs); + } + return true; + } + + // no match + return false; +} + +static +void +parse(int argc, const char* const* argv) +{ + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); + + // set defaults + ARG->m_name = ARCH->getHostName(); + + // parse options + int i; + for (i = 1; i < argc; ++i) { + if (isArg(i, argc, argv, "-d", "--debug", 1)) { + // change logging level + ARG->m_logFilter = argv[++i]; + } + + else if (isArg(i, argc, argv, "-n", "--name", 1)) { + // save screen name + ARG->m_name = argv[++i]; + } + + else if (isArg(i, argc, argv, NULL, "--camp")) { + // ignore -- included for backwards compatibility + } + + else if (isArg(i, argc, argv, NULL, "--no-camp")) { + // ignore -- included for backwards compatibility + } + + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + // not a daemon + ARG->m_daemon = false; + } + + else if (isArg(i, argc, argv, NULL, "--daemon")) { + // daemonize + ARG->m_daemon = true; + } + + else if (isArg(i, argc, argv, "-1", "--no-restart")) { + // don't try to restart + ARG->m_restartable = false; + } + + else if (isArg(i, argc, argv, NULL, "--restart")) { + // try to restart + ARG->m_restartable = true; + } + + else if (isArg(i, argc, argv, "-z", NULL)) { + ARG->m_backend = true; + } + + else if (isArg(i, argc, argv, "-h", "--help")) { + help(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, NULL, "--version")) { + version(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, "--", NULL)) { + // remaining arguments are not options + ++i; + break; + } + + else if (argv[i][0] == '-') { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + else { + // this and remaining arguments are not options + break; + } + } + + // exactly one non-option argument (server-address) + if (i == argc) { + LOG((CLOG_PRINT "%s: a server address or name is required" BYE, + ARG->m_pname, ARG->m_pname)); + bye(kExitArgs); + } + if (i + 1 != argc) { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + // save server address + try { + ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + } + catch (XSocketAddress& e) { + LOG((CLOG_PRINT "%s: %s" BYE, + ARG->m_pname, e.what(), ARG->m_pname)); + bye(kExitFailed); + } + + // increase default filter level for daemon. the user must + // explicitly request another level for a daemon. + if (ARG->m_daemon && ARG->m_logFilter == NULL) { +#if WINDOWS_LIKE + if (CArchMiscWindows::isWindows95Family()) { + // windows 95 has no place for logging so avoid showing + // the log console window. + ARG->m_logFilter = "FATAL"; + } + else +#endif + { + ARG->m_logFilter = "NOTE"; + } + } + + // set log filter + if (!CLOG->setFilter(ARG->m_logFilter)) { + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); + bye(kExitArgs); + } +} + + +// +// platform dependent entry points +// + +#if WINDOWS_LIKE + +static bool s_hasImportantLogMessages = false; + +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + +bool +CMessageBoxOutputter::write(ELevel level, const char* message) +{ + // note any important messages the user may need to know about + if (level <= CLog::kWARNING) { + s_hasImportantLogMessages = true; + } + + // FATAL and PRINT messages get a dialog box if not running as + // backend. if we're running as a backend the user will have + // a chance to see the messages when we exit. + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; + } + else { + return true; + } +} + +static +void +byeThrow(int x) +{ + CArchMiscWindows::daemonFailed(x); +} + +static +int +daemonStartup(int argc, const char** argv) +{ + CSystemLogger sysLogger(DAEMON_NAME); + + // have to cancel this thread to quit + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // catch errors that would normally exit + bye = &byeThrow; + + // parse command line + parse(argc, argv); + + // cannot run as backend if running as a service + ARG->m_backend = false; + + // run as a service + return CArchMiscWindows::runDaemon(realMain); +} + +static +int +daemonStartup95(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); +} + +static +int +run(int argc, char** argv) +{ + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + + // parse command line + parse(argc, argv); + + // daemonize if requested + if (ARG->m_daemon) { + // start as a daemon + if (CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + else { + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); + } + } + else { + // run + return runMainInThread(); + } +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CArch arch(instance); + CLOG; + CArgs args; + + // save instance + CMSWindowsScreen::init(instance); + + // get program name + ARG->m_pname = ARCH->getBasename(__argv[0]); + + // send PRINT and FATAL output to a message box + CLOG->insert(new CMessageBoxOutputter); + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance, + &logBuffer); + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + int result; + try { + // run in foreground or as a daemon + result = run(__argc, __argv); + } + catch (...) { + // note that we don't rethrow thread cancellation. we'll + // be exiting soon so it doesn't matter. what we'd like + // is for everything after this try/catch to be in a + // finally block. + result = kExitFailed; + } + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (ARG->m_backend && s_hasImportantLogMessages) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); + } + + delete CLOG; + return result; +} + +#elif UNIX_LIKE + +static +int +daemonStartup(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return realMain(); +} + +int +main(int argc, char** argv) +{ + CArch arch; + CLOG; + CArgs args; + + // get program name + ARG->m_pname = ARCH->getBasename(argv[0]); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // parse command line + parse(argc, argv); + + // daemonize if requested + int result; + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to daemonize")); + result = kExitFailed; + } + } + else { + result = realMain(); + } + + // done with task bar receiver + delete s_taskBarReceiver; + + return result; +} + +#else + +#error no main() for platform + +#endif diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp new file mode 100644 index 0000000..3321561 --- /dev/null +++ b/cmd/synergyc/synergyc.dsp @@ -0,0 +1,153 @@ +# Microsoft Developer Studio Project File - Name="synergyc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=synergyc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "synergyc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "synergyc.mak" CFG="synergyc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergyc - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergyc - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergyc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "synergyc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "synergyc - Win32 Release" +# Name "synergyc - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClientTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClientTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergyc.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergyc.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClientTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClientTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergyc.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_error.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_idle.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_run.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_wait.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/synergyc/synergyc.ico b/cmd/synergyc/synergyc.ico new file mode 100644 index 0000000..23d9a09 Binary files /dev/null and b/cmd/synergyc/synergyc.ico differ diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc new file mode 100644 index 0000000..9fd0020 --- /dev/null +++ b/cmd/synergyc/synergyc.rc @@ -0,0 +1,118 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergyc.ico" +IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico" +IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico" +IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 18 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKBAR MENU DISCARDABLE +BEGIN + POPUP "Synergy" + BEGIN + MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + MENUITEM SEPARATOR + MENUITEM "Quit", IDC_TASKBAR_QUIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/synergyc/tb_error.ico b/cmd/synergyc/tb_error.ico new file mode 100644 index 0000000..a304dc1 Binary files /dev/null and b/cmd/synergyc/tb_error.ico differ diff --git a/cmd/synergyc/tb_idle.ico b/cmd/synergyc/tb_idle.ico new file mode 100644 index 0000000..9ba599d Binary files /dev/null and b/cmd/synergyc/tb_idle.ico differ diff --git a/cmd/synergyc/tb_run.ico b/cmd/synergyc/tb_run.ico new file mode 100644 index 0000000..86aa9f3 Binary files /dev/null and b/cmd/synergyc/tb_run.ico differ diff --git a/cmd/synergyc/tb_wait.ico b/cmd/synergyc/tb_wait.ico new file mode 100644 index 0000000..3e2edd5 Binary files /dev/null and b/cmd/synergyc/tb_wait.ico differ diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp new file mode 100644 index 0000000..ca6525e --- /dev/null +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -0,0 +1,330 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsServerTaskBarReceiver.h" +#include "CServer.h" +#include "CMSWindowsClipboard.h" +#include "LogOutputters.h" +#include "BasicTypes.h" +#include "CArch.h" +#include "CArchTaskBarWindows.h" +#include "resource.h" + +static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = +{ + IDI_TASKBAR_NOT_RUNNING, + IDI_TASKBAR_NOT_WORKING, + IDI_TASKBAR_NOT_CONNECTED, + IDI_TASKBAR_CONNECTED +}; + +// +// CMSWindowsServerTaskBarReceiver +// + +CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( + HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : + CServerTaskBarReceiver(), + m_appInstance(appInstance), + m_window(NULL), + m_logBuffer(logBuffer) +{ + for (UInt32 i = 0; i < kMaxState; ++i) { + m_icon[i] = loadIcon(g_stateToIconID[i]); + } + m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); + + // don't create the window yet. we'll create it on demand. this + // has the side benefit of being created in the thread used for + // the task bar. that's good because it means the existence of + // the window won't prevent changing the main thread's desktop. + + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CMSWindowsServerTaskBarReceiver::~CMSWindowsServerTaskBarReceiver() +{ + ARCH->removeReceiver(this); + for (UInt32 i = 0; i < kMaxState; ++i) { + deleteIcon(m_icon[i]); + } + DestroyMenu(m_menu); + destroyWindow(); +} + +void +CMSWindowsServerTaskBarReceiver::showStatus() +{ + // create the window + createWindow(); + + // lock self while getting status + lock(); + + // get the current status + std::string status = getToolTip(); + + // get the connect clients, if any + typedef std::vector CClientList; + CClientList clients; + CServer* server = getServer(); + if (server != NULL) { + server->getClients(clients); + } + + // done getting status + unlock(); + + // update dialog + HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); + child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CClientList::const_iterator index = clients.begin(); + index != clients.end(); ) { + const char* client = index->c_str(); + if (++index == clients.end()) { + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)client); + } + else { + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)client); + } + } + + if (!IsWindowVisible(m_window)) { + // position it by the mouse + POINT cursorPos; + GetCursorPos(&cursorPos); + RECT windowRect; + GetWindowRect(m_window, &windowRect); + int x = cursorPos.x; + int y = cursorPos.y; + int fw = GetSystemMetrics(SM_CXDLGFRAME); + int fh = GetSystemMetrics(SM_CYDLGFRAME); + int ww = windowRect.right - windowRect.left; + int wh = windowRect.bottom - windowRect.top; + int sw = GetSystemMetrics(SM_CXFULLSCREEN); + int sh = GetSystemMetrics(SM_CYFULLSCREEN); + if (fw < 1) { + fw = 1; + } + if (fh < 1) { + fh = 1; + } + if (x + ww - fw > sw) { + x -= ww - fw; + } + else { + x -= fw; + } + if (x < 0) { + x = 0; + } + if (y + wh - fh > sh) { + y -= wh - fh; + } + else { + y -= fh; + } + if (y < 0) { + y = 0; + } + SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh, + SWP_SHOWWINDOW); + } +} + +void +CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) +{ + // do popup menu. we need a window to pass to TrackPopupMenu(). + // the SetForegroundWindow() and SendMessage() calls around + // TrackPopupMenu() are to get the menu to be dismissed when + // another window gets activated and are just one of those + // win32 weirdnesses. + createWindow(); + SetForegroundWindow(m_window); + HMENU menu = GetSubMenu(m_menu, 0); + SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + int n = TrackPopupMenu(menu, + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_LEFTBUTTON | + TPM_RIGHTBUTTON, + x, y, 0, m_window, NULL); + SendMessage(m_window, WM_NULL, 0, 0); + + // perform the requested operation + switch (n) { + case IDC_TASKBAR_STATUS: + showStatus(); + break; + + case IDC_TASKBAR_LOG: + copyLog(); + break; + + case IDC_TASKBAR_QUIT: + quit(); + break; + } +} + +void +CMSWindowsServerTaskBarReceiver::primaryAction() +{ + showStatus(); +} + +const IArchTaskBarReceiver::Icon +CMSWindowsServerTaskBarReceiver::getIcon() const +{ + return reinterpret_cast(m_icon[getState()]); +} + +void +CMSWindowsServerTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.emptyUnowned(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + +void +CMSWindowsServerTaskBarReceiver::onStatusChanged() +{ + if (IsWindowVisible(m_window)) { + showStatus(); + } +} + +HICON +CMSWindowsServerTaskBarReceiver::loadIcon(UINT id) +{ + HANDLE icon = LoadImage(m_appInstance, + MAKEINTRESOURCE(id), + IMAGE_ICON, + 0, 0, + LR_DEFAULTCOLOR); + return reinterpret_cast(icon); +} + +void +CMSWindowsServerTaskBarReceiver::deleteIcon(HICON icon) +{ + if (icon != NULL) { + DestroyIcon(icon); + } +} + +void +CMSWindowsServerTaskBarReceiver::createWindow() +{ + // ignore if already created + if (m_window != NULL) { + return; + } + + // get the status dialog + m_window = CreateDialogParam(m_appInstance, + MAKEINTRESOURCE(IDD_TASKBAR_STATUS), + NULL, + &CMSWindowsServerTaskBarReceiver::staticDlgProc, + reinterpret_cast( + reinterpret_cast(this))); + + // window should appear on top of everything, including (especially) + // the task bar. + DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + SetWindowLong(m_window, GWL_EXSTYLE, style); + + // tell the task bar about this dialog + CArchTaskBarWindows::addDialog(m_window); +} + +void +CMSWindowsServerTaskBarReceiver::destroyWindow() +{ + if (m_window != NULL) { + CArchTaskBarWindows::removeDialog(m_window); + DestroyWindow(m_window); + m_window = NULL; + } +} + +BOOL +CMSWindowsServerTaskBarReceiver::dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_INITDIALOG: + // use default focus + return TRUE; + + case WM_ACTIVATE: + // hide when another window is activated + if (LOWORD(wParam) == WA_INACTIVE) { + ShowWindow(hwnd, SW_HIDE); + } + break; + } + return FALSE; +} + +BOOL CALLBACK +CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_INITDIALOG, extract the CMSWindowsServerTaskBarReceiver* + // and put it in the extra window data then forward the call. + CMSWindowsServerTaskBarReceiver* self = NULL; + if (msg == WM_INITDIALOG) { + self = reinterpret_cast( + reinterpret_cast(lParam)); + SetWindowLong(hwnd, GWL_USERDATA, lParam); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, GWL_USERDATA); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->dlgProc(hwnd, msg, wParam, lParam); + } + else { + return (msg == WM_INITDIALOG) ? TRUE : FALSE; + } +} diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h new file mode 100644 index 0000000..d1c3dc6 --- /dev/null +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSERVERTASKBARRECEIVER_H +#define CMSWINDOWSSERVERTASKBARRECEIVER_H + +#define WIN32_LEAN_AND_MEAN + +#include "CServerTaskBarReceiver.h" +#include + +class CBufferedLogOutputter; + +//! Implementation of CServerTaskBarReceiver for Microsoft Windows +class CMSWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { +public: + CMSWindowsServerTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); + virtual ~CMSWindowsServerTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + void copyLog() const; + + // CServerTaskBarReceiver overrides + virtual void onStatusChanged(); + +private: + HICON loadIcon(UINT); + void deleteIcon(HICON); + void createWindow(); + void destroyWindow(); + + BOOL dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK + staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + +private: + HINSTANCE m_appInstance; + HWND m_window; + HMENU m_menu; + HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; +}; + +#endif diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp new file mode 100644 index 0000000..8349a13 --- /dev/null +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -0,0 +1,171 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServerTaskBarReceiver.h" +#include "CServer.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CServerTaskBarReceiver +// + +CServerTaskBarReceiver::CServerTaskBarReceiver() : + m_quit(NULL), + m_state(kNotRunning), + m_server(NULL) +{ + // create a job for getting notification when the server's + // status changes. + m_job = new TMethodJob(this, + &CServerTaskBarReceiver::statusChanged, NULL); +} + +CServerTaskBarReceiver::~CServerTaskBarReceiver() +{ + if (m_server != NULL) { + m_server->removeStatusJob(m_job); + } + delete m_job; + delete m_quit; +} + +void +CServerTaskBarReceiver::setServer(CServer* server) +{ + { + CLock lock(&m_mutex); + if (m_server != server) { + if (m_server != NULL) { + m_server->removeStatusJob(m_job); + } + m_server = server; + if (m_server != NULL) { + m_server->addStatusJob(m_job); + } + } + } + ARCH->updateReceiver(this); +} + +void +CServerTaskBarReceiver::setState(EState state) +{ + { + CLock lock(&m_mutex); + m_state = state; + } + ARCH->updateReceiver(this); +} + +void +CServerTaskBarReceiver::setQuitJob(IJob* job) +{ + CLock lock(&m_mutex); + delete m_quit; + m_quit = job; +} + +CServerTaskBarReceiver::EState +CServerTaskBarReceiver::getState() const +{ + return m_state; +} + +CServer* +CServerTaskBarReceiver::getServer() const +{ + return m_server; +} + +void +CServerTaskBarReceiver::lock() const +{ + m_mutex.lock(); +} + +void +CServerTaskBarReceiver::unlock() const +{ + m_mutex.unlock(); +} + +std::string +CServerTaskBarReceiver::getToolTip() const +{ + switch (m_state) { + case kNotRunning: + return "Synergy: Not running"; + + case kNotWorking: + return CString("Synergy: ") + m_errorMessage; + + case kNotConnected: + return "Synergy: Waiting for clients"; + + case kConnected: + return "Synergy: Connected"; + + default: + return ""; + } +} + +void +CServerTaskBarReceiver::quit() +{ + if (m_quit != NULL) { + m_quit->run(); + } +} + +void +CServerTaskBarReceiver::onStatusChanged() +{ + // do nothing +} + +void +CServerTaskBarReceiver::statusChanged(void*) +{ + // update our status + switch (m_server->getStatus(&m_errorMessage)) { + case CServer::kNotRunning: + setState(kNotRunning); + break; + + case CServer::kRunning: + if (m_server->getNumClients() > 1) + setState(kConnected); + else + setState(kNotConnected); + break; + + case CServer::kServerNameUnknown: + m_errorMessage = "Server name is not in configuration"; + setState(kNotWorking); + break; + + case CServer::kError: + setState(kNotWorking); + break; + + default: + break; + } + + // let subclasses have a go + onStatusChanged(); +} diff --git a/cmd/synergys/CServerTaskBarReceiver.h b/cmd/synergys/CServerTaskBarReceiver.h new file mode 100644 index 0000000..0591a09 --- /dev/null +++ b/cmd/synergys/CServerTaskBarReceiver.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSERVERTASKBARRECEIVER_H +#define CSERVERTASKBARRECEIVER_H + +#include "CMutex.h" +#include "CString.h" +#include "IArchTaskBarReceiver.h" + +class CServer; +class IJob; + +//! Implementation of IArchTaskBarReceiver for the synergy server +class CServerTaskBarReceiver : public IArchTaskBarReceiver { +public: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnected, + kMaxState + }; + + CServerTaskBarReceiver(); + virtual ~CServerTaskBarReceiver(); + + //! @name manipulators + //@{ + + //! Set server + /*! + Sets the server. The receiver will query state from this server. + */ + void setServer(CServer*); + + //! Set state + /*! + Sets the current server state. + */ + void setState(EState); + + //! Set the quit job that causes the server to quit + /*! + Set the job that causes the server to quit. + */ + void setQuitJob(IJob* adopted); + + //@} + //! @name accessors + //@{ + + //! Get state + /*! + Returns the current server state. The receiver is not locked + by this call; the caller must do the locking. + */ + EState getState() const; + + //! Get server + /*! + Returns the server set by \c setServer(). + */ + CServer* getServer() const; + + //@} + + // IArchTaskBarReceiver overrides + virtual void showStatus() = 0; + virtual void runMenu(int x, int y) = 0; + virtual void primaryAction() = 0; + virtual void lock() const; + virtual void unlock() const; + virtual const Icon getIcon() const = 0; + virtual std::string getToolTip() const; + +protected: + void quit(); + + //! Status change notification + /*! + Called when status changes. The default implementation does + nothing. + */ + virtual void onStatusChanged(); + +private: + void statusChanged(void*); + +private: + CMutex m_mutex; + IJob* m_quit; + EState m_state; + CServer* m_server; + IJob* m_job; + CString m_errorMessage; +}; + +#endif diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp new file mode 100644 index 0000000..2011873 --- /dev/null +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsServerTaskBarReceiver.h" +#include "CArch.h" + +// +// CXWindowsServerTaskBarReceiver +// + +CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver() +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CXWindowsServerTaskBarReceiver::~CXWindowsServerTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +CXWindowsServerTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +CXWindowsServerTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +CXWindowsServerTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +CXWindowsServerTaskBarReceiver::getIcon() const +{ + return NULL; +} + +void +CXWindowsServerTaskBarReceiver::onStatusChanged() +{ + // do nothing +} diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.h b/cmd/synergys/CXWindowsServerTaskBarReceiver.h new file mode 100644 index 0000000..a05a430 --- /dev/null +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.h @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSERVERTASKBARRECEIVER_H +#define CXWINDOWSSERVERTASKBARRECEIVER_H + +#include "CServerTaskBarReceiver.h" + +//! Implementation of CServerTaskBarReceiver for X Windows +class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { +public: + CXWindowsServerTaskBarReceiver(); + virtual ~CXWindowsServerTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CServerTaskBarReceiver overrides + virtual void onStatusChanged(); +}; + +#endif diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am new file mode 100644 index 0000000..b3384ce --- /dev/null +++ b/cmd/synergys/Makefile.am @@ -0,0 +1,71 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsServerTaskBarReceiver.cpp \ + CMSWindowsServerTaskBarReceiver.h \ + resource.h \ + synergys.ico \ + synergys.dsp \ + synergys.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +bin_PROGRAMS = synergys +synergys_SOURCES = \ + CServerTaskBarReceiver.cpp \ + CServerTaskBarReceiver.h \ + CXWindowsServerTaskBarReceiver.cpp \ + CXWindowsServerTaskBarReceiver.h \ + synergys.cpp \ + $(NULL) +synergys_LDADD = \ + $(DEPTH)/lib/server/libserver.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/http/libhttp.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/server \ + $(NULL) diff --git a/cmd/synergys/Makefile.in b/cmd/synergys/Makefile.in new file mode 100644 index 0000000..7674cc6 --- /dev/null +++ b/cmd/synergys/Makefile.in @@ -0,0 +1,400 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + CMSWindowsServerTaskBarReceiver.cpp \ + CMSWindowsServerTaskBarReceiver.h \ + resource.h \ + synergys.ico \ + synergys.dsp \ + synergys.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +bin_PROGRAMS = synergys +synergys_SOURCES = \ + CServerTaskBarReceiver.cpp \ + CServerTaskBarReceiver.h \ + CXWindowsServerTaskBarReceiver.cpp \ + CXWindowsServerTaskBarReceiver.h \ + synergys.cpp \ + $(NULL) + +synergys_LDADD = \ + $(DEPTH)/lib/server/libserver.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/http/libhttp.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/server \ + $(NULL) + +subdir = cmd/synergys +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +bin_PROGRAMS = synergys$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) + +am_synergys_OBJECTS = CServerTaskBarReceiver.$(OBJEXT) \ + CXWindowsServerTaskBarReceiver.$(OBJEXT) synergys.$(OBJEXT) +synergys_OBJECTS = $(am_synergys_OBJECTS) +synergys_DEPENDENCIES = $(DEPTH)/lib/server/libserver.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/http/libhttp.a $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a +synergys_LDFLAGS = + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CServerTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsServerTaskBarReceiver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/synergys.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(synergys_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(synergys_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cmd/synergys/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$f; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ + rm -f $(DESTDIR)$(bindir)/$$f; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +synergys$(EXEEXT): $(synergys_OBJECTS) $(synergys_DEPENDENCIES) + @rm -f synergys$(EXEEXT) + $(CXXLINK) $(synergys_LDFLAGS) $(synergys_OBJECTS) $(synergys_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CServerTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsServerTaskBarReceiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/synergys.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic distclean distclean-compile distclean-depend \ + distclean-generic distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h new file mode 100644 index 0000000..5ac2470 --- /dev/null +++ b/cmd/synergys/resource.h @@ -0,0 +1,28 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergys.rc +// +#define IDS_FAILED 1 +#define IDI_SYNERGY 101 +#define IDI_TASKBAR_NOT_RUNNING 102 +#define IDI_TASKBAR_NOT_WORKING 103 +#define IDI_TASKBAR_NOT_CONNECTED 104 +#define IDI_TASKBAR_CONNECTED 105 +#define IDR_TASKBAR 107 +#define IDD_TASKBAR_STATUS 108 +#define IDC_TASKBAR_STATUS_STATUS 1000 +#define IDC_TASKBAR_STATUS_CLIENTS 1001 +#define IDC_TASKBAR_QUIT 40003 +#define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40006 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp new file mode 100644 index 0000000..e3fb74d --- /dev/null +++ b/cmd/synergys/synergys.cpp @@ -0,0 +1,883 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServer.h" +#include "CConfig.h" +#include "IPrimaryScreenFactory.h" +#include "ProtocolTypes.h" +#include "Version.h" +#include "XScreen.h" +#include "CTCPSocketFactory.h" +#include "XSocket.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "XThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "stdfstream.h" +#include + +#define DAEMON_RUNNING(running_) +#if WINDOWS_LIKE +#include "CMSWindowsScreen.h" +#include "CMSWindowsPrimaryScreen.h" +#include "CArchMiscWindows.h" +#include "CMSWindowsServerTaskBarReceiver.h" +#include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) +#elif UNIX_LIKE +#include "CXWindowsPrimaryScreen.h" +#include "CXWindowsServerTaskBarReceiver.h" +#endif + +// platform dependent name of a daemon +#if WINDOWS_LIKE +#define DAEMON_NAME "Synergy Server" +#elif UNIX_LIKE +#define DAEMON_NAME "synergys" +#endif + +// configuration file name +#if WINDOWS_LIKE +#define USR_CONFIG_NAME "synergy.sgc" +#define SYS_CONFIG_NAME "synergy.sgc" +#elif UNIX_LIKE +#define USR_CONFIG_NAME ".synergy.conf" +#define SYS_CONFIG_NAME "synergy.conf" +#endif + +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_configFile(NULL), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + const char* m_configFile; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_synergyAddress; + CNetworkAddress m_httpAddress; + CConfig m_config; +}; + +CArgs* CArgs::s_instance = NULL; + + +// +// platform dependent factories +// + +//! Factory for creating primary screens +/*! +Objects of this type create primary screens appropriate for the +platform. +*/ +class CPrimaryScreenFactory : public IPrimaryScreenFactory { +public: + CPrimaryScreenFactory() { } + virtual ~CPrimaryScreenFactory() { } + + // IPrimaryScreenFactory overrides + virtual CPrimaryScreen* + create(IScreenReceiver*, IPrimaryScreenReceiver*); +}; + +CPrimaryScreen* +CPrimaryScreenFactory::create(IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) +{ +#if WINDOWS_LIKE + return new CMSWindowsPrimaryScreen(receiver, primaryReceiver); +#elif UNIX_LIKE + return new CXWindowsPrimaryScreen(receiver, primaryReceiver); +#endif +} + + +//! CQuitJob +/*! +A job that cancels a given thread. +*/ +class CQuitJob : public IJob { +public: + CQuitJob(const CThread& thread); + ~CQuitJob(); + + // IJob overrides + virtual void run(); + +private: + CThread m_thread; +}; + +CQuitJob::CQuitJob(const CThread& thread) : + m_thread(thread) +{ + // do nothing +} + +CQuitJob::~CQuitJob() +{ + // do nothing +} + +void +CQuitJob::run() +{ + m_thread.cancel(); +} + + +// +// platform independent main +// + +static CServer* s_server = NULL; +static CServerTaskBarReceiver* s_taskBarReceiver = NULL; + +static +int +realMain(void) +{ + int result = kExitSuccess; + do { + bool opened = false; + bool locked = true; + try { + // if configuration has no screens then add this system + // as the default + if (ARG->m_config.begin() == ARG->m_config.end()) { + ARG->m_config.addScreen(ARG->m_name); + } + + // set the contact address, if provided, in the config. + // otherwise, if the config doesn't have an address, use + // the default. + if (ARG->m_synergyAddress.isValid()) { + ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); + } + else if (!ARG->m_config.getSynergyAddress().isValid()) { + ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + } + + // set HTTP address if provided + if (ARG->m_httpAddress.isValid()) { + ARG->m_config.setHTTPAddress(ARG->m_httpAddress); + } + + // create server + s_server = new CServer(ARG->m_name); + s_server->setConfig(ARG->m_config); + s_server->setScreenFactory(new CPrimaryScreenFactory); + s_server->setSocketFactory(new CTCPSocketFactory); + s_server->setStreamFilterFactory(NULL); + + // open server + try { + s_taskBarReceiver->setServer(s_server); + s_server->open(); + opened = true; + + // run server + DAEMON_RUNNING(true); + locked = false; + s_server->mainLoop(); + + // clean up +#define FINALLY do { \ + if (!locked) { \ + DAEMON_RUNNING(false); \ + locked = true; \ + } \ + if (opened) { \ + s_server->close(); \ + } \ + s_taskBarReceiver->setServer(NULL); \ + delete s_server; \ + s_server = NULL; \ + } while (false) + FINALLY; + } + catch (XScreenUnavailable& e) { + // wait before retrying if we're going to retry + if (ARG->m_restartable) { + ARCH->sleep(e.getRetryTime()); + } + else { + result = kExitFailed; + } + FINALLY; + } + catch (XThread&) { + FINALLY; + throw; + } + catch (...) { + // don't try to restart and fail + ARG->m_restartable = false; + result = kExitFailed; + FINALLY; + } +#undef FINALLY + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed: %s", e.what())); + } + catch (XThread&) { + // terminated + ARG->m_restartable = false; + result = kExitTerminated; + } + } while (ARG->m_restartable); + + return result; +} + +static +void +realMainEntry(void* vresult) +{ + *reinterpret_cast(vresult) = realMain(); +} + +static +int +runMainInThread(void) +{ + int result = 0; + CThread appThread(new CFunctionJob(&realMainEntry, &result)); + try { +#if WINDOWS_LIKE + MSG msg; + while (appThread.waitForEvent(-1.0) == CThread::kEvent) { + // check for a quit event + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + CThread::getCurrentThread().cancel(); + } + } + } +#else + appThread.wait(-1.0); +#endif + return result; + } + catch (XThread&) { + appThread.cancel(); + appThread.wait(-1.0); + throw; + } +} + + +// +// command line parsing +// + +#define BYE "\nTry `%s --help' for more information." + +static void (*bye)(int) = &exit; + +static +void +version() +{ + LOG((CLOG_PRINT +"%s %s, protocol version %d.%d\n" +"%s", + ARG->m_pname, + kVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); +} + +static +void +help() +{ +#if WINDOWS_LIKE + +# define PLATFORM_ARGS \ +" {--daemon|--no-daemon}" +# define PLATFORM_DESC +# define PLATFORM_EXTRA \ +"At least one command line argument is required. If you don't otherwise\n" \ +"need an argument use `--daemon'.\n" \ +"\n" + +#else + +# define PLATFORM_ARGS \ +" [--daemon|--no-daemon]" +# define PLATFORM_DESC +# define PLATFORM_EXTRA + +#endif + + LOG((CLOG_PRINT +"Usage: %s" +" [--address
]" +" [--config ]" +" [--debug ]" +" [--name ]" +" [--restart|--no-restart]\n" +PLATFORM_ARGS +"\n" +"Start the synergy mouse/keyboard sharing server.\n" +"\n" +" -a, --address
listen for clients on the given address.\n" +" -c, --config use the named configuration file instead.\n" +" -d, --debug filter out log messages with priorty below level.\n" +" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" +" DEBUG, DEBUG1, DEBUG2.\n" +" -f, --no-daemon run the server in the foreground.\n" +"* --daemon run the server as a daemon.\n" +" -n, --name use screen-name instead the hostname to identify\n" +" this screen in the configuration.\n" +" -1, --no-restart do not try to restart the server if it fails for\n" +" some reason.\n" +"* --restart restart the server automatically if it fails.\n" +PLATFORM_DESC +" -h, --help display this help and exit.\n" +" --version display version information and exit.\n" +"\n" +"* marks defaults.\n" +"\n" +PLATFORM_EXTRA +"The argument for --address is of the form: [][:]. The\n" +"hostname must be the address or hostname of an interface on the system.\n" +"The default is to listen on all interfaces. The port overrides the\n" +"default port, %d.\n" +"\n" +"If no configuration file pathname is provided then the first of the\n" +"following to load successfully sets the configuration:\n" +" %s\n" +" %s\n" +"If no configuration file can be loaded then the configuration uses its\n" +"defaults with just the server screen.\n" +"\n" +"Where log messages go depends on the platform and whether or not the\n" +"server is running as a daemon.", + ARG->m_pname, + kDefaultPort, + ARCH->concatPath( + ARCH->getUserDirectory(), + USR_CONFIG_NAME).c_str(), + ARCH->concatPath( + ARCH->getSystemDirectory(), + SYS_CONFIG_NAME).c_str())); +} + +static +bool +isArg(int argi, int argc, const char* const* argv, + const char* name1, const char* name2, + int minRequiredParameters = 0) +{ + if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || + (name2 != NULL && strcmp(argv[argi], name2) == 0)) { + // match. check args left. + if (argi + minRequiredParameters >= argc) { + LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + ARG->m_pname, argv[argi], ARG->m_pname)); + bye(kExitArgs); + } + return true; + } + + // no match + return false; +} + +static +void +parse(int argc, const char* const* argv) +{ + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); + + // set defaults + ARG->m_name = ARCH->getHostName(); + + // parse options + int i; + for (i = 1; i < argc; ++i) { + if (isArg(i, argc, argv, "-d", "--debug", 1)) { + // change logging level + ARG->m_logFilter = argv[++i]; + } + + else if (isArg(i, argc, argv, "-a", "--address", 1)) { + // save listen address + try { + ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], + kDefaultPort); + } + catch (XSocketAddress& e) { + LOG((CLOG_PRINT "%s: %s" BYE, + ARG->m_pname, e.what(), ARG->m_pname)); + bye(kExitArgs); + } + ++i; + } + + else if (isArg(i, argc, argv, NULL, "--http", 1)) { + // save listen address + try { + ARG->m_httpAddress = CNetworkAddress(argv[i + 1], + kDefaultPort + 1); + } + catch (XSocketAddress& e) { + LOG((CLOG_PRINT "%s: %s" BYE, + ARG->m_pname, e.what(), ARG->m_pname)); + bye(kExitArgs); + } + ++i; + } + + else if (isArg(i, argc, argv, "-n", "--name", 1)) { + // save screen name + ARG->m_name = argv[++i]; + } + + else if (isArg(i, argc, argv, "-c", "--config", 1)) { + // save configuration file path + ARG->m_configFile = argv[++i]; + } + + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + // not a daemon + ARG->m_daemon = false; + } + + else if (isArg(i, argc, argv, NULL, "--daemon")) { + // daemonize + ARG->m_daemon = true; + } + + else if (isArg(i, argc, argv, "-1", "--no-restart")) { + // don't try to restart + ARG->m_restartable = false; + } + + else if (isArg(i, argc, argv, NULL, "--restart")) { + // try to restart + ARG->m_restartable = true; + } + + else if (isArg(i, argc, argv, "-z", NULL)) { + ARG->m_backend = true; + } + + else if (isArg(i, argc, argv, "-h", "--help")) { + help(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, NULL, "--version")) { + version(); + bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, "--", NULL)) { + // remaining arguments are not options + ++i; + break; + } + + else if (argv[i][0] == '-') { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + else { + // this and remaining arguments are not options + break; + } + } + + // no non-option arguments are allowed + if (i != argc) { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + ARG->m_pname, argv[i], ARG->m_pname)); + bye(kExitArgs); + } + + // increase default filter level for daemon. the user must + // explicitly request another level for a daemon. + if (ARG->m_daemon && ARG->m_logFilter == NULL) { +#if WINDOWS_LIKE + if (CArchMiscWindows::isWindows95Family()) { + // windows 95 has no place for logging so avoid showing + // the log console window. + ARG->m_logFilter = "FATAL"; + } + else +#endif + { + ARG->m_logFilter = "NOTE"; + } + } + + // set log filter + if (!CLOG->setFilter(ARG->m_logFilter)) { + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); + bye(kExitArgs); + } +} + +static +bool +loadConfig(const char* pathname, bool require) +{ + assert(pathname != NULL); + + try { + // load configuration + LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname)); + std::ifstream configStream(pathname); + if (!configStream) { + throw XConfigRead("cannot open file"); + } + configStream >> ARG->m_config; + LOG((CLOG_DEBUG "configuration read successfully")); + return true; + } + catch (XConfigRead& e) { + if (require) { + LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s", + ARG->m_pname, pathname, e.what())); + bye(kExitConfig); + } + else { + LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", + pathname, e.what())); + } + } + return false; +} + +static +void +loadConfig() +{ + // load the config file, if specified + if (ARG->m_configFile != NULL) { + // require the user specified file to load correctly + loadConfig(ARG->m_configFile, true); + } + + // load the default configuration if no explicit file given + else { + // get the user's home directory. use the effective user id + // so a user can't get a setuid root program to load his file. + bool loaded = false; + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + // complete path + path = ARCH->concatPath(path, USR_CONFIG_NAME); + + // now try loading the user's configuration + loaded = loadConfig(path.c_str(), false); + } + if (!loaded) { + // try the system-wide config file + path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, SYS_CONFIG_NAME); + loadConfig(path.c_str(), false); + } + } + } +} + + +// +// platform dependent entry points +// + +#if WINDOWS_LIKE + +static bool s_hasImportantLogMessages = false; + +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + +bool +CMessageBoxOutputter::write(ELevel level, const char* message) +{ + // note any important messages the user may need to know about + if (level <= CLog::kWARNING) { + s_hasImportantLogMessages = true; + } + + // FATAL and PRINT messages get a dialog box if not running as + // backend. if we're running as a backend the user will have + // a chance to see the messages when we exit. + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; + } + else { + return true; + } +} + +static +void +byeThrow(int x) +{ + CArchMiscWindows::daemonFailed(x); +} + +static +int +daemonStartup(int argc, const char** argv) +{ + CSystemLogger sysLogger(DAEMON_NAME); + + // have to cancel this thread to quit + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // catch errors that would normally exit + bye = &byeThrow; + + // parse command line + parse(argc, argv); + + // cannot run as backend if running as a service + ARG->m_backend = false; + + // load configuration + loadConfig(); + + // run as a service + return CArchMiscWindows::runDaemon(realMain); +} + +static +int +daemonStartup95(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); +} + +static +int +run(int argc, char** argv) +{ + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + if (ARG->m_daemon) { + // start as a daemon + if (CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + else { + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); + } + } + else { + // run + return runMainInThread(); + } +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CArch arch(instance); + CLOG; + CArgs args; + + // save instance + CMSWindowsScreen::init(instance); + + // get program name + ARG->m_pname = ARCH->getBasename(__argv[0]); + + // send PRINT and FATAL output to a message box + CLOG->insert(new CMessageBoxOutputter); + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance, + &logBuffer); + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + int result; + try { + // run in foreground or as a daemon + result = run(__argc, __argv); + } + catch (...) { + // note that we don't rethrow thread cancellation. we'll + // be exiting soon so it doesn't matter. what we'd like + // is for everything after this try/catch to be in a + // finally block. + result = kExitFailed; + } + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (ARG->m_backend && s_hasImportantLogMessages) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); + } + + delete CLOG; + return result; +} + +#elif UNIX_LIKE + +static +int +daemonStartup(int, const char**) +{ + CSystemLogger sysLogger(DAEMON_NAME); + return realMain(); +} + +int +main(int argc, char** argv) +{ + CArch arch; + CLOG; + CArgs args; + + // get program name + ARG->m_pname = ARCH->getBasename(argv[0]); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CXWindowsServerTaskBarReceiver; + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + int result; + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to daemonize")); + result = kExitFailed; + } + } + else { + result = realMain(); + } + + // done with task bar receiver + delete s_taskBarReceiver; + + return result; +} + +#else + +#error no main() for platform + +#endif diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp new file mode 100644 index 0000000..a7ad81d --- /dev/null +++ b/cmd/synergys/synergys.dsp @@ -0,0 +1,153 @@ +# Microsoft Developer Studio Project File - Name="synergys" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=synergys - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "synergys.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "synergys.mak" CFG="synergys - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergys - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergys - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergys - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "synergys - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "synergys - Win32 Release" +# Name "synergys - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsServerTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergys.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergys.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsServerTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\CServerTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergys.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_error.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_idle.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_run.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_wait.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/synergys/synergys.ico b/cmd/synergys/synergys.ico new file mode 100644 index 0000000..23d9a09 Binary files /dev/null and b/cmd/synergys/synergys.ico differ diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc new file mode 100644 index 0000000..9f19075 --- /dev/null +++ b/cmd/synergys/synergys.rc @@ -0,0 +1,121 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergys.ico" +IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico" +IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico" +IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKBAR MENU DISCARDABLE +BEGIN + POPUP "Synergy" + BEGIN + MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + MENUITEM SEPARATOR + MENUITEM "Quit", IDC_TASKBAR_QUIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 60 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LISTBOX IDC_TASKBAR_STATUS_CLIENTS,3,17,139,40,NOT LBS_NOTIFY | + LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_VSCROLL | + WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/synergys/tb_error.ico b/cmd/synergys/tb_error.ico new file mode 100644 index 0000000..a304dc1 Binary files /dev/null and b/cmd/synergys/tb_error.ico differ diff --git a/cmd/synergys/tb_idle.ico b/cmd/synergys/tb_idle.ico new file mode 100644 index 0000000..9ba599d Binary files /dev/null and b/cmd/synergys/tb_idle.ico differ diff --git a/cmd/synergys/tb_run.ico b/cmd/synergys/tb_run.ico new file mode 100644 index 0000000..86aa9f3 Binary files /dev/null and b/cmd/synergys/tb_run.ico differ diff --git a/cmd/synergys/tb_wait.ico b/cmd/synergys/tb_wait.ico new file mode 100644 index 0000000..e94db3c Binary files /dev/null and b/cmd/synergys/tb_wait.ico differ diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..16bdae7 --- /dev/null +++ b/config.h.in @@ -0,0 +1,158 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if your compiler has bool support. */ +#undef HAVE_CXX_BOOL + +/* Define if your compiler has C++ cast support. */ +#undef HAVE_CXX_CASTS + +/* Define if your compiler has exceptions support. */ +#undef HAVE_CXX_EXCEPTIONS + +/* Define if your compiler has mutable support. */ +#undef HAVE_CXX_MUTABLE + +/* Define if your compiler has standard C++ library support. */ +#undef HAVE_CXX_STDLIB + +/* Define if you have a working `getpwuid_r\' function. */ +#undef HAVE_GETPWUID_R + +/* Define if you have the `gmtime_r' function. */ +#undef HAVE_GMTIME_R + +/* Define if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if you have the header file. */ +#undef HAVE_ISTREAM + +/* Define if you have the `mbrtowc' function. */ +#undef HAVE_MBRTOWC + +/* Define if you have the `mbsinit' function. */ +#undef HAVE_MBSINIT + +/* Define if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the `nanosleep\' function. */ +#undef HAVE_NANOSLEEP + +/* Define if you have the header file. */ +#undef HAVE_OSTREAM + +/* Define if you have the `poll\' function. */ +#undef HAVE_POLL + +/* Define if you have a POSIX `sigwait\' function. */ +#undef HAVE_POSIX_SIGWAIT + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define if you have `pthread_sigmask\' and `pthread_kill\' functions. */ +#undef HAVE_PTHREAD_SIGNAL + +/* Define if your compiler defines socklen_t. */ +#undef HAVE_SOCKLEN_T + +/* Define if you have the header file. */ +#undef HAVE_SSTREAM + +/* Define if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the header file. */ +#undef HAVE_WCHAR_H + +/* Define if you have the `wcrtomb' function. */ +#undef HAVE_WCRTOMB + +/* Define if you have the header file. */ +#undef HAVE_X11_EXTENSIONS_XINERAMA_H + +/* Define if you have the header file. */ +#undef HAVE_X11_EXTENSIONS_XTEST_H + +/* Define if you have the header file. */ +#undef HAVE_X11_XF86KEYSYM_H + +/* Name of package */ +#undef PACKAGE + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to the type of arg 1 for `select'. */ +#undef SELECT_TYPE_ARG1 + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#undef SELECT_TYPE_ARG234 + +/* Define to the type of arg 5 for `select'. */ +#undef SELECT_TYPE_ARG5 + +/* The size of a `char', as computed by sizeof. */ +#undef SIZEOF_CHAR + +/* The size of a `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The size of a `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of a `short', as computed by sizeof. */ +#undef SIZEOF_SHORT + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Version number of package */ +#undef VERSION + +/* Define if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Define to `unsigned' if does not define. */ +#undef size_t diff --git a/config/config.guess b/config/config.guess new file mode 100644 index 0000000..6ead80a --- /dev/null +++ b/config/config.guess @@ -0,0 +1,1327 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-08-21' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c ; + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ; + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + rm -f $dummy.c $dummy.o $dummy.rel ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_MACHINE}" in + i?86) + test -z "$VENDOR" && VENDOR=pc + ;; + *) + test -z "$VENDOR" && VENDOR=unknown + ;; +esac +test -f /etc/SuSE-release && VENDOR=suse + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # Netbsd (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + sparc*:NetBSD:*) + echo `uname -p`-unknown-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + case "${HPUX_REV}" in + 11.[0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + esac ;; + esac + fi ;; + esac + if [ "${HP_ARCH}" = "" ]; then + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + eval $set_cc_for_build + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + mips:Linux:*:*) + case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in + big) echo mips-${VENDOR}-linux && exit 0 ;; + little) echo mipsel-${VENDOR}-linux && exit 0 ;; + esac + case `sed -n '/^system type/s/^.*: \([^ ]*\).*/\1/p' < /proc/cpuinfo` in + SGI|sgi) echo mips-${VENDOR}-linux-gnu && exit 0 ;; + esac + ;; + ppc:Linux:*:*|ppc64:Linux:*:*) + echo powerpc-${VENDOR}-linux + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-${VENDOR}-linux${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-${VENDOR}-linux ;; + PA8*) echo hppa2.0-${VENDOR}-linux ;; + *) echo hppa-${VENDOR}-linux ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-${VENDOR}-linux + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-${VENDOR}-linux + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_targets=`cd /; ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-${VENDOR}-linux" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-${VENDOR}-linuxaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-${VENDOR}-linuxcoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linuxoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-${VENDOR}-linuxoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-${VENDOR}-linux\n", argv[1]); +# else + printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]); +# endif +# else + printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]); +# endif +#else + printf ("%s-${VENDOR}-linuxaout\n", argv[1]); +#endif + return 0; +} +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +eval $set_cc_for_build +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/config.sub b/config/config.sub new file mode 100644 index 0000000..83f4b01 --- /dev/null +++ b/config/config.sub @@ -0,0 +1,1410 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-08-13' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dsp16xx \ + | fr30 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips16 | mips64 | mips64el | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el | mips64vr4300 \ + | mips64vr4300el | mips64vr5000 | mips64vr5000el \ + | mipsbe | mipsel | mipsle | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | ns16k | ns32k \ + | openrisc \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | s390 | s390x \ + | sh | sh[34] | sh[34]eb | shbe | shle \ + | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 \ + | we32k \ + | x86 | xscale \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alphapca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armv*-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c54x-* \ + | clipper-* | cray2-* | cydra-* \ + | d10v-* | d30v-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | m32r-* \ + | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \ + | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipsel-* \ + | mipsle-* | mipstx39-* | mipstx39el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | s390-* | s390x-* \ + | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \ + | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \ + | v850-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + ;; + mips*-linux*) + basic_machine=mips-unknown + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc64) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + windows32) + basic_machine=i386-pc + os=-windows32-msvcrt + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + case $os in + linux*) + basic_machine=mips-unknown + ;; + *) + basic_machine=mips-mips + ;; + esac + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh3eb | sh4eb) + basic_machine=sh-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/depcomp b/config/depcomp new file mode 100644 index 0000000..6589965 --- /dev/null +++ b/config/depcomp @@ -0,0 +1,411 @@ +#! /bin/sh + +# depcomp - compile a program generating dependencies as side-effects +# Copyright 1999, 2000 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi +# `libtool' can also be set to `yes' or `no'. + +depfile=${depfile-`echo "$object" | sed 's,\([^/]*\)$,.deps/\1,;s/\.\([^.]*\)$/.P\1/'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. This file always lives in the current directory. + # Also, the AIX compiler puts `$object:' at the start of each line; + # $object doesn't have directory information. + stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + outname="$stripped.o" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 AIX compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + + tmpdepfile1="$object.d" + tmpdepfile2=`echo "$object" | sed -e 's/.o$/.d/'` + if test "$libtool" = yes; then + "$@" -Wc,-MD + else + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + else + tmpdepfile="$tmpdepfile2" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a space and a tab in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + test -z "$dashmflag" && dashmflag=-M + ( IFS=" " + case " $* " in + *" --mode=compile "*) # this is libtool, let us make it quiet + for arg + do # cycle over the arguments + case "$arg" in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + # X makedepend + ( + shift + cleared=no + for arg in "$@"; do + case $cleared in no) + set ""; shift + cleared=yes + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift;; + -*) + ;; + *) + set fnord "$@" "$arg"; shift;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tail +3 "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 diff --git a/config/install-sh b/config/install-sh new file mode 100644 index 0000000..e9de238 --- /dev/null +++ b/config/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/config/missing b/config/missing new file mode 100644 index 0000000..0a7fb5a --- /dev/null +++ b/config/missing @@ -0,0 +1,283 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing 0.3 - GNU automake" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar ${1+"$@"} && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar ${1+"$@"} && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/config/mkinstalldirs b/config/mkinstalldirs new file mode 100644 index 0000000..4f58503 --- /dev/null +++ b/config/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.13 1999/01/05 03:18:55 bje Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/configure b/configure new file mode 100644 index 0000000..df7ecee --- /dev/null +++ b/configure @@ -0,0 +1,7498 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by Autoconf 2.52. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +cross_compiling=no +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +ac_unique_file="lib/common/common.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: should be removed in autoconf 3.0. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo "$ac_prog" | sed 's%[\\/][^\\/][^\\/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat < if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +EOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_subdir in : $ac_subdirs_all; do test "x$ac_subdir" = x: && continue + cd $ac_subdir + # A "../" for each directory in /$ac_subdir. + ac_dots=`echo $ac_subdir | + sed 's,^\./,,;s,[^/]$,&/,;s,[^/]*/,../,g'` + + case $srcdir in + .) # No --srcdir option. We are building in place. + ac_sub_srcdir=$srcdir ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_sub_srcdir=$srcdir/$ac_subdir ;; + *) # Relative path. + ac_sub_srcdir=$ac_dots$srcdir/$ac_subdir ;; + esac + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_sub_srcdir/configure.gnu; then + echo + $SHELL $ac_sub_srcdir/configure.gnu --help=recursive + elif test -f $ac_sub_srcdir/configure; then + echo + $SHELL $ac_sub_srcdir/configure --help=recursive + elif test -f $ac_sub_srcdir/configure.ac || + test -f $ac_sub_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_subdir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\EOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +EOF + exit 0 +fi +exec 5>config.log +cat >&5 </dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +PATH = $PATH + +_ASUNAME +} >&5 + +cat >&5 <\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + ac_sep=" " ;; + *) ac_configure_args="$ac_configure_args$ac_sep$ac_arg" + ac_sep=" " ;; + esac + # Get rid of the leading space. +done + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + echo >&5 + echo "## ----------------- ##" >&5 + echo "## Cache variables. ##" >&5 + echo "## ----------------- ##" >&5 + echo >&5 + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} >&5 + sed "/^$/d" confdefs.h >conftest.log + if test -s conftest.log; then + echo >&5 + echo "## ------------ ##" >&5 + echo "## confdefs.h. ##" >&5 + echo "## ------------ ##" >&5 + echo >&5 + cat conftest.log >&5 + fi + (echo; echo) >&5 + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" >&5 + echo "$as_me: exit $exit_status" >&5 + rm -rf conftest* confdefs* core core.* *.core conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:879: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + cat "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:890: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:898: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:914: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:918: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:924: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:926: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:928: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. It doesn't matter if + # we pass some twice (in addition to the command line arguments). + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + *) ac_configure_args="$ac_configure_args $ac_var=$ac_new_val" + ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:947: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:949: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac +echo "#! $SHELL" >conftest.sh +echo "exit 0" >>conftest.sh +chmod +x conftest.sh +if { (echo "$as_me:969: PATH=\".;.\"; conftest.sh") >&5 + (PATH=".;."; conftest.sh) 2>&5 + ac_status=$? + echo "$as_me:972: \$? = $ac_status" >&5 + (exit $ac_status); }; then + ac_path_separator=';' +else + ac_path_separator=: +fi +PATH_SEPARATOR="$ac_path_separator" +rm -f conftest.sh + +ac_aux_dir= +for ac_dir in config $srcdir/config; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:998: error: cannot find install-sh or install.sh in config $srcdir/config" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in config $srcdir/config" >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +MAJOR_VERSION=1 +MINOR_VERSION=0 +RELEASE_VERSION=14 + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:1022: checking for a BSD compatible install" >&5 +echo $ECHO_N "checking for a BSD compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_IFS=$IFS; IFS=$ac_path_separator + for ac_dir in $PATH; do + IFS=$ac_save_IFS + # Account for people who put trailing slashes in PATH elements. + case $ac_dir/ in + / | ./ | .// | /cC/* \ + | /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* \ + | /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if $as_executable_p "$ac_dir/$ac_prog"; then + if test $ac_prog = install && + grep dspmsg "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:1071: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:1082: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:1106: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:1119: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:1125: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + am_backtick='`' + { echo "$as_me:1150: WARNING: ${am_backtick}missing' script is too old or missing" >&5 +echo "$as_me: WARNING: ${am_backtick}missing' script is too old or missing" >&2;} +fi + +for ac_prog in mawk gawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1158: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_AWK="$ac_prog" +echo "$as_me:1173: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:1181: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:1184: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:1191: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \${MAKE}... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:1211: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:1215: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + +rm -f .deps 2>/dev/null +mkdir .deps 2>/dev/null +if test -d .deps; then + DEPDIR=.deps +else + # MS-DOS does not allow filenames that begin with a dot. + DEPDIR=_deps +fi +rmdir .deps 2>/dev/null + +# test to see if srcdir already configured +if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:1251: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# Define the identity of the package. +PACKAGE=synergy +VERSION=$MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION + +cat >>confdefs.h <>confdefs.h <&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" +echo "$as_me:1327: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:1335: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:1338: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1351: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CXX="$ac_prog" +echo "$as_me:1366: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:1374: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:1377: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + +# Provide some information about the compiler. +echo "$as_me:1389:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:1392: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:1395: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1397: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:1400: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1402: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:1405: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line 1409 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:1425: checking for C++ compiler default output" >&5 +echo $ECHO_N "checking for C++ compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:1428: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:1431: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. +for ac_file in `ls a.exe conftest.exe 2>/dev/null; + ls a.out conftest 2>/dev/null; + ls a.* conftest.* 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + a.out ) # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool --akim. + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1454: error: C++ compiler cannot create executables" >&5 +echo "$as_me: error: C++ compiler cannot create executables" >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:1460: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1465: checking whether the C++ compiler works" >&5 +echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:1471: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1474: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:1481: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'." >&5 +echo "$as_me: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:1489: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1496: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:1498: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:1501: checking for executable suffix" >&5 +echo $ECHO_N "checking for executable suffix... $ECHO_C" >&6 +if { (eval echo "$as_me:1503: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:1506: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in `(ls conftest.exe; ls conftest; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:1522: error: cannot compute EXEEXT: cannot compile and link" >&5 +echo "$as_me: error: cannot compute EXEEXT: cannot compile and link" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:1528: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:1534: checking for object suffix" >&5 +echo $ECHO_N "checking for object suffix... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1540 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:1552: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1555: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1567: error: cannot compute OBJEXT: cannot compile" >&5 +echo "$as_me: error: cannot compute OBJEXT: cannot compile" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:1574: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:1578: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1584 "configure" +#include "confdefs.h" + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1599: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1602: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1605: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1608: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:1620: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:1626: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1632 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1644: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1647: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1650: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1653: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_prog_cxx_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:1663: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line 1690 "configure" +#include "confdefs.h" +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1703: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1706: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1709: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1712: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line 1722 "configure" +#include "confdefs.h" +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1734: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1737: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1740: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1743: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_config_commands="$ac_config_commands default-1" + +am_make=${MAKE-make} +cat > confinc << 'END' +doit: + @echo done +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:1773: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include='#' +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote='"' + _am_result=BSD + fi +fi + +echo "$as_me:1800: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +depcc="$CXX" am_compiler_list= + +echo "$as_me:1806: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + echo '#include "conftest.h"' > conftest.c + echo 'int i;' > conftest.h + echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=conftest.c object=conftest.o \ + depfile=conftest.Po tmpdepfile=conftest.TPo \ + $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && + grep conftest.h conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +echo "$as_me:1868: result: $am_cv_CXX_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6 +CXXDEPMODE="depmode=$am_cv_CXX_dependencies_compiler_type" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:1875: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" +echo "$as_me:1890: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:1898: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:1901: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:1910: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_RANLIB="ranlib" +echo "$as_me:1925: found $ac_dir/$ac_word" >&5 +break +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:1934: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:1937: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +# Extract the first word of "dot", so it can be a program name with args. +set dummy dot; ac_word=$2 +echo "$as_me:1948: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_DOT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_DOT"; then + ac_cv_prog_HAVE_DOT="$HAVE_DOT" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_HAVE_DOT="YES" +echo "$as_me:1963: found $ac_dir/$ac_word" >&5 +break +done + + test -z "$ac_cv_prog_HAVE_DOT" && ac_cv_prog_HAVE_DOT="NO" +fi +fi +HAVE_DOT=$ac_cv_prog_HAVE_DOT +if test -n "$HAVE_DOT"; then + echo "$as_me:1972: result: $HAVE_DOT" >&5 +echo "${ECHO_T}$HAVE_DOT" >&6 +else + echo "$as_me:1975: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +ac_ext=cpp + + echo "$as_me:1987: checking if g++ defines correct C++ macro" >&5 +echo $ECHO_N "checking if g++ defines correct C++ macro... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 1990 "configure" +#include "confdefs.h" + +int +main () +{ + + #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS) + #error wrong macro + #endif + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:2005: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:2008: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:2011: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2014: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_macro_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_macro_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:2023: result: $acx_cxx_macro_ok" >&5 +echo "${ECHO_T}$acx_cxx_macro_ok" >&6 + if test x"$acx_cxx_macro_ok" = xyes; then + SYNERGY_CXXFLAGS="" + else + SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS" + fi + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:2033: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:2037: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:2046: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:2050: error: $ac_config_sub $ac_cv_build_alias failed." >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed." >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:2055: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +test -z "$build_alias" && + build_alias=$ac_cv_build + +echo "$as_me:2065: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:2074: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:2079: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +test -z "$host_alias" && + host_alias=$ac_cv_host + +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + echo "$as_me:2103: checking for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS" >&5 +echo $ECHO_N "checking for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2106 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_join (); +int +main () +{ +pthread_join (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2125: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2128: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2131: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2134: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_pthread_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:2142: result: $acx_pthread_ok" >&5 +echo "${ECHO_T}$acx_pthread_ok" >&6 + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + echo "$as_me:2201: checking whether pthreads work without any flags" >&5 +echo $ECHO_N "checking whether pthreads work without any flags... $ECHO_C" >&6 + ;; + + -*) + echo "$as_me:2206: checking whether pthreads work with $flag" >&5 +echo $ECHO_N "checking whether pthreads work with $flag... $ECHO_C" >&6 + PTHREAD_CFLAGS="$flag" + ;; + + *) + echo "$as_me:2212: checking for the pthreads library -l$flag" >&5 +echo $ECHO_N "checking for the pthreads library -l$flag... $ECHO_C" >&6 + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + cat >conftest.$ac_ext <<_ACEOF +#line 2233 "configure" +#include "confdefs.h" +#include +int +main () +{ +pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2247: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2250: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2253: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2256: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_pthread_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + echo "$as_me:2268: result: $acx_pthread_ok" >&5 +echo "${ECHO_T}$acx_pthread_ok" >&6 + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + echo "$as_me:2288: checking for joinable pthread attribute" >&5 +echo $ECHO_N "checking for joinable pthread attribute... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2291 "configure" +#include "confdefs.h" +#include +int +main () +{ +int attr=PTHREAD_CREATE_JOINABLE; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2303: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2306: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2309: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2312: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=PTHREAD_CREATE_JOINABLE +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=unknown +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test x"$ok" = xunknown; then + cat >conftest.$ac_ext <<_ACEOF +#line 2323 "configure" +#include "confdefs.h" +#include +int +main () +{ +int attr=PTHREAD_CREATE_UNDETACHED; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2335: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2338: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2341: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2344: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=PTHREAD_CREATE_UNDETACHED +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=unknown +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + +cat >>confdefs.h <<\EOF +#define PTHREAD_CREATE_JOINABLE $ok +EOF + + fi + echo "$as_me:2361: result: ${ok}" >&5 +echo "${ECHO_T}${ok}" >&6 + if test x"$ok" = xunknown; then + { echo "$as_me:2364: WARNING: we do not know how to create joinable pthreads" >&5 +echo "$as_me: WARNING: we do not know how to create joinable pthreads" >&2;} + fi + + echo "$as_me:2368: checking if more special flags are required for pthreads" >&5 +echo $ECHO_N "checking if more special flags are required for pthreads... $ECHO_C" >&6 + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + alpha*-osf*) flag="-D_REENTRANT";; + *solaris*) flag="-D_REENTRANT";; + esac + echo "$as_me:2376: result: ${flag}" >&5 +echo "${ECHO_T}${flag}" >&6 + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + # Detect POSIX sigwait() + echo "$as_me:2383: checking for POSIX sigwait" >&5 +echo $ECHO_N "checking for POSIX sigwait... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2386 "configure" +#include "confdefs.h" +#include + #include +int +main () +{ +sigset_t sigset; int signal; sigwait(&sigset, &signal); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2399: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2402: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2405: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2408: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=unknown +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test x"$ok" = xunknown; then + save_CXXFLAGS2="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + cat >conftest.$ac_ext <<_ACEOF +#line 2421 "configure" +#include "confdefs.h" +#include + #include +int +main () +{ +sigset_t sigset; int signal; sigwait(&sigset, &signal); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2434: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2437: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2440: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2443: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=-D_POSIX_PTHREAD_SEMANTICS +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + CXXFLAGS="$save_CXXFLAGS2" + fi + echo "$as_me:2454: result: ${ok}" >&5 +echo "${ECHO_T}${ok}" >&6 + if test x"$ok" != xno; then + +cat >>confdefs.h <<\EOF +#define HAVE_POSIX_SIGWAIT 1 +EOF + + if test x"$ok" != xyes; then + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi + fi + + # Detect pthread signal functions + echo "$as_me:2468: checking for pthread signal functions" >&5 +echo $ECHO_N "checking for pthread signal functions... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 2471 "configure" +#include "confdefs.h" +#include + #include +int +main () +{ +pthread_kill(pthread_self(), SIGTERM); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2484: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2487: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2490: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2493: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:2502: result: ${ok}" >&5 +echo "${ECHO_T}${ok}" >&6 + if test x"$ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_PTHREAD_SIGNAL 1 +EOF + + fi + + LIBS="$save_LIBS" + CXXFLAGS="$save_CXXFLAGS" + + # More AIX lossage: must compile with cc_r + # Extract the first word of "cc_r", so it can be a program name with args. +set dummy cc_r; ac_word=$2 +echo "$as_me:2518: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_PTHREAD_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_PTHREAD_CC="cc_r" +echo "$as_me:2533: found $ac_dir/$ac_word" >&5 +break +done + + test -z "$ac_cv_prog_PTHREAD_CC" && ac_cv_prog_PTHREAD_CC="${CC}" +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + echo "$as_me:2542: result: $PTHREAD_CC" >&5 +echo "${ECHO_T}$PTHREAD_CC" >&6 +else + echo "$as_me:2545: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +else + PTHREAD_CC="$CC" +fi + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_PTHREAD 1 +EOF + + : +else + acx_pthread_ok=no + { { echo "$as_me:2563: error: You must have pthreads to compile synergy" >&5 +echo "$as_me: error: You must have pthreads to compile synergy" >&2;} + { (exit 1); exit 1; }; } +fi + + acx_nanosleep_ok=no + acx_nanosleep_list="" + + save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS" + if test x"$NANOSLEEP_LIBS" != x; then + acx_nanosleep_list=user + fi + + acx_nanosleep_list="none $acx_nanosleep_list rt" + for flag in $acx_nanosleep_list; do + case $flag in + none) + echo "$as_me:2580: checking for nanosleep" >&5 +echo $ECHO_N "checking for nanosleep... $ECHO_C" >&6 + NANOSLEEP_LIBS="" + ;; + + user) + echo "$as_me:2586: checking for nanosleep in $save_user_NANOSLEEP_LIBS" >&5 +echo $ECHO_N "checking for nanosleep in $save_user_NANOSLEEP_LIBS... $ECHO_C" >&6 + NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS" + ;; + + *) + echo "$as_me:2592: checking for nanosleep in -l$flag" >&5 +echo $ECHO_N "checking for nanosleep in -l$flag... $ECHO_C" >&6 + NANOSLEEP_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$NANOSLEEP_LIBS $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 2601 "configure" +#include "confdefs.h" +#include +int +main () +{ +struct timespec t = { 1, 1000 }; nanosleep(&t, NULL); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2613: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2616: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2619: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2622: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_nanosleep_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_nanosleep_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LIBS="$save_LIBS" + echo "$as_me:2632: result: $acx_nanosleep_ok" >&5 +echo "${ECHO_T}$acx_nanosleep_ok" >&6 + if test x"$acx_nanosleep_ok" = xyes; then + break; + fi + NANOSLEEP_LIBS="" + done + + # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: + if test x"$acx_nanosleep_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_NANOSLEEP 1 +EOF + + : + else + acx_nanosleep_ok=no + + fi + + acx_inet_aton_ok=no + acx_inet_aton_list="" + + save_user_INET_ATON_LIBS="$INET_ATON_LIBS" + if test x"$INET_ATON_LIBS" != x; then + acx_inet_aton_list=user + fi + + acx_inet_aton_list="none $acx_inet_aton_list resolv" + for flag in $acx_inet_aton_list; do + case $flag in + none) + echo "$as_me:2665: checking for inet_aton" >&5 +echo $ECHO_N "checking for inet_aton... $ECHO_C" >&6 + INET_ATON_LIBS="" + ;; + + user) + echo "$as_me:2671: checking for inet_aton in $save_user_INET_ATON_LIBS" >&5 +echo $ECHO_N "checking for inet_aton in $save_user_INET_ATON_LIBS... $ECHO_C" >&6 + INET_ATON_LIBS="$save_user_INET_ATON_LIBS" + ;; + + *) + echo "$as_me:2677: checking for inet_aton in -l$flag" >&5 +echo $ECHO_N "checking for inet_aton in -l$flag... $ECHO_C" >&6 + INET_ATON_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$INET_ATON_LIBS $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 2686 "configure" +#include "confdefs.h" +#include + #include + #include + #include +int +main () +{ +struct in_addr addr; inet_aton("foo.bar", &addr); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2701: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2704: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2707: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2710: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_inet_aton_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_inet_aton_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LIBS="$save_LIBS" + echo "$as_me:2720: result: $acx_inet_aton_ok" >&5 +echo "${ECHO_T}$acx_inet_aton_ok" >&6 + if test x"$acx_inet_aton_ok" = xyes; then + break; + fi + INET_ATON_LIBS="" + done + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +echo "$as_me:2733: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 2750 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:2755: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2761: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 2784 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2788: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2794: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +echo "$as_me:2831: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6 +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 2841 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:2846: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2852: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 2875 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2879: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2885: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:2913: error: C++ preprocessor \"$CXXCPP\" fails sanity check" >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +echo "$as_me:2924: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2930 "configure" +#include "confdefs.h" +#include +#include +#include +#include + +_ACEOF +if { (eval echo "$as_me:2938: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2944: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line 2966 "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line 2984 "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +#line 3005 "configure" +#include "confdefs.h" +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:3031: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3034: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:3036: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3039: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_header_stdc=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:3052: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_header in unistd.h sys/time.h sys/types.h wchar.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:3065: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3071 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:3075: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3081: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:3100: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3119 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:3123: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3129: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:3148: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3167 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:3171: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3177: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:3196: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3212 "configure" +#include "confdefs.h" +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:3228: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:3231: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:3234: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3237: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_header_time=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:3247: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +echo "$as_me:3257: checking for X" >&5 +echo $ECHO_N "checking for X... $ECHO_C" >&6 + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + +fi; +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else + if test "${ac_cv_have_x+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=no ac_x_libraries=no +rm -fr conftest.dir +if mkdir conftest.dir; then + cd conftest.dir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat >Imakefile <<'EOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case $ac_im_incroot in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; + esac + case $ac_im_usrlibdir in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; + esac + fi + cd .. + rm -fr conftest.dir +fi + +# Standard set of common directories for X headers. +# Check X11 before X11Rn because it is often a symlink to the current release. +ac_x_header_dirs=' +/usr/X11/include +/usr/X11R6/include +/usr/X11R5/include +/usr/X11R4/include + +/usr/include/X11 +/usr/include/X11R6 +/usr/include/X11R5 +/usr/include/X11R4 + +/usr/local/X11/include +/usr/local/X11R6/include +/usr/local/X11R5/include +/usr/local/X11R4/include + +/usr/local/include/X11 +/usr/local/include/X11R6 +/usr/local/include/X11R5 +/usr/local/include/X11R4 + +/usr/X386/include +/usr/x386/include +/usr/XFree86/include/X11 + +/usr/include +/usr/local/include +/usr/unsupported/include +/usr/athena/include +/usr/local/x11r5/include +/usr/lpp/Xamples/include + +/usr/openwin/include +/usr/openwin/share/include' + +if test "$ac_x_includes" = no; then + # Guess where to find include files, by looking for Intrinsic.h. + # First, try using that file with no special directory specified. + cat >conftest.$ac_ext <<_ACEOF +#line 3354 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:3358: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3364: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + for ac_dir in $ac_x_header_dirs; do + if test -r "$ac_dir/X11/Intrinsic.h"; then + ac_x_includes=$ac_dir + break + fi +done +fi +rm -f conftest.err conftest.$ac_ext +fi # $ac_x_includes = no + +if test "$ac_x_libraries" = no; then + # Check for the libraries. + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS=$LIBS + LIBS="-lXt $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 3397 "configure" +#include "confdefs.h" +#include +int +main () +{ +XtMalloc (0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3409: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3412: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3415: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3418: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + LIBS=$ac_save_LIBS +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +LIBS=$ac_save_LIBS +for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +do + # Don't even attempt the hair of trying to link an X program! + for ac_extension in a so sl; do + if test -r $ac_dir/libXt.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi # $ac_x_libraries = no + +if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$as_me:3456: result: $have_x" >&5 +echo "${ECHO_T}$have_x" >&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$as_me:3466: result: libraries $x_libraries, headers $x_includes" >&5 +echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 +fi + +if test "$no_x" = yes; then + # Not all programs may use this symbol, but it does not hurt to define it. + +cat >>confdefs.h <<\EOF +#define X_DISPLAY_MISSING 1 +EOF + + X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= +else + if test -n "$x_includes"; then + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + # It would also be nice to do this for all -L options, not just this one. + if test -n "$x_libraries"; then + X_LIBS="$X_LIBS -L$x_libraries" + # For Solaris; some versions of Sun CC require a space after -R and + # others require no space. Words are not sufficient . . . . + case `(uname -sr) 2>/dev/null` in + "SunOS 5"*) + echo "$as_me:3490: checking whether -R must be followed by a space" >&5 +echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6 + ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries" + cat >conftest.$ac_ext <<_ACEOF +#line 3494 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3506: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3509: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3512: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3515: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_nospace=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_R_nospace=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test $ac_R_nospace = yes; then + echo "$as_me:3525: result: no" >&5 +echo "${ECHO_T}no" >&6 + X_LIBS="$X_LIBS -R$x_libraries" + else + LIBS="$ac_xsave_LIBS -R $x_libraries" + cat >conftest.$ac_ext <<_ACEOF +#line 3531 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3543: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3546: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3549: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3552: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_space=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_R_space=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + if test $ac_R_space = yes; then + echo "$as_me:3562: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + X_LIBS="$X_LIBS -R $x_libraries" + else + echo "$as_me:3566: result: neither works" >&5 +echo "${ECHO_T}neither works" >&6 + fi + fi + LIBS=$ac_xsave_LIBS + esac + fi + + # Check for system-dependent libraries X programs must link with. + # Do this before checking for the system-independent R6 libraries + # (-lICE), since we may need -lsocket or whatever for X linking. + + if test "$ISC" = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" + else + # Martyn Johnson says this is needed for Ultrix, if the X + # libraries were built with DECnet support. And Karl Berry says + # the Alpha needs dnet_stub (dnet does not exist). + ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11" + cat >conftest.$ac_ext <<_ACEOF +#line 3586 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char XOpenDisplay (); +int +main () +{ +XOpenDisplay (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3605: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3608: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3611: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3614: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +echo "$as_me:3620: checking for dnet_ntoa in -ldnet" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3628 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3647: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3650: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3653: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3656: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_dnet_dnet_ntoa=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3667: result: $ac_cv_lib_dnet_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" +fi + + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + echo "$as_me:3674: checking for dnet_ntoa in -ldnet_stub" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet_stub $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3682 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3701: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3704: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3707: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3710: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_stub_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_dnet_stub_dnet_ntoa=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3721: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" +fi + + fi +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + LIBS="$ac_xsave_LIBS" + + # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, + # to get the SysV transport functions. + # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4) + # needs -lnsl. + # The nsl library prevents programs from opening the X display + # on Irix 5.2, according to T.E. Dickey. + # The functions gethostbyname, getservbyname, and inet_addr are + # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking. + echo "$as_me:3740: checking for gethostbyname" >&5 +echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_func_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3746 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char gethostbyname (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +f = gethostbyname; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3777: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3780: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3783: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3786: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3796: result: $ac_cv_func_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6 + + if test $ac_cv_func_gethostbyname = no; then + echo "$as_me:3800: checking for gethostbyname in -lnsl" >&5 +echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6 +if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3808 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3827: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3830: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3833: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3836: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_nsl_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_nsl_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3847: result: $ac_cv_lib_nsl_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6 +if test $ac_cv_lib_nsl_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" +fi + + if test $ac_cv_lib_nsl_gethostbyname = no; then + echo "$as_me:3854: checking for gethostbyname in -lbsd" >&5 +echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6 +if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3862 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3881: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3884: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3887: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3890: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_bsd_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_bsd_gethostbyname=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3901: result: $ac_cv_lib_bsd_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6 +if test $ac_cv_lib_bsd_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd" +fi + + fi + fi + + # lieder@skyler.mavd.honeywell.com says without -lsocket, + # socket/setsockopt and other routines are undefined under SCO ODT + # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary + # on later versions), says Simon Leinen: it contains gethostby* + # variants that don't use the nameserver (or something). -lsocket + # must be given before -lnsl if both are needed. We assume that + # if connect needs -lnsl, so does gethostbyname. + echo "$as_me:3917: checking for connect" >&5 +echo $ECHO_N "checking for connect... $ECHO_C" >&6 +if test "${ac_cv_func_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3923 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char connect (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_connect) || defined (__stub___connect) +choke me +#else +f = connect; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3954: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3957: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3960: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3963: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_connect=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_connect=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:3973: result: $ac_cv_func_connect" >&5 +echo "${ECHO_T}$ac_cv_func_connect" >&6 + + if test $ac_cv_func_connect = no; then + echo "$as_me:3977: checking for connect in -lsocket" >&5 +echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6 +if test "${ac_cv_lib_socket_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3985 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +int +main () +{ +connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4004: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4007: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4010: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4013: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_socket_connect=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_socket_connect=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4024: result: $ac_cv_lib_socket_connect" >&5 +echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6 +if test $ac_cv_lib_socket_connect = yes; then + X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" +fi + + fi + + # Guillermo Gomez says -lposix is necessary on A/UX. + echo "$as_me:4033: checking for remove" >&5 +echo $ECHO_N "checking for remove... $ECHO_C" >&6 +if test "${ac_cv_func_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4039 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char remove (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_remove) || defined (__stub___remove) +choke me +#else +f = remove; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4070: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4073: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4076: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4079: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_remove=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_remove=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:4089: result: $ac_cv_func_remove" >&5 +echo "${ECHO_T}$ac_cv_func_remove" >&6 + + if test $ac_cv_func_remove = no; then + echo "$as_me:4093: checking for remove in -lposix" >&5 +echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6 +if test "${ac_cv_lib_posix_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lposix $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4101 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +int +main () +{ +remove (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4120: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4123: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4126: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4129: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_posix_remove=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_posix_remove=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4140: result: $ac_cv_lib_posix_remove" >&5 +echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6 +if test $ac_cv_lib_posix_remove = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" +fi + + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + echo "$as_me:4149: checking for shmat" >&5 +echo $ECHO_N "checking for shmat... $ECHO_C" >&6 +if test "${ac_cv_func_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4155 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shmat (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shmat) || defined (__stub___shmat) +choke me +#else +f = shmat; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4186: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4189: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4192: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4195: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_shmat=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_shmat=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:4205: result: $ac_cv_func_shmat" >&5 +echo "${ECHO_T}$ac_cv_func_shmat" >&6 + + if test $ac_cv_func_shmat = no; then + echo "$as_me:4209: checking for shmat in -lipc" >&5 +echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6 +if test "${ac_cv_lib_ipc_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lipc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4217 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +int +main () +{ +shmat (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4236: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4239: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4242: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4245: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ipc_shmat=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_ipc_shmat=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4256: result: $ac_cv_lib_ipc_shmat" >&5 +echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6 +if test $ac_cv_lib_ipc_shmat = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" +fi + + fi + fi + + # Check for libraries that X11R6 Xt/Xaw programs need. + ac_save_LDFLAGS=$LDFLAGS + test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" + # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to + # check for ICE first), but we must link in the order -lSM -lICE or + # we get undefined symbols. So assume we have SM if we have ICE. + # These have to be linked with before -lX11, unlike the other + # libraries we check for below, so use a different variable. + # John Interrante, Karl Berry + echo "$as_me:4274: checking for IceConnectionNumber in -lICE" >&5 +echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6 +if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lICE $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4282 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char IceConnectionNumber (); +int +main () +{ +IceConnectionNumber (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4301: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4304: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4307: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4310: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ICE_IceConnectionNumber=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_ICE_IceConnectionNumber=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4321: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5 +echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6 +if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then + X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" +fi + + LDFLAGS=$ac_save_LDFLAGS + +fi + +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$X_CFLAGS $CPPFLAGS" + +for ac_header in X11/extensions/XTest.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:4337: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4343 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4347: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4353: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:4372: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4391 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4395: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4401: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:4420: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for XineramaQueryExtension in -lXinerama... $ECHO_C" >&6 +if test "${ac_cv_lib_Xinerama_XineramaQueryExtension+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXinerama $X_LIBS -lXext -lX11 $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 4438 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char XineramaQueryExtension (); +int +main () +{ +XineramaQueryExtension (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:4457: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4460: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:4463: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4466: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_Xinerama_XineramaQueryExtension=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_Xinerama_XineramaQueryExtension=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:4477: result: $ac_cv_lib_Xinerama_XineramaQueryExtension" >&5 +echo "${ECHO_T}$ac_cv_lib_Xinerama_XineramaQueryExtension" >&6 +if test $ac_cv_lib_Xinerama_XineramaQueryExtension = yes; then + +for ac_header in X11/extensions/Xinerama.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:4484: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4490 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:4494: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:4500: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:4519: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4545 "configure" +#include "confdefs.h" +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4551: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4554: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4557: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4560: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4570: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4586 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4601: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4604: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4607: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4610: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_size_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4620: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <&5 +echo $ECHO_N "checking for socklen_t... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 4635 "configure" +#include "confdefs.h" + + #include + #include + +int +main () +{ +socklen_t len; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4650: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4653: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4656: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4659: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_socklen_t_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_socklen_t_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:4668: result: $acx_socklen_t_ok" >&5 +echo "${ECHO_T}$acx_socklen_t_ok" >&6 + if test x"$acx_socklen_t_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_SOCKLEN_T 1 +EOF + + : + else + acx_socklen_t_ok=no + + fi + +echo "$as_me:4682: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4688 "configure" +#include "confdefs.h" +#include +#include + +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4702: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4705: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4708: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4711: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4721: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + +echo "$as_me:4731: checking for char" >&5 +echo $ECHO_N "checking for char... $ECHO_C" >&6 +if test "${ac_cv_type_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4737 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((char *) 0) + return 0; +if (sizeof (char)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4752: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4755: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4758: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4761: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_char=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_char=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:4771: result: $ac_cv_type_char" >&5 +echo "${ECHO_T}$ac_cv_type_char" >&6 + +echo "$as_me:4774: checking size of char" >&5 +echo $ECHO_N "checking size of char... $ECHO_C" >&6 +if test "${ac_cv_sizeof_char+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_char" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 4783 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4795: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4798: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4801: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4804: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4809 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4821: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4824: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4827: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4830: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 4846 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4858: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4861: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4864: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4867: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 4883 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (char)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4895: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4898: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4901: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4904: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_char=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:4917: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 4922 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (char))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:4938: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:4941: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:4943: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4946: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_char=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_char=0 +fi +fi +echo "$as_me:4962: result: $ac_cv_sizeof_char" >&5 +echo "${ECHO_T}$ac_cv_sizeof_char" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for short... $ECHO_C" >&6 +if test "${ac_cv_type_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 4974 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((short *) 0) + return 0; +if (sizeof (short)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:4989: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:4992: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:4995: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:4998: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_short=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_short=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5008: result: $ac_cv_type_short" >&5 +echo "${ECHO_T}$ac_cv_type_short" >&6 + +echo "$as_me:5011: checking size of short" >&5 +echo $ECHO_N "checking size of short... $ECHO_C" >&6 +if test "${ac_cv_sizeof_short+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_short" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5020 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5032: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5035: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5038: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5041: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5046 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5058: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5061: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5064: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5067: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5083 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5095: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5098: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5101: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5104: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5120 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (short)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5132: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5135: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5138: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5141: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_short=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5154: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5159 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (short))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5175: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5178: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5180: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5183: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_short=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_short=0 +fi +fi +echo "$as_me:5199: result: $ac_cv_sizeof_short" >&5 +echo "${ECHO_T}$ac_cv_sizeof_short" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for int... $ECHO_C" >&6 +if test "${ac_cv_type_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5211 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((int *) 0) + return 0; +if (sizeof (int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5226: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5229: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5232: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5235: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_int=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_int=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5245: result: $ac_cv_type_int" >&5 +echo "${ECHO_T}$ac_cv_type_int" >&6 + +echo "$as_me:5248: checking size of int" >&5 +echo $ECHO_N "checking size of int... $ECHO_C" >&6 +if test "${ac_cv_sizeof_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_int" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5257 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5269: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5272: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5275: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5278: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5283 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5295: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5298: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5301: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5304: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5320 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5332: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5335: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5338: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5341: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5357 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (int)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5369: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5372: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5375: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5378: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_int=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5391: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5396 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (int))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5412: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5415: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5417: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5420: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_int=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_int=0 +fi +fi +echo "$as_me:5436: result: $ac_cv_sizeof_int" >&5 +echo "${ECHO_T}$ac_cv_sizeof_int" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for long... $ECHO_C" >&6 +if test "${ac_cv_type_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 5448 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +if ((long *) 0) + return 0; +if (sizeof (long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5463: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5466: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5469: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5472: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_type_long=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:5482: result: $ac_cv_type_long" >&5 +echo "${ECHO_T}$ac_cv_type_long" >&6 + +echo "$as_me:5485: checking size of long" >&5 +echo $ECHO_N "checking size of long... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long" = yes; then + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +#line 5494 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) >= 0)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5506: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5509: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5512: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5515: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5520 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5532: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5535: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5538: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5541: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1`; ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +#line 5557 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) >= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5569: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5572: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5575: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5578: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_hi=`expr $ac_mid - 1`; ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done +fi +rm -f conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +#line 5594 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +int _array_ [1 - 2 * !((sizeof (long)) <= $ac_mid)] + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5606: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5609: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5612: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5615: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_lo=`expr $ac_mid + 1` +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +ac_cv_sizeof_long=$ac_lo +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:5628: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 5633 "configure" +#include "confdefs.h" +$ac_includes_default +int +main () +{ +FILE *f = fopen ("conftest.val", "w"); +if (!f) + exit (1); +fprintf (f, "%d", (sizeof (long))); +fclose (f); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5649: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5652: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5654: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5657: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long=0 +fi +fi +echo "$as_me:5673: result: $ac_cv_sizeof_long" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long" >&6 +cat >>confdefs.h <&5 +echo $ECHO_N "checking for bool support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5682 "configure" +#include "confdefs.h" + +int +main () +{ +bool t = true, f = false; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5694: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5697: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5700: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5703: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_bool_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_bool_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5712: result: $acx_cxx_bool_ok" >&5 +echo "${ECHO_T}$acx_cxx_bool_ok" >&6 + if test x"$acx_cxx_bool_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_BOOL 1 +EOF + + : + else + acx_cxx_bool_ok=no + { { echo "$as_me:5723: error: Your compiler must support bool to compile synergy" >&5 +echo "$as_me: error: Your compiler must support bool to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5728: checking for exception support" >&5 +echo $ECHO_N "checking for exception support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5731 "configure" +#include "confdefs.h" + +int +main () +{ +try{throw int(4);}catch(int){throw;}catch(...){} + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5743: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5746: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5749: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5752: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_exception_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_exception_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5761: result: $acx_cxx_exception_ok" >&5 +echo "${ECHO_T}$acx_cxx_exception_ok" >&6 + if test x"$acx_cxx_exception_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_EXCEPTIONS 1 +EOF + + : + else + acx_cxx_exception_ok=no + { { echo "$as_me:5772: error: Your compiler must support exceptions to compile synergy" >&5 +echo "$as_me: error: Your compiler must support exceptions to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5777: checking for C++ cast support" >&5 +echo $ECHO_N "checking for C++ cast support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5780 "configure" +#include "confdefs.h" + +int +main () +{ +const char* f="a";const_cast(f); + reinterpret_cast(f);static_cast(4.5); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5793: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5796: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5799: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5802: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_cast_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_cast_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5811: result: $acx_cxx_cast_ok" >&5 +echo "${ECHO_T}$acx_cxx_cast_ok" >&6 + if test x"$acx_cxx_cast_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_CASTS 1 +EOF + + : + else + acx_cxx_cast_ok=no + { { echo "$as_me:5822: error: Your compiler must support C++ casts to compile synergy" >&5 +echo "$as_me: error: Your compiler must support C++ casts to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5827: checking for mutable support" >&5 +echo $ECHO_N "checking for mutable support... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5830 "configure" +#include "confdefs.h" + +int +main () +{ +struct A{mutable int b;void f() const {b=0;}}; + A a;a.f(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:5843: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:5846: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:5849: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5852: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_mutable_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_mutable_ok=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + echo "$as_me:5861: result: $acx_cxx_mutable_ok" >&5 +echo "${ECHO_T}$acx_cxx_mutable_ok" >&6 + if test x"$acx_cxx_mutable_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_MUTABLE 1 +EOF + + : + else + acx_cxx_mutable_ok=no + { { echo "$as_me:5872: error: Your compiler must support mutable to compile synergy" >&5 +echo "$as_me: error: Your compiler must support mutable to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + + echo "$as_me:5877: checking for C++ standard library" >&5 +echo $ECHO_N "checking for C++ standard library... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 5880 "configure" +#include "confdefs.h" +#include +int +main () +{ +std::set a; a.insert(3); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:5892: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5895: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:5898: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5901: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_cxx_stdlib_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_cxx_stdlib_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:5910: result: $acx_cxx_stdlib_ok" >&5 +echo "${ECHO_T}$acx_cxx_stdlib_ok" >&6 + if test x"$acx_cxx_stdlib_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_CXX_STDLIB 1 +EOF + + : + else + acx_cxx_stdlib_ok=no + { { echo "$as_me:5921: error: Your compiler must support the C++ standard library to compile synergy" >&5 +echo "$as_me: error: Your compiler must support the C++ standard library to compile synergy" >&2;} + { (exit 1); exit 1; }; } + fi + +echo "$as_me:5926: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +#line 5935 "configure" +#include "confdefs.h" + +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + exit (1); + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + exit (1); + } + exit (0); + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:5971: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:5974: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:5976: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:5979: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_func_memcmp_working=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:5991: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 +test $ac_cv_func_memcmp_working = no && LIBOBJS="$LIBOBJS memcmp.$ac_objext" + +for ac_func in strftime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:5998: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6004 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6035: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6038: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6041: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6044: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6054: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_strftime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 6071 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime (); +int +main () +{ +strftime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6090: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6093: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6096: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6099: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_strftime=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_intl_strftime=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:6110: result: $ac_cv_lib_intl_strftime" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 +if test $ac_cv_lib_intl_strftime = yes; then + cat >>confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +LIBS="-lintl $LIBS" +fi + +fi +done + +for ac_func in gmtime_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:6126: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6132 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6163: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6166: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6169: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6172: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6182: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for working getpwuid_r... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 6195 "configure" +#include "confdefs.h" +#include +int +main () +{ +char buffer[4096]; struct passwd pwd, *pwdp; + getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6208: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6211: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6214: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6217: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_getpwuid_r_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_getpwuid_r_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:6226: result: $acx_getpwuid_r_ok" >&5 +echo "${ECHO_T}$acx_getpwuid_r_ok" >&6 + if test x"$acx_getpwuid_r_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_GETPWUID_R 1 +EOF + + : + else + acx_getpwuid_r_ok=no + + fi + +for ac_func in vsnprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:6243: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6249 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6280: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6283: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6286: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6289: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6299: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6318 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6349: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6352: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6355: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6358: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:6368: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 6387 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:6391: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:6397: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:6416: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 +if test "${ac_cv_func_select_args+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + for ac_arg234 in 'fd_set *' 'int *' 'void *'; do + for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do + for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do + cat >conftest.$ac_ext <<_ACEOF +#line 6435 "configure" +#include "confdefs.h" +$ac_includes_default +#if HAVE_SYS_SELECT_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + +int +main () +{ +extern int select ($ac_arg1, + $ac_arg234, $ac_arg234, $ac_arg234, + $ac_arg5); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:6456: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:6459: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:6462: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6465: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext + done + done +done +# Provide a safe default value. +: ${ac_cv_func_select_args='int,int *,struct timeval *'} + +fi +echo "$as_me:6480: result: $ac_cv_func_select_args" >&5 +echo "${ECHO_T}$ac_cv_func_select_args" >&6 +ac_save_IFS=$IFS; IFS=',' +set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` +IFS=$ac_save_IFS +shift + +cat >>confdefs.h <>confdefs.h <>confdefs.h <&5 +echo $ECHO_N "checking for poll... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +#line 6504 "configure" +#include "confdefs.h" +#include +int +main () +{ +struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:6516: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:6519: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:6522: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:6525: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + acx_poll_ok=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +acx_poll_ok=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + echo "$as_me:6534: result: $acx_poll_ok" >&5 +echo "${ECHO_T}$acx_poll_ok" >&6 + if test x"$acx_poll_ok" = xyes; then + +cat >>confdefs.h <<\EOF +#define HAVE_POLL 1 +EOF + + : + else + acx_poll_ok=no + + fi + +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" + +ac_config_files="$ac_config_files Makefile lib/Makefile lib/arch/Makefile lib/base/Makefile lib/common/Makefile lib/mt/Makefile lib/io/Makefile lib/http/Makefile lib/net/Makefile lib/synergy/Makefile lib/platform/Makefile lib/client/Makefile lib/server/Makefile cmd/Makefile cmd/launcher/Makefile cmd/synergyc/Makefile cmd/synergys/Makefile dist/Makefile dist/rpm/Makefile dist/rpm/synergy.spec doc/doxygen.cfg" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overriden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if cmp -s $cache_file confcache; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:6631: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +SHELL=\${CONFIG_SHELL-$SHELL} +ac_cs_invocation="\$0 \$@" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +exec 6>&1 + +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\EOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." +EOF + +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + shift + set dummy "$ac_option" "$ac_optarg" ${1+"$@"} + shift + ;; + -*);; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_need_defaults=false;; + esac + + case $1 in + # Handling of the options. +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:6807: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + shift + CONFIG_FILES="$CONFIG_FILES $1" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + shift + CONFIG_HEADERS="$CONFIG_HEADERS $1" + ac_need_defaults=false;; + + # This is an error. + -*) { { echo "$as_me:6826: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +exec 5>>config.log +cat >&5 << _ACEOF + +## ----------------------- ## +## Running config.status. ## +## ----------------------- ## + +This file was extended by $as_me 2.52, executed with + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + > $ac_cs_invocation +on `(hostname || uname -n) 2>/dev/null | sed 1q` + +_ACEOF +EOF + +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; + "lib/arch/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/arch/Makefile" ;; + "lib/base/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/base/Makefile" ;; + "lib/common/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/common/Makefile" ;; + "lib/mt/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/mt/Makefile" ;; + "lib/io/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/io/Makefile" ;; + "lib/http/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/http/Makefile" ;; + "lib/net/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/net/Makefile" ;; + "lib/synergy/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/synergy/Makefile" ;; + "lib/platform/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/platform/Makefile" ;; + "lib/client/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/client/Makefile" ;; + "lib/server/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/server/Makefile" ;; + "cmd/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/Makefile" ;; + "cmd/launcher/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/launcher/Makefile" ;; + "cmd/synergyc/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/synergyc/Makefile" ;; + "cmd/synergys/Makefile" ) CONFIG_FILES="$CONFIG_FILES cmd/synergys/Makefile" ;; + "dist/Makefile" ) CONFIG_FILES="$CONFIG_FILES dist/Makefile" ;; + "dist/rpm/Makefile" ) CONFIG_FILES="$CONFIG_FILES dist/rpm/Makefile" ;; + "dist/rpm/synergy.spec" ) CONFIG_FILES="$CONFIG_FILES dist/rpm/synergy.spec" ;; + "doc/doxygen.cfg" ) CONFIG_FILES="$CONFIG_FILES doc/doxygen.cfg" ;; + "default-1" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default-1" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + *) { { echo "$as_me:6894: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. +: ${TMPDIR=/tmp} +{ + tmp=`(umask 077 && mktemp -d -q "$TMPDIR/csXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=$TMPDIR/cs$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in $TMPDIR" >&2 + { (exit 1); exit 1; } +} + +EOF + +cat >>$CONFIG_STATUS <\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@DEFS@,$DEFS,;t t +s,@LIBS@,$LIBS,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@AMTAR@,$AMTAR,;t t +s,@install_sh@,$install_sh,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CXX@,$ac_ct_CXX,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@CXXDEPMODE@,$CXXDEPMODE,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@HAVE_DOT@,$HAVE_DOT,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@PTHREAD_CC@,$PTHREAD_CC,;t t +s,@PTHREAD_LIBS@,$PTHREAD_LIBS,;t t +s,@PTHREAD_CFLAGS@,$PTHREAD_CFLAGS,;t t +s,@NANOSLEEP_LIBS@,$NANOSLEEP_LIBS,;t t +s,@INET_ATON_LIBS@,$INET_ATON_LIBS,;t t +s,@CXXCPP@,$CXXCPP,;t t +s,@X_CFLAGS@,$X_CFLAGS,;t t +s,@X_PRE_LIBS@,$X_PRE_LIBS,;t t +s,@X_LIBS@,$X_LIBS,;t t +s,@X_EXTRA_LIBS@,$X_EXTRA_LIBS,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +CEOF + +EOF + + cat >>$CONFIG_STATUS <<\EOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +EOF +cat >>$CONFIG_STATUS <<\EOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || mkdir "$as_incr_dir" + ;; + esac +done; } + + ac_dir_suffix="/`echo $ac_dir|sed 's,^\./,,'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo "$ac_dir_suffix" | sed 's,/[^/]*,../,g'` + else + ac_dir_suffix= ac_dots= + fi + + case $srcdir in + .) ac_srcdir=. + if test -z "$ac_dots"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_dots | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_dots$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_dots$srcdir ;; + esac + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_dots$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:7141: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + configure_input="Generated automatically from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:7159: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:7172: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +EOF +cat >>$CONFIG_STATUS <<\EOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:7233: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:7244: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:7257: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +EOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\EOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\(\([^ (][^ (]*\)([^)]*)\)[ ]*\(.*\)$,${ac_dA}\2${ac_dB}\1${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +EOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\EOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +EOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if egrep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # egrep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\EOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated automatically by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated automatically by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if cmp -s $ac_file $tmp/config.h 2>/dev/null; then + { echo "$as_me:7374: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || mkdir "$as_incr_dir" + ;; + esac +done; } + + fi + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi + # Run the commands associated with the file. + case $ac_file in + config.h ) # update the timestamp +echo timestamp >"./stamp-h1" + ;; + esac +done +EOF +cat >>$CONFIG_STATUS <<\EOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + + case $ac_dest in + default-1 ) +test x"$AMDEP_TRUE" != x"" || +for mf in $CONFIG_FILES; do + case "$mf" in + Makefile) dirpart=.;; + */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;; + *) continue;; + esac + grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`echo "$file" | sed -e 's|/[^/]*$||'` + $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1 + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + esac +done +EOF + +cat >>$CONFIG_STATUS <<\EOF + +{ (exit 0); exit 0; } +EOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + exec 5>/dev/null + $SHELL $CONFIG_STATUS || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..22e44f7 --- /dev/null +++ b/configure.in @@ -0,0 +1,124 @@ +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + +dnl Process this file with autoconf to produce a configure script. + +dnl initialize +AC_INIT(lib/common/common.h) +AC_CONFIG_AUX_DIR(config) + +dnl current version +MAJOR_VERSION=1 +MINOR_VERSION=0 +RELEASE_VERSION=14 + +dnl initialize automake +AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) +AM_CONFIG_HEADER(config.h) + +dnl information on the package + +dnl checks for programs +AC_PROG_CXX +AC_PROG_RANLIB +AC_CHECK_PROG(HAVE_DOT, dot, YES, NO) + +dnl do checks using C++ +AC_LANG_CPLUSPLUS + +dnl our files end in .cpp not .C so tests should also end in .cpp +ac_ext=cpp + +dnl check compiler +ACX_CHECK_CXX + +dnl checks for libraries +ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) +ACX_CHECK_NANOSLEEP +ACX_CHECK_INET_ATON + +dnl checks for header files +AC_HEADER_STDC +AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h wchar.h]) +AC_CHECK_HEADERS([sys/socket.h sys/select.h]) +AC_CHECK_HEADERS([istream ostream sstream]) +AC_HEADER_TIME +AC_PATH_X +AC_PATH_XTRA +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$X_CFLAGS $CPPFLAGS" +AC_CHECK_HEADERS([X11/extensions/XTest.h]) +AC_CHECK_HEADERS([X11/XF86keysym.h]) + +AC_CHECK_LIB(Xinerama, XineramaQueryExtension, AC_CHECK_HEADERS([X11/extensions/Xinerama.h]) [X_LIBS="$X_LIBS -lXinerama"], , [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) +CPPFLAGS="$save_CPPFLAGS" + +dnl checks for types +AC_TYPE_SIZE_T +ACX_CHECK_SOCKLEN_T + +dnl checks for structures +AC_STRUCT_TM + +dnl checks for compiler characteristics +AC_CHECK_SIZEOF(char, 1) +AC_CHECK_SIZEOF(short, 2) +AC_CHECK_SIZEOF(int, 2) +AC_CHECK_SIZEOF(long, 4) +ACX_CHECK_CXX_BOOL(,AC_MSG_ERROR(Your compiler must support bool to compile synergy)) +ACX_CHECK_CXX_EXCEPTIONS(,AC_MSG_ERROR(Your compiler must support exceptions to compile synergy)) +ACX_CHECK_CXX_CASTS(,AC_MSG_ERROR(Your compiler must support C++ casts to compile synergy)) +ACX_CHECK_CXX_MUTABLE(,AC_MSG_ERROR(Your compiler must support mutable to compile synergy)) +ACX_CHECK_CXX_STDLIB(,AC_MSG_ERROR(Your compiler must support the C++ standard library to compile synergy)) + +dnl checks for library functions +dnl AC_TYPE_SIGNAL +AC_FUNC_MEMCMP +AC_FUNC_STRFTIME +AC_CHECK_FUNCS(gmtime_r) +ACX_CHECK_GETPWUID_R +AC_CHECK_FUNCS(vsnprintf) +AC_CHECK_FUNCS(wcrtomb mbrtowc mbsinit) +AC_FUNC_SELECT_ARGTYPES +ACX_CHECK_POLL +dnl use AC_REPLACE_FUNCS() for stuff in string.h + +dnl checks for system services + + +dnl adjust variables for X11 and pthreads +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" + +AC_OUTPUT([ +Makefile +lib/Makefile +lib/arch/Makefile +lib/base/Makefile +lib/common/Makefile +lib/mt/Makefile +lib/io/Makefile +lib/http/Makefile +lib/net/Makefile +lib/synergy/Makefile +lib/platform/Makefile +lib/client/Makefile +lib/server/Makefile +cmd/Makefile +cmd/launcher/Makefile +cmd/synergyc/Makefile +cmd/synergys/Makefile +dist/Makefile +dist/rpm/Makefile +dist/rpm/synergy.spec +doc/doxygen.cfg +]) diff --git a/dist/Makefile.am b/dist/Makefile.am new file mode 100644 index 0000000..bf906cb --- /dev/null +++ b/dist/Makefile.am @@ -0,0 +1,27 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + rpm \ + $(NULL) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/dist/Makefile.in b/dist/Makefile.in new file mode 100644 index 0000000..7062214 --- /dev/null +++ b/dist/Makefile.in @@ -0,0 +1,348 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + rpm \ + $(NULL) + + +EXTRA_DIST = \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = dist +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu dist/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/dist/rpm/Makefile.am b/dist/rpm/Makefile.am new file mode 100644 index 0000000..1f0c046 --- /dev/null +++ b/dist/rpm/Makefile.am @@ -0,0 +1,23 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/dist/rpm/Makefile.in b/dist/rpm/Makefile.in new file mode 100644 index 0000000..77f9c60 --- /dev/null +++ b/dist/rpm/Makefile.in @@ -0,0 +1,228 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = dist/rpm +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = synergy.spec +DIST_SOURCES = +DIST_COMMON = Makefile.am Makefile.in synergy.spec.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu dist/rpm/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +synergy.spec: $(top_builddir)/config.status synergy.spec.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= CONFIG_LINKS= $(SHELL) ./config.status +uninstall-info-am: +tags: TAGS +TAGS: + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in new file mode 100644 index 0000000..073f2f6 --- /dev/null +++ b/dist/rpm/synergy.spec.in @@ -0,0 +1,48 @@ +Summary: Mouse and keyboard sharing utility +Name: @PACKAGE@ +Version: @VERSION@ +Release: 1 +License: GPL +Packager: Chris Schoeneman +Group: System Environment/Daemons +Prefixes: /usr/bin +Source: @PACKAGE@-@VERSION@.tar.gz +Buildroot: /var/tmp/@PACKAGE@-@VERSION@-root + +%description +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own display. + +%prep +%setup +CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=/usr + +%build +make + +%install +make install DESTDIR=$RPM_BUILD_ROOT +strip $RPM_BUILD_ROOT/usr/bin/synergyc +strip $RPM_BUILD_ROOT/usr/bin/synergys + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-, root, root) +/usr/bin/synergyc +/usr/bin/synergys +%doc AUTHORS +%doc BUGS +%doc COPYING +%doc ChangeLog +%doc FAQ +%doc HISTORY +%doc INSTALL +%doc NEWS +%doc README +%doc TODO +%doc examples/synergy.conf diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in new file mode 100644 index 0000000..4abf52f --- /dev/null +++ b/doc/doxygen.cfg.in @@ -0,0 +1,898 @@ +# Doxyfile 1.2.13.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = @PACKAGE@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/doxygen + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, +# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, +# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl + +FILE_PATTERNS = *.cpp *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 3 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line and do not end with a semicolon. Such function macros are typically +# used for boiler-plate code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = @HAVE_DOT@ + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/examples/synergy.conf b/examples/synergy.conf new file mode 100644 index 0000000..2586dfa --- /dev/null +++ b/examples/synergy.conf @@ -0,0 +1,37 @@ +# sample synergy configuration file +# +# comments begin with the # character and continue to the end of +# line. comments may appear anywhere the syntax permits. + +section: screens + # three hosts named: moe, larry, and curly + moe: + larry: + curly: +end + +section: links + # larry is to the right of moe and curly is above moe + moe: + right = larry + up = curly + + # moe is to the left of larry and curly is above larry. + # note that curly is above both moe and larry and moe + # and larry have a symmetric connection (they're in + # opposite directions of each other). + larry: + left = moe + up = curly + + # larry is below curly. if you move up from moe and then + # down, you'll end up on larry. + curly: + down = larry +end + +section: aliases + # curly is also known as shemp + curly: + shemp +end diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..cababc3 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,37 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + common \ + arch \ + base \ + mt \ + io \ + http \ + net \ + synergy \ + platform \ + client \ + server \ + $(NULL) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..47c129c --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,358 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + common \ + arch \ + base \ + mt \ + io \ + http \ + net \ + synergy \ + platform \ + client \ + server \ + $(NULL) + + +EXTRA_DIST = \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +subdir = lib +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp new file mode 100644 index 0000000..b8322c3 --- /dev/null +++ b/lib/arch/CArch.cpp @@ -0,0 +1,611 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" +#include "CArch.h" + +#undef ARCH_CONSOLE +#undef ARCH_DAEMON +#undef ARCH_FILE +#undef ARCH_LOG +#undef ARCH_MULTITHREAD +#undef ARCH_NETWORK +#undef ARCH_SLEEP +#undef ARCH_STRING +#undef ARCH_TASKBAR +#undef ARCH_TIME + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchConsoleWindows.h" +# include "CArchDaemonWindows.h" +# include "CArchFileWindows.h" +# include "CArchLogWindows.h" +# include "CArchMultithreadWindows.h" +# include "CArchNetworkWinsock.h" +# include "CArchSleepWindows.h" +# include "CArchStringWindows.h" +# include "CArchTaskBarWindows.h" +# include "CArchTimeWindows.h" +#elif UNIX_LIKE +# include "CArchConsoleUnix.h" +# include "CArchDaemonUnix.h" +# include "CArchFileUnix.h" +# include "CArchLogUnix.h" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.h" +# endif +# include "CArchNetworkBSD.h" +# include "CArchSleepUnix.h" +# include "CArchStringUnix.h" +# include "CArchTaskBarXWindows.h" +# include "CArchTimeUnix.h" +#endif + +#if !defined(ARCH_CONSOLE) +# error unsupported platform for console +#endif + +#if !defined(ARCH_DAEMON) +# error unsupported platform for daemon +#endif + +#if !defined(ARCH_FILE) +# error unsupported platform for file +#endif + +#if !defined(ARCH_LOG) +# error unsupported platform for logging +#endif + +#if !defined(ARCH_MULTITHREAD) +# error unsupported platform for multithreading +#endif + +#if !defined(ARCH_NETWORK) +# error unsupported platform for network +#endif + +#if !defined(ARCH_SLEEP) +# error unsupported platform for sleep +#endif + +#if !defined(ARCH_STRING) +# error unsupported platform for string +#endif + +#if !defined(ARCH_TASKBAR) +# error unsupported platform for taskbar +#endif + +#if !defined(ARCH_TIME) +# error unsupported platform for time +#endif + +// +// CArch +// + +CArch* CArch::s_instance = NULL; + +CArch::CArch(ARCH_ARGS* args) +{ + // only once instance of CArch + assert(s_instance == NULL); + s_instance = this; + + // create architecture implementation objects + m_mt = new ARCH_MULTITHREAD; + m_file = new ARCH_FILE; + m_log = new ARCH_LOG; + m_net = new ARCH_NETWORK; + m_sleep = new ARCH_SLEEP; + m_string = new ARCH_STRING; + m_time = new ARCH_TIME; + m_console = new ARCH_CONSOLE; + m_daemon = new ARCH_DAEMON; + m_taskbar = new ARCH_TASKBAR(args); +} + +CArch::~CArch() +{ + // clean up + delete m_taskbar; + delete m_daemon; + delete m_console; + delete m_time; + delete m_string; + delete m_sleep; + delete m_net; + delete m_log; + delete m_file; + delete m_mt; + + // no instance + s_instance = NULL; +} + +CArch* +CArch::getInstance() +{ + assert(s_instance != NULL); + + return s_instance; +} + +void +CArch::openConsole(const char* title) +{ + m_console->openConsole(title); +} + +void +CArch::closeConsole() +{ + m_console->closeConsole(); +} + +void +CArch::writeConsole(const char* str) +{ + m_console->writeConsole(str); +} + +const char* +CArch::getNewlineForConsole() +{ + return m_console->getNewlineForConsole(); +} + +void +CArch::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) +{ + m_daemon->installDaemon(name, description, pathname, commandLine, allUsers); +} + +void +CArch::uninstallDaemon(const char* name, bool allUsers) +{ + m_daemon->uninstallDaemon(name, allUsers); +} + +int +CArch::daemonize(const char* name, DaemonFunc func) +{ + return m_daemon->daemonize(name, func); +} + +bool +CArch::canInstallDaemon(const char* name, bool allUsers) +{ + return m_daemon->canInstallDaemon(name, allUsers); +} + +bool +CArch::isDaemonInstalled(const char* name, bool allUsers) +{ + return m_daemon->isDaemonInstalled(name, allUsers); +} + +const char* +CArch::getBasename(const char* pathname) +{ + return m_file->getBasename(pathname); +} + +std::string +CArch::getUserDirectory() +{ + return m_file->getUserDirectory(); +} + +std::string +CArch::getSystemDirectory() +{ + return m_file->getSystemDirectory(); +} + +std::string +CArch::concatPath(const std::string& prefix, const std::string& suffix) +{ + return m_file->concatPath(prefix, suffix); +} + +void +CArch::openLog(const char* name) +{ + m_log->openLog(name); +} + +void +CArch::closeLog() +{ + m_log->closeLog(); +} + +void +CArch::writeLog(ELevel level, const char* msg) +{ + m_log->writeLog(level, msg); +} + +CArchCond +CArch::newCondVar() +{ + return m_mt->newCondVar(); +} + +void +CArch::closeCondVar(CArchCond cond) +{ + m_mt->closeCondVar(cond); +} + +void +CArch::signalCondVar(CArchCond cond) +{ + m_mt->signalCondVar(cond); +} + +void +CArch::broadcastCondVar(CArchCond cond) +{ + m_mt->broadcastCondVar(cond); +} + +bool +CArch::waitCondVar(CArchCond cond, CArchMutex mutex, double timeout) +{ + return m_mt->waitCondVar(cond, mutex, timeout); +} + +CArchMutex +CArch::newMutex() +{ + return m_mt->newMutex(); +} + +void +CArch::closeMutex(CArchMutex mutex) +{ + m_mt->closeMutex(mutex); +} + +void +CArch::lockMutex(CArchMutex mutex) +{ + m_mt->lockMutex(mutex); +} + +void +CArch::unlockMutex(CArchMutex mutex) +{ + m_mt->unlockMutex(mutex); +} + +CArchThread +CArch::newThread(ThreadFunc func, void* data) +{ + return m_mt->newThread(func, data); +} + +CArchThread +CArch::newCurrentThread() +{ + return m_mt->newCurrentThread(); +} + +CArchThread +CArch::copyThread(CArchThread thread) +{ + return m_mt->copyThread(thread); +} + +void +CArch::closeThread(CArchThread thread) +{ + m_mt->closeThread(thread); +} + +void +CArch::cancelThread(CArchThread thread) +{ + m_mt->cancelThread(thread); +} + +void +CArch::setPriorityOfThread(CArchThread thread, int n) +{ + m_mt->setPriorityOfThread(thread, n); +} + +void +CArch::testCancelThread() +{ + m_mt->testCancelThread(); +} + +bool +CArch::wait(CArchThread thread, double timeout) +{ + return m_mt->wait(thread, timeout); +} + +IArchMultithread::EWaitResult +CArch::waitForEvent(CArchThread thread, double timeout) +{ + return m_mt->waitForEvent(thread, timeout); +} + +bool +CArch::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return m_mt->isSameThread(thread1, thread2); +} + +bool +CArch::isExitedThread(CArchThread thread) +{ + return m_mt->isExitedThread(thread); +} + +void* +CArch::getResultOfThread(CArchThread thread) +{ + return m_mt->getResultOfThread(thread); +} + +IArchMultithread::ThreadID +CArch::getIDOfThread(CArchThread thread) +{ + return m_mt->getIDOfThread(thread); +} + +CArchSocket +CArch::newSocket(EAddressFamily family, ESocketType type) +{ + return m_net->newSocket(family, type); +} + +CArchSocket +CArch::copySocket(CArchSocket s) +{ + return m_net->copySocket(s); +} + +void +CArch::closeSocket(CArchSocket s) +{ + m_net->closeSocket(s); +} + +void +CArch::closeSocketForRead(CArchSocket s) +{ + m_net->closeSocketForRead(s); +} + +void +CArch::closeSocketForWrite(CArchSocket s) +{ + m_net->closeSocketForWrite(s); +} + +void +CArch::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + m_net->bindSocket(s, addr); +} + +void +CArch::listenOnSocket(CArchSocket s) +{ + m_net->listenOnSocket(s); +} + +CArchSocket +CArch::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + return m_net->acceptSocket(s, addr); +} + +void +CArch::connectSocket(CArchSocket s, CArchNetAddress name) +{ + m_net->connectSocket(s, name); +} + +int +CArch::pollSocket(CPollEntry pe[], int num, double timeout) +{ + return m_net->pollSocket(pe, num, timeout); +} + +size_t +CArch::readSocket(CArchSocket s, void* buf, size_t len) +{ + return m_net->readSocket(s, buf, len); +} + +size_t +CArch::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + return m_net->writeSocket(s, buf, len); +} + +void +CArch::throwErrorOnSocket(CArchSocket s) +{ + m_net->throwErrorOnSocket(s); +} + +bool +CArch::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + return m_net->setBlockingOnSocket(s, blocking); +} + +bool +CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + return m_net->setNoDelayOnSocket(s, noDelay); +} + +std::string +CArch::getHostName() +{ + return m_net->getHostName(); +} + +CArchNetAddress +CArch::newAnyAddr(EAddressFamily family) +{ + return m_net->newAnyAddr(family); +} + +CArchNetAddress +CArch::copyAddr(CArchNetAddress addr) +{ + return m_net->copyAddr(addr); +} + +CArchNetAddress +CArch::nameToAddr(const std::string& name) +{ + return m_net->nameToAddr(name); +} + +void +CArch::closeAddr(CArchNetAddress addr) +{ + m_net->closeAddr(addr); +} + +std::string +CArch::addrToName(CArchNetAddress addr) +{ + return m_net->addrToName(addr); +} + +std::string +CArch::addrToString(CArchNetAddress addr) +{ + return m_net->addrToString(addr); +} + +IArchNetwork::EAddressFamily +CArch::getAddrFamily(CArchNetAddress addr) +{ + return m_net->getAddrFamily(addr); +} + +void +CArch::setAddrPort(CArchNetAddress addr, int port) +{ + m_net->setAddrPort(addr, port); +} + +int +CArch::getAddrPort(CArchNetAddress addr) +{ + return m_net->getAddrPort(addr); +} + +bool +CArch::isAnyAddr(CArchNetAddress addr) +{ + return m_net->isAnyAddr(addr); +} + +void +CArch::sleep(double timeout) +{ + m_sleep->sleep(timeout); +} + +int +CArch::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + return m_string->vsnprintf(str, size, fmt, ap); +} + +CArchMBState +CArch::newMBState() +{ + return m_string->newMBState(); +} + +void +CArch::closeMBState(CArchMBState state) +{ + m_string->closeMBState(state); +} + +void +CArch::initMBState(CArchMBState state) +{ + m_string->initMBState(state); +} + +bool +CArch::isInitMBState(CArchMBState state) +{ + return m_string->isInitMBState(state); +} + +int +CArch::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState state) +{ + return m_string->convMBToWC(dst, src, n, state); +} + +int +CArch::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + return m_string->convWCToMB(dst, src, state); +} + +IArchString::EWideCharEncoding +CArch::getWideCharEncoding() +{ + return m_string->getWideCharEncoding(); +} + +void +CArch::addReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->addReceiver(receiver); +} + +void +CArch::removeReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->removeReceiver(receiver); +} + +void +CArch::updateReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->updateReceiver(receiver); +} + +double +CArch::time() +{ + return m_time->time(); +} diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h new file mode 100644 index 0000000..61315b6 --- /dev/null +++ b/lib/arch/CArch.h @@ -0,0 +1,192 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCH_H +#define CARCH_H + +#include "IArchConsole.h" +#include "IArchDaemon.h" +#include "IArchFile.h" +#include "IArchLog.h" +#include "IArchMultithread.h" +#include "IArchNetwork.h" +#include "IArchSleep.h" +#include "IArchString.h" +#include "IArchTaskBar.h" +#include "IArchTime.h" + +/*! +\def ARCH +This macro evaluates to the singleton CArch object. +*/ +#define ARCH (CArch::getInstance()) + +#define ARCH_ARGS void + +//! Delegating mplementation of architecture dependent interfaces +/*! +This class is a centralized interface to all architecture dependent +interface implementations (except miscellaneous functions). It +instantiates an implementation of each interface and delegates calls +to each method to those implementations. Clients should use the +\c ARCH macro to access this object. Clients must also instantiate +exactly one of these objects before attempting to call any method, +typically at the beginning of \c main(). +*/ +class CArch : public IArchConsole, + public IArchDaemon, + public IArchFile, + public IArchLog, + public IArchMultithread, + public IArchNetwork, + public IArchSleep, + public IArchString, + public IArchTaskBar, + public IArchTime { +public: + CArch(ARCH_ARGS* args = NULL); + ~CArch(); + + // + // accessors + // + + //! Return the singleton instance + /*! + The client must have instantiated exactly once CArch object before + calling this function. + */ + static CArch* getInstance(); + + // IArchConsole overrides + virtual void openConsole(const char*); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); + + // IArchLog overrides + virtual void openLog(const char*); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + + // IArchSleep overrides + virtual void sleep(double timeout); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); + + // IArchTaskBar + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); + + // IArchTime overrides + virtual double time(); + +private: + static CArch* s_instance; + + IArchConsole* m_console; + IArchDaemon* m_daemon; + IArchFile* m_file; + IArchLog* m_log; + IArchMultithread* m_mt; + IArchNetwork* m_net; + IArchSleep* m_sleep; + IArchString* m_string; + IArchTaskBar* m_taskbar; + IArchTime* m_time; +}; + +#endif diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp new file mode 100644 index 0000000..79c4ae2 --- /dev/null +++ b/lib/arch/CArchConsoleUnix.cpp @@ -0,0 +1,54 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleUnix.h" +#include + +// +// CArchConsoleUnix +// + +CArchConsoleUnix::CArchConsoleUnix() +{ + // do nothing +} + +CArchConsoleUnix::~CArchConsoleUnix() +{ + // do nothing +} + +void +CArchConsoleUnix::openConsole(const char*) +{ + // do nothing +} + +void +CArchConsoleUnix::closeConsole() +{ + // do nothing +} + +void +CArchConsoleUnix::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleUnix::getNewlineForConsole() +{ + return "\n"; +} diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h new file mode 100644 index 0000000..5e560fe --- /dev/null +++ b/lib/arch/CArchConsoleUnix.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEUNIX_H +#define CARCHCONSOLEUNIX_H + +#include "IArchConsole.h" + +#define ARCH_CONSOLE CArchConsoleUnix + +//! Unix implementation of IArchConsole +class CArchConsoleUnix : public IArchConsole { +public: + CArchConsoleUnix(); + virtual ~CArchConsoleUnix(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); +}; + +#endif diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp new file mode 100644 index 0000000..a89a42f --- /dev/null +++ b/lib/arch/CArchConsoleWindows.cpp @@ -0,0 +1,109 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleWindows.h" +#include "IArchMultithread.h" +#include "CArch.h" +#include + +// +// CArchConsoleWindows +// + +CArchThread CArchConsoleWindows::s_thread = 0; + +CArchConsoleWindows::CArchConsoleWindows() : + m_output(NULL) +{ + s_thread = ARCH->newCurrentThread(); + + m_mutex = ARCH->newMutex(); +} + +CArchConsoleWindows::~CArchConsoleWindows() +{ + ARCH->closeMutex(m_mutex); + ARCH->closeThread(s_thread); +} + +void +CArchConsoleWindows::openConsole(const char* title) +{ + ARCH->lockMutex(m_mutex); + if (m_output == NULL) { + if (AllocConsole()) { + // get console output handle + m_output = GetStdHandle(STD_ERROR_HANDLE); + + // set console title + if (title != NULL) { + SetConsoleTitle(title); + } + + // prep console. windows 95 and its ilk have braindead + // consoles that can't even resize independently of the + // buffer size. use a 25 line buffer for those systems. + OSVERSIONINFO osInfo; + COORD size = { 80, 1000 }; + osInfo.dwOSVersionInfoSize = sizeof(osInfo); + if (GetVersionEx(&osInfo) && + osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + size.Y = 25; + SetConsoleScreenBufferSize(m_output, size); + SetConsoleTextAttribute(m_output, + FOREGROUND_RED | + FOREGROUND_GREEN | + FOREGROUND_BLUE); + + // catch console signals + SetConsoleCtrlHandler(&CArchConsoleWindows::signalHandler, TRUE); + + // reopen stderr to point at console + freopen("con", "w", stderr); + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::closeConsole() +{ + ARCH->lockMutex(m_mutex); + if (m_output != NULL) { + if (FreeConsole()) { + m_output = NULL; + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleWindows::getNewlineForConsole() +{ + return "\r\n"; +} + +BOOL WINAPI +CArchConsoleWindows::signalHandler(DWORD) +{ + // terminate thread and skip remaining handlers + ARCH->cancelThread(s_thread); + return TRUE; +} diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h new file mode 100644 index 0000000..bf9f0a3 --- /dev/null +++ b/lib/arch/CArchConsoleWindows.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEWINDOWS_H +#define CARCHCONSOLEWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchConsole.h" +#include "IArchMultithread.h" +#include + +#define ARCH_CONSOLE CArchConsoleWindows + +//! Win32 implementation of IArchConsole +class CArchConsoleWindows : public IArchConsole { +public: + CArchConsoleWindows(); + virtual ~CArchConsoleWindows(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + +private: + static BOOL WINAPI signalHandler(DWORD); + +private: + static CArchThread s_thread; + + CArchMutex m_mutex; + HANDLE m_output; +}; + +#endif diff --git a/lib/arch/CArchDaemonNone.cpp b/lib/arch/CArchDaemonNone.cpp new file mode 100644 index 0000000..35a5823 --- /dev/null +++ b/lib/arch/CArchDaemonNone.cpp @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonNone.h" + +// +// CArchDaemonNone +// + +CArchDaemonNone::CArchDaemonNone() +{ + // do nothing +} + +CArchDaemonNone::~CArchDaemonNone() +{ + // do nothing +} + +void +CArchDaemonNone::installDaemon(const char*, + const char*, + const char*, + const char*, + bool) +{ + // do nothing +} + +void +CArchDaemonNone::uninstallDaemon(const char*, bool) +{ + // do nothing +} + +int +CArchDaemonNone::daemonize(const char* name, DaemonFunc func) +{ + // simply forward the call to func. obviously, this doesn't + // do any daemonizing. + return func(1, &name); +} + +bool +CArchDaemonNone::canInstallDaemon(const char*, bool) +{ + return false; +} + +bool +CArchDaemonNone::isDaemonInstalled(const char* name, bool allUsers) +{ + return false; +} diff --git a/lib/arch/CArchDaemonNone.h b/lib/arch/CArchDaemonNone.h new file mode 100644 index 0000000..5b54060 --- /dev/null +++ b/lib/arch/CArchDaemonNone.h @@ -0,0 +1,46 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONNONE_H +#define CARCHDAEMONNONE_H + +#include "IArchDaemon.h" + +#define ARCH_DAEMON CArchDaemonNone + +//! Dummy implementation of IArchDaemon +/*! +This class implements IArchDaemon for a platform that does not have +daemons. The install and uninstall functions do nothing, the query +functions return false, and \c daemonize() simply calls the passed +function and returns its result. +*/ +class CArchDaemonNone : public IArchDaemon { +public: + CArchDaemonNone(); + virtual ~CArchDaemonNone(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); +}; + +#endif diff --git a/lib/arch/CArchDaemonUnix.cpp b/lib/arch/CArchDaemonUnix.cpp new file mode 100644 index 0000000..748780d --- /dev/null +++ b/lib/arch/CArchDaemonUnix.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonUnix.h" +#include "XArchUnix.h" +#include +#include +#include +#include +#include + +// we derive from CArchDaemonNone +#include "CArchDaemonNone.cpp" + +// +// CArchDaemonUnix +// + +CArchDaemonUnix::CArchDaemonUnix() +{ + // do nothing +} + +CArchDaemonUnix::~CArchDaemonUnix() +{ + // do nothing +} + +int +CArchDaemonUnix::daemonize(const char* name, DaemonFunc func) +{ + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + throw XArchDaemonFailed(new XArchEvalUnix(errno)); + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + + // chdir to root so we don't keep mounted filesystems points busy + chdir("/"); + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + dup(1); + + // invoke function + return func(1, &name); +} diff --git a/lib/arch/CArchDaemonUnix.h b/lib/arch/CArchDaemonUnix.h new file mode 100644 index 0000000..923004e --- /dev/null +++ b/lib/arch/CArchDaemonUnix.h @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONUNIX_H +#define CARCHDAEMONUNIX_H + +#include "CArchDaemonNone.h" + +#undef ARCH_DAEMON +#define ARCH_DAEMON CArchDaemonUnix + +//! Unix implementation of IArchDaemon +class CArchDaemonUnix : public CArchDaemonNone { +public: + CArchDaemonUnix(); + virtual ~CArchDaemonUnix(); + + // IArchDaemon overrides + virtual int daemonize(const char* name, DaemonFunc func); +}; + +#endif diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp new file mode 100644 index 0000000..54dc5b4 --- /dev/null +++ b/lib/arch/CArchDaemonWindows.cpp @@ -0,0 +1,801 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonWindows.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "XArchWindows.h" +#include "stdvector.h" + +// +// CArchDaemonWindows +// + +CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; + +CArchDaemonWindows::CArchDaemonWindows() : + m_daemonThread(NULL) +{ + // do nothing +} + +CArchDaemonWindows::~CArchDaemonWindows() +{ + // do nothing +} + +int +CArchDaemonWindows::runDaemon(RunFunc runFunc) +{ + assert(s_daemon != NULL); + + return s_daemon->doRunDaemon(runFunc); +} + +void +CArchDaemonWindows::daemonRunning(bool running) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + s_daemon->doDaemonRunning(running); + } +} + +void +CArchDaemonWindows::daemonFailed(int result) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + throw XArchDaemonRunFailed(result); + } +} + +void +CArchDaemonWindows::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // open registry + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + // can't open key + throw XArchDaemonInstallFailed(new XArchEvalWindows); + } + + // construct entry + std::string value; + value += "\""; + value += pathname; + value += "\" "; + value += commandLine; + + // install entry + CArchMiscWindows::setValue(key, name, value); + + // clean up + CArchMiscWindows::closeKey(key); + } + + // windows NT family services + else { + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + // can't open service manager + throw XArchDaemonInstallFailed(new XArchEvalWindows); + } + + // create the service + SC_HANDLE service = CreateService(mgr, + name, + name, + 0, + SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + pathname, + NULL, + NULL, + NULL, + NULL, + NULL); + if (service == NULL) { + // can't create service + // FIXME -- handle ERROR_SERVICE_EXISTS + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + + // done with service and manager + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + // open the registry key for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + if (key == NULL) { + // can't open key + DWORD err = GetLastError(); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + + // set the description + CArchMiscWindows::setValue(key, _T("Description"), description); + + // set command line + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key == NULL) { + // can't open key + DWORD err = GetLastError(); + CArchMiscWindows::closeKey(key); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine); + + // done with registry + CArchMiscWindows::closeKey(key); + } +} + +void +CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // open registry + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + // can't open key. daemon is probably not installed. + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows); + } + + // remove entry + CArchMiscWindows::deleteValue(key, name); + + // clean up + CArchMiscWindows::closeKey(key); + } + + // windows NT family services + else { + // remove parameters for this service. ignore failures. + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + if (key != NULL) { + CArchMiscWindows::deleteKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + // can't open service manager + throw XArchDaemonUninstallFailed(new XArchEvalWindows); + } + + // open the service. oddly, you must open a service to delete it. + SC_HANDLE service = OpenService(mgr, name, DELETE); + if (service == NULL) { + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + if (err != ERROR_SERVICE_DOES_NOT_EXIST) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } + + // delete the service + const bool okay = (DeleteService(service) == 0); + const DWORD err = GetLastError(); + + // clean up + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + // handle failure. ignore error if service isn't installed anymore. + if (!okay && isDaemonInstalled(name, allUsers)) { + if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } + } +} + +int +CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) +{ + assert(name != NULL); + assert(func != NULL); + + // windows 95 family services + if (CArchMiscWindows::isWindows95Family()) { + typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD); + + // mark this process as a service so it's not killed when the + // user logs off. + HINSTANCE kernel = LoadLibrary("kernel32.dll"); + if (kernel == NULL) { + throw XArchDaemonFailed(new XArchEvalWindows); + } + RegisterServiceProcessT RegisterServiceProcess = + reinterpret_cast( + GetProcAddress(kernel, + "RegisterServiceProcess")); + if (RegisterServiceProcess == NULL) { + // missing RegisterServiceProcess function + DWORD err = GetLastError(); + FreeLibrary(kernel); + throw XArchDaemonFailed(new XArchEvalWindows(err)); + } + if (RegisterServiceProcess(NULL, 1) == 0) { + // RegisterServiceProcess failed + DWORD err = GetLastError(); + FreeLibrary(kernel); + throw XArchDaemonFailed(new XArchEvalWindows(err)); + } + FreeLibrary(kernel); + + // now simply call the daemon function + return func(1, &name); + } + + // windows NT family services + else { + // save daemon function + m_daemonFunc = func; + + // construct the service entry + SERVICE_TABLE_ENTRY entry[2]; + entry[0].lpServiceName = const_cast(name); + entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry; + entry[1].lpServiceName = NULL; + entry[1].lpServiceProc = NULL; + + // hook us up to the service control manager. this won't return + // (if successful) until the processes have terminated. + s_daemon = this; + if (StartServiceCtrlDispatcher(entry) == 0) { + // StartServiceCtrlDispatcher failed + s_daemon = NULL; + throw XArchDaemonFailed(new XArchEvalWindows); + } + + s_daemon = NULL; + return m_daemonResult; + } +} + +bool +CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // check if we can open the registry key + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + CArchMiscWindows::closeKey(key); + return (key != NULL); + } + + // windows NT family services + else { + // check if we can open service manager for write + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + return false; + } + CloseServiceHandle(mgr); + + // check if we can open the registry key for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); + + return (key != NULL); + } +} + +bool +CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // check if we can open the registry key + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + return false; + } + + // check for entry + const bool installed = !CArchMiscWindows::readValueString(key, + name).empty(); + + // clean up + CArchMiscWindows::closeKey(key); + + return installed; + } + + // windows NT family services + else { + // check parameters for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key != NULL) { + const bool installed = !CArchMiscWindows::readValueString(key, + _T("CommandLine")).empty(); + CArchMiscWindows::closeKey(key); + if (!installed) { + return false; + } + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); + if (mgr == NULL) { + return false; + } + + // open the service + SC_HANDLE service = OpenService(mgr, name, GENERIC_READ); + + // clean up + if (service != NULL) { + CloseServiceHandle(service); + } + CloseServiceHandle(mgr); + + return (service != NULL); + } +} + +HKEY +CArchDaemonWindows::openNTServicesKey() +{ + static const char* s_keyNames[] = { + _T("SYSTEM"), + _T("CurrentControlSet"), + _T("Services"), + NULL + }; + + return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +HKEY +CArchDaemonWindows::open95ServicesKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("RunServices"), + NULL + }; + + return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +HKEY +CArchDaemonWindows::openUserStartupKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("Run"), + NULL + }; + + return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); +} + +int +CArchDaemonWindows::doRunDaemon(RunFunc run) +{ + // should only be called from DaemonFunc + assert(m_serviceMutex != NULL); + assert(run != NULL); + + ARCH->lockMutex(m_serviceMutex); + try { + int result; + m_serviceHandlerWaiting = false; + m_serviceRunning = false; + for (;;) { + // mark server as running + setStatus(SERVICE_RUNNING); + + // run callback in another thread + m_serviceRunning = true; + m_daemonThread = ARCH->newThread( + &CArchDaemonWindows::runDaemonThreadEntry, run); + ARCH->wait(m_daemonThread, -1.0); + result = reinterpret_cast( + ARCH->getResultOfThread(m_daemonThread)); + m_serviceRunning = false; + + // notify handler that the server stopped. if handler + // isn't waiting then we stopped unexpectedly and we + // quit. + if (m_serviceHandlerWaiting) { + m_serviceHandlerWaiting = false; + ARCH->broadcastCondVar(m_serviceCondVar); + } + else { + break; + } + + // wait until we're told what to do next + while (m_serviceState != SERVICE_RUNNING && + m_serviceState != SERVICE_STOPPED) { + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + + // exit loop if we've been told to stop + if (m_serviceState == SERVICE_STOPPED) { + break; + } + + // done with callback thread + ARCH->closeThread(m_daemonThread); + m_daemonThread = NULL; + } + + // prevent daemonHandler from changing state + m_serviceState = SERVICE_STOPPED; + + // tell service control that the service is stopped. + // FIXME -- hopefully this will ensure that our handler won't + // be called again but i can't find documentation that + // verifies that. if it does it'll crash on the mutex that + // we're about to destroy. + setStatus(m_serviceState); + + // clean up + if (m_daemonThread != NULL) { + ARCH->closeThread(m_daemonThread); + m_daemonThread = NULL; + } + ARCH->unlockMutex(m_serviceMutex); + + return result; + } + catch (...) { + // FIXME -- report error + + // prevent serviceHandler from changing state + m_serviceState = SERVICE_STOPPED; + + // set status + setStatusError(0); + + // wake up serviceHandler if it's waiting then wait for it + if (m_serviceHandlerWaiting) { + m_serviceHandlerWaiting = false; + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + // serviceHandler has exited by now + } + + ARCH->unlockMutex(m_serviceMutex); + throw; + } +} + +void +CArchDaemonWindows::doDaemonRunning(bool running) +{ + if (running) { + ARCH->unlockMutex(m_serviceMutex); + } + else { + ARCH->lockMutex(m_serviceMutex); + } +} + +void* +CArchDaemonWindows::runDaemonThread(RunFunc run) +{ + return reinterpret_cast(run()); +} + +void* +CArchDaemonWindows::runDaemonThreadEntry(void* vrun) +{ + assert(s_daemon != NULL); + + return s_daemon->runDaemonThread(reinterpret_cast(vrun)); +} + +void +CArchDaemonWindows::setStatus(DWORD state) +{ + setStatus(state, 0, 0); +} + +void +CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) +{ + assert(s_daemon != NULL); + + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = step; + status.dwWaitHint = waitHint; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::setStatusError(DWORD error) +{ + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwServiceSpecificExitCode = error; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) +{ + typedef std::vector ArgList; + typedef std::vector Arguments; + const char** argv = const_cast(argvIn); + + // create synchronization objects + m_serviceMutex = ARCH->newMutex(); + m_serviceCondVar = ARCH->newCondVar(); + m_serviceState = SERVICE_RUNNING; + + // register our service handler functiom + m_statusHandle = RegisterServiceCtrlHandler(argv[0], + &CArchDaemonWindows::serviceHandlerEntry); + if (m_statusHandle == NULL) { + // cannot start as service + m_daemonResult = -1; + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); + return; + } + + // tell service control manager that we're starting + setStatus(SERVICE_START_PENDING, 0, 10000); + + // if no arguments supplied then try getting them from the registry. + // the first argument doesn't count because it's the service name. + Arguments args; + ArgList myArgv; + if (argc <= 1) { + // read command line + std::string commandLine; + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, argvIn[0]); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key != NULL) { + commandLine = CArchMiscWindows::readValueString(key, + _T("CommandLine")); + } + + // if the command line isn't empty then parse and use it + if (!commandLine.empty()) { + // parse, honoring double quoted substrings + std::string::size_type i = commandLine.find_first_not_of(" \t"); + while (i != std::string::npos && i != commandLine.size()) { + // find end of string + std::string::size_type e; + if (commandLine[i] == '\"') { + // quoted. find closing quote. + ++i; + e = commandLine.find("\"", i); + + // whitespace must follow closing quote + if (e == std::string::npos || + (e + 1 != commandLine.size() && + commandLine[e + 1] != ' ' && + commandLine[e + 1] != '\t')) { + args.clear(); + break; + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + else { + // unquoted. find next whitespace. + e = commandLine.find_first_of(" \t", i); + if (e == std::string::npos) { + e = commandLine.size(); + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + + // next argument + i = commandLine.find_first_not_of(" \t", i); + } + + // service name goes first + myArgv.push_back(argv[0]); + + // get pointers + for (size_t i = 0; i < args.size(); ++i) { + myArgv.push_back(args[i].c_str()); + } + + // adjust argc/argv + argc = myArgv.size(); + argv = &myArgv[0]; + } + } + + try { + // invoke daemon function + m_daemonResult = m_daemonFunc(static_cast(argc), argv); + } + catch (XArchDaemonRunFailed& e) { + setStatusError(e.m_result); + m_daemonResult = -1; + } + catch (...) { + setStatusError(1); + m_daemonResult = -1; + } + + // clean up + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); +} + +void WINAPI +CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv) +{ + s_daemon->serviceMain(argc, argv); +} + +void +CArchDaemonWindows::serviceHandler(DWORD ctrl) +{ + assert(m_serviceMutex != NULL); + assert(m_serviceCondVar != NULL); + + ARCH->lockMutex(m_serviceMutex); + + // ignore request if service is already stopped + if (m_serviceState == SERVICE_STOPPED) { + setStatus(m_serviceState); + ARCH->unlockMutex(m_serviceMutex); + return; + } + + switch (ctrl) { + case SERVICE_CONTROL_PAUSE: + // update state + m_serviceState = SERVICE_PAUSE_PENDING; + setStatus(m_serviceState, 0, 5000); + + // stop run callback if running and wait for it to finish + if (m_serviceRunning) { + m_serviceHandlerWaiting = true; + ARCH->cancelThread(m_daemonThread); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + + // update state if service hasn't stopped while we were waiting + if (m_serviceState != SERVICE_STOPPED) { + m_serviceState = SERVICE_PAUSED; + } + ARCH->broadcastCondVar(m_serviceCondVar); + break; + + case SERVICE_CONTROL_CONTINUE: + // required status update + setStatus(m_serviceState); + + // update state but let main loop send RUNNING notification + m_serviceState = SERVICE_RUNNING; + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->unlockMutex(m_serviceMutex); + return; + + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + // update state + m_serviceState = SERVICE_STOP_PENDING; + setStatus(m_serviceState, 0, 5000); + + // stop run callback if running and wait for it to finish + if (m_serviceRunning) { + m_serviceHandlerWaiting = true; + ARCH->cancelThread(m_daemonThread); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + + // update state + m_serviceState = SERVICE_STOPPED; + ARCH->broadcastCondVar(m_serviceCondVar); + break; + + default: + // unknown service command + // fall through + + case SERVICE_CONTROL_INTERROGATE: + break; + } + + // send update + setStatus(m_serviceState); + + ARCH->unlockMutex(m_serviceMutex); +} + +void WINAPI +CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl) +{ + s_daemon->serviceHandler(ctrl); +} diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h new file mode 100644 index 0000000..57fe74f --- /dev/null +++ b/lib/arch/CArchDaemonWindows.h @@ -0,0 +1,123 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONWINDOWS_H +#define CARCHDAEMONWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchDaemon.h" +#include "IArchMultithread.h" +#include "stdstring.h" +#include +#include + +#define ARCH_DAEMON CArchDaemonWindows + +//! Win32 implementation of IArchDaemon +class CArchDaemonWindows : public IArchDaemon { +public: + typedef int (*RunFunc)(void); + + CArchDaemonWindows(); + virtual ~CArchDaemonWindows(); + + //! Run the daemon + /*! + When the client calls \c daemonize(), the \c DaemonFunc should call this + function after initialization and argument parsing to perform the + daemon processing. The \c runFunc should perform the daemon's + main loop, calling \c daemonRunning(true) when it enters the main loop + (i.e. after initialization) and \c daemonRunning(false) when it leaves + the main loop. The \c runFunc is called in a new thread and when the + daemon must exit the main loop due to some external control the + thread is cancelled on behalf of the client. This function returns + what \c runFunc returns. \c runFunc should call \c daemonFailed() if + the daemon fails. + */ + static int runDaemon(RunFunc runFunc); + + //! Indicate daemon is in main loop + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate when it has entered (\c running is \c true) or exited + (\c running is \c false) the main loop. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate failure. \c result is returned by \c daemonize(). + */ + static void daemonFailed(int result); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + +private: + static HKEY openNTServicesKey(); + static HKEY open95ServicesKey(); + static HKEY openUserStartupKey(); + + int doRunDaemon(RunFunc runFunc); + void doDaemonRunning(bool running); + + static void setStatus(DWORD state); + static void setStatus(DWORD state, DWORD step, DWORD waitHint); + static void setStatusError(DWORD error); + + void* runDaemonThread(RunFunc); + static void* runDaemonThreadEntry(void*); + + void serviceMain(DWORD, LPTSTR*); + static void WINAPI serviceMainEntry(DWORD, LPTSTR*); + + void serviceHandler(DWORD ctrl); + static void WINAPI serviceHandlerEntry(DWORD ctrl); + +private: + class XArchDaemonRunFailed { + public: + XArchDaemonRunFailed(int result) : m_result(result) { } + + public: + int m_result; + }; + +private: + static CArchDaemonWindows* s_daemon; + + CArchMutex m_serviceMutex; + CArchCond m_serviceCondVar; + DWORD m_serviceState; + bool m_serviceHandlerWaiting; + bool m_serviceRunning; + + CArchThread m_daemonThread; + DaemonFunc m_daemonFunc; + int m_daemonResult; + + SERVICE_STATUS_HANDLE m_statusHandle; +}; + +#endif diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp new file mode 100644 index 0000000..a708cc8 --- /dev/null +++ b/lib/arch/CArchFileUnix.cpp @@ -0,0 +1,94 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileUnix.h" +#include +#include +#include +#include + +// +// CArchFileUnix +// + +CArchFileUnix::CArchFileUnix() +{ + // do nothing +} + +CArchFileUnix::~CArchFileUnix() +{ + // do nothing +} + +const char* +CArchFileUnix::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + return basename + 1; + } + else { + return pathname; + } +} + +std::string +CArchFileUnix::getUserDirectory() +{ +#if HAVE_GETPWUID_R + struct passwd pwent; + struct passwd* pwentp; +#if defined(_SC_GETPW_R_SIZE_MAX) + long size = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + long size = BUFSIZ; +#endif + char* buffer = new char[size]; + getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); + delete[] buffer; +#else + struct passwd* pwentp = getpwuid(getuid()); +#endif + if (pwentp != NULL && pwentp->pw_dir != NULL) { + return pwentp->pw_dir; + } + else { + return std::string(); + } +} + +std::string +CArchFileUnix::getSystemDirectory() +{ + return "/etc"; +} + +std::string +CArchFileUnix::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || path[path.size() - 1] != '/') { + path += '/'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileUnix.h b/lib/arch/CArchFileUnix.h new file mode 100644 index 0000000..41d00e9 --- /dev/null +++ b/lib/arch/CArchFileUnix.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEUNIX_H +#define CARCHFILEUNIX_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileUnix + +//! Unix implementation of IArchFile +class CArchFileUnix : public IArchFile { +public: + CArchFileUnix(); + virtual ~CArchFileUnix(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchFileWindows.cpp b/lib/arch/CArchFileWindows.cpp new file mode 100644 index 0000000..7d9fd51 --- /dev/null +++ b/lib/arch/CArchFileWindows.cpp @@ -0,0 +1,132 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileWindows.h" +#include +#include +#include +#include + +// +// CArchFileWindows +// + +CArchFileWindows::CArchFileWindows() +{ + // do nothing +} + +CArchFileWindows::~CArchFileWindows() +{ + // do nothing +} + +const char* +CArchFileWindows::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + // check for last slash + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + ++basename; + } + else { + basename = pathname; + } + + // check for last backslash + const char* basename2 = strrchr(pathname, '\\'); + if (basename2 != NULL && basename2 > basename) { + basename = basename2 + 1; + } + + return basename; +} + +std::string +CArchFileWindows::getUserDirectory() +{ + // try %HOMEPATH% + TCHAR dir[MAX_PATH]; + DWORD size = sizeof(dir) / sizeof(TCHAR); + DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size); + if (result != 0 && result <= size) { + // sanity check -- if dir doesn't appear to start with a + // drive letter and isn't a UNC name then don't use it + // FIXME -- allow UNC names + if (dir[0] != '\0' && (dir[1] == ':' || + ((dir[0] == '\\' || dir[0] == '/') && + (dir[1] == '\\' || dir[1] == '/')))) { + return dir; + } + } + + // get the location of the personal files. that's as close to + // a home directory as we're likely to find. + ITEMIDLIST* idl; + if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) { + TCHAR* path = NULL; + if (SHGetPathFromIDList(idl, dir)) { + DWORD attr = GetFileAttributes(dir); + if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + path = dir; + } + + IMalloc* shalloc; + if (SUCCEEDED(SHGetMalloc(&shalloc))) { + shalloc->Free(idl); + shalloc->Release(); + } + + if (path != NULL) { + return path; + } + } + + // use root of C drive as a default + return "C:"; +} + +std::string +CArchFileWindows::getSystemDirectory() +{ + // get windows directory + char dir[MAX_PATH]; + if (GetWindowsDirectory(dir, sizeof(dir)) != 0) { + return dir; + } + else { + // can't get it. use C:\ as a default. + return "C:"; + } +} + +std::string +CArchFileWindows::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || + (path[path.size() - 1] != '\\' && + path[path.size() - 1] != '/')) { + path += '\\'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileWindows.h b/lib/arch/CArchFileWindows.h new file mode 100644 index 0000000..617b1c4 --- /dev/null +++ b/lib/arch/CArchFileWindows.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEWINDOWS_H +#define CARCHFILEWINDOWS_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileWindows + +//! Win32 implementation of IArchFile +class CArchFileWindows : public IArchFile { +public: + CArchFileWindows(); + virtual ~CArchFileWindows(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchImpl.cpp b/lib/arch/CArchImpl.cpp new file mode 100644 index 0000000..9e57d0a --- /dev/null +++ b/lib/arch/CArchImpl.cpp @@ -0,0 +1,45 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchMiscWindows.cpp" +# include "CArchConsoleWindows.cpp" +# include "CArchDaemonWindows.cpp" +# include "CArchFileWindows.cpp" +# include "CArchLogWindows.cpp" +# include "CArchMultithreadWindows.cpp" +# include "CArchNetworkWinsock.cpp" +# include "CArchSleepWindows.cpp" +# include "CArchStringWindows.cpp" +# include "CArchTaskBarWindows.cpp" +# include "CArchTimeWindows.cpp" +# include "XArchWindows.cpp" +#elif UNIX_LIKE +# include "CArchConsoleUnix.cpp" +# include "CArchDaemonUnix.cpp" +# include "CArchFileUnix.cpp" +# include "CArchLogUnix.cpp" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.cpp" +# endif +# include "CArchNetworkBSD.cpp" +# include "CArchSleepUnix.cpp" +# include "CArchStringUnix.cpp" +# include "CArchTaskBarXWindows.cpp" +# include "CArchTimeUnix.cpp" +# include "XArchUnix.cpp" +#endif diff --git a/lib/arch/CArchLogUnix.cpp b/lib/arch/CArchLogUnix.cpp new file mode 100644 index 0000000..c75696e --- /dev/null +++ b/lib/arch/CArchLogUnix.cpp @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogUnix.h" +#include + +// +// CArchLogUnix +// + +CArchLogUnix::CArchLogUnix() +{ + // do nothing +} + +CArchLogUnix::~CArchLogUnix() +{ + // do nothing +} + +void +CArchLogUnix::openLog(const char* name) +{ + openlog(name, 0, LOG_DAEMON); +} + +void +CArchLogUnix::closeLog() +{ + closelog(); +} + +void +CArchLogUnix::writeLog(ELevel level, const char* msg) +{ + // convert level + int priority; + switch (level) { + case kERROR: + priority = LOG_ERR; + break; + + case kWARNING: + priority = LOG_WARNING; + break; + + case kNOTE: + priority = LOG_NOTICE; + break; + + case kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} diff --git a/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h new file mode 100644 index 0000000..b371784 --- /dev/null +++ b/lib/arch/CArchLogUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGUNIX_H +#define CARCHLOGUNIX_H + +#include "IArchLog.h" + +#define ARCH_LOG CArchLogUnix + +//! Unix implementation of IArchLog +class CArchLogUnix : public IArchLog { +public: + CArchLogUnix(); + virtual ~CArchLogUnix(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); +}; + +#endif diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp new file mode 100644 index 0000000..6d21d6b --- /dev/null +++ b/lib/arch/CArchLogWindows.cpp @@ -0,0 +1,84 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogWindows.h" +#include "CArchMiscWindows.h" +#include + +// +// CArchLogWindows +// + +CArchLogWindows::CArchLogWindows() : m_eventLog(NULL) +{ + // do nothing +} + +CArchLogWindows::~CArchLogWindows() +{ + // do nothing +} + +void +CArchLogWindows::openLog(const char* name) +{ + if (m_eventLog == NULL && !CArchMiscWindows::isWindows95Family()) { + m_eventLog = RegisterEventSource(NULL, name); + } +} + +void +CArchLogWindows::closeLog() +{ + if (m_eventLog != NULL) { + DeregisterEventSource(m_eventLog); + m_eventLog = NULL; + } +} + +void +CArchLogWindows::writeLog(ELevel level, const char* msg) +{ + if (m_eventLog != NULL) { + // convert priority + WORD type; + switch (level) { + case kERROR: + type = EVENTLOG_ERROR_TYPE; + break; + + case kWARNING: + type = EVENTLOG_WARNING_TYPE; + break; + + default: + type = EVENTLOG_INFORMATION_TYPE; + break; + } + + // log it + // FIXME -- win32 wants to use a message table to look up event + // strings. log messages aren't organized that way so we'll + // just dump our string into the raw data section of the event + // so users can at least see the message. note that we use our + // level as the event category. + ReportEvent(m_eventLog, type, static_cast(level), + 0, // event ID + NULL, + 0, + strlen(msg) + 1, // raw data size + NULL, + const_cast(msg));// raw data + } +} diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h new file mode 100644 index 0000000..d238576 --- /dev/null +++ b/lib/arch/CArchLogWindows.h @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGWINDOWS_H +#define CARCHLOGWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchLog.h" +#include + +#define ARCH_LOG CArchLogWindows + +//! Win32 implementation of IArchLog +class CArchLogWindows : public IArchLog { +public: + CArchLogWindows(); + virtual ~CArchLogWindows(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + +private: + HANDLE m_eventLog; +}; + +#endif diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp new file mode 100644 index 0000000..f76b42f --- /dev/null +++ b/lib/arch/CArchMiscWindows.cpp @@ -0,0 +1,183 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchMiscWindows.h" +#include "CArchDaemonWindows.h" + +// +// CArchMiscWindows +// + +bool +CArchMiscWindows::isWindows95Family() +{ + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume windows 95 family + return true; + } + return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); +} + +int +CArchMiscWindows::runDaemon(RunFunc runFunc) +{ + return CArchDaemonWindows::runDaemon(runFunc); +} + +void +CArchMiscWindows::daemonRunning(bool running) +{ + CArchDaemonWindows::daemonRunning(running); +} + +void +CArchMiscWindows::daemonFailed(int result) +{ + CArchDaemonWindows::daemonFailed(result); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) +{ + // ignore if parent is NULL + if (key == NULL) { + return NULL; + } + + // open next key + HKEY newKey; + LONG result = RegOpenKeyEx(key, keyName, 0, + KEY_WRITE | KEY_QUERY_VALUE, &newKey); + if (result != ERROR_SUCCESS) { + DWORD disp; + result = RegCreateKeyEx(key, keyName, 0, TEXT(""), + 0, KEY_WRITE | KEY_QUERY_VALUE, + NULL, &newKey, &disp); + } + if (result != ERROR_SUCCESS) { + RegCloseKey(key); + return NULL; + } + + // switch to new key + RegCloseKey(key); + return newKey; +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +{ + for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { + // open next key + key = openKey(key, keyNames[i]); + } + return key; +} + +void +CArchMiscWindows::closeKey(HKEY key) +{ + assert(key != NULL); + RegCloseKey(key); +} + +void +CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteKey(key, name); +} + +void +CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteValue(key, name); +} + +bool +CArchMiscWindows::hasValue(HKEY key, const TCHAR* name) +{ + DWORD type; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); + return (result == ERROR_SUCCESS && + (type == REG_DWORD || type == REG_SZ)); +} + +void +CArchMiscWindows::setValue(HKEY key, + const TCHAR* name, const std::string& value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_SZ, + reinterpret_cast(value.c_str()), + value.size() + 1); +} + +void +CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_DWORD, + reinterpret_cast(&value), + sizeof(DWORD)); +} + +std::string +CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) +{ + // get the size of the string + DWORD type; + DWORD size = 0; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + return std::string(); + } + + // allocate space + char* buffer = new char[size]; + + // read it + result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(buffer), &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + delete[] buffer; + return std::string(); + } + + // clean up and return value + std::string value(buffer); + delete[] buffer; + return value; +} + +DWORD +CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) +{ + DWORD type; + DWORD value; + DWORD size = sizeof(value); + LONG result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(&value), &size); + if (result != ERROR_SUCCESS || type != REG_DWORD) { + return 0; + } + return value; +} diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h new file mode 100644 index 0000000..dc99336 --- /dev/null +++ b/lib/arch/CArchMiscWindows.h @@ -0,0 +1,85 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMISCWINDOWS_H +#define CARCHMISCWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "common.h" +#include "stdstring.h" +#include + +//! Miscellaneous win32 functions. +class CArchMiscWindows { +public: + typedef int (*RunFunc)(void); + + //! Test if windows 95, et al. + /*! + Returns true iff the platform is win95/98/me. + */ + static bool isWindows95Family(); + + //! Run the daemon + /*! + Delegates to CArchDaemonWindows. + */ + static int runDaemon(RunFunc runFunc); + + //! Indicate daemon is in main loop + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonFailed(int result); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* child); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* const* keyPath); + + //! Close a key + static void closeKey(HKEY); + + //! Delete a key (which should have no subkeys) + static void deleteKey(HKEY parent, const TCHAR* name); + + //! Delete a value + static void deleteValue(HKEY parent, const TCHAR* name); + + //! Test if a value exists + static bool hasValue(HKEY key, const TCHAR* name); + + //! Set a string value in the registry + static void setValue(HKEY key, const TCHAR* name, + const std::string& value); + + //! Set a DWORD value in the registry + static void setValue(HKEY key, const TCHAR* name, DWORD value); + + //! Read a string value from the registry + static std::string readValueString(HKEY, const TCHAR* name); + + //! Read a DWORD value from the registry + static DWORD readValueInt(HKEY, const TCHAR* name); +}; + +#endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp new file mode 100644 index 0000000..bef806b --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -0,0 +1,771 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchMultithreadPosix.h" +#include "CArch.h" +#include "XArch.h" +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include + +#define SIGWAKEUP SIGUSR1 + +#if !HAVE_PTHREAD_SIGNAL + // boy, is this platform broken. forget about pthread signal + // handling and let signals through to every process. synergy + // will not terminate cleanly when it gets SIGTERM or SIGINT. +# define pthread_sigmask sigprocmask +# define pthread_kill(tid_, sig_) kill(0, (sig_)) +# define sigwait(set_, sig_) +# undef HAVE_POSIX_SIGWAIT +# define HAVE_POSIX_SIGWAIT 1 +#endif + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + +public: + int m_refCount; + IArchMultithread::ThreadID m_id; + pthread_t m_thread; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + bool m_cancel; + bool m_cancelling; + bool m_exited; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_id(0), + m_func(NULL), + m_userData(NULL), + m_cancel(false), + m_cancelling(false), + m_exited(false), + m_result(NULL) +{ + // do nothing +} + + +// +// CArchMultithreadPosix +// + +CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; + +CArchMultithreadPosix::CArchMultithreadPosix() : + m_newThreadCalled(false), + m_nextID(0) +{ + assert(s_instance == NULL); + + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + m_mainThread = new CArchThreadImpl; + m_mainThread->m_thread = pthread_self(); + insert(m_mainThread); + + // install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt + // system calls. we use that when cancelling a thread to force it + // to wake up immediately if it's blocked in a system call. we + // won't need this until another thread is created but it's fine + // to install it now. + struct sigaction act; + sigemptyset(&act.sa_mask); +# if defined(SA_INTERRUPT) + act.sa_flags = SA_INTERRUPT; +# else + act.sa_flags = 0; +# endif + act.sa_handler = &threadCancel; + sigaction(SIGWAKEUP, &act, NULL); + + // set desired signal dispositions. let SIGWAKEUP through but + // ignore SIGPIPE (we'll handle EPIPE). + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGWAKEUP); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); +} + +CArchMultithreadPosix::~CArchMultithreadPosix() +{ + assert(s_instance != NULL); + + closeMutex(m_threadMutex); + s_instance = NULL; +} + +CArchCond +CArchMultithreadPosix::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + int status = pthread_cond_init(&cond->m_cond, NULL); + assert(status == 0); + return cond; +} + +void +CArchMultithreadPosix::closeCondVar(CArchCond cond) +{ + int status = pthread_cond_destroy(&cond->m_cond); + assert(status == 0); + delete cond; +} + +void +CArchMultithreadPosix::signalCondVar(CArchCond cond) +{ + int status = pthread_cond_signal(&cond->m_cond); + assert(status == 0); +} + +void +CArchMultithreadPosix::broadcastCondVar(CArchCond cond) +{ + int status = pthread_cond_broadcast(&cond->m_cond); + assert(status == 0); +} + +bool +CArchMultithreadPosix::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // get final time + struct timeval now; + gettimeofday(&now, NULL); + struct timespec finalTime; + finalTime.tv_sec = now.tv_sec; + finalTime.tv_nsec = now.tv_usec * 1000; + if (timeout >= 0.0) { + const long timeout_sec = (long)timeout; + const long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec)); + finalTime.tv_sec += timeout_sec; + finalTime.tv_nsec += timeout_nsec; + if (finalTime.tv_nsec >= 1000000000) { + finalTime.tv_nsec -= 1000000000; + finalTime.tv_sec += 1; + } + } + + // repeat until we reach the final time + int status; + for (;;) { + // get current time + gettimeofday(&now, NULL); + struct timespec endTime; + endTime.tv_sec = now.tv_sec; + endTime.tv_nsec = now.tv_usec * 1000; + + // done if past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + status = ETIMEDOUT; + break; + } + } + + // compute the next timeout + endTime.tv_nsec += 50000000; + if (endTime.tv_nsec >= 1000000000) { + endTime.tv_nsec -= 1000000000; + endTime.tv_sec += 1; + } + + // don't wait past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + endTime = finalTime; + } + } + + // see if we should cancel this thread + testCancelThread(); + + // wait + status = pthread_cond_timedwait(&cond->m_cond, + &mutex->m_mutex, &endTime); + + // check for cancel again + testCancelThread(); + + // check wait status + if (status != ETIMEDOUT && status != EINTR) { + break; + } + } + + switch (status) { + case 0: + // success + return true; + + case ETIMEDOUT: + return false; + + default: + assert(0 && "condition variable wait error"); + return false; + } +} + +CArchMutex +CArchMultithreadPosix::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + int status = pthread_mutex_init(&mutex->m_mutex, NULL); + assert(status == 0); +/* + status = pthread_mutexattr_settype(&mutex->m_mutex, + PTHREAD_MUTEX_RECURSIVE); + assert(status == 0); +*/ + return mutex; +} + +void +CArchMultithreadPosix::closeMutex(CArchMutex mutex) +{ + int status = pthread_mutex_destroy(&mutex->m_mutex); + assert(status == 0); + delete mutex; +} + +void +CArchMultithreadPosix::lockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_lock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EDEADLK: + assert(0 && "lock already owned"); + break; + + case EAGAIN: + assert(0 && "too many recursive locks"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +void +CArchMultithreadPosix::unlockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_unlock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EPERM: + assert(0 && "thread doesn't own a lock"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +CArchThread +CArchMultithreadPosix::newThread(ThreadFunc func, void* data) +{ + assert(func != NULL); + + // initialize signal handler. we do this here instead of the + // constructor so we can avoid daemonizing (using fork()) + // when there are multiple threads. clients can safely + // use condition variables and mutexes before creating a + // new thread and they can safely use the only thread + // they have access to, the main thread, so they really + // can't tell the difference. + if (!m_newThreadCalled) { + m_newThreadCalled = true; +#if HAVE_PTHREAD_SIGNAL + startSignalHandler(); +#endif + } + + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // mask some signals in all threads except the main thread + sigset_t sigset, oldsigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); + + // create the thread. pthread_create() on RedHat 7.2 smp fails + // if passed a NULL attr so use a default attr. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&thread->m_thread, &attr, + &CArchMultithreadPosix::threadFunc, thread); + pthread_attr_destroy(&attr); + } + + // restore signals + pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); + + // check if thread was started + if (status != 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadPosix::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(pthread_self()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadPosix::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // detach from thread (unless it's the main thread) + if (thread->m_func != NULL) { + pthread_detach(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRef(thread->m_thread) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadPosix::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadPosix::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel and wakeup flags if thread can be cancelled + bool wakeup = false; + lockMutex(m_threadMutex); + if (!thread->m_exited && !thread->m_cancelling) { + thread->m_cancel = true; + wakeup = true; + } + unlockMutex(m_threadMutex); + + // force thread to exit system calls if wakeup is true + if (wakeup) { + pthread_kill(thread->m_thread, SIGWAKEUP); + } +} + +void +CArchMultithreadPosix::setPriorityOfThread(CArchThread thread, int /*n*/) +{ + assert(thread != NULL); + + // FIXME +} + +void +CArchMultithreadPosix::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(pthread_self()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadPosix::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(pthread_self()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + try { + // do first test regardless of timeout + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // wait and repeat test if there's a timeout + if (timeout != 0.0) { + const double start = ARCH->time(); + do { + // wait a little + ARCH->sleep(0.05); + + // repeat test + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // repeat wait and test until timed out + } while (timeout < 0.0 || (ARCH->time() - start) <= timeout); + } + + closeThread(target); + return false; + } + catch (...) { + closeThread(target); + throw; + } +} + +IArchMultithread::EWaitResult +CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/) +{ + // not implemented + return kTimeout; +} + +bool +CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadPosix::isExitedThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + bool exited = thread->m_exited; + unlockMutex(m_threadMutex); + return exited; +} + +void* +CArchMultithreadPosix::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadPosix::getIDOfThread(CArchThread thread) +{ + return thread->m_id; +} + +void +CArchMultithreadPosix::startSignalHandler() +{ + // set signal mask. the main thread blocks these signals and + // the signal handler thread will listen for them. + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); + + // fire up the INT and TERM signal handler thread. we could + // instead arrange to catch and handle these signals but + // we'd be unable to cancel the main thread since no pthread + // calls are allowed in a signal handler. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&m_signalThread, &attr, + &CArchMultithreadPosix::threadSignalHandler, + m_mainThread); + pthread_attr_destroy(&attr); + } + if (status != 0) { + // can't create thread to wait for signal so don't block + // the signals. + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + } +} + +CArchThreadImpl* +CArchMultithreadPosix::find(pthread_t thread) +{ + CArchThreadImpl* impl = findNoRef(thread); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadPosix::findNoRef(pthread_t thread) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_thread == thread) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadPosix::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRef(thread->m_thread) == NULL); + + // set thread id. note that we don't worry about m_nextID + // wrapping back to 0 and duplicating thread ID's since the + // likelihood of synergy running that long is vanishingly + // small. + thread->m_id = ++m_nextID; + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadPosix::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadPosix::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRef(thread->m_thread) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadPosix::testCancelThreadImpl(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = false; + if (thread->m_cancel && !thread->m_cancelling) { + thread->m_cancelling = true; + thread->m_cancel = false; + cancel = true; + } + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +void* +CArchMultithreadPosix::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // setup pthreads + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return NULL; +} + +void +CArchMultithreadPosix::doThreadFunc(CArchThread thread) +{ + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + lockMutex(m_threadMutex); + thread->m_exited = true; + unlockMutex(m_threadMutex); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + thread->m_exited = true; + unlockMutex(m_threadMutex); + + // done with thread + closeThread(thread); +} + +void +CArchMultithreadPosix::threadCancel(int) +{ + // do nothing +} + +void* +CArchMultithreadPosix::threadSignalHandler(void* vrep) +{ + CArchThreadImpl* mainThread = reinterpret_cast(vrep); + + // detach + pthread_detach(pthread_self()); + + // add signal to mask + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + + // also wait on SIGABRT. on linux (others?) this thread (process) + // will persist after all the other threads evaporate due to an + // assert unless we wait on SIGABRT. that means our resources (like + // the socket we're listening on) are not released and never will be + // until the lingering thread is killed. i don't know why sigwait() + // should protect the thread from being killed. note that sigwait() + // doesn't actually return if we receive SIGABRT and, for some + // reason, we don't have to block SIGABRT. + sigaddset(&sigset, SIGABRT); + + // we exit the loop via thread cancellation in sigwait() + for (;;) { + // wait +#if HAVE_POSIX_SIGWAIT + int signal; + sigwait(&sigset, &signal); +#else + sigwait(&sigset); +#endif + + // if we get here then the signal was raised. cancel the main + // thread so it can shut down cleanly. + ARCH->cancelThread(mainThread); + } +} diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h new file mode 100644 index 0000000..d62e2d4 --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.h @@ -0,0 +1,95 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADPOSIX_H +#define CARCHMULTITHREADPOSIX_H + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadPosix + +class CArchCondImpl { +public: + pthread_cond_t m_cond; +}; + +class CArchMutexImpl { +public: + pthread_mutex_t m_mutex; +}; + +//! Posix implementation of IArchMultithread +class CArchMultithreadPosix : public IArchMultithread { +public: + CArchMultithreadPosix(); + virtual ~CArchMultithreadPosix(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + void startSignalHandler(); + + CArchThreadImpl* find(pthread_t thread); + CArchThreadImpl* findNoRef(pthread_t thread); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static void* threadFunc(void* vrep); + static void threadCancel(int); + static void* threadSignalHandler(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadPosix* s_instance; + + bool m_newThreadCalled; + + CArchMutex m_threadMutex; + CArchThread m_mainThread; + CThreadList m_threadList; + ThreadID m_nextID; + + pthread_t m_signalThread; +}; + +#endif diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp new file mode 100644 index 0000000..2a457a5 --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -0,0 +1,783 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if !defined(_MT) +# error multithreading compile option is required +#endif + +#include "CArchMultithreadWindows.h" +#include "CArch.h" +#include "XArch.h" +#include + +// +// note -- implementation of condition variable taken from: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// titled "Strategies for Implementing POSIX Condition Variables +// on Win32." it also provides an implementation that doesn't +// suffer from the incorrectness problem described in our +// corresponding header but it is slower, still unfair, and +// can cause busy waiting. +// + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + ~CArchThreadImpl(); + +public: + int m_refCount; + HANDLE m_thread; + DWORD m_id; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + HANDLE m_cancel; + bool m_cancelling; + HANDLE m_exit; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_thread(NULL), + m_id(0), + m_func(NULL), + m_userData(NULL), + m_cancelling(false), + m_result(NULL) +{ + m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); + m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +CArchThreadImpl::~CArchThreadImpl() +{ + CloseHandle(m_exit); + CloseHandle(m_cancel); +} + + +// +// CArchMultithreadWindows +// + +CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; + +CArchMultithreadWindows::CArchMultithreadWindows() +{ + assert(s_instance == NULL); + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + CArchThreadImpl* mainThread = new CArchThreadImpl; + mainThread->m_thread = NULL; + mainThread->m_id = GetCurrentThreadId(); + insert(mainThread); +} + +CArchMultithreadWindows::~CArchMultithreadWindows() +{ + s_instance = NULL; + + // clean up thread list + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + delete *index; + } + + // done with mutex + delete m_threadMutex; +} + +HANDLE +CArchMultithreadWindows::getCancelEventForCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + return thread->m_cancel; +} + +CArchMultithreadWindows* +CArchMultithreadWindows::getInstance() +{ + return s_instance; +} + +CArchCond +CArchMultithreadWindows::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + cond->m_events[CArchCondImpl::kSignal] = CreateEvent(NULL, + FALSE, FALSE, NULL); + cond->m_events[CArchCondImpl::kBroadcast] = CreateEvent(NULL, + TRUE, FALSE, NULL); + cond->m_waitCountMutex = newMutex(); + cond->m_waitCount = 0; + return cond; +} + +void +CArchMultithreadWindows::closeCondVar(CArchCond cond) +{ + CloseHandle(cond->m_events[CArchCondImpl::kSignal]); + CloseHandle(cond->m_events[CArchCondImpl::kBroadcast]); + closeMutex(cond->m_waitCountMutex); + delete cond; +} + +void +CArchMultithreadWindows::signalCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake one thread if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kSignal]); + } +} + +void +CArchMultithreadWindows::broadcastCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake all threads if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } +} + +bool +CArchMultithreadWindows::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // prepare to wait + const DWORD winTimeout = (timeout < 0.0) ? INFINITE : + static_cast(1000.0 * timeout); + + // make a list of the condition variable events and the cancel event + // for the current thread. + HANDLE handles[3]; + handles[0] = cond->m_events[CArchCondImpl::kSignal]; + handles[1] = cond->m_events[CArchCondImpl::kBroadcast]; + handles[2] = getCancelEventForCurrentThread(); + + // update waiter count + lockMutex(cond->m_waitCountMutex); + ++cond->m_waitCount; + unlockMutex(cond->m_waitCountMutex); + + // release mutex. this should be atomic with the wait so that it's + // impossible for another thread to signal us between the unlock and + // the wait, which would lead to a lost signal on broadcasts. + // however, we're using a manual reset event for broadcasts which + // stays set until we reset it, so we don't lose the broadcast. + unlockMutex(mutex); + + // wait for a signal or broadcast + DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 2 && + WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 2; + } + + // update the waiter count and check if we're the last waiter + lockMutex(cond->m_waitCountMutex); + --cond->m_waitCount; + const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0); + unlockMutex(cond->m_waitCountMutex); + + // reset the broadcast event if we're the last waiter + if (last) { + ResetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } + + // reacquire the mutex + lockMutex(mutex); + + // cancel thread if necessary + if (result == WAIT_OBJECT_0 + 2) { + ARCH->testCancelThread(); + } + + // return success or failure + return (result == WAIT_OBJECT_0 + 0 || + result == WAIT_OBJECT_0 + 1); +} + +CArchMutex +CArchMultithreadWindows::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + InitializeCriticalSection(&mutex->m_mutex); + return mutex; +} + +void +CArchMultithreadWindows::closeMutex(CArchMutex mutex) +{ + DeleteCriticalSection(&mutex->m_mutex); + delete mutex; +} + +void +CArchMultithreadWindows::lockMutex(CArchMutex mutex) +{ + EnterCriticalSection(&mutex->m_mutex); +} + +void +CArchMultithreadWindows::unlockMutex(CArchMutex mutex) +{ + LeaveCriticalSection(&mutex->m_mutex); +} + +CArchThread +CArchMultithreadWindows::newThread(ThreadFunc func, void* data) +{ + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // create thread + unsigned int id; + thread->m_thread = reinterpret_cast(_beginthreadex(NULL, 0, + threadFunc, (void*)thread, 0, &id)); + thread->m_id = static_cast(id); + + // check if thread was started + if (thread->m_thread == 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadWindows::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadWindows::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // close the handle (main thread has a NULL handle) + if (thread->m_thread != NULL) { + CloseHandle(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRefOrCreate(thread->m_id) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadWindows::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadWindows::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel flag + SetEvent(thread->m_cancel); +} + +void +CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) +{ + struct CPriorityInfo { + public: + DWORD m_class; + int m_level; + }; + static const CPriorityInfo s_pClass[] = { + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_IDLE }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_IDLE }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL} + }; +#if defined(_DEBUG) + // don't use really high priorities when debugging + static const size_t s_pMax = 13; +#else + static const size_t s_pMax = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1; +#endif + static const size_t s_pBase = 8; // index of normal priority + + assert(thread != NULL); + + size_t index; + if (n > 0 && s_pBase < n) { + // lowest priority + index = 0; + } + else { + index = s_pBase - n; + if (index > s_pMax) { + // highest priority + index = s_pMax; + } + } + SetPriorityClass(GetCurrentProcess(), s_pClass[index].m_class); + SetThreadPriority(thread->m_thread, s_pClass[index].m_level); +} + +void +CArchMultithreadWindows::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadWindows::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + HANDLE handles[2]; + handles[0] = target->m_exit; + handles[1] = self->m_cancel; + DWORD result = WaitForMultipleObjects(2, handles, FALSE, t); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 1 && + WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 1; + } + + // release target + closeThread(target); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // target thread terminated + return true; + + case WAIT_OBJECT_0 + 1: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} + +IArchMultithread::EWaitResult +CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout) +{ + // find current thread. ref the target so it can't go away while + // we're watching it. + lockMutex(m_threadMutex); + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + assert(self != NULL); + if (target != NULL) { + refThread(target); + } + unlockMutex(m_threadMutex); + + // see if we've been cancelled before checking if any events + // are pending. + DWORD result = WaitForSingleObject(self->m_cancel, 0); + if (result == WAIT_OBJECT_0) { + if (target != NULL) { + closeThread(target); + } + testCancelThreadImpl(self); + } + + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return kEvent; + } + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + DWORD n = (target == NULL || target == self) ? 1 : 2; + HANDLE handles[2]; + handles[0] = self->m_cancel; + handles[1] = (n == 2) ? target->m_exit : NULL; + result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 0 && + WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 0; + } + + // release target + if (target != NULL) { + closeThread(target); + } + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + case WAIT_OBJECT_0 + 1: + // target thread terminated + if (n == 2) { + return kExit; + } + // fall through + + case WAIT_OBJECT_0 + 2: + // message is available + return kEvent; + + default: + // timeout or error + return kTimeout; + } +} + +/* +bool +CArchMultithreadWindows::waitForEvent(double timeout) +{ + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return true; + } + + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(self != NULL); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for a message + HANDLE handles[1]; + handles[0] = self->m_cancel; + DWORD result = MsgWaitForMultipleObjects(1, handles, FALSE, t, QS_ALLINPUT); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 1: + // message is available + return true; + + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} +*/ + +bool +CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadWindows::isExitedThread(CArchThread thread) +{ + // poll exit event + return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0); +} + +void* +CArchMultithreadWindows::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadWindows::getIDOfThread(CArchThread thread) +{ + return static_cast(thread->m_id); +} + +CArchThreadImpl* +CArchMultithreadWindows::find(DWORD id) +{ + CArchThreadImpl* impl = findNoRef(id); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadWindows::findNoRef(DWORD id) +{ + CArchThreadImpl* impl = findNoRefOrCreate(id); + if (impl == NULL) { + // create thread for calling thread which isn't in our list and + // add it to the list. this won't normally happen but it can if + // the system calls us under a new thread, like it does when we + // run as a service. + impl = new CArchThreadImpl; + impl->m_thread = NULL; + impl->m_id = GetCurrentThreadId(); + insert(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadWindows::findNoRefOrCreate(DWORD id) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_id == id) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadWindows::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRefOrCreate(thread->m_id) == NULL); + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadWindows::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadWindows::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRefOrCreate(thread->m_id) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadWindows::testCancelThreadImpl(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // poll cancel event. return if not set. + const DWORD result = WaitForSingleObject(thread->m_cancel, 0); + if (result != WAIT_OBJECT_0) { + return; + } + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = !thread->m_cancelling; + thread->m_cancelling = true; + ResetEvent(thread->m_cancel); + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +unsigned int __stdcall +CArchMultithreadWindows::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return 0; +} + +void +CArchMultithreadWindows::doThreadFunc(CArchThread thread) +{ + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + SetEvent(thread->m_exit); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + unlockMutex(m_threadMutex); + SetEvent(thread->m_exit); + + // done with thread + closeThread(thread); +} diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h new file mode 100644 index 0000000..2a8d546 --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.h @@ -0,0 +1,101 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADWINDOWS_H +#define CARCHMULTITHREADWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadWindows + +class CArchCondImpl { +public: + enum { kSignal = 0, kBroadcast }; + + HANDLE m_events[2]; + mutable int m_waitCount; + CArchMutex m_waitCountMutex; +}; + +class CArchMutexImpl { +public: + CRITICAL_SECTION m_mutex; +}; + +//! Win32 implementation of IArchMultithread +class CArchMultithreadWindows : public IArchMultithread { +public: + CArchMultithreadWindows(); + virtual ~CArchMultithreadWindows(); + + // + // accessors + // + + HANDLE getCancelEventForCurrentThread(); + + static CArchMultithreadWindows* getInstance(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + CArchThreadImpl* find(DWORD id); + CArchThreadImpl* findNoRef(DWORD id); + CArchThreadImpl* findNoRefOrCreate(DWORD id); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static unsigned int __stdcall threadFunc(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadWindows* s_instance; + + CArchMutex m_threadMutex; + + CThreadList m_threadList; +}; + +#endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp new file mode 100644 index 0000000..9b6eec6 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.cpp @@ -0,0 +1,853 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchNetworkBSD.h" +#include "CArch.h" +#include "XArchUnix.h" +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif +#include +#include +#if !defined(TCP_NODELAY) +# include +#endif +#include +#include +#include + +#if HAVE_POLL +# include +#else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +#endif + +static const int s_family[] = { + PF_UNSPEC, + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +// +// CArchNetworkBSD +// + +CArchNetworkBSD::CArchNetworkBSD() +{ + // create mutex to make some calls thread safe + m_mutex = ARCH->newMutex(); +} + +CArchNetworkBSD::~CArchNetworkBSD() +{ + ARCH->closeMutex(m_mutex); +} + +CArchSocket +CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* newSocket = new CArchSocketImpl; + + // create socket + int fd = socket(s_family[family], s_type[type], 0); + if (fd == -1) { + throwError(errno); + } + + newSocket->m_fd = fd; + newSocket->m_connected = false; + newSocket->m_refCount = 1; + return newSocket; +} + +CArchSocket +CArchNetworkBSD::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkBSD::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close(s->m_fd) == -1) { + // close failed + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkBSD::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 0) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 1) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + throwError(errno); + } +} + +void +CArchNetworkBSD::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen(s->m_fd, 3) == -1) { + throwError(errno); + } +} + +CArchSocket +CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* newSocket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + int fd; + do { + fd = accept(s->m_fd, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == -1) { + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == ECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete newSocket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + newSocket->m_fd = fd; + newSocket->m_connected = true; + newSocket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return newSocket; +} + +void +CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (errno == EISCONN) { + // already connected + break; + } + + if (errno == EAGAIN) { + // connecting + throw XArchNetworkConnecting(new XArchEvalUnix(errno)); + } + + throwError(errno); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +#if HAVE_POLL + +int +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + assert(pe != NULL || num == 0); + + // return if nothing to do + if (num == 0) { + if (timeout > 0.0) { + ARCH->sleep(timeout); + } + return 0; + } + + // allocate space for translated query + struct pollfd* pfd = new struct pollfd[num]; + + // translate query + for (int i = 0; i < num; ++i) { + pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd; + pfd[i].events = 0; + if ((pe[i].m_events & kPOLLIN) != 0) { + pfd[i].events |= POLLIN; + } + if ((pe[i].m_events & kPOLLOUT) != 0) { + pfd[i].events |= POLLOUT; + } + } + + // do the poll + int n; + do { + n = poll(pfd, num, static_cast(1000.0 * timeout)); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + delete[] pfd; + throwError(errno); + } + } while (false); + + // translate back + for (int i = 0; i < num; ++i) { + pe[i].m_revents = 0; + if ((pfd[i].revents & POLLIN) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((pfd[i].revents & POLLOUT) != 0) { + pe[i].m_revents |= kPOLLOUT; + } + if ((pfd[i].revents & POLLERR) != 0) { + pe[i].m_revents |= kPOLLERR; + } + if ((pfd[i].revents & POLLNVAL) != 0) { + pe[i].m_revents |= kPOLLNVAL; + } + } + + // done with translated query + delete[] pfd; + + return n; +} + +#else + +int +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + int fdi = pe[i].m_socket->m_fd; + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_fd, &readSet); + readSetP = &readSet; + if (fdi > n) { + n = fdi; + } + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_fd, &writeSet); + writeSetP = &writeSet; + if (fdi > n) { + n = fdi; + } + } + if (true) { + FD_SET(pe[i].m_socket->m_fd, &errSet); + errSetP = &errSet; + if (fdi > n) { + n = fdi; + } + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +#endif + +size_t +CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = read(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +size_t +CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = write(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +void +CArchNetworkBSD::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + socklen_t size = sizeof(err); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, &err, &size) == -1) { + err = errno; + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkBSD::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int mode = fcntl(s->m_fd, F_GETFL, 0); + if (mode == -1) { + throwError(errno); + } + bool old = ((mode & O_NDELAY) == 0); + if (blocking) { + mode &= ~O_NDELAY; + } + else { + mode |= O_NDELAY; + } + if (fcntl(s->m_fd, F_SETFL, mode) == -1) { + throwError(errno); + } + return old; +} + +bool +CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = sizeof(oflag); + if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &oflag, &size) == -1) { + throwError(errno); + } + + int flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + +std::string +CArchNetworkBSD::getHostName() +{ + char name[256]; + if (gethostname(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkBSD::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkBSD::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkBSD::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + if (inet_aton(name.c_str(), &inaddr.sin_addr) != 0) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // mutexed address lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyname(name.c_str()); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + delete addr; + throwNameError(h_errno); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + + // done with static buffer + ARCH->unlockMutex(m_mutex); + } + + return addr; +} + +void +CArchNetworkBSD::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkBSD::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // mutexed name lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyaddr( + reinterpret_cast(&addr->m_addr), + addr->m_len, addr->m_addr.sa_family); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + throwNameError(h_errno); + } + + // save (primary) name + std::string name = info->h_name; + + // done with static buffer + ARCH->unlockMutex(m_mutex); + + return name; +} + +std::string +CArchNetworkBSD::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ARCH->lockMutex(m_mutex); + std::string s = inet_ntoa(ipAddr->sin_addr); + ARCH->unlockMutex(m_mutex); + return s; + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkBSD::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkBSD::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons(port); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkBSD::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkBSD::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkBSD::throwError(int err) +{ + switch (err) { + case EAGAIN: + throw XArchNetworkWouldBlock(new XArchEvalUnix(err)); + + case EACCES: + case EPERM: + throw XArchNetworkAccess(new XArchEvalUnix(err)); + + case ENFILE: + case EMFILE: + case ENODEV: + case ENOBUFS: + case ENOMEM: + case ENETDOWN: +#if defined(ENOSR) + case ENOSR: +#endif + throw XArchNetworkResource(new XArchEvalUnix(err)); + + case EPROTOTYPE: + case EPROTONOSUPPORT: + case EAFNOSUPPORT: + case EPFNOSUPPORT: + case ESOCKTNOSUPPORT: + case EINVAL: + case ENOPROTOOPT: + case EOPNOTSUPP: + case ESHUTDOWN: +#if defined(ENOPKG) + case ENOPKG: +#endif + throw XArchNetworkSupport(new XArchEvalUnix(err)); + + case EIO: + throw XArchNetworkIO(new XArchEvalUnix(err)); + + case EADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalUnix(err)); + + case EADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalUnix(err)); + + case EHOSTUNREACH: + case ENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalUnix(err)); + + case ENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalUnix(err)); + + case EPIPE: + case ECONNABORTED: + case ECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalUnix(err)); + + case ECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalUnix(err)); + + case EINPROGRESS: + case EALREADY: + throw XArchNetworkConnecting(new XArchEvalUnix(err)); + + case EHOSTDOWN: + case ETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalUnix(err)); + + default: + throw XArchNetwork(new XArchEvalUnix(err)); + } +} + +void +CArchNetworkBSD::throwNameError(int err) +{ + static const char* s_msg[] = { + "The specified host is unknown", + "The requested name is valid but does not have an IP address", + "A non-recoverable name server error occurred", + "A temporary error occurred on an authoritative name server", + "An unknown name server error occurred" + }; + + switch (err) { + case HOST_NOT_FOUND: + throw XArchNetworkNameUnknown(s_msg[0]); + + case NO_DATA: + throw XArchNetworkNameNoAddress(s_msg[1]); + + case NO_RECOVERY: + throw XArchNetworkNameFailure(s_msg[2]); + + case TRY_AGAIN: + throw XArchNetworkNameUnavailable(s_msg[3]); + + default: + throw XArchNetworkName(s_msg[4]); + } +} diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h new file mode 100644 index 0000000..9718d71 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.h @@ -0,0 +1,89 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKBSD_H +#define CARCHNETWORKBSD_H + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#if HAVE_SYS_SOCKET_H +# include +#endif + +#if !defined(HAVE_SOCKLEN_T) +typedef int socklen_t; +#endif + +#define ARCH_NETWORK CArchNetworkBSD + +class CArchSocketImpl { +public: + int m_fd; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + socklen_t m_len; +}; + +//! Berkeley (BSD) sockets implementation of IArchNetwork +class CArchNetworkBSD : public IArchNetwork { +public: + CArchNetworkBSD(); + virtual ~CArchNetworkBSD(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp new file mode 100644 index 0000000..09b507a --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -0,0 +1,839 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include "CArchNetworkWinsock.h" +#include "CArch.h" +#include "XArchWindows.h" + +static const int s_family[] = { + PF_UNSPEC, + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +static SOCKET (PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen); +static int (PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen); +static int (PASCAL FAR *close_winsock)(SOCKET s); +static int (PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen); +static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen); +static int (PASCAL FAR *getsockerror_winsock)(void); +static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen); +static u_short (PASCAL FAR *htons_winsock)(u_short v); +static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); +static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); +static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR * data); +static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog); +static u_short (PASCAL FAR *ntohs_winsock)(u_short v); +static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags); +static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); +static int (PASCAL FAR *send_winsock)(SOCKET s, const void FAR * buf, int len, int flags); +static int (PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR * optval, int optlen); +static int (PASCAL FAR *shutdown_winsock)(SOCKET s, int how); +static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol); +static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); +static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); +static int (PASCAL FAR *WSACleanup_winsock)(void); +static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset); + +#undef FD_ISSET +#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) + +#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) + +static HMODULE s_networkModule = NULL; + +static +FARPROC +netGetProcAddress(HMODULE module, LPCSTR name) +{ + FARPROC func = ::GetProcAddress(module, name); + if (!func) { + throw XArchNetworkSupport(""); + } + return func; +} + +// +// CArchNetworkWinsock +// + +CArchNetworkWinsock::CArchNetworkWinsock() +{ + static const char* s_library[] = { "ws2_32.dll", "wsock32.dll" }; + + assert(WSACleanup_winsock == NULL); + assert(s_networkModule == NULL); + + // try each winsock library + for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) { + try { + init((HMODULE)::LoadLibrary(s_library[i])); + m_mutex = ARCH->newMutex(); + return; + } + catch (XArchNetwork&) { + // ignore + } + } + + // can't initialize any library + throw XArchNetworkSupport("Cannot load winsock library"); +} + +CArchNetworkWinsock::~CArchNetworkWinsock() +{ + if (s_networkModule != NULL) { + WSACleanup_winsock(); + ::FreeLibrary(s_networkModule); + + WSACleanup_winsock = NULL; + s_networkModule = NULL; + } + ARCH->closeMutex(m_mutex); +} + +void +CArchNetworkWinsock::init(HMODULE module) +{ + assert(module != NULL); + + // get startup function address + int (PASCAL FAR *startup)(WORD, LPWSADATA); + setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); + + // startup network library + WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); + WSADATA data; + int err = startup(version, &data); + if (data.wVersion != version) { + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + } + if (err != 0) { + // some other initialization error + throwError(err); + } + + // get function addresses + setfunc(accept_winsock, accept, SOCKET (PASCAL FAR *)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)); + setfunc(bind_winsock, bind, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen)); + setfunc(close_winsock, closesocket, int (PASCAL FAR *)(SOCKET s)); + setfunc(connect_winsock, connect, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen)); + setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen)); + setfunc(htons_winsock, htons, u_short (PASCAL FAR *)(u_short v)); + setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(SOCKET s, int cmd, void FAR *)); + setfunc(listen_winsock, listen, int (PASCAL FAR *)(SOCKET s, int backlog)); + setfunc(ntohs_winsock, ntohs, u_short (PASCAL FAR *)(u_short v)); + setfunc(recv_winsock, recv, int (PASCAL FAR *)(SOCKET s, void FAR * buf, int len, int flags)); + setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); + setfunc(send_winsock, send, int (PASCAL FAR *)(SOCKET s, const void FAR * buf, int len, int flags)); + setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR * optval, int optlen)); + setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(SOCKET s, int how)); + setfunc(socket_winsock, socket, SOCKET (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); + setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *)); + + s_networkModule = module; +} + +CArchSocket +CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* socket = new CArchSocketImpl; + + // create socket + SOCKET fd = socket_winsock(s_family[family], s_type[type], 0); + if (fd == INVALID_SOCKET) { + throwError(getsockerror_winsock()); + } + + socket->m_socket = fd; + socket->m_connected = false; + socket->m_refCount = 1; + return socket; +} + +CArchSocket +CArchNetworkWinsock::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkWinsock::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close_winsock(s->m_socket) == SOCKET_ERROR) { + // close failed + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkWinsock::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +void +CArchNetworkWinsock::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +CArchSocket +CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* socket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + SOCKET fd; + do { + fd = accept_winsock(s->m_socket, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == INVALID_SOCKET) { + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == WSAECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete socket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + socket->m_socket = fd; + socket->m_connected = true; + socket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return socket; +} + +void +CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect_winsock(s->m_socket, &addr->m_addr, + addr->m_len) == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (getsockerror_winsock() == WSAEISCONN) { + // already connected + break; + } + + if (getsockerror_winsock() == WSAEWOULDBLOCK) { + // connecting + throw XArchNetworkConnecting(new XArchEvalWinsock( + getsockerror_winsock())); + } + + throwError(getsockerror_winsock()); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +int +CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_socket, &readSet); + readSetP = &readSet; + n = 1; + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_socket, &writeSet); + writeSetP = &writeSet; + n = 1; + } + if (true) { + FD_SET(pe[i].m_socket->m_socket, &errSet); + errSetP = &errSet; + n = 1; + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select_winsock(0, readSetP, writeSetP, errSetP, timeout2P); + + // handle results + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_socket, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +size_t +CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = recv_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +size_t +CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = send_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +void +CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + int size = sizeof(err); + if (getsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_ERROR, &err, &size) == SOCKET_ERROR) { + err = getsockerror_winsock(); + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkWinsock::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int flag = blocking ? 0 : 1; + if (ioctl_winsock(s->m_socket, FIONBIO, &flag) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + // FIXME -- can't get the current blocking state of socket? + return true; +} + +bool +CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + BOOL oflag; + int size = sizeof(oflag); + if (getsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + // set new state + BOOL flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &flag, size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + return (oflag != 0); +} + +std::string +CArchNetworkWinsock::getHostName() +{ + char name[256]; + if (gethostname_winsock(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkWinsock::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkWinsock::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkWinsock::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str()); + if (inaddr.sin_addr.s_addr != INADDR_NONE) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // address lookup + struct hostent* info = gethostbyname_winsock(name.c_str()); + if (info == NULL) { + delete addr; + throwNameError(getsockerror_winsock()); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + return addr; +} + +void +CArchNetworkWinsock::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkWinsock::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // name lookup + struct hostent* info = gethostbyaddr_winsock( + reinterpret_cast(&addr->m_addr), + addr->m_len, addr->m_addr.sa_family); + if (info == NULL) { + throwNameError(getsockerror_winsock()); + } + + // return (primary) name + return info->h_name; +} + +std::string +CArchNetworkWinsock::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return inet_ntoa_winsock(ipAddr->sin_addr); + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkWinsock::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkWinsock::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons_winsock(static_cast(port)); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkWinsock::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs_winsock(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkWinsock::throwError(int err) +{ + switch (err) { + case WSAEWOULDBLOCK: + throw XArchNetworkWouldBlock(new XArchEvalWinsock(err)); + + case WSAEACCES: + throw XArchNetworkAccess(new XArchEvalWinsock(err)); + + case WSAEMFILE: + case WSAENOBUFS: + case WSAENETDOWN: + throw XArchNetworkResource(new XArchEvalWinsock(err)); + + case WSAEPROTOTYPE: + case WSAEPROTONOSUPPORT: + case WSAEAFNOSUPPORT: + case WSAEPFNOSUPPORT: + case WSAESOCKTNOSUPPORT: + case WSAEINVAL: + case WSAENOPROTOOPT: + case WSAEOPNOTSUPP: + case WSAESHUTDOWN: + case WSANOTINITIALISED: + case WSAVERNOTSUPPORTED: + case WSASYSNOTREADY: + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + + case WSAEADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalWinsock(err)); + + case WSAEADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalWinsock(err)); + + case WSAEHOSTUNREACH: + case WSAENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalWinsock(err)); + + case WSAENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalWinsock(err)); + + case WSAENETRESET: + case WSAEDISCON: + case WSAECONNABORTED: + case WSAECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalWinsock(err)); + + case WSAECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err)); + + case WSAEINPROGRESS: + case WSAEALREADY: + throw XArchNetworkConnecting(new XArchEvalWinsock(err)); + + case WSAEHOSTDOWN: + case WSAETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalWinsock(err)); + + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetwork(new XArchEvalWinsock(err)); + } +} + +void +CArchNetworkWinsock::throwNameError(int err) +{ + switch (err) { + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetworkName(new XArchEvalWinsock(err)); + } +} diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h new file mode 100644 index 0000000..58ce9ca --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.h @@ -0,0 +1,92 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKWINSOCK_H +#define CARCHNETWORKWINSOCK_H + +#define WIN32_LEAN_AND_MEAN + +// declare no functions in winsock2 +#define INCL_WINSOCK_API_PROTOTYPES 0 +#define INCL_WINSOCK_API_TYPEDEFS 0 + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#include +#include + +#define ARCH_NETWORK CArchNetworkWinsock + +class CArchSocketImpl { +public: + SOCKET m_socket; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + int m_len; +}; + +//! Win32 implementation of IArchNetwork +class CArchNetworkWinsock : public IArchNetwork { +public: + CArchNetworkWinsock(); + virtual ~CArchNetworkWinsock(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void init(HMODULE); + + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchSleepUnix.cpp b/lib/arch/CArchSleepUnix.cpp new file mode 100644 index 0000000..3501072 --- /dev/null +++ b/lib/arch/CArchSleepUnix.cpp @@ -0,0 +1,88 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepUnix.h" +#include "CArch.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#if !HAVE_NANOSLEEP +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +#endif + +// +// CArchSleepUnix +// + +CArchSleepUnix::CArchSleepUnix() +{ + // do nothing +} + +CArchSleepUnix::~CArchSleepUnix() +{ + // do nothing +} + +void +CArchSleepUnix::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + +#if HAVE_NANOSLEEP + // prep timeout + struct timespec t; + t.tv_sec = (long)timeout; + t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec)); + + // wait + while (nanosleep(&t, &t) < 0) + ARCH->testCancelThread(); +#else + /* emulate nanosleep() with select() */ + double startTime = ARCH->time(); + double timeLeft = timeout; + while (timeLeft > 0.0) { + struct timeval timeout2; + timeout2.tv_sec = static_cast(timeLeft); + timeout2.tv_usec = static_cast(1.0e+6 * (timeLeft - + timeout2.tv_sec)); + select((SELECT_TYPE_ARG1) 0, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 &timeout2); + ARCH->testCancelThread(); + timeLeft = timeout - (ARCH->time() - startTime); + } +#endif +} diff --git a/lib/arch/CArchSleepUnix.h b/lib/arch/CArchSleepUnix.h new file mode 100644 index 0000000..939ca40 --- /dev/null +++ b/lib/arch/CArchSleepUnix.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPUNIX_H +#define CARCHSLEEPUNIX_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepUnix + +//! Unix implementation of IArchSleep +class CArchSleepUnix : public IArchSleep { +public: + CArchSleepUnix(); + virtual ~CArchSleepUnix(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/arch/CArchSleepWindows.cpp b/lib/arch/CArchSleepWindows.cpp new file mode 100644 index 0000000..f6c8bed --- /dev/null +++ b/lib/arch/CArchSleepWindows.cpp @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepWindows.h" +#include "CArch.h" +#include "CArchMultithreadWindows.h" + +// +// CArchSleepWindows +// + +CArchSleepWindows::CArchSleepWindows() +{ + // do nothing +} + +CArchSleepWindows::~CArchSleepWindows() +{ + // do nothing +} + +void +CArchSleepWindows::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + + // get the cancel event from the current thread. this only + // works if we're using the windows multithread object but + // this is windows so that's pretty certain; we'll get a + // link error if we're not, though. + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + if (mt != NULL) { + HANDLE cancelEvent = mt->getCancelEventForCurrentThread(); + WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout)); + if (timeout == 0.0) { + Sleep(0); + } + } + else { + Sleep((DWORD)(1000.0 * timeout)); + } + ARCH->testCancelThread(); +} diff --git a/lib/arch/CArchSleepWindows.h b/lib/arch/CArchSleepWindows.h new file mode 100644 index 0000000..a5a5fa9 --- /dev/null +++ b/lib/arch/CArchSleepWindows.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPWINDOWS_H +#define CARCHSLEEPWINDOWS_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepWindows + +//! Win32 implementation of IArchSleep +class CArchSleepWindows : public IArchSleep { +public: + CArchSleepWindows(); + virtual ~CArchSleepWindows(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/arch/CArchStringUnix.cpp b/lib/arch/CArchStringUnix.cpp new file mode 100644 index 0000000..32b2ad4 --- /dev/null +++ b/lib/arch/CArchStringUnix.cpp @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchStringUnix.h" +#include + +#include "CMultibyte.cpp" + +// +// CArchStringUnix +// + +CArchStringUnix::CArchStringUnix() +{ + initMB(); +} + +CArchStringUnix::~CArchStringUnix() +{ + cleanMB(); +} + +#include "vsnprintf.cpp" + +IArchString::EWideCharEncoding +CArchStringUnix::getWideCharEncoding() +{ + return kUCS4; +} diff --git a/lib/arch/CArchStringUnix.h b/lib/arch/CArchStringUnix.h new file mode 100644 index 0000000..b9fddcb --- /dev/null +++ b/lib/arch/CArchStringUnix.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGUNIX_H +#define CARCHSTRINGUNIX_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringUnix + +//! Unix implementation of IArchString +class CArchStringUnix : public IArchString { +public: + CArchStringUnix(); + virtual ~CArchStringUnix(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); +}; + +#endif diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp new file mode 100644 index 0000000..4aefda2 --- /dev/null +++ b/lib/arch/CArchStringWindows.cpp @@ -0,0 +1,44 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define WIN32_LEAN_AND_MEAN + +#include "CArchStringWindows.h" +#include + +#include "CMultibyte.cpp" + +// +// CArchStringWindows +// + +CArchStringWindows::CArchStringWindows() +{ + initMB(); +} + +CArchStringWindows::~CArchStringWindows() +{ + cleanMB(); +} + +#define HAVE_VSNPRINTF 1 +#define ARCH_VSNPRINTF _vsnprintf +#include "vsnprintf.cpp" + +IArchString::EWideCharEncoding +CArchStringWindows::getWideCharEncoding() +{ + return kUTF16; +} diff --git a/lib/arch/CArchStringWindows.h b/lib/arch/CArchStringWindows.h new file mode 100644 index 0000000..c130352 --- /dev/null +++ b/lib/arch/CArchStringWindows.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGWINDOWS_H +#define CARCHSTRINGWINDOWS_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringWindows + +//! Win32 implementation of IArchString +class CArchStringWindows : public IArchString { +public: + CArchStringWindows(); + virtual ~CArchStringWindows(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); +}; + +#endif diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp new file mode 100644 index 0000000..987ac6c --- /dev/null +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -0,0 +1,518 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTaskBarWindows.h" +#include "IArchTaskBarReceiver.h" +#include "CArch.h" +#include "XArch.h" +#include +#include + +static const UINT kAddReceiver = WM_USER + 10; +static const UINT kRemoveReceiver = WM_USER + 11; +static const UINT kUpdateReceiver = WM_USER + 12; +static const UINT kNotifyReceiver = WM_USER + 13; +static const UINT kFirstReceiverID = WM_USER + 14; + +// +// CArchTaskBarWindows +// + +CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL; +HINSTANCE CArchTaskBarWindows::s_appInstance = NULL; + +CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : + m_nextID(kFirstReceiverID) +{ + // save the singleton instance + s_instance = this; + + // save app instance + s_appInstance = reinterpret_cast(appInstance); + + // we need a mutex + m_mutex = ARCH->newMutex(); + + // and a condition variable which uses the above mutex + m_ready = false; + m_condVar = ARCH->newCondVar(); + + // we're going to want to get a result from the thread we're + // about to create to know if it initialized successfully. + // so we lock the condition variable. + ARCH->lockMutex(m_mutex); + + // open a window and run an event loop in a separate thread. + // this has to happen in a separate thread because if we + // create a window on the current desktop with the current + // thread then the current thread won't be able to switch + // desktops if it needs to. + m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this); + + // wait for child thread + while (!m_ready) { + ARCH->waitCondVar(m_condVar, m_mutex, -1.0); + } + + // ready + ARCH->unlockMutex(m_mutex); +} + +CArchTaskBarWindows::~CArchTaskBarWindows() +{ + if (m_thread != NULL) { + ARCH->cancelThread(m_thread); + ARCH->wait(m_thread, -1.0); + ARCH->closeThread(m_thread); + } + ARCH->closeCondVar(m_condVar); + ARCH->closeMutex(m_mutex); + s_instance = NULL; +} + +void +CArchTaskBarWindows::addDialog(HWND hwnd) +{ + // add dialog to added dialogs list + ARCH->lockMutex(s_instance->m_mutex); + s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true)); + ARCH->unlockMutex(s_instance->m_mutex); +} + +void +CArchTaskBarWindows::removeDialog(HWND hwnd) +{ + // mark dialog as removed + ARCH->lockMutex(s_instance->m_mutex); + CDialogs::iterator index = s_instance->m_dialogs.find(hwnd); + if (index != s_instance->m_dialogs.end()) { + index->second = false; + } + s_instance->m_addedDialogs.erase(hwnd); + ARCH->unlockMutex(s_instance->m_mutex); +} + +void +CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver) +{ + // ignore bogus receiver + if (receiver == NULL) { + return; + } + + // add receiver if necessary + CReceiverToInfoMap::iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + // add it, creating a new message ID for it + CReceiverInfo info; + info.m_id = getNextID(); + index = m_receivers.insert(std::make_pair(receiver, info)).first; + + // add ID to receiver mapping + m_idTable.insert(std::make_pair(info.m_id, index)); + } + + // add receiver + PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0); +} + +void +CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver) +{ + // find receiver + CReceiverToInfoMap::iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + return; + } + + // remove icon. wait for this to finish before returning. + SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0); + + // recycle the ID + recycleID(index->second.m_id); + + // discard + m_idTable.erase(index->second.m_id); + m_receivers.erase(index); +} + +void +CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver) +{ + // find receiver + CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + return; + } + + // update icon and tool tip + PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0); +} + +UINT +CArchTaskBarWindows::getNextID() +{ + if (m_oldIDs.empty()) { + return m_nextID++; + } + UINT id = m_oldIDs.back(); + m_oldIDs.pop_back(); + return id; +} + +void +CArchTaskBarWindows::recycleID(UINT id) +{ + m_oldIDs.push_back(id); +} + +void +CArchTaskBarWindows::addIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + CIDToReceiverMap::const_iterator index = m_idTable.find(id); + if (index != m_idTable.end()) { + modifyIconNoLock(index->second, NIM_ADD); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::removeIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + removeIconNoLock(id); + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::updateIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + CIDToReceiverMap::const_iterator index = m_idTable.find(id); + if (index != m_idTable.end()) { + modifyIconNoLock(index->second, NIM_MODIFY); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::addAllIcons() +{ + ARCH->lockMutex(m_mutex); + for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); + index != m_receivers.end(); ++index) { + modifyIconNoLock(index, NIM_ADD); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::removeAllIcons() +{ + ARCH->lockMutex(m_mutex); + for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); + index != m_receivers.end(); ++index) { + removeIconNoLock(index->second.m_id); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::modifyIconNoLock( + CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage) +{ + // get receiver + UINT id = index->second.m_id; + IArchTaskBarReceiver* receiver = index->first; + + // lock receiver so icon and tool tip are guaranteed to be consistent + receiver->lock(); + + // get icon data + HICON icon = reinterpret_cast( + const_cast(receiver->getIcon())); + + // get tool tip + std::string toolTip = receiver->getToolTip(); + + // done querying + receiver->unlock(); + + // prepare to add icon + NOTIFYICONDATA data; + data.cbSize = sizeof(NOTIFYICONDATA); + data.hWnd = m_hwnd; + data.uID = id; + data.uFlags = NIF_MESSAGE; + data.uCallbackMessage = kNotifyReceiver; + data.hIcon = icon; + if (icon != NULL) { + data.uFlags |= NIF_ICON; + } + if (!toolTip.empty()) { + strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip)); + data.szTip[sizeof(data.szTip) - 1] = '\0'; + data.uFlags |= NIF_TIP; + } + else { + data.szTip[0] = '\0'; + } + + // add icon + if (Shell_NotifyIcon(taskBarMessage, &data) == 0) { + // failed + } +} + +void +CArchTaskBarWindows::removeIconNoLock(UINT id) +{ + NOTIFYICONDATA data; + data.cbSize = sizeof(NOTIFYICONDATA); + data.hWnd = m_hwnd; + data.uID = id; + if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) { + // failed + } +} + +void +CArchTaskBarWindows::handleIconMessage( + IArchTaskBarReceiver* receiver, LPARAM lParam) +{ + // process message + switch (lParam) { + case WM_LBUTTONDOWN: + receiver->showStatus(); + break; + + case WM_LBUTTONDBLCLK: + receiver->primaryAction(); + break; + + case WM_RBUTTONUP: { + POINT p; + GetCursorPos(&p); + receiver->runMenu(p.x, p.y); + break; + } + + case WM_MOUSEMOVE: + // currently unused + break; + + default: + // unused + break; + } +} + +bool +CArchTaskBarWindows::processDialogs(MSG* msg) +{ + // only one thread can be in this method on any particular object + // at any given time. that's not a problem since only our event + // loop calls this method and there's just one of those. + + ARCH->lockMutex(m_mutex); + + // remove removed dialogs + m_dialogs.erase(false); + + // merge added dialogs into the dialog list + for (CDialogs::const_iterator index = m_addedDialogs.begin(); + index != m_addedDialogs.end(); ++index) { + m_dialogs.insert(std::make_pair(index->first, index->second)); + } + m_addedDialogs.clear(); + + ARCH->unlockMutex(m_mutex); + + // check message against all dialogs until one handles it. + // note that we don't hold a lock while checking because + // the message is processed and may make calls to this + // object. that's okay because addDialog() and + // removeDialog() don't change the map itself (just the + // values of some elements). + ARCH->lockMutex(m_mutex); + for (CDialogs::const_iterator index = m_dialogs.begin(); + index != m_dialogs.end(); ++index) { + if (index->second) { + ARCH->unlockMutex(m_mutex); + if (IsDialogMessage(index->first, msg)) { + return true; + } + ARCH->lockMutex(m_mutex); + } + } + ARCH->unlockMutex(m_mutex); + + return false; +} + +LRESULT +CArchTaskBarWindows::wndProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case kNotifyReceiver: { + // lookup receiver + CIDToReceiverMap::const_iterator index = m_idTable.find(wParam); + if (index != m_idTable.end()) { + IArchTaskBarReceiver* receiver = index->second->first; + handleIconMessage(receiver, lParam); + return 0; + } + break; + } + + case kAddReceiver: + addIcon(wParam); + break; + + case kRemoveReceiver: + removeIcon(wParam); + break; + + case kUpdateReceiver: + updateIcon(wParam); + break; + + default: + if (msg == m_taskBarRestart) { + // task bar was recreated so re-add our icons + addAllIcons(); + } + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put + // it in the extra window data then forward the call. + CArchTaskBarWindows* self = NULL; + if (msg == WM_NCCREATE) { + CREATESTRUCT* createInfo; + createInfo = reinterpret_cast(lParam); + self = reinterpret_cast( + createInfo->lpCreateParams); + SetWindowLong(hwnd, 0, reinterpret_cast(self)); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, 0); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->wndProc(hwnd, msg, wParam, lParam); + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); + } +} + +void +CArchTaskBarWindows::threadMainLoop() +{ + // register the task bar restart message + m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_NOCLOSE; + classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyTaskBar"); + classInfo.hIconSm = NULL; + ATOM windowClass = RegisterClassEx(&classInfo); + + // create window + m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, + reinterpret_cast(windowClass), + TEXT("Synergy Task Bar"), + WS_POPUP, + 0, 0, 1, 1, + NULL, + NULL, + s_appInstance, + reinterpret_cast(this)); + + // signal ready + ARCH->lockMutex(m_mutex); + m_ready = true; + ARCH->broadcastCondVar(m_condVar); + ARCH->unlockMutex(m_mutex); + + // handle failure + if (m_hwnd == NULL) { + UnregisterClass((LPCTSTR)windowClass, s_appInstance); + return; + } + + try { + // main loop + MSG msg; + for (;;) { + // wait for message + if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) { + continue; + } + + // peek for message and remove it. we don't GetMessage() + // because we should never block here, only in waitForEvent(). + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + continue; + } + + // check message against dialogs + if (!processDialogs(&msg)) { + // process message + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + catch (XThread&) { + // clean up + removeAllIcons(); + DestroyWindow(m_hwnd); + UnregisterClass((LPCTSTR)windowClass, s_appInstance); + throw; + } +} + +void* +CArchTaskBarWindows::threadEntry(void* self) +{ + reinterpret_cast(self)->threadMainLoop(); + return NULL; +} diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h new file mode 100644 index 0000000..67e9af1 --- /dev/null +++ b/lib/arch/CArchTaskBarWindows.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTASKBARWINDOWS_H +#define CARCHTASKBARWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchTaskBar.h" +#include "IArchMultithread.h" +#include "stdmap.h" +#include "stdvector.h" +#include + +#define ARCH_TASKBAR CArchTaskBarWindows + +//! Win32 implementation of IArchTaskBar +class CArchTaskBarWindows : public IArchTaskBar { +public: + CArchTaskBarWindows(void*); + virtual ~CArchTaskBarWindows(); + + //! Add a dialog window + /*! + Tell the task bar event loop about a dialog. Win32 annoyingly + requires messages destined for modeless dialog boxes to be + dispatched differently than other messages. + */ + static void addDialog(HWND); + + //! Remove a dialog window + /*! + Remove a dialog window added via \c addDialog(). + */ + static void removeDialog(HWND); + + // IArchTaskBar overrides + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); + +private: + class CReceiverInfo { + public: + UINT m_id; + }; + + typedef std::map CReceiverToInfoMap; + typedef std::map CIDToReceiverMap; + typedef std::vector CIDStack; + typedef std::map CDialogs; + + UINT getNextID(); + void recycleID(UINT); + + void addIcon(UINT); + void removeIcon(UINT); + void updateIcon(UINT); + void addAllIcons(); + void removeAllIcons(); + void modifyIconNoLock(CReceiverToInfoMap::const_iterator, + DWORD taskBarMessage); + void removeIconNoLock(UINT id); + void handleIconMessage(IArchTaskBarReceiver*, LPARAM); + + bool processDialogs(MSG*); + LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK + staticWndProc(HWND, UINT, WPARAM, LPARAM); + void threadMainLoop(); + static void* threadEntry(void*); + +private: + static CArchTaskBarWindows* s_instance; + static HINSTANCE s_appInstance; + + // multithread data + CArchMutex m_mutex; + CArchCond m_condVar; + bool m_ready; + int m_result; + CArchThread m_thread; + + // child thread data + HWND m_hwnd; + UINT m_taskBarRestart; + + // shared data + CReceiverToInfoMap m_receivers; + CIDToReceiverMap m_idTable; + CIDStack m_oldIDs; + UINT m_nextID; + + // dialogs + CDialogs m_dialogs; + CDialogs m_addedDialogs; +}; + +#endif diff --git a/lib/arch/CArchTaskBarXWindows.cpp b/lib/arch/CArchTaskBarXWindows.cpp new file mode 100644 index 0000000..6934f27 --- /dev/null +++ b/lib/arch/CArchTaskBarXWindows.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTaskBarXWindows.h" + +// +// CArchTaskBarXWindows +// + +CArchTaskBarXWindows::CArchTaskBarXWindows(void*) +{ + // do nothing +} + +CArchTaskBarXWindows::~CArchTaskBarXWindows() +{ + // do nothing +} + +void +CArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +CArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +CArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} diff --git a/lib/arch/CArchTaskBarXWindows.h b/lib/arch/CArchTaskBarXWindows.h new file mode 100644 index 0000000..abf2801 --- /dev/null +++ b/lib/arch/CArchTaskBarXWindows.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTASKBARXWINDOWS_H +#define CARCHTASKBARXWINDOWS_H + +#include "IArchTaskBar.h" + +#define ARCH_TASKBAR CArchTaskBarXWindows + +//! X11 implementation of IArchTaskBar +class CArchTaskBarXWindows : public IArchTaskBar { +public: + CArchTaskBarXWindows(void*); + virtual ~CArchTaskBarXWindows(); + + // IArchTaskBar overrides + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); +}; + +#endif diff --git a/lib/arch/CArchTimeUnix.cpp b/lib/arch/CArchTimeUnix.cpp new file mode 100644 index 0000000..49506ba --- /dev/null +++ b/lib/arch/CArchTimeUnix.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTimeUnix.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +// +// CArchTimeUnix +// + +CArchTimeUnix::CArchTimeUnix() +{ + // do nothing +} + +CArchTimeUnix::~CArchTimeUnix() +{ + // do nothing +} + +double +CArchTimeUnix::time() +{ + struct timeval t; + gettimeofday(&t, NULL); + return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; +} diff --git a/lib/arch/CArchTimeUnix.h b/lib/arch/CArchTimeUnix.h new file mode 100644 index 0000000..78c6ff6 --- /dev/null +++ b/lib/arch/CArchTimeUnix.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEUNIX_H +#define CARCHTIMEUNIX_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeUnix + +//! Generic Unix implementation of IArchTime +class CArchTimeUnix : public IArchTime { +public: + CArchTimeUnix(); + virtual ~CArchTimeUnix(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CArchTimeWindows.cpp b/lib/arch/CArchTimeWindows.cpp new file mode 100644 index 0000000..57aee29 --- /dev/null +++ b/lib/arch/CArchTimeWindows.cpp @@ -0,0 +1,86 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +// avoid getting a lot a crap from mmsystem.h that we don't need +#define MMNODRV // Installable driver support +#define MMNOSOUND // Sound support +#define MMNOWAVE // Waveform support +#define MMNOMIDI // MIDI support +#define MMNOAUX // Auxiliary audio support +#define MMNOMIXER // Mixer support +#define MMNOJOY // Joystick support +#define MMNOMCI // MCI support +#define MMNOMMIO // Multimedia file I/O support +#define MMNOMMSYSTEM // General MMSYSTEM functions + +#define WIN32_LEAN_AND_MEAN + +#include "CArchTimeWindows.h" +#include +#include + +typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void); + +static double s_freq = 0.0; +static HINSTANCE s_mmInstance = NULL; +static PTimeGetTime s_tgt = NULL; + + +// +// CArchTimeWindows +// + +CArchTimeWindows::CArchTimeWindows() +{ + assert(s_freq == 0.0 || s_mmInstance == NULL); + + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) { + s_freq = 1.0 / static_cast(freq.QuadPart); + } + else { + // load winmm.dll and get timeGetTime + s_mmInstance = LoadLibrary("winmm"); + if (s_mmInstance != NULL) { + s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); + } + } +} + +CArchTimeWindows::~CArchTimeWindows() +{ + s_freq = 0.0; + if (s_mmInstance == NULL) { + FreeLibrary(reinterpret_cast(s_mmInstance)); + s_tgt = NULL; + s_mmInstance = NULL; + } +} + +double +CArchTimeWindows::time() +{ + // get time. we try three ways, in order of descending precision + if (s_freq != 0.0) { + LARGE_INTEGER c; + QueryPerformanceCounter(&c); + return s_freq * static_cast(c.QuadPart); + } + else if (s_tgt != NULL) { + return 0.001 * static_cast(s_tgt()); + } + else { + return 0.001 * static_cast(GetTickCount()); + } +} diff --git a/lib/arch/CArchTimeWindows.h b/lib/arch/CArchTimeWindows.h new file mode 100644 index 0000000..fb9b1e9 --- /dev/null +++ b/lib/arch/CArchTimeWindows.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEWINDOWS_H +#define CARCHTIMEWINDOWS_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeWindows + +//! Win32 implementation of IArchTime +class CArchTimeWindows : public IArchTime { +public: + CArchTimeWindows(); + virtual ~CArchTimeWindows(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp new file mode 100644 index 0000000..43b8b41 --- /dev/null +++ b/lib/arch/CMultibyte.cpp @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMULTIBYTE_H +#define CMULTIBYTE_H + +#include "common.h" + +#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || WINDOWS_LIKE +#include "CMultibyteOS.cpp" +#else +#include "CMultibyteEmu.cpp" +#endif + +CArchMBState +ARCH_STRING::newMBState() +{ + CArchMBState state = new CArchMBStateImpl; + initMBState(state); + return state; +} + +void +ARCH_STRING::closeMBState(CArchMBState state) +{ + delete state; +} + +#endif diff --git a/lib/arch/CMultibyteEmu.cpp b/lib/arch/CMultibyteEmu.cpp new file mode 100644 index 0000000..0fd3087 --- /dev/null +++ b/lib/arch/CMultibyteEmu.cpp @@ -0,0 +1,114 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArch.h" +#include +#if HAVE_WCHAR_H +# include +#elif __APPLE__ + // wtf? Darwin puts mbtowc() et al. in stdlib +# include +#else + // platform apparently has no wchar_t support. provide dummy + // implementations. hopefully at least the C++ compiler has + // a built-in wchar_t type. +# undef HAVE_MBSINIT + +static inline +int +mbtowc(wchar_t* dst, const char* src, int n) +{ + *dst = static_cast(*src); + return 1; +} + +static inline +int +wctomb(char* dst, wchar_t src) +{ + *dst = static_cast(src); + return 1; +} + +#endif + +class CArchMBStateImpl { +public: +#if HAVE_MBSINIT + mbstate_t m_mbstate; +#else + int m_mbstate; // dummy data +#endif +}; + +// +// use C library non-reentrant multibyte conversion with mutex +// + +static CArchMutex s_mutex; + +static +void +initMB() +{ + s_mutex = ARCH->newMutex(); +} + +static +void +cleanMB() +{ + ARCH->closeMutex(s_mutex); +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ + // if we're using this file mbsinit() probably doesn't exist + // but use it if it does +#if HAVE_MBSINIT + return (mbsinit(&state->m_mbstate) != 0); +#else + return true; +#endif +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState) +{ + wchar_t dummy; + ARCH->lockMutex(s_mutex); + int result = mbtowc(dst != NULL ? dst : &dummy, src, n); + ARCH->unlockMutex(s_mutex); + if (result < 0) + return -1; + else + return result; +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState) +{ + char dummy[MB_LEN_MAX]; + ARCH->lockMutex(s_mutex); + int n = wctomb(dst != NULL ? dst : dummy, src); + ARCH->unlockMutex(s_mutex); + return n; +} diff --git a/lib/arch/CMultibyteOS.cpp b/lib/arch/CMultibyteOS.cpp new file mode 100644 index 0000000..49a8522 --- /dev/null +++ b/lib/arch/CMultibyteOS.cpp @@ -0,0 +1,68 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +class CArchMBStateImpl { +public: + mbstate_t m_mbstate; +}; + +// +// use C library reentrant multibyte conversion +// + +static +void +initMB() +{ + // do nothing +} + +static +void +cleanMB() +{ + // do nothing +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ + return (mbsinit(&state->m_mbstate) != 0); +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, + int n, CArchMBState state) +{ + wchar_t dummy; + return static_cast(mbrtowc(dst != NULL ? dst : &dummy, + src, static_cast(n), &state->m_mbstate)); +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + char dummy[MB_LEN_MAX]; + return static_cast(wcrtomb(dst != NULL ? dst : dummy, + src, &state->m_mbstate)); +} diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h new file mode 100644 index 0000000..c9da410 --- /dev/null +++ b/lib/arch/IArchConsole.h @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHCONSOLE_H +#define IARCHCONSOLE_H + +#include "IInterface.h" + +//! Interface for architecture dependent console output +/*! +This interface defines the console operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchConsole : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open the console + /*! + Opens the console for writing. The console is opened automatically + on the first write so calling this method is optional. Uses \c title + for the console's title if appropriate for the architecture. Calling + this method on an already open console must have no effect. + */ + virtual void openConsole(const char* title) = 0; + + //! Close the console + /*! + Close the console. Calling this method on an already closed console + must have no effect. + */ + virtual void closeConsole() = 0; + + //! Write to the console + /*! + Writes the given string to the console, opening it if necessary. + */ + virtual void writeConsole(const char*) = 0; + + //! Returns the newline sequence for the console + /*! + Different consoles use different character sequences for newlines. + This method returns the appropriate newline sequence for the console. + */ + virtual const char* getNewlineForConsole() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchDaemon.h b/lib/arch/IArchDaemon.h new file mode 100644 index 0000000..ce04eb4 --- /dev/null +++ b/lib/arch/IArchDaemon.h @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHDAEMON_H +#define IARCHDAEMON_H + +#include "IInterface.h" + +//! Interface for architecture dependent daemonizing +/*! +This interface defines the operations required by synergy for installing +uninstalling daeamons and daemonizing a process. Each architecture must +implement this interface. +*/ +class IArchDaemon : public IInterface { +public: + typedef int (*DaemonFunc)(int argc, const char** argv); + + //! @name manipulators + //@{ + + //! Install daemon + /*! + Install a daemon. \c name is the name of the daemon passed to the + system and \c description is a short human readable description of + the daemon. \c pathname is the path to the daemon executable. + \c commandLine should \b not include the name of program as the + first argument. If \c allUsers is true then the daemon will be + installed to start at boot time, otherwise it will be installed to + start when the current user logs in. Throws an \c XArchDaemon + exception on failure. + */ + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) = 0; + + //! Uninstall daemon + /*! + Uninstall a daemon. Throws an \c XArchDaemon on failure. + */ + virtual void uninstallDaemon(const char* name, bool allUsers) = 0; + + //! Daemonize the process + /*! + Daemonize. Throw XArchDaemonFailed on error. \c name is the name + of the daemon. Once daemonized, \c func is invoked and daemonize + returns when and what it does. + + Exactly what happens when daemonizing depends on the platform. +
    +
  • unix: + Detaches from terminal. \c func gets passed one argument, the + name passed to daemonize(). +
  • win32: + Becomes a service. Argument 0 is the name of the service + and the rest are the arguments passed to StartService(). + \c func is only called when the service is actually started. + \c func must call \c CArchMiscWindows::runDaemon() to finally + becoming a service. The \c runFunc function passed to \c runDaemon() + must call \c CArchMiscWindows::daemonRunning(true) when it + enters the main loop (i.e. after initialization) and + \c CArchMiscWindows::daemonRunning(false) when it leaves + the main loop. The \c stopFunc function passed to \c runDaemon() + is called when the daemon must exit the main loop and it must cause + \c runFunc to return. \c func should return what \c runDaemon() + returns. \c func or \c runFunc can call + \c CArchMiscWindows::daemonFailed() to indicate startup failure. +
+ */ + virtual int daemonize(const char* name, DaemonFunc func) = 0; + + //! Check if user has permission to install the daemon + /*! + Returns true iff the caller has permission to install or + uninstall the daemon. Note that even if this method returns + true it's possible that installing/uninstalling the service + may still fail. This method ignores whether or not the + service is already installed. + */ + virtual bool canInstallDaemon(const char* name, bool allUsers) = 0; + + //! Check if the daemon is installed + /*! + Returns true iff the daemon is installed. + */ + virtual bool isDaemonInstalled(const char* name, bool allUsers) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchFile.h b/lib/arch/IArchFile.h new file mode 100644 index 0000000..8594053 --- /dev/null +++ b/lib/arch/IArchFile.h @@ -0,0 +1,64 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHFILE_H +#define IARCHFILE_H + +#include "IInterface.h" +#include "stdstring.h" + +//! Interface for architecture dependent file system operations +/*! +This interface defines the file system operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchFile : public IInterface { +public: + //! @name manipulators + //@{ + + //! Extract base name + /*! + Find the base name in the given \c pathname. + */ + virtual const char* getBasename(const char* pathname) = 0; + + //! Get user's home directory + /*! + Returns the user's home directory. Returns the empty string if + this cannot be determined. + */ + virtual std::string getUserDirectory() = 0; + + //! Get system directory + /*! + Returns the ussystem configuration file directory. + */ + virtual std::string getSystemDirectory() = 0; + + //! Concatenate path components + /*! + Concatenate pathname components with a directory separator + between them. This should not check if the resulting path + is longer than allowed by the system; we'll rely on the + system calls to tell us that. + */ + virtual std::string concatPath( + const std::string& prefix, + const std::string& suffix) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h new file mode 100644 index 0000000..0c25918 --- /dev/null +++ b/lib/arch/IArchLog.h @@ -0,0 +1,64 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHLOG_H +#define IARCHLOG_H + +#include "IInterface.h" + +//! Interface for architecture dependent logging +/*! +This interface defines the logging operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchLog : public IInterface { +public: + //! Log levels + /*! + The logging priority levels in order of highest to lowest priority. + */ + enum ELevel { + kERROR, //!< For serious or fatal errors + kWARNING, //!< For minor errors and warnings + kNOTE, //!< For messages about notable events + kINFO, //!< For informational messages + kDEBUG //!< For debugging messages + }; + + //! @name manipulators + //@{ + + //! Open the log + /*! + Opens the log for writing. The log must be opened before being + written to. + */ + virtual void openLog(const char* name) = 0; + + //! Close the log + /*! + Close the log. + */ + virtual void closeLog() = 0; + + //! Write to the log + /*! + Writes the given string to the log with the given level. + */ + virtual void writeLog(ELevel, const char*) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h new file mode 100644 index 0000000..ee37677 --- /dev/null +++ b/lib/arch/IArchMultithread.h @@ -0,0 +1,265 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHMULTITHREAD_H +#define IARCHMULTITHREAD_H + +#include "IInterface.h" + +/*! +\class CArchCondImpl +\brief Internal condition variable data. +An architecture dependent type holding the necessary data for a +condition variable. +*/ +class CArchCondImpl; + +/*! +\var CArchCond +\brief Opaque condition variable type. +An opaque type representing a condition variable. +*/ +typedef CArchCondImpl* CArchCond; + +/*! +\class CArchMutexImpl +\brief Internal mutex data. +An architecture dependent type holding the necessary data for a mutex. +*/ +class CArchMutexImpl; + +/*! +\var CArchMutex +\brief Opaque mutex type. +An opaque type representing a mutex. +*/ +typedef CArchMutexImpl* CArchMutex; + +/*! +\class CArchThreadImpl +\brief Internal thread data. +An architecture dependent type holding the necessary data for a thread. +*/ +class CArchThreadImpl; + +/*! +\var CArchThread +\brief Opaque thread type. +An opaque type representing a thread. +*/ +typedef CArchThreadImpl* CArchThread; + +//! Interface for architecture dependent multithreading +/*! +This interface defines the multithreading operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchMultithread : public IInterface { +public: + //! Result of waitForEvent() + enum EWaitResult { + kEvent, //!< An event is pending + kExit, //!< Thread exited + kTimeout //!< Wait timed out + }; + + //! Type of thread entry point + typedef void* (*ThreadFunc)(void*); + //! Type of thread identifier + typedef unsigned int ThreadID; + + //! @name manipulators + //@{ + + // + // condition variable methods + // + + //! Create a condition variable + /*! + The condition variable is an opaque data type. + */ + virtual CArchCond newCondVar() = 0; + + //! Destroy a condition variable + virtual void closeCondVar(CArchCond) = 0; + + //! Signal a condition variable + /*! + Signalling a condition variable releases one waiting thread. + */ + virtual void signalCondVar(CArchCond) = 0; + + //! Broadcast a condition variable + /*! + Broadcasting a condition variable releases all waiting threads. + */ + virtual void broadcastCondVar(CArchCond) = 0; + + //! Wait on a condition variable + /*! + Wait on a conditation variable for up to \c timeout seconds. + If \c timeout is < 0 then there is no timeout. The mutex must + be locked when this method is called. The mutex is unlocked + during the wait and locked again before returning. Returns + true if the condition variable was signalled and false on + timeout. + + (Cancellation point) + */ + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout) = 0; + + // + // mutex methods + // + + //! Create a non-recursive mutex + /*! + Creates a non-recursive mutex. A thread must not lock a + non-recursive mutex when it already holds a lock on that mutex. + If it does it will deadlock. The mutex is an opaque data type. + */ + virtual CArchMutex newMutex() = 0; + + //! Destroy a mutex + virtual void closeMutex(CArchMutex) = 0; + + //! Lock a mutex + /*! + (Cancellation point) + */ + virtual void lockMutex(CArchMutex) = 0; + + //! Unlock a mutex + virtual void unlockMutex(CArchMutex) = 0; + + // + // thread methods + // + + //! Start a new thread + /*! + Creates and starts a new thread, using \c func as the entry point + and passing it \c userData. The thread is an opaque data type. + */ + virtual CArchThread newThread(ThreadFunc func, void* userData) = 0; + + //! Get a reference to the calling thread + /*! + Returns a thread representing the current (i.e. calling) thread. + */ + virtual CArchThread newCurrentThread() = 0; + + //! Copy a thread object + /*! + Returns a reference to to thread referred to by \c thread. + */ + virtual CArchThread copyThread(CArchThread thread) = 0; + + //! Release a thread reference + /*! + Deletes the given thread object. This does not destroy the thread + the object referred to, even if there are no remaining references. + Use cancelThread() and waitThread() to stop a thread and wait for + it to exit. + */ + virtual void closeThread(CArchThread) = 0; + + //! Force a thread to exit + /*! + Causes \c thread to exit when it next calls a cancellation point. + A thread avoids cancellation as long as it nevers calls a + cancellation point. Once it begins the cancellation process it + must always let cancellation go to completion but may take as + long as necessary to clean up. + */ + virtual void cancelThread(CArchThread thread) = 0; + + //! Change thread priority + /*! + Changes the priority of \c thread by \c n. If \c n is positive + the thread has a lower priority and if negative a higher priority. + Some architectures may not support either or both directions. + */ + virtual void setPriorityOfThread(CArchThread, int n) = 0; + + //! Cancellation point + /*! + This method does nothing but is a cancellation point. Clients + can make their own functions cancellation points by calling this + method at appropriate times. + */ + virtual void testCancelThread() = 0; + + //! Wait for a thread to exit + /*! + Waits for up to \c timeout seconds for \c thread to exit (normally + or by cancellation). Waits forever if \c timeout < 0. Returns + true if the thread exited, false otherwise. Waiting on the current + thread returns immediately with false. + + (Cancellation point) + */ + virtual bool wait(CArchThread thread, double timeout) = 0; + + //! Wait for a user event + /*! + Waits for up to \c timeout seconds for a pending user event or + \c thread to exit (normally or by cancellation). Waits forever + if \c timeout < 0. Returns kEvent if an event occurred, kExit + if \c thread exited, or kTimeout if the timeout expired. If + \c thread is NULL then it doesn't wait for any thread to exit + and it will not return kExit. + + This method is not required by all platforms. + + (Cancellation point) + */ + virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0; + + //! Compare threads + /*! + Returns true iff two thread objects refer to the same thread. + Note that comparing thread objects directly is meaningless. + */ + virtual bool isSameThread(CArchThread, CArchThread) = 0; + + //! Test if thread exited + /*! + Returns true iff \c thread has exited. + */ + virtual bool isExitedThread(CArchThread thread) = 0; + + //! Returns the exit code of a thread + /*! + Waits indefinitely for \c thread to exit (if it hasn't yet) then + returns the thread's exit code. + + (Cancellation point) + */ + virtual void* getResultOfThread(CArchThread thread) = 0; + + //! Returns an ID for a thread + /*! + Returns some ID number for \c thread. This is for logging purposes. + All thread objects referring to the same thread return the same ID. + However, clients should us isSameThread() to compare thread objects + instead of comparing IDs. + */ + virtual ThreadID getIDOfThread(CArchThread thread) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h new file mode 100644 index 0000000..1af8cd7 --- /dev/null +++ b/lib/arch/IArchNetwork.h @@ -0,0 +1,278 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHNETWORK_H +#define IARCHNETWORK_H + +#include "IInterface.h" +#include "stdstring.h" + +/*! +\class CArchSocketImpl +\brief Internal socket data. +An architecture dependent type holding the necessary data for a socket. +*/ +class CArchSocketImpl; + +/*! +\var CArchSocket +\brief Opaque socket type. +An opaque type representing a socket. +*/ +typedef CArchSocketImpl* CArchSocket; + +/*! +\class CArchNetAddressImpl +\brief Internal network address data. +An architecture dependent type holding the necessary data for a network +address. +*/ +class CArchNetAddressImpl; + +/*! +\var CArchNetAddress +\brief Opaque network address type. +An opaque type representing a network address. +*/ +typedef CArchNetAddressImpl* CArchNetAddress; + +//! Interface for architecture dependent networking +/*! +This interface defines the networking operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchNetwork : public IInterface { +public: + //! Supported address families + enum EAddressFamily { + kUNKNOWN, + kINET, + }; + + //! Supported socket types + enum ESocketType { + kDGRAM, + kSTREAM + }; + + //! Events for \c poll() + /*! + Events for \c poll() are bitmasks and can be combined using the + bitwise operators. + */ + enum { + kPOLLIN = 1, //!< Socket is readable + kPOLLOUT = 2, //!< Socket is writable + kPOLLERR = 4, //!< The socket is in an error state + kPOLLNVAL = 8 //!< The socket is invalid + }; + + //! A socket query for \c poll() + class CPollEntry { + public: + //! The socket to query + CArchSocket m_socket; + + //! The events to query for + /*! + The events to query for can be any combination of kPOLLIN and + kPOLLOUT. + */ + unsigned short m_events; + + //! The result events + unsigned short m_revents; + }; + + //! @name manipulators + //@{ + + //! Create a new socket + /*! + The socket is an opaque data type. + */ + virtual CArchSocket newSocket(EAddressFamily, ESocketType) = 0; + + //! Copy a socket object + /*! + Returns a reference to to socket referred to by \c s. + */ + virtual CArchSocket copySocket(CArchSocket s) = 0; + + //! Release a socket reference + /*! + Deletes the given socket object. This does not destroy the socket + the object referred to until there are no remaining references for + the socket. + */ + virtual void closeSocket(CArchSocket s) = 0; + + //! Close socket for further reads + /*! + Calling this disallows future reads on socket \c s. + */ + virtual void closeSocketForRead(CArchSocket s) = 0; + + //! Close socket for further writes + /*! + Calling this disallows future writes on socket \c s. + */ + virtual void closeSocketForWrite(CArchSocket s) = 0; + + //! Bind socket to address + /*! + Binds socket \c s to the address \c addr. + */ + virtual void bindSocket(CArchSocket s, CArchNetAddress addr) = 0; + + //! Listen for connections on socket + /*! + Causes the socket \c s to begin listening for incoming connections. + */ + virtual void listenOnSocket(CArchSocket s) = 0; + + //! Accept connection on socket + /*! + Accepts a connection on socket \c s, returning a new socket for the + connection and filling in \c addr with the address of the remote + end. \c addr may be NULL if the remote address isn't required. + The original socket \c s is unaffected and remains in the listening + state. The new socket shares most of the properties of \c s except + it's not in the listening state, it's connected, and is not + non-blocking even is \c s is. + + This call blocks if \c s is not non-blocking and there are no + pending connection requests. + + (Cancellation point) + */ + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr) = 0; + + //! Connect socket + /*! + Connects the socket \c s to the remote address \c addr. This call + blocks if \c s is not non-blocking. If \c s is non-blocking then + the client can \c poll() for writability to detect a connection. + + (Cancellation point) + */ + virtual void connectSocket(CArchSocket s, CArchNetAddress addr) = 0; + + //! Check socket state + /*! + Tests the state of \c num sockets for readability and/or writability. + Waits up to \c timeout seconds for some socket to become readable + and/or writable (or indefinitely if \c timeout < 0). Returns the + number of sockets that were readable (if readability was being + queried) or writable (if writablility was being queried) and sets + the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL + are set in \c m_revents as appropriate. If a socket indicates + \c kPOLLERR then \c throwErrorOnSocket() can be used to determine + the type of error. + + (Cancellation point) + */ + virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; + + //! Read data from socket + /*! + Read up to \c len bytes from socket \c s in \c buf and return the + number of bytes read. The number of bytes can be less than \c len + if not enough data is available. Returns 0 if the remote end has + disconnected and there is no more queued received data. Blocks if + the socket is not non-blocking and there is no queued received data. + If non-blocking and there is no queued received data then throws + XArchNetworkWouldBlock. + + (Cancellation point) + */ + virtual size_t readSocket(CArchSocket s, void* buf, size_t len) = 0; + + //! Write data from socket + /*! + Write up to \c len bytes to socket \c s from \c buf and return the + number of bytes written. The number of bytes can be less than + \c len if the remote end disconnected or the socket is non-blocking + and the internal buffers are full. If non-blocking and the internal + buffers are full before any data is written then throws + XArchNetworkWouldBlock. + + (Cancellation point) + */ + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len) = 0; + + //! Check error on socket + /*! + If the socket \c s is in an error state then throws an appropriate + XArchNetwork exception. + */ + virtual void throwErrorOnSocket(CArchSocket s) = 0; + + //! Set socket to (non-)blocking operation + /*! + Set socket to block or not block on accept, connect, poll, read and + write (i.e. calls that may take an arbitrary amount of time). + Returns the previous state. + */ + virtual bool setBlockingOnSocket(CArchSocket, bool blocking) = 0; + + //! Turn Nagle algorithm on or off on socket + /*! + Set socket to send messages immediately (true) or to collect small + messages into one packet (false). Returns the previous state. + */ + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0; + + //! Return local host's name + virtual std::string getHostName() = 0; + + //! Create an "any" network address + virtual CArchNetAddress newAnyAddr(EAddressFamily) = 0; + + //! Copy a network address + virtual CArchNetAddress copyAddr(CArchNetAddress) = 0; + + //! Convert a name to a network address + virtual CArchNetAddress nameToAddr(const std::string&) = 0; + + //! Destroy a network address + virtual void closeAddr(CArchNetAddress) = 0; + + //! Convert an address to a host name + virtual std::string addrToName(CArchNetAddress) = 0; + + //! Convert an address to a string + virtual std::string addrToString(CArchNetAddress) = 0; + + //! Get an address's family + virtual EAddressFamily getAddrFamily(CArchNetAddress) = 0; + + //! Set the port of an address + virtual void setAddrPort(CArchNetAddress, int port) = 0; + + //! Get the port of an address + virtual int getAddrPort(CArchNetAddress) = 0; + + //! Test for the "any" address + /*! + Returns true if \c addr is the "any" address. \c newAnyAddr() + returns an "any" address. + */ + virtual bool isAnyAddr(CArchNetAddress addr) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchSleep.h b/lib/arch/IArchSleep.h new file mode 100644 index 0000000..95a2852 --- /dev/null +++ b/lib/arch/IArchSleep.h @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSLEEP_H +#define IARCHSLEEP_H + +#include "IInterface.h" + +//! Interface for architecture dependent sleeping +/*! +This interface defines the sleep operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchSleep : public IInterface { +public: + //! @name manipulators + //@{ + + //! Sleep + /*! + Blocks the calling thread for \c timeout seconds. If + \c timeout < 0.0 then the call returns immediately. If \c timeout + == 0.0 then the calling thread yields the CPU. + + (cancellation point) + */ + virtual void sleep(double timeout) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchString.h b/lib/arch/IArchString.h new file mode 100644 index 0000000..2d9a507 --- /dev/null +++ b/lib/arch/IArchString.h @@ -0,0 +1,93 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSTRING_H +#define IARCHSTRING_H + +#include "IInterface.h" +#include + +/*! +\class CArchMBStateImpl +\brief Internal multibyte conversion data. +An architecture dependent type holding the necessary data for a +multibyte to/from wide character conversion. +*/ +class CArchMBStateImpl; + +/*! +\var CArchMBState +\brief Opaque multibyte conversion state type. +An opaque type representing multibyte conversion state. +*/ +typedef CArchMBStateImpl* CArchMBState; + +//! Interface for architecture dependent string operations +/*! +This interface defines the string operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchString : public IInterface { +public: + //! Wide character encodings + /*! + The known wide character encodings + */ + enum EWideCharEncoding { + kUCS2, //!< The UCS-2 encoding + kUCS4, //!< The UCS-4 encoding + kUTF16, //!< The UTF-16 encoding + kUTF32 //!< The UTF-32 encoding + }; + + //! @name manipulators + //@{ + + //! printf() to limited size buffer with va_list + /*! + This method is equivalent to vsprintf() except it will not write + more than \c n bytes to the buffer, returning -1 if the output + was truncated and the number of bytes written not including the + trailing NUL otherwise. + */ + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap) = 0; + + //! Create a new multibyte conversion state + virtual CArchMBState newMBState() = 0; + + //! Destroy a multibyte conversion state + virtual void closeMBState(CArchMBState) = 0; + + //! Initialize a multibyte conversion state + virtual void initMBState(CArchMBState) = 0; + + //! Test a multibyte conversion state + virtual bool isInitMBState(CArchMBState) = 0; + + //! Convert multibyte to wide character + virtual int convMBToWC(wchar_t*, + const char*, int, CArchMBState) = 0; + + //! Convert wide character to multibyte + virtual int convWCToMB(char*, wchar_t, CArchMBState) = 0; + + //! Return the architecture's native wide character encoding + virtual EWideCharEncoding + getWideCharEncoding() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTaskBar.h b/lib/arch/IArchTaskBar.h new file mode 100644 index 0000000..2cd20de --- /dev/null +++ b/lib/arch/IArchTaskBar.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTASKBAR_H +#define IARCHTASKBAR_H + +#include "IInterface.h" + +class IArchTaskBarReceiver; + +//! Interface for architecture dependent task bar control +/*! +This interface defines the task bar icon operations required +by synergy. Each architecture must implement this interface +though each operation can be a no-op. +*/ +class IArchTaskBar : public IInterface { +public: + // Event data is architecture dependent + typedef void* Event; + + //! @name manipulators + //@{ + + //! Add a receiver + /*! + Add a receiver object to be notified of user and application + events. This should be called before other methods. When + the receiver is added to the task bar, its icon appears on + the task bar. + */ + virtual void addReceiver(IArchTaskBarReceiver*) = 0; + + //! Remove a receiver + /*! + Remove a receiver object from the task bar. This removes the + icon from the task bar. + */ + virtual void removeReceiver(IArchTaskBarReceiver*) = 0; + + //! Update a receiver + /*! + Updates the display of the receiver on the task bar. This + should be called when the receiver appearance may have changed + (e.g. it's icon or tool tip has changed). + */ + virtual void updateReceiver(IArchTaskBarReceiver*) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTaskBarReceiver.h b/lib/arch/IArchTaskBarReceiver.h new file mode 100644 index 0000000..917f2fb --- /dev/null +++ b/lib/arch/IArchTaskBarReceiver.h @@ -0,0 +1,90 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTASKBARRECEIVER_H +#define IARCHTASKBARRECEIVER_H + +#include "IInterface.h" +#include "stdstring.h" + +//! Interface for architecture dependent task bar event handling +/*! +This interface defines the task bar icon event handlers required +by synergy. Each architecture must implement this interface +though each operation can be a no-op. +*/ +class IArchTaskBarReceiver : public IInterface { +public: + // Icon data is architecture dependent + typedef void* Icon; + + //! @name manipulators + //@{ + + //! Show status window + /*! + Open a window displaying current status. This should return + immediately without waiting for the window to be closed. + */ + virtual void showStatus() = 0; + + //! Popup menu + /*! + Popup a menu of operations at or around \c x,y and perform the + chosen operation. + */ + virtual void runMenu(int x, int y) = 0; + + //! Perform primary action + /*! + Perform the primary (default) action. + */ + virtual void primaryAction() = 0; + + //@} + //! @name accessors + //@{ + + //! Lock receiver + /*! + Locks the receiver from changing state. The receiver should be + locked when querying it's state to ensure consistent results. + Each call to \c lock() must have a matching \c unlock() and + locks cannot be nested. + */ + virtual void lock() const = 0; + + //! Unlock receiver + virtual void unlock() const = 0; + + //! Get icon + /*! + Returns the icon to display in the task bar. The interface + to set the icon is left to subclasses. Getting and setting + the icon must be thread safe. + */ + virtual const Icon getIcon() const = 0; + + //! Get tooltip + /*! + Returns the tool tip to display in the task bar. The interface + to set the tooltip is left to sublclasses. Getting and setting + the icon must be thread safe. + */ + virtual std::string getToolTip() const = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTime.h b/lib/arch/IArchTime.h new file mode 100644 index 0000000..dade64b --- /dev/null +++ b/lib/arch/IArchTime.h @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTIME_H +#define IARCHTIME_H + +#include "IInterface.h" + +//! Interface for architecture dependent time operations +/*! +This interface defines the time operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchTime : public IInterface { +public: + //! @name manipulators + //@{ + + //! Get the current time + /*! + Returns the number of seconds since some arbitrary starting time. + This should return as high a precision as reasonable. + */ + virtual double time() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am new file mode 100644 index 0000000..2b32473 --- /dev/null +++ b/lib/arch/Makefile.am @@ -0,0 +1,101 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + arch.dsp \ + CArchConsoleWindows.cpp \ + CArchDaemonWindows.cpp \ + CArchFileWindows.cpp \ + CArchLogWindows.cpp \ + CArchMiscWindows.cpp \ + CArchMultithreadWindows.cpp \ + CArchNetworkWinsock.cpp \ + CArchSleepWindows.cpp \ + CArchStringWindows.cpp \ + CArchTaskBarWindows.cpp \ + CArchTimeWindows.cpp \ + XArchWindows.cpp \ + CArchConsoleWindows.h \ + CArchDaemonWindows.h \ + CArchFileWindows.h \ + CArchLogWindows.h \ + CArchMiscWindows.h \ + CArchMultithreadWindows.h \ + CArchNetworkWinsock.h \ + CArchSleepWindows.h \ + CArchStringWindows.h \ + CArchTaskBarWindows.h \ + CArchTimeWindows.h \ + XArchWindows.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libarch.a +libarch_a_SOURCES = \ + CArch.cpp \ + CArchImpl.cpp \ + XArch.cpp \ + CArch.h \ + IArchConsole.h \ + IArchDaemon.h \ + IArchFile.h \ + IArchLog.h \ + IArchMultithread.h \ + IArchNetwork.h \ + IArchSleep.h \ + IArchString.h \ + IArchTaskBar.h \ + IArchTaskBarReceiver.h \ + IArchTime.h \ + XArch.h \ + $(NULL) +EXTRA_libarch_a_SOURCES = \ + CArchConsoleUnix.cpp \ + CArchDaemonNone.cpp \ + CArchDaemonUnix.cpp \ + CArchFileUnix.cpp \ + CArchLogUnix.cpp \ + CArchMultithreadPosix.cpp \ + CArchNetworkBSD.cpp \ + CArchSleepUnix.cpp \ + CArchStringUnix.cpp \ + CArchTaskBarXWindows.cpp \ + CArchTimeUnix.cpp \ + CMultibyte.cpp \ + CMultibyteOS.cpp \ + CMultibyteEmu.cpp \ + XArchUnix.cpp \ + vsnprintf.cpp \ + CArchConsoleUnix.h \ + CArchDaemonNone.h \ + CArchDaemonUnix.h \ + CArchFileUnix.h \ + CArchLogUnix.h \ + CArchMultithreadPosix.h \ + CArchNetworkBSD.h \ + CArchSleepUnix.h \ + CArchStringUnix.h \ + CArchTaskBarXWindows.h \ + CArchTimeUnix.h \ + XArchUnix.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + $(NULL) diff --git a/lib/arch/Makefile.in b/lib/arch/Makefile.in new file mode 100644 index 0000000..621b7cd --- /dev/null +++ b/lib/arch/Makefile.in @@ -0,0 +1,433 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + arch.dsp \ + CArchConsoleWindows.cpp \ + CArchDaemonWindows.cpp \ + CArchFileWindows.cpp \ + CArchLogWindows.cpp \ + CArchMiscWindows.cpp \ + CArchMultithreadWindows.cpp \ + CArchNetworkWinsock.cpp \ + CArchSleepWindows.cpp \ + CArchStringWindows.cpp \ + CArchTaskBarWindows.cpp \ + CArchTimeWindows.cpp \ + XArchWindows.cpp \ + CArchConsoleWindows.h \ + CArchDaemonWindows.h \ + CArchFileWindows.h \ + CArchLogWindows.h \ + CArchMiscWindows.h \ + CArchMultithreadWindows.h \ + CArchNetworkWinsock.h \ + CArchSleepWindows.h \ + CArchStringWindows.h \ + CArchTaskBarWindows.h \ + CArchTimeWindows.h \ + XArchWindows.h \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libarch.a +libarch_a_SOURCES = \ + CArch.cpp \ + CArchImpl.cpp \ + XArch.cpp \ + CArch.h \ + IArchConsole.h \ + IArchDaemon.h \ + IArchFile.h \ + IArchLog.h \ + IArchMultithread.h \ + IArchNetwork.h \ + IArchSleep.h \ + IArchString.h \ + IArchTaskBar.h \ + IArchTaskBarReceiver.h \ + IArchTime.h \ + XArch.h \ + $(NULL) + +EXTRA_libarch_a_SOURCES = \ + CArchConsoleUnix.cpp \ + CArchDaemonNone.cpp \ + CArchDaemonUnix.cpp \ + CArchFileUnix.cpp \ + CArchLogUnix.cpp \ + CArchMultithreadPosix.cpp \ + CArchNetworkBSD.cpp \ + CArchSleepUnix.cpp \ + CArchStringUnix.cpp \ + CArchTaskBarXWindows.cpp \ + CArchTimeUnix.cpp \ + CMultibyte.cpp \ + CMultibyteOS.cpp \ + CMultibyteEmu.cpp \ + XArchUnix.cpp \ + vsnprintf.cpp \ + CArchConsoleUnix.h \ + CArchDaemonNone.h \ + CArchDaemonUnix.h \ + CArchFileUnix.h \ + CArchLogUnix.h \ + CArchMultithreadPosix.h \ + CArchNetworkBSD.h \ + CArchSleepUnix.h \ + CArchStringUnix.h \ + CArchTaskBarXWindows.h \ + CArchTimeUnix.h \ + XArchUnix.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + $(NULL) + +subdir = lib/arch +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libarch_a_AR = $(AR) cru +libarch_a_LIBADD = +am_libarch_a_OBJECTS = CArch.$(OBJEXT) CArchImpl.$(OBJEXT) \ + XArch.$(OBJEXT) +libarch_a_OBJECTS = $(am_libarch_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CArch.Po $(DEPDIR)/CArchConsoleUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchDaemonNone.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchDaemonUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchFileUnix.Po $(DEPDIR)/CArchImpl.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchLogUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchMultithreadPosix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchNetworkBSD.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchSleepUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchStringUnix.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchTaskBarXWindows.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CArchTimeUnix.Po $(DEPDIR)/CMultibyte.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CMultibyteEmu.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CMultibyteOS.Po $(DEPDIR)/XArch.Po \ +@AMDEP_TRUE@ $(DEPDIR)/XArchUnix.Po $(DEPDIR)/vsnprintf.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libarch_a_SOURCES) $(EXTRA_libarch_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libarch_a_SOURCES) $(EXTRA_libarch_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/arch/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libarch.a: $(libarch_a_OBJECTS) $(libarch_a_DEPENDENCIES) + -rm -f libarch.a + $(libarch_a_AR) libarch.a $(libarch_a_OBJECTS) $(libarch_a_LIBADD) + $(RANLIB) libarch.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchConsoleUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchDaemonNone.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchDaemonUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchFileUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchImpl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchLogUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchMultithreadPosix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchNetworkBSD.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchSleepUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchStringUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchTaskBarXWindows.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CArchTimeUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMultibyte.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMultibyteEmu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMultibyteOS.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XArch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XArchUnix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vsnprintf.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/arch/XArch.cpp b/lib/arch/XArch.cpp new file mode 100644 index 0000000..9dce528 --- /dev/null +++ b/lib/arch/XArch.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArch.h" + +// +// XArch +// + +std::string +XArch::what() const throw() +{ + try { + if (m_what.empty() && m_eval != NULL) { + m_what = m_eval->eval(); + } + } + catch (...) { + // ignore + } + return m_what; +} diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h new file mode 100644 index 0000000..44c9394 --- /dev/null +++ b/lib/arch/XArch.h @@ -0,0 +1,166 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCH_H +#define XARCH_H + +#include "common.h" +#include "stdstring.h" + +//! Generic thread exception +/*! +Exceptions derived from this class are used by the multithreading +library to perform stack unwinding when a thread terminates. These +exceptions must always be rethrown by clients when caught. +*/ +class XThread { }; + +//! Thread exception to cancel +/*! +Thrown to cancel a thread. Clients must not throw this type, but +must rethrow it if caught (by XThreadCancel, XThread, or ...). +*/ +class XThreadCancel : public XThread { }; + +/*! +\def RETHROW_XTHREAD +Convenience macro to rethrow an XThread exception but ignore other +exceptions. Put this in your catch (...) handler after necessary +cleanup but before leaving or returning from the handler. +*/ +#define RETHROW_XTHREAD \ + try { throw; } catch (XThread&) { throw; } catch (...) { } + +//! Lazy error message string evaluation +/*! +This class encapsulates platform dependent error string lookup. +Platforms subclass this type, taking an appropriate error code +type in the c'tor and overriding eval() to return the error +string for that error code. +*/ +class XArchEval { +public: + XArchEval() { } + virtual ~XArchEval() { } + + virtual XArchEval* clone() const throw() = 0; + + virtual std::string eval() const throw() = 0; +}; + +//! Generic exception architecture dependent library +class XArch { +public: + XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { } + XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { } + XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { } + ~XArch() { delete m_eval; } + + std::string what() const throw(); + +private: + XArchEval* m_eval; + mutable std::string m_what; +}; + +// Macro to declare XArch derived types +#define XARCH_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_(XArchEval* adoptedEvaluator) : super_(adoptedEvaluator) { } \ + name_(const std::string& msg) : super_(msg) { } \ +} + +//! Generic network exception +/*! +Exceptions derived from this class are used by the networking +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchNetwork, XArch); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkWouldBlock, XArchNetwork); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork); + +//! Network insufficient resources +XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork); + +//! No support for requested network resource/service +XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork); + +//! Network I/O error +XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork); + +//! Network address is unavailable or not local +XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork); + +//! Network address in use +XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork); + +//! No route to address +XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork); + +//! Socket not connected +XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork); + +//! Remote end of socket has disconnected +XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork); + +//! Remote end of socket refused connection +XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork); + +//! Connection is in progress +XARCH_SUBCLASS(XArchNetworkConnecting, XArchNetwork); + +//! Remote end of socket is not responding +XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork); + +//! Generic network name lookup erros +XARCH_SUBCLASS(XArchNetworkName, XArchNetwork); + +//! The named host is unknown +XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName); + +//! The named host is known but has to address +XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName); + +//! Non-recoverable name server error +XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName); + +//! Temporary name server error +XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName); + +//! Generic daemon exception +/*! +Exceptions derived from this class are used by the daemon +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchDaemon, XArch); + +//! Could not daemonize +XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon); + +//! Could not install daemon +XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon); + +//! Could not uninstall daemon +XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon); + +//! Attempted to uninstall a daemon that was not installed +XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed); + + +#endif diff --git a/lib/arch/XArchUnix.cpp b/lib/arch/XArchUnix.cpp new file mode 100644 index 0000000..6f4047d --- /dev/null +++ b/lib/arch/XArchUnix.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchUnix.h" +#include + +// +// XArchEvalUnix +// + +XArchEval* +XArchEvalUnix::clone() const throw() +{ + return new XArchEvalUnix(m_errno); +} + +std::string +XArchEvalUnix::eval() const throw() +{ + // FIXME -- not thread safe + return strerror(m_errno); +} diff --git a/lib/arch/XArchUnix.h b/lib/arch/XArchUnix.h new file mode 100644 index 0000000..19e0df4 --- /dev/null +++ b/lib/arch/XArchUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHUNIX_H +#define XARCHUNIX_H + +#include "XArch.h" + +//! Lazy error message string evaluation for unix +class XArchEvalUnix : public XArchEval { +public: + XArchEvalUnix(int err) : m_errno(err) { } + virtual ~XArchEvalUnix() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/XArchWindows.cpp b/lib/arch/XArchWindows.cpp new file mode 100644 index 0000000..a57bc71 --- /dev/null +++ b/lib/arch/XArchWindows.cpp @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchWindows.h" + +// +// XArchEvalWindows +// + +XArchEval* +XArchEvalWindows::clone() const throw() +{ + return new XArchEvalWindows(m_errno); +} + +std::string +XArchEvalWindows::eval() const throw() +{ + char* cmsg; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + m_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&cmsg, + 0, + NULL) == 0) { + cmsg = NULL; + return "Unknown error"; + } + std::string smsg(cmsg); + LocalFree(cmsg); + return smsg; +} + + +// +// XArchEvalWinsock +// + +XArchEval* +XArchEvalWinsock::clone() const throw() +{ + return new XArchEvalWinsock(m_errno); +} + +std::string +XArchEvalWinsock::eval() const throw() +{ + // built-in windows function for looking up error message strings + // may not look up network error messages correctly. we'll have + // to do it ourself. + static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { + /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, + /* 10009 */{WSAEBADF, "Bad file handle"}, + /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, + /* 10014 */{WSAEFAULT, "WSAEFAULT"}, + /* 10022 */{WSAEINVAL, "WSAEINVAL"}, + /* 10024 */{WSAEMFILE, "No more file descriptors available"}, + /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, + /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, + /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, + /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, + /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, + /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, + /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, + /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, + /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, + /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, + /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, + /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, + /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, + /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, + /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, + /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, + /* 10051 */{WSAENETUNREACH, "The network can't be reached from this hos at this time"}, + /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, + /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, + /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, + /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, + /* 10056 */{WSAEISCONN, "The socket is already connected"}, + /* 10057 */{WSAENOTCONN, "The socket is not connected"}, + /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, + /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, + /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, + /* 10061 */{WSAECONNREFUSED, "The attempt to connect was forcefully rejected"}, + /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, + /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, + /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, + /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, + /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, + /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, + /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, + /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, + /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, + /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, + /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, + /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, + /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, + /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, + /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, + /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, + /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, + /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, + /* end */{0, NULL} + }; + + for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { + if (s_netErrorCodes[i].m_code == m_errno) { + return s_netErrorCodes[i].m_msg; + } + } + return "Unknown error"; +} diff --git a/lib/arch/XArchWindows.h b/lib/arch/XArchWindows.h new file mode 100644 index 0000000..56c2400 --- /dev/null +++ b/lib/arch/XArchWindows.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHWINDOWS_H +#define XARCHWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "XArch.h" +#include + +//! Lazy error message string evaluation for windows +class XArchEvalWindows : public XArchEval { +public: + XArchEvalWindows() : m_errno(GetLastError()) { } + XArchEvalWindows(DWORD err) : m_errno(err) { } + virtual ~XArchEvalWindows() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + DWORD m_errno; +}; + +//! Lazy error message string evaluation for winsock +class XArchEvalWinsock : public XArchEval { +public: + XArchEvalWinsock(int err) : m_errno(err) { } + virtual ~XArchEvalWinsock() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp new file mode 100644 index 0000000..db750f4 --- /dev/null +++ b/lib/arch/arch.dsp @@ -0,0 +1,294 @@ +# Microsoft Developer Studio Project File - Name="arch" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=arch - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "arch.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "arch.mak" CFG="arch - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "arch - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "arch - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "arch - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "arch - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "arch - Win32 Release" +# Name "arch - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CArch.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.cpp +# End Source File +# Begin Source File + +SOURCE=.\XArch.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CArch.h +# End Source File +# Begin Source File + +SOURCE=.\CArchConsoleWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchFileWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.h +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.h +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchTaskBarWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.h +# End Source File +# Begin Source File + +SOURCE=.\IArchConsole.h +# End Source File +# Begin Source File + +SOURCE=.\IArchDaemon.h +# End Source File +# Begin Source File + +SOURCE=.\IArchFile.h +# End Source File +# Begin Source File + +SOURCE=.\IArchLog.h +# End Source File +# Begin Source File + +SOURCE=.\IArchMultithread.h +# End Source File +# Begin Source File + +SOURCE=.\IArchNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\IArchSleep.h +# End Source File +# Begin Source File + +SOURCE=.\IArchString.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTaskBar.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTime.h +# End Source File +# Begin Source File + +SOURCE=.\XArch.h +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.h +# End Source File +# End Group +# Begin Group "Included Files" + +# PROP Default_Filter "inc" +# Begin Source File + +SOURCE=.\CArchConsoleWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchFileWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchTaskBarWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyte.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteEmu.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteOS.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\vsnprintf.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp new file mode 100644 index 0000000..f06c0a3 --- /dev/null +++ b/lib/arch/vsnprintf.cpp @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if HAVE_VSNPRINTF + +#if !defined(ARCH_VSNPRINTF) +# define ARCH_VSNPRINTF vsnprintf +#endif + +int +ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + int n = ::ARCH_VSNPRINTF(str, size, fmt, ap); + if (n > size) { + n = -1; + } + return n; +} + +#elif UNIX_LIKE // !HAVE_VSNPRINTF + +#include + +int +ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + static FILE* bitbucket = fopen("/dev/null", "w"); + if (bitbucket == NULL) { + // uh oh + if (size > 0) { + str[0] = '\0'; + } + return 0; + } + else { + // count the characters using the bitbucket + int n = vfprintf(bitbucket, fmt, ap); + if (n + 1 <= size) { + // it'll fit so print it into str + vsprintf(str, fmt, ap); + } + return n; + } +} + +#else // !HAVE_VSNPRINTF && !UNIX_LIKE + +// FIXME +#error vsnprintf not implemented + +#endif // !HAVE_VSNPRINTF diff --git a/lib/base/CFunctionJob.cpp b/lib/base/CFunctionJob.cpp new file mode 100644 index 0000000..bc16c50 --- /dev/null +++ b/lib/base/CFunctionJob.cpp @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CFunctionJob.h" + +// +// CFunctionJob +// + +CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) : + m_func(func), + m_arg(arg) +{ + // do nothing +} + +CFunctionJob::~CFunctionJob() +{ + // do nothing +} + +void +CFunctionJob::run() +{ + if (m_func != NULL) { + m_func(m_arg); + } +} diff --git a/lib/base/CFunctionJob.h b/lib/base/CFunctionJob.h new file mode 100644 index 0000000..e30bbfa --- /dev/null +++ b/lib/base/CFunctionJob.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CFUNCTIONJOB_H +#define CFUNCTIONJOB_H + +#include "IJob.h" + +//! Use a function as a job +/*! +A job class that invokes a function. +*/ +class CFunctionJob : public IJob { +public: + //! run() invokes \c func(arg) + CFunctionJob(void (*func)(void*), void* arg = NULL); + virtual ~CFunctionJob(); + + // IJob overrides + virtual void run(); + +private: + void (*m_func)(void*); + void* m_arg; +}; + +#endif diff --git a/lib/base/CJobList.cpp b/lib/base/CJobList.cpp new file mode 100644 index 0000000..3735889 --- /dev/null +++ b/lib/base/CJobList.cpp @@ -0,0 +1,113 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CJobList.h" +#include "IJob.h" +#include "CArch.h" + +// +// CJobList +// + +CJobList::CJobList() +{ + m_mutex = ARCH->newMutex(); +} + +CJobList::~CJobList() +{ + ARCH->closeMutex(m_mutex); +} + +void +CJobList::addJob(IJob* job) +{ + // ignore bogus job + if (job == NULL) { + return; + } + + // make a temporary list with the job. later we'll splice this + // list into our jobs list. since splice() never throws we + // don't have to try/catch to unlock the mutex. + CJobs tmpList; + tmpList.push_front(job); + + // add job to list + ARCH->lockMutex(m_mutex); + m_jobs.splice(m_jobs.begin(), tmpList); + ARCH->unlockMutex(m_mutex); +} + +void +CJobList::removeJob(IJob* job) +{ + if (job != NULL) { + ARCH->lockMutex(m_mutex); + m_jobs.remove(job); + ARCH->unlockMutex(m_mutex); + } +} + +void +CJobList::runJobs() const +{ + // this is a little tricky. to allow any number of threads to + // traverse the list while jobs are added and removed while + // not holding the mutex when running a job (so other threads + // or the running job can add and remove jobs), we insert a + // new element into the list. this element has a NULL job and + // is a "safe place" while we traverse the list. the safe place + // is inserted at the start of the list (with the mutex locked) + // then, for each job, we lock the mutex and move the safe place + // past the next job, unlock the mutex, run the job and repeat + // until there are no more jobs. the safe place will not be + // removed by any other thread and is after every job that has + // been run and before every job that still needs to run. when + // all the jobs have been run we remove the safe place. + + // add the safe place + CJobs tmpList; + tmpList.push_front(NULL); + ARCH->lockMutex(m_mutex); + m_jobs.splice(m_jobs.begin(), tmpList); + CJobs::iterator safePlace = m_jobs.begin(); + + // find the next non-NULL job (NULL jobs are safe places) + CJobs::iterator next = safePlace; + while (next != m_jobs.end() && *next == NULL) + ++next; + while (next != m_jobs.end()) { + // found a job. run it without holding a lock. note the + // race condition here: we release the lock, allowing + // removeJob() to remove this job before we run the job. + // therefore the caller cannot safely destroy the job + // until all runJobs() complete. + IJob* job = *next; + ++next; + m_jobs.splice(next, m_jobs, safePlace); + ARCH->unlockMutex(m_mutex); + job->run(); + + // next real job + ARCH->lockMutex(m_mutex); + next = safePlace; + while (next != m_jobs.end() && *next == NULL) + ++next; + } + + // remove the safe place + m_jobs.erase(safePlace); + ARCH->unlockMutex(m_mutex); +} diff --git a/lib/base/CJobList.h b/lib/base/CJobList.h new file mode 100644 index 0000000..780d19d --- /dev/null +++ b/lib/base/CJobList.h @@ -0,0 +1,72 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CJOBLIST_H +#define CJOBLIST_H + +#include "IArchMultithread.h" +#include "stdlist.h" + +class IJob; + +class CJobList { +public: + CJobList(); + ~CJobList(); + + //! @name manipulators + //@{ + + //! Add a job + /*! + Add a job to the list. The client keeps ownership of the job. + Jobs can be safely added while \c runJobs() is executing. + */ + void addJob(IJob*); + + //! Remove a job + /*! + Remove a job from the list. The client keeps ownership of the job. + Jobs can be safely removed while \c runJobs() is executing. + */ + void removeJob(IJob*); + + //@} + //! @name accessors + //@{ + + //! Run all jobs + /*! + Run all jobs in the list. Any number of threads can call + \c runJobs() at once. Jobs can be added and removed while + \c runJobs() is executing in the same or another thread. + Any job added after \c runJobs() starts will not be run + by that call to runJobs(). Destroying a removed job + while \c runJobs() is executing is not safe unless the + removed completed before \c runJobs() started. + */ + void runJobs() const; + + //@} + +private: + typedef std::list CJobs; + typedef CJobs::iterator iterator; + + CArchMutex m_mutex; + mutable CJobs m_jobs; +}; + +#endif + diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp new file mode 100644 index 0000000..4df3058 --- /dev/null +++ b/lib/base/CLog.cpp @@ -0,0 +1,313 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CLog.h" +#include "CString.h" +#include "CStringUtil.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "Version.h" +#include +#include + +// names of priorities +static const char* g_priority[] = { + "FATAL", + "ERROR", + "WARNING", + "NOTE", + "INFO", + "DEBUG", + "DEBUG1", + "DEBUG2" + }; + +// number of priorities +static const int g_numPriority = (int)(sizeof(g_priority) / + sizeof(g_priority[0])); + +// the default priority +#if defined(NDEBUG) +static const int g_defaultMaxPriority = 4; +#else +static const int g_defaultMaxPriority = 5; +#endif + +// length of longest string in g_priority +static const int g_maxPriorityLength = 7; + +// length of suffix string (": ") +static const int g_prioritySuffixLength = 2; + +// amount of padded required to fill in the priority prefix +static const int g_priorityPad = g_maxPriorityLength + + g_prioritySuffixLength; + +//! Convenience object to lock/unlock a mutex +class CLogLock { +public: + CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); } + ~CLogLock() { ARCH->unlockMutex(m_mutex); } + +private: + CArchMutex m_mutex; +}; + + +// +// CLog +// + +CLog* CLog::s_log = NULL; + +CLog::CLog() +{ + assert(s_log == NULL); + + // create mutex for multithread safe operation + m_mutex = ARCH->newMutex(); + + // other initalization + m_maxPriority = g_defaultMaxPriority; + m_maxNewlineLength = 0; + insert(new CConsoleLogOutputter); +} + +CLog::~CLog() +{ + // clean up + for (COutputterList::iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + delete *index; + } + for (COutputterList::iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); ++index) { + delete *index; + } + ARCH->closeMutex(m_mutex); + s_log = NULL; +} + +CLog* +CLog::getInstance() +{ + // note -- not thread safe; client must initialize log safely + if (s_log == NULL) { + s_log = new CLog; + } + return s_log; +} + +void +CLog::print(const char* fmt, ...) const +{ + // check if fmt begins with a priority argument + int priority = 4; + if (fmt[0] == '%' && fmt[1] == 'z') { + priority = fmt[2] - '\060'; + fmt += 3; + } + + // done if below priority threshold + if (priority > getFilter()) { + return; + } + + // compute prefix padding length + int pad = g_priorityPad; + + // print to buffer + char stack[1024]; + va_list args; + va_start(args, fmt); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, m_maxNewlineLength, fmt, args); + va_end(args); + + // output buffer + output(priority, buffer); + + // clean up + if (buffer != stack) + delete[] buffer; +} + +void +CLog::printt(const char* file, int line, const char* fmt, ...) const +{ + // check if fmt begins with a priority argument + int priority = 4; + if (fmt[0] == '%' && fmt[1] == 'z') { + priority = fmt[2] - '\060'; + fmt += 3; + } + + // done if below priority threshold + if (priority > getFilter()) { + return; + } + + // compute prefix padding length + char stack[1024]; + sprintf(stack, "%d", line); + int pad = strlen(file) + 1 /* comma */ + + strlen(stack) + 1 /* colon */ + 1 /* space */ + + g_priorityPad; + + // print to buffer, leaving space for a newline at the end + va_list args; + va_start(args, fmt); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, m_maxNewlineLength, fmt, args); + va_end(args); + + // print the prefix to the buffer. leave space for priority label. + sprintf(buffer + g_priorityPad, "%s,%d:", file, line); + buffer[pad - 1] = ' '; + + // discard file and line if priority < 0 + char* message = buffer; + if (priority < 0) { + message += pad - g_priorityPad; + } + + // output buffer + output(priority, message); + + // clean up + if (buffer != stack) + delete[] buffer; +} + +void +CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) +{ + assert(outputter != NULL); + assert(outputter->getNewline() != NULL); + + CLogLock lock(m_mutex); + if (alwaysAtHead) { + m_alwaysOutputters.push_front(outputter); + } + else { + m_outputters.push_front(outputter); + } + int newlineLength = strlen(outputter->getNewline()); + if (newlineLength > m_maxNewlineLength) { + m_maxNewlineLength = newlineLength; + } +} + +void +CLog::remove(ILogOutputter* outputter) +{ + CLogLock lock(m_mutex); + m_outputters.remove(outputter); + m_alwaysOutputters.remove(outputter); +} + +void +CLog::pop_front(bool alwaysAtHead) +{ + CLogLock lock(m_mutex); + COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; + if (!list->empty()) { + delete list->front(); + list->pop_front(); + } +} + +bool +CLog::setFilter(const char* maxPriority) +{ + if (maxPriority != NULL) { + for (int i = 0; i < g_numPriority; ++i) { + if (strcmp(maxPriority, g_priority[i]) == 0) { + setFilter(i); + return true; + } + } + return false; + } + return true; +} + +void +CLog::setFilter(int maxPriority) +{ + CLogLock lock(m_mutex); + m_maxPriority = maxPriority; +} + +int +CLog::getFilter() const +{ + CLogLock lock(m_mutex); + return m_maxPriority; +} + +void +CLog::output(int priority, char* msg) const +{ + assert(priority >= -1 && priority < g_numPriority); + assert(msg != NULL); + + // insert priority label + int n = -g_prioritySuffixLength; + if (priority >= 0) { + n = strlen(g_priority[priority]); + strcpy(msg + g_maxPriorityLength - n, g_priority[priority]); + msg[g_maxPriorityLength + 0] = ':'; + msg[g_maxPriorityLength + 1] = ' '; + msg[g_maxPriorityLength + 1] = ' '; + } + + // write to each outputter + CLogLock lock(m_mutex); + for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); + ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); + + // open the outputter + outputter->open(kApplication); + + // write message + outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n); + } + for (COutputterList::const_iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); + + // open the outputter + outputter->open(kApplication); + + // write message and break out of loop if it returns false + if (!outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n)) { + break; + } + } +} diff --git a/lib/base/CLog.h b/lib/base/CLog.h new file mode 100644 index 0000000..b25e0ba --- /dev/null +++ b/lib/base/CLog.h @@ -0,0 +1,207 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CLOG_H +#define CLOG_H + +#include "common.h" +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define CLOG (CLog::getInstance()) + +class ILogOutputter; + +//! Logging facility +/*! +The logging class; all console output should go through this class. +It supports multithread safe operation, several message priority levels, +filtering by priority, and output redirection. The macros LOG() and +LOGC() provide convenient access. +*/ +class CLog { +public: + //! Log levels + /*! + The logging priority levels in order of highest to lowest priority. + */ + enum ELevel { + kFATAL, //!< For fatal errors + kERROR, //!< For serious errors + kWARNING, //!< For minor errors and warnings + kNOTE, //!< For messages about notable events + kINFO, //!< For informational messages + kDEBUG, //!< For important debugging messages + kDEBUG1, //!< For more detailed debugging messages + kDEBUG2 //!< For even more detailed debugging messages + }; + + ~CLog(); + + //! @name manipulators + //@{ + + //! Add an outputter to the head of the list + /*! + Inserts an outputter to the head of the outputter list. When the + logger writes a message, it goes to the outputter at the head of + the outputter list. If that outputter's \c write() method returns + true then it also goes to the next outputter, as so on until an + outputter returns false or there are no more outputters. Outputters + still in the outputter list when the log is destroyed will be + deleted. If \c alwaysAtHead is true then the outputter is always + called before all outputters with \c alwaysAtHead false and the + return value of the outputter is ignored. + + By default, the logger has one outputter installed which writes to + the console. + */ + void insert(ILogOutputter* adopted, + bool alwaysAtHead = false); + + //! Remove an outputter from the list + /*! + Removes the first occurrence of the given outputter from the + outputter list. It does nothing if the outputter is not in the + list. The outputter is not deleted. + */ + void remove(ILogOutputter* orphaned); + + //! Remove the outputter from the head of the list + /*! + Removes and deletes the outputter at the head of the outputter list. + This does nothing if the outputter list is empty. Only removes + outputters that were inserted with the matching \c alwaysAtHead. + */ + void pop_front(bool alwaysAtHead = false); + + //! Set the minimum priority filter. + /*! + Set the filter. Messages below this priority are discarded. + The default priority is 4 (INFO) (unless built without NDEBUG + in which case it's 5 (DEBUG)). setFilter(const char*) returns + true if the priority \c name was recognized; if \c name is NULL + then it simply returns true. + */ + bool setFilter(const char* name); + void setFilter(int); + + //@} + //! @name accessors + //@{ + + //! Print a log message + /*! + Print a log message using the printf-like \c format and arguments. + */ + void print(const char* format, ...) const; + + //! Print a log message + /*! + Print a log message using the printf-like \c format and arguments + preceded by the filename and line number. + */ + void printt(const char* file, int line, + const char* format, ...) const; + + //! Get the minimum priority level. + int getFilter() const; + + //! Get the singleton instance of the log + static CLog* getInstance(); + + //@} + +private: + CLog(); + + void output(int priority, char* msg) const; + +private: + typedef std::list COutputterList; + + static CLog* s_log; + + CArchMutex m_mutex; + COutputterList m_outputters; + COutputterList m_alwaysOutputters; + int m_maxNewlineLength; + int m_maxPriority; +}; + +/*! +\def LOG(arg) +Write to the log. Because macros cannot accept variable arguments, this +should be invoked like so: +\code +LOG((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal")); +\endcode +In particular, notice the double open and close parentheses. Also note +that there is no comma after the \c CLOG_XXX. The \c XXX should be +replaced by one of enumerants in \c CLog::ELevel without the leading +\c k. For example, \c CLOG_INFO. The special \c CLOG_PRINT level will +not be filtered and is never prefixed by the filename and line number. + +If \c NOLOGGING is defined during the build then this macro expands to +nothing. If \c NDEBUG is defined during the build then it expands to a +call to CLog::print. Otherwise it expands to a call to CLog::printt, +which includes the filename and line number. +*/ + +/*! +\def LOGC(expr, arg) +Write to the log if and only if expr is true. Because macros cannot accept +variable arguments, this should be invoked like so: +\code +LOGC(x == y, (CLOG_XXX "%d and %d are equal", x, y)); +\endcode +In particular, notice the parentheses around everything after the boolean +expression. Also note that there is no comma after the \c CLOG_XXX. +The \c XXX should be replaced by one of enumerants in \c CLog::ELevel +without the leading \c k. For example, \c CLOG_INFO. The special +\c CLOG_PRINT level will not be filtered and is never prefixed by the +filename and line number. + +If \c NOLOGGING is defined during the build then this macro expands to +nothing. If \c NDEBUG is defined during the build then it expands to a +call to CLog::print. Otherwise it expands to a call to CLog::printt, +which includes the filename and line number. +*/ + +#if defined(NOLOGGING) +#define LOG(_a1) +#define LOGC(_a1, _a2) +#define CLOG_TRACE +#elif defined(NDEBUG) +#define LOG(_a1) CLOG->print _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 +#define CLOG_TRACE +#else +#define LOG(_a1) CLOG->printt _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->printt _a2 +#define CLOG_TRACE __FILE__, __LINE__, +#endif + +#define CLOG_PRINT CLOG_TRACE "%z\057" +#define CLOG_CRIT CLOG_TRACE "%z\060" +#define CLOG_ERR CLOG_TRACE "%z\061" +#define CLOG_WARN CLOG_TRACE "%z\062" +#define CLOG_NOTE CLOG_TRACE "%z\063" +#define CLOG_INFO CLOG_TRACE "%z\064" +#define CLOG_DEBUG CLOG_TRACE "%z\065" +#define CLOG_DEBUG1 CLOG_TRACE "%z\066" +#define CLOG_DEBUG2 CLOG_TRACE "%z\067" + +#endif diff --git a/lib/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp new file mode 100644 index 0000000..89edce2 --- /dev/null +++ b/lib/base/CStopwatch.cpp @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CStopwatch.h" +#include "CArch.h" + +// +// CStopwatch +// + +CStopwatch::CStopwatch(bool triggered) : + m_mark(0.0), + m_triggered(triggered), + m_stopped(triggered) +{ + if (!triggered) { + m_mark = ARCH->time(); + } +} + +CStopwatch::~CStopwatch() +{ + // do nothing +} + +double +CStopwatch::reset() +{ + if (m_stopped) { + const double dt = m_mark; + m_mark = 0.0; + return dt; + } + else { + const double t = ARCH->time(); + const double dt = t - m_mark; + m_mark = t; + return dt; + } +} + +void +CStopwatch::stop() +{ + if (m_stopped) { + return; + } + + // save the elapsed time + m_mark = ARCH->time() - m_mark; + m_stopped = true; +} + +void +CStopwatch::start() +{ + m_triggered = false; + if (!m_stopped) { + return; + } + + // set the mark such that it reports the time elapsed at stop() + m_mark = ARCH->time() - m_mark; + m_stopped = false; +} + +void +CStopwatch::setTrigger() +{ + stop(); + m_triggered = true; +} + +double +CStopwatch::getTime() +{ + if (m_triggered) { + const double dt = m_mark; + start(); + return dt; + } + else if (m_stopped) { + return m_mark; + } + else { + return ARCH->time() - m_mark; + } +} + +CStopwatch::operator double() +{ + return getTime(); +} + +bool +CStopwatch::isStopped() const +{ + return m_stopped; +} + +double +CStopwatch::getTime() const +{ + if (m_stopped) { + return m_mark; + } + else { + return ARCH->time() - m_mark; + } +} + +CStopwatch::operator double() const +{ + return getTime(); +} diff --git a/lib/base/CStopwatch.h b/lib/base/CStopwatch.h new file mode 100644 index 0000000..e6a3471 --- /dev/null +++ b/lib/base/CStopwatch.h @@ -0,0 +1,108 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTOPWATCH_H +#define CSTOPWATCH_H + +#include "common.h" + +//! A timer class +/*! +This class measures time intervals. All time interval measurement +should use this class. +*/ +class CStopwatch { +public: + /*! + The default constructor does an implicit reset() or setTrigger(). + If triggered == false then the clock starts ticking. + */ + CStopwatch(bool triggered = false); + ~CStopwatch(); + + //! @name manipulators + //@{ + + //! Reset the timer to zero + /*! + Set the start time to the current time, returning the time since + the last reset. This does not remove the trigger if it's set nor + does it start a stopped clock. If the clock is stopped then + subsequent reset()'s will return 0. + */ + double reset(); + + //! Stop the timer + /*! + Stop the stopwatch. The time interval while stopped is not + counted by the stopwatch. stop() does not remove the trigger. + Has no effect if already stopped. + */ + void stop(); + + //! Start the timer + /*! + Start the stopwatch. start() removes the trigger, even if the + stopwatch was already started. + */ + void start(); + + //! Stop the timer and set the trigger + /*! + setTrigger() stops the clock like stop() except there's an + implicit start() the next time (non-const) getTime() is called. + This is useful when you want the clock to start the first time + you check it. + */ + void setTrigger(); + + //! Get elapsed time + /*! + Returns the time since the last reset() (or calls reset() and + returns zero if the trigger is set). + */ + double getTime(); + //! Same as getTime() + operator double(); + //@} + //! @name accessors + //@{ + + //! Check if timer is stopped + /*! + Returns true if the stopwatch is stopped. + */ + bool isStopped() const; + + // return the time since the last reset(). + //! Get elapsed time + /*! + Returns the time since the last reset(). This cannot trigger the + stopwatch to start and will not clear the trigger. + */ + double getTime() const; + //! Same as getTime() const + operator double() const; + //@} + +private: + double getClock() const; + +private: + double m_mark; + bool m_triggered; + bool m_stopped; +}; + +#endif diff --git a/lib/base/CString.h b/lib/base/CString.h new file mode 100644 index 0000000..bc90500 --- /dev/null +++ b/lib/base/CString.h @@ -0,0 +1,25 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTRING_H +#define CSTRING_H + +#include "common.h" +#include "stdstring.h" + +// use standard C++ string class for our string class +typedef std::string CString; + +#endif + diff --git a/lib/base/CStringUtil.cpp b/lib/base/CStringUtil.cpp new file mode 100644 index 0000000..2e61c04 --- /dev/null +++ b/lib/base/CStringUtil.cpp @@ -0,0 +1,211 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CStringUtil.h" +#include "CArch.h" +#include "common.h" +#include "stdvector.h" +#include +#include +#include +#include + +// +// CStringUtil +// + +CString +CStringUtil::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vformat(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vformat(const char* fmt, va_list args) +{ + // find highest indexed substitution and the locations of substitutions + std::vector pos; + std::vector width; + std::vector index; + int maxIndex = 0; + for (const char* scan = fmt; *scan != '\0'; ++scan) { + if (*scan == '%') { + ++scan; + if (*scan == '\0') { + break; + } + else if (*scan == '%') { + // literal + index.push_back(0); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(2); + } + else if (*scan == '{') { + // get argument index + char* end; + int i = static_cast(strtol(scan + 1, &end, 10)); + if (*end != '}') { + // invalid index -- ignore + scan = end - 1; + } + else { + index.push_back(i); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(static_cast(end - scan + 2)); + if (i > maxIndex) { + maxIndex = i; + } + scan = end; + } + } + else { + // improper escape -- ignore + } + } + } + + // get args + std::vector value; + std::vector length; + value.push_back("%"); + length.push_back(1); + for (int i = 0; i < maxIndex; ++i) { + const char* arg = va_arg(args, const char*); + size_t len = strlen(arg); + value.push_back(arg); + length.push_back(len); + } + + // compute final length + size_t resultLength = strlen(fmt); + const int n = static_cast(pos.size()); + for (int i = 0; i < n; ++i) { + resultLength -= width[i]; + resultLength += length[index[i]]; + } + + // substitute + CString result; + result.reserve(resultLength); + size_t src = 0; + for (int i = 0; i < n; ++i) { + result.append(fmt + src, pos[i] - src); + result.append(value[index[i]]); + src = pos[i] + width[i]; + } + result.append(fmt + src); + + return result; +} + +CString +CStringUtil::print(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vprint(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vprint(const char* fmt, va_list args) +{ + char tmp[1024]; + char* buffer = vsprint(tmp, sizeof(tmp) / sizeof(tmp[0]), 0, 0, fmt, args); + if (buffer == tmp) { + return buffer; + } + else { + CString result(buffer); + delete[] buffer; + return result; + } +} + +char* +CStringUtil::vsprint(char* buffer, int len, + int prefix, int suffix, const char* fmt, va_list args) +{ + assert(len > 0); + + // try writing to input buffer + int n; + if (buffer != NULL && len >= prefix + suffix) { + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); + if (n >= 0 && n <= len - (prefix + suffix)) + return buffer; + } + + // start allocating buffers until we write the whole string + buffer = NULL; + do { + delete[] buffer; + len *= 2; + buffer = new char[len + (prefix + suffix)]; + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); + } while (n < 0 || n > len - (prefix + suffix)); + + return buffer; +} + + +// +// CStringUtil::CaselessCmp +// + +bool +CStringUtil::CaselessCmp::cmpEqual( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it + return tolower(a) == tolower(b); +} + +bool +CStringUtil::CaselessCmp::cmpLess( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it + return tolower(a) < tolower(b); +} + +bool +CStringUtil::CaselessCmp::less(const CString& a, const CString& b) +{ + return std::lexicographical_compare( + a.begin(), a.end(), + b.begin(), b.end(), + &CStringUtil::CaselessCmp::cmpLess); +} + +bool +CStringUtil::CaselessCmp::equal(const CString& a, const CString& b) +{ + return !(less(a, b) || less(b, a)); +} + +bool +CStringUtil::CaselessCmp::operator()(const CString& a, const CString& b) const +{ + return less(a, b); +} diff --git a/lib/base/CStringUtil.h b/lib/base/CStringUtil.h new file mode 100644 index 0000000..1ad5824 --- /dev/null +++ b/lib/base/CStringUtil.h @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTRINGUTIL_H +#define CSTRINGUTIL_H + +#include "CString.h" +#include + +//! String utilities +/*! +This class provides various functions for string manipulation. +*/ +class CStringUtil { +public: + //! Format positional arguments + /*! + Format a string using positional arguments. fmt has literal + characters and conversion specifications introduced by `\%': + - \c\%\% -- literal `\%' + - \c\%{n} -- positional element n, n a positive integer, {} are literal + + All arguments in the variable list are const char*. Positional + elements are indexed from 1. + */ + static CString format(const char* fmt, ...); + + //! Format positional arguments + /*! + Same as format() except takes va_list. + */ + static CString vformat(const char* fmt, va_list); + + //! Print a string using printf-style formatting + /*! + Equivalent to printf() except the result is returned as a CString. + */ + static CString print(const char* fmt, ...); + + //! Print a string using printf-style formatting + /*! + Same as print() except takes va_list. + */ + static CString vprint(const char* fmt, va_list); + + //! Print a string using printf-style formatting into a buffer + /*! + This is like print but print into a given buffer. If the resulting + string will not fit into \c buffer then a new buffer is allocated and + returned, otherwise \c buffer is returned. the caller must delete[] + the returned memory if is not \c buffer. + + \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and + at least \c suffix characters are available in the buffer before + and after the printed string, respectively. \c bufferLength is the + length of buffer and should not be adjusted by the caller to + account for \c prefix or \c suffix. + */ + static char* vsprint(char* buffer, int bufferLength, + int prefix, int suffix, const char* fmt, va_list); + + //! Case-insensitive comparisons + /*! + This class provides case-insensitve comparison functions. + */ + class CaselessCmp { + public: + //! Same as less() + bool operator()(const CString& a, const CString& b) const; + + //! Returns true iff \c a is lexicographically less than \c b + static bool less(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool equal(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically less than \c b + static bool cmpLess(const CString::value_type& a, + const CString::value_type& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool cmpEqual(const CString::value_type& a, + const CString::value_type& b); + }; +}; + +#endif + diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp new file mode 100644 index 0000000..9d8fd8d --- /dev/null +++ b/lib/base/CUnicode.cpp @@ -0,0 +1,798 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CUnicode.h" +#include "CArch.h" +#include + +// +// local utility functions +// + +inline +static +UInt16 +decode16(const UInt8* n) +{ + union x16 { + UInt8 n8[2]; + UInt16 n16; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + return c.n16; +} + +inline +static +UInt32 +decode32(const UInt8* n) +{ + union x32 { + UInt8 n8[4]; + UInt32 n32; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + c.n8[2] = n[2]; + c.n8[3] = n[3]; + return c.n32; +} + +inline +static +void +resetError(bool* errors) +{ + if (errors != NULL) { + *errors = false; + } +} + +inline +static +void +setError(bool* errors) +{ + if (errors != NULL) { + *errors = true; + } +} + + +// +// CUnicode +// + +UInt32 CUnicode::s_invalid = 0x0000ffff; +UInt32 CUnicode::s_replacement = 0x0000fffd; + +bool +CUnicode::isUTF8(const CString& src) +{ + // convert and test each character + const UInt8* data = reinterpret_cast(src.c_str()); + for (UInt32 n = src.size(); n > 0; ) { + if (fromUTF8(data, n) == s_invalid) { + return false; + } + } + return true; +} + +CString +CUnicode::UTF8ToUCS2(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00010000) { + setError(errors); + c = s_replacement; + } + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + + return dst; +} + +CString +CUnicode::UTF8ToUCS4(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(4 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + dst.append(reinterpret_cast(&c), 4); + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF16(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00110000) { + setError(errors); + c = s_replacement; + } + if (c < 0x00010000) { + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + else { + c -= 0x00010000; + UInt16 utf16h = static_cast((c >> 10) + 0xd800); + UInt16 utf16l = static_cast((c & 0x03ff) + 0xdc00); + dst.append(reinterpret_cast(&utf16h), 2); + dst.append(reinterpret_cast(&utf16l), 2); + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF32(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get size of input string and reserve some space in output + UInt32 n = src.size(); + CString dst; + dst.reserve(4 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00110000) { + setError(errors); + c = s_replacement; + } + dst.append(reinterpret_cast(&c), 4); + } + + return dst; +} + +CString +CUnicode::UTF8ToText(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert to wide char + UInt32 size; + wchar_t* tmp = UTF8ToWideChar(src, size, errors); + + // get length of multibyte string + int mblen; + CArchMBState state = ARCH->newMBState(); + size_t len = 0; + UInt32 n = size; + for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { + mblen = ARCH->convWCToMB(NULL, *scan, state); + if (mblen == -1) { + // unconvertable character + setError(errors); + len += 1; + } + else { + len += mblen; + } + } + + // handle nul terminator + mblen = ARCH->convWCToMB(NULL, L'\0', state); + if (mblen != -1) { + len += mblen; + } + assert(ARCH->isInitMBState(state) != 0); + + // allocate multibyte string + char* mbs = new char[len]; + + // convert to multibyte + char* dst = mbs; + n = size; + for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { + mblen = ARCH->convWCToMB(dst, *scan, state); + if (mblen == -1) { + // unconvertable character + *dst++ = '?'; + } + else { + dst += mblen; + } + } + mblen = ARCH->convWCToMB(dst, L'\0', state); + if (mblen != -1) { + // don't include nul terminator + dst += mblen - 1; + } + CString text(mbs, dst - mbs); + + // clean up + delete[] mbs; + delete[] tmp; + ARCH->closeMBState(state); + + return text; +} + +CString +CUnicode::UCS2ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 1; + return doUCS2ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UCS4ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 2; + return doUCS4ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UTF16ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 1; + return doUTF16ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UTF32ToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // convert + UInt32 n = src.size() >> 2; + return doUTF32ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::textToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get length of multibyte string + UInt32 n = src.size(); + size_t len = 0; + CArchMBState state = ARCH->newMBState(); + for (const char* scan = src.c_str(); n > 0; ) { + int mblen = ARCH->convMBToWC(NULL, scan, n, state); + switch (mblen) { + case -2: + // incomplete last character. convert to unknown character. + setError(errors); + len += 1; + n = 0; + break; + + case -1: + // invalid character. count one unknown character and + // start at the next byte. + setError(errors); + len += 1; + scan += 1; + n -= 1; + break; + + case 0: + len += 1; + scan += 1; + n -= 1; + break; + + default: + // normal character + len += 1; + scan += mblen; + n -= mblen; + break; + } + } + ARCH->initMBState(state); + + // allocate wide character string + wchar_t* wcs = new wchar_t[len]; + + // convert multibyte to wide char + n = src.size(); + wchar_t* dst = wcs; + for (const char* scan = src.c_str(); n > 0; ++dst) { + int mblen = ARCH->convMBToWC(dst, scan, n, state); + switch (mblen) { + case -2: + // incomplete character. convert to unknown character. + *dst = (wchar_t)0xfffd; + n = 0; + break; + + case -1: + // invalid character. count one unknown character and + // start at the next byte. + *dst = (wchar_t)0xfffd; + scan += 1; + n -= 1; + break; + + case 0: + *dst = (wchar_t)0x0000; + scan += 1; + n -= 1; + break; + + default: + // normal character + scan += mblen; + n -= mblen; + break; + } + } + + // convert to UTF8 + CString utf8 = wideCharToUTF8(wcs, len, errors); + + // clean up + delete[] wcs; + ARCH->closeMBState(state); + + return utf8; +} + +wchar_t* +CUnicode::UTF8ToWideChar(const CString& src, UInt32& size, bool* errors) +{ + // convert to platform's wide character encoding + CString tmp; + switch (ARCH->getWideCharEncoding()) { + case IArchString::kUCS2: + tmp = UTF8ToUCS2(src, errors); + size = tmp.size() >> 1; + break; + + case IArchString::kUCS4: + tmp = UTF8ToUCS4(src, errors); + size = tmp.size() >> 2; + break; + + case IArchString::kUTF16: + tmp = UTF8ToUTF16(src, errors); + size = tmp.size() >> 1; + break; + + case IArchString::kUTF32: + tmp = UTF8ToUTF32(src, errors); + size = tmp.size() >> 2; + break; + + default: + assert(0 && "unknown wide character encoding"); + } + + // copy to a wchar_t array + wchar_t* dst = new wchar_t[size]; + ::memcpy(dst, tmp.data(), sizeof(wchar_t) * size); + return dst; +} + +CString +CUnicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors) +{ + // convert from platform's wide character encoding. + // note -- this must include a wide nul character (independent of + // the CString's nul character). + switch (ARCH->getWideCharEncoding()) { + case IArchString::kUCS2: + return doUCS2ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUCS4: + return doUCS4ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUTF16: + return doUTF16ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUTF32: + return doUTF32ToUTF8(reinterpret_cast(src), size, errors); + + default: + assert(0 && "unknown wide character encoding"); + return CString(); + } +} + +CString +CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + toUTF8(dst, c, errors); + } + + return dst; +} + +CString +CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 4, --n) { + UInt32 c = decode32(data); + toUTF8(dst, c, errors); + } + + return dst; +} + +CString +CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + if (c < 0x0000d800 || c > 0x0000dfff) { + toUTF8(dst, c, errors); + } + else if (n == 1) { + // error -- missing second word + setError(errors); + toUTF8(dst, s_replacement, NULL); + } + else if (c >= 0x0000d800 && c <= 0x0000dbff) { + UInt32 c2 = decode16(data); + data += 2; + --n; + if (c2 < 0x0000dc00 || c2 > 0x0000dfff) { + // error -- [d800,dbff] not followed by [dc00,dfff] + setError(errors); + toUTF8(dst, s_replacement, NULL); + } + else { + c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000; + toUTF8(dst, c, errors); + } + } + else { + // error -- [dc00,dfff] without leading [d800,dbff] + setError(errors); + toUTF8(dst, s_replacement, NULL); + } + } + + return dst; +} + +CString +CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 4, --n) { + UInt32 c = decode32(data); + if (c >= 0x00110000) { + setError(errors); + c = s_replacement; + } + toUTF8(dst, c, errors); + } + + return dst; +} + +UInt32 +CUnicode::fromUTF8(const UInt8*& data, UInt32& n) +{ + assert(data != NULL); + assert(n != 0); + + // compute character encoding length, checking for overlong + // sequences (i.e. characters that don't use the shortest + // possible encoding). + UInt32 size; + if (data[0] < 0x80) { + // 0xxxxxxx + size = 1; + } + else if (data[0] < 0xc0) { + // 10xxxxxx -- in the middle of a multibyte character. counts + // as one invalid character. + --n; + ++data; + return s_invalid; + } + else if (data[0] < 0xe0) { + // 110xxxxx + size = 2; + } + else if (data[0] < 0xf0) { + // 1110xxxx + size = 3; + } + else if (data[0] < 0xf8) { + // 11110xxx + size = 4; + } + else if (data[0] < 0xfc) { + // 111110xx + size = 5; + } + else if (data[0] < 0xfe) { + // 1111110x + size = 6; + } + else { + // invalid sequence. dunno how many bytes to skip so skip one. + --n; + ++data; + return s_invalid; + } + + // make sure we have enough data + if (size > n) { + data += n; + n = 0; + return s_invalid; + } + + // extract character + UInt32 c; + switch (size) { + case 1: + c = static_cast(data[0]); + break; + + case 2: + c = ((static_cast(data[0]) & 0x1f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 3: + c = ((static_cast(data[0]) & 0x0f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[2]) & 0x3f) ); + break; + + case 4: + c = ((static_cast(data[0]) & 0x07) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 5: + c = ((static_cast(data[0]) & 0x03) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 6: + c = ((static_cast(data[0]) & 0x01) << 30) | + ((static_cast(data[1]) & 0x3f) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + default: + assert(0 && "invalid size"); + return s_invalid; + } + + // check that all bytes after the first have the pattern 10xxxxxx. + // truncated sequences are treated as a single malformed character. + bool truncated = false; + switch (size) { + case 6: + if ((data[5] & 0xc0) != 0x80) { + truncated = true; + size = 5; + } + // fall through + + case 5: + if ((data[4] & 0xc0) != 0x80) { + truncated = true; + size = 4; + } + // fall through + + case 4: + if ((data[3] & 0xc0) != 0x80) { + truncated = true; + size = 3; + } + // fall through + + case 3: + if ((data[2] & 0xc0) != 0x80) { + truncated = true; + size = 2; + } + // fall through + + case 2: + if ((data[1] & 0xc0) != 0x80) { + truncated = true; + size = 1; + } + } + + // update parameters + data += size; + n -= size; + + // invalid if sequence was truncated + if (truncated) { + return s_invalid; + } + + // check for characters that didn't use the smallest possible encoding + static UInt32 s_minChar[] = { + 0, + 0x00000000, + 0x00000080, + 0x00000800, + 0x00010000, + 0x00200000, + 0x04000000 + }; + if (c < s_minChar[size]) { + return s_invalid; + } + + // check for characters not in ISO-10646 + if (c >= 0x0000d800 && c <= 0x0000dfff) { + return s_invalid; + } + if (c >= 0x0000fffe && c <= 0x0000ffff) { + return s_invalid; + } + + return c; +} + +void +CUnicode::toUTF8(CString& dst, UInt32 c, bool* errors) +{ + UInt8 data[6]; + + // handle characters outside the valid range + if ((c >= 0x0000d800 && c <= 0x0000dfff) || c >= 0x80000000) { + setError(errors); + c = s_replacement; + } + + // convert to UTF-8 + if (c < 0x00000080) { + data[0] = static_cast(c); + dst.append(reinterpret_cast(data), 1); + } + else if (c < 0x00000800) { + data[0] = static_cast(((c >> 6) & 0x0000001f) + 0xc0); + data[1] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 2); + } + else if (c < 0x00010000) { + data[0] = static_cast(((c >> 12) & 0x0000000f) + 0xe0); + data[1] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[2] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 3); + } + else if (c < 0x00200000) { + data[0] = static_cast(((c >> 18) & 0x00000007) + 0xf0); + data[1] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[3] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 4); + } + else if (c < 0x04000000) { + data[0] = static_cast(((c >> 24) & 0x00000003) + 0xf8); + data[1] = static_cast(((c >> 18) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[3] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[4] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 5); + } + else if (c < 0x80000000) { + data[0] = static_cast(((c >> 30) & 0x00000001) + 0xfc); + data[1] = static_cast(((c >> 24) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 18) & 0x0000003f) + 0x80); + data[3] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[4] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[5] = static_cast((c & 0x0000003f) + 0x80); + dst.append(reinterpret_cast(data), 6); + } + else { + assert(0 && "character out of range"); + } +} diff --git a/lib/base/CUnicode.h b/lib/base/CUnicode.h new file mode 100644 index 0000000..85afbfd --- /dev/null +++ b/lib/base/CUnicode.h @@ -0,0 +1,143 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CUNICODE_H +#define CUNICODE_H + +#include "CString.h" +#include "BasicTypes.h" + +//! Unicode utility functions +/*! +This class provides functions for converting between various Unicode +encodings and the current locale encoding. +*/ +class CUnicode { +public: + //! @name accessors + //@{ + + //! Test UTF-8 string for validity + /*! + Returns true iff the string contains a valid sequence of UTF-8 + encoded characters. + */ + static bool isUTF8(const CString&); + + //! Convert from UTF-8 to UCS-2 encoding + /*! + Convert from UTF-8 to UCS-2. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UCS-2. + Decoding errors do not set *errors. + */ + static CString UTF8ToUCS2(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UCS-4 encoding + /*! + Convert from UTF-8 to UCS-4. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UCS-4. + Decoding errors do not set *errors. + */ + static CString UTF8ToUCS4(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UTF-16 encoding + /*! + Convert from UTF-8 to UTF-16. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UTF-16. + Decoding errors do not set *errors. + */ + static CString UTF8ToUTF16(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UTF-32 encoding + /*! + Convert from UTF-8 to UTF-32. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UTF-32. + Decoding errors do not set *errors. + */ + static CString UTF8ToUTF32(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to the current locale encoding + /*! + Convert from UTF-8 to the current locale encoding. If errors is not + NULL then *errors is set to true iff any character could not be encoded. + Decoding errors do not set *errors. + */ + static CString UTF8ToText(const CString&, bool* errors = NULL); + + //! Convert from UCS-2 to UTF-8 + /*! + Convert from UCS-2 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UCS2ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UCS-4 to UTF-8 + /*! + Convert from UCS-4 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UCS4ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UTF-16 to UTF-8 + /*! + Convert from UTF-16 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UTF16ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UTF-32 to UTF-8 + /*! + Convert from UTF-32 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ + static CString UTF32ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from the current locale encoding to UTF-8 + /*! + Convert from the current locale encoding to UTF-8. If errors is not + NULL then *errors is set to true iff any character could not be decoded. + */ + static CString textToUTF8(const CString&, bool* errors = NULL); + + //@} + +private: + // convert UTF8 to wchar_t string (using whatever encoding is native + // to the platform). caller must delete[] the returned string. the + // string is *not* nul terminated; the length (in characters) is + // returned in size. + static wchar_t* UTF8ToWideChar(const CString&, + UInt32& size, bool* errors); + + // convert nul terminated wchar_t string (in platform's native + // encoding) to UTF8. + static CString wideCharToUTF8(const wchar_t*, + UInt32 size, bool* errors); + + // internal conversion to UTF8 + static CString doUCS2ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUCS4ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUTF16ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUTF32ToUTF8(const UInt8* src, UInt32 n, bool* errors); + + // convert characters to/from UTF8 + static UInt32 fromUTF8(const UInt8*& src, UInt32& size); + static void toUTF8(CString& dst, UInt32 c, bool* errors); + +private: + static UInt32 s_invalid; + static UInt32 s_replacement; +}; + +#endif diff --git a/lib/base/IJob.h b/lib/base/IJob.h new file mode 100644 index 0000000..30ef5f5 --- /dev/null +++ b/lib/base/IJob.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IJOB_H +#define IJOB_H + +#include "IInterface.h" + +//! Job interface +/*! +A job is an interface for executing some function. +*/ +class IJob : public IInterface { +public: + //! Run the job + virtual void run() = 0; +}; + +#endif diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h new file mode 100644 index 0000000..585598c --- /dev/null +++ b/lib/base/ILogOutputter.h @@ -0,0 +1,72 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ILOGOUTPUTTER_H +#define ILOGOUTPUTTER_H + +#include "IInterface.h" +#include "CLog.h" + +//! Outputter interface +/*! +Type of outputter interface. The logger performs all output through +outputters. ILogOutputter overrides must not call any log functions +directly or indirectly. +*/ +class ILogOutputter : public IInterface { +public: + typedef CLog::ELevel ELevel; + + //! @name manipulators + //@{ + + //! Open the outputter + /*! + Opens the outputter for writing. Calling this method on an + already open outputter must have no effect. + */ + virtual void open(const char* title) = 0; + + //! Close the outputter + /*! + Close the outputter. Calling this method on an already closed + outputter must have no effect. + */ + virtual void close() = 0; + + //! Write a message with level + /*! + Writes \c message, which has the given \c level, to a log. + If this method returns true then CLog will stop passing the + message to all outputters in the outputter chain, otherwise + it continues. Most implementations should return true. + */ + virtual bool write(ELevel level, const char* message) = 0; + + //@} + //! @name accessors + //@{ + + //! Returns the newline sequence for the outputter + /*! + Different outputters use different character sequences for newlines. + This method returns the appropriate newline sequence for this + outputter. + */ + virtual const char* getNewline() const = 0; + + //@} +}; + +#endif diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp new file mode 100644 index 0000000..5f7a40b --- /dev/null +++ b/lib/base/LogOutputters.cpp @@ -0,0 +1,238 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "LogOutputters.h" +#include "CArch.h" + +// +// CStopLogOutputter +// + +CStopLogOutputter::CStopLogOutputter() +{ + // do nothing +} + +CStopLogOutputter::~CStopLogOutputter() +{ + // do nothing +} + +void +CStopLogOutputter::open(const char*) +{ + // do nothing +} + +void +CStopLogOutputter::close() +{ + // do nothing +} + +bool +CStopLogOutputter::write(ELevel, const char*) +{ + return false; +} + +const char* +CStopLogOutputter::getNewline() const +{ + return ""; +} + + +// +// CConsoleLogOutputter +// + +CConsoleLogOutputter::CConsoleLogOutputter() +{ + // do nothing +} + +CConsoleLogOutputter::~CConsoleLogOutputter() +{ + // do nothing +} + +void +CConsoleLogOutputter::open(const char* title) +{ + ARCH->openConsole(title); +} + +void +CConsoleLogOutputter::close() +{ + ARCH->closeConsole(); +} + +bool +CConsoleLogOutputter::write(ELevel, const char* msg) +{ + ARCH->writeConsole(msg); + return true; +} + +const char* +CConsoleLogOutputter::getNewline() const +{ + return ARCH->getNewlineForConsole(); +} + + +// +// CSystemLogOutputter +// + +CSystemLogOutputter::CSystemLogOutputter() +{ + // do nothing +} + +CSystemLogOutputter::~CSystemLogOutputter() +{ + // do nothing +} + +void +CSystemLogOutputter::open(const char* title) +{ + ARCH->openLog(title); +} + +void +CSystemLogOutputter::close() +{ + ARCH->closeLog(); +} + +bool +CSystemLogOutputter::write(ELevel level, const char* msg) +{ + IArchLog::ELevel archLogLevel; + switch (level) { + case CLog::kFATAL: + case CLog::kERROR: + archLogLevel = IArchLog::kERROR; + break; + + case CLog::kWARNING: + archLogLevel = IArchLog::kWARNING; + break; + + case CLog::kNOTE: + archLogLevel = IArchLog::kNOTE; + break; + + case CLog::kINFO: + archLogLevel = IArchLog::kINFO; + break; + + default: + archLogLevel = IArchLog::kDEBUG; + break; + + }; + ARCH->writeLog(archLogLevel, msg); + return true; +} + +const char* +CSystemLogOutputter::getNewline() const +{ + return ""; +} + + +// +// CSystemLogger +// + +CSystemLogger::CSystemLogger(const char* title) +{ + // redirect log messages + m_syslog = new CSystemLogOutputter; + m_stop = new CStopLogOutputter; + m_syslog->open(title); + CLOG->insert(m_stop); + CLOG->insert(m_syslog); +} + +CSystemLogger::~CSystemLogger() +{ + CLOG->remove(m_syslog); + CLOG->remove(m_stop); + delete m_stop; + delete m_syslog; +} + + +// +// CBufferedLogOutputter +// + +CBufferedLogOutputter::CBufferedLogOutputter(UInt32 maxBufferSize) : + m_maxBufferSize(maxBufferSize) +{ + // do nothing +} + +CBufferedLogOutputter::~CBufferedLogOutputter() +{ + // do nothing +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::begin() const +{ + return m_buffer.begin(); +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::end() const +{ + return m_buffer.end(); +} + +void +CBufferedLogOutputter::open(const char*) +{ + // do nothing +} + +void +CBufferedLogOutputter::close() +{ + // remove all elements from the buffer + m_buffer.clear(); +} + +bool +CBufferedLogOutputter::write(ELevel, const char* message) +{ + while (m_buffer.size() >= m_maxBufferSize) { + m_buffer.pop_front(); + } + m_buffer.push_back(CString(message)); + return true; +} + +const char* +CBufferedLogOutputter::getNewline() const +{ + return ""; +} diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h new file mode 100644 index 0000000..64befe5 --- /dev/null +++ b/lib/base/LogOutputters.h @@ -0,0 +1,128 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LOGOUTPUTTERS_H +#define LOGOUTPUTTERS_H + +#include "BasicTypes.h" +#include "ILogOutputter.h" +#include "CString.h" +#include "stddeque.h" + +//! Stop traversing log chain outputter +/*! +This outputter performs no output and returns false from \c write(), +causing the logger to stop traversing the outputter chain. Insert +this to prevent already inserted outputters from writing. +*/ +class CStopLogOutputter : public ILogOutputter { +public: + CStopLogOutputter(); + virtual ~CStopLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to console +/*! +This outputter writes output to the console. The level for each +message is ignored. +*/ +class CConsoleLogOutputter : public ILogOutputter { +public: + CConsoleLogOutputter(); + virtual ~CConsoleLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to system log +/*! +This outputter writes output to the system log. +*/ +class CSystemLogOutputter : public ILogOutputter { +public: + CSystemLogOutputter(); + virtual ~CSystemLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to system log only +/*! +Creating an object of this type inserts a CStopLogOutputter followed +by a CSystemLogOutputter into CLog. The destructor removes those +outputters. Add one of these to any scope that needs to write to +the system log (only) and restore the old outputters when exiting +the scope. +*/ +class CSystemLogger { +public: + CSystemLogger(const char* title); + ~CSystemLogger(); + +private: + ILogOutputter* m_syslog; + ILogOutputter* m_stop; +}; + +//! Save log history +/*! +This outputter records the last N log messages. +*/ +class CBufferedLogOutputter : public ILogOutputter { +private: + typedef std::deque CBuffer; + +public: + typedef CBuffer::const_iterator const_iterator; + + CBufferedLogOutputter(UInt32 maxBufferSize); + virtual ~CBufferedLogOutputter(); + + //! @name accessors + //@{ + + //! Get start of buffer + const_iterator begin() const; + + //! Get end of buffer + const_iterator end() const; + + //@} + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; + +private: + UInt32 m_maxBufferSize; + CBuffer m_buffer; +}; + +#endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am new file mode 100644 index 0000000..f750ba7 --- /dev/null +++ b/lib/base/Makefile.am @@ -0,0 +1,52 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + base.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libbase.a +libbase_a_SOURCES = \ + CFunctionJob.cpp \ + CJobList.cpp \ + CLog.cpp \ + CStopwatch.cpp \ + CStringUtil.cpp \ + CUnicode.cpp \ + LogOutputters.cpp \ + XBase.cpp \ + CFunctionJob.h \ + CJobList.h \ + CLog.h \ + CStopwatch.h \ + CString.h \ + CStringUtil.h \ + CUnicode.h \ + IJob.h \ + ILogOutputter.h \ + LogOutputters.h \ + TMethodJob.h \ + XBase.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + $(NULL) diff --git a/lib/base/Makefile.in b/lib/base/Makefile.in new file mode 100644 index 0000000..b9a6af1 --- /dev/null +++ b/lib/base/Makefile.in @@ -0,0 +1,363 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + base.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libbase.a +libbase_a_SOURCES = \ + CFunctionJob.cpp \ + CJobList.cpp \ + CLog.cpp \ + CStopwatch.cpp \ + CStringUtil.cpp \ + CUnicode.cpp \ + LogOutputters.cpp \ + XBase.cpp \ + CFunctionJob.h \ + CJobList.h \ + CLog.h \ + CStopwatch.h \ + CString.h \ + CStringUtil.h \ + CUnicode.h \ + IJob.h \ + ILogOutputter.h \ + LogOutputters.h \ + TMethodJob.h \ + XBase.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + $(NULL) + +subdir = lib/base +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libbase_a_AR = $(AR) cru +libbase_a_LIBADD = +am_libbase_a_OBJECTS = CFunctionJob.$(OBJEXT) CJobList.$(OBJEXT) \ + CLog.$(OBJEXT) CStopwatch.$(OBJEXT) CStringUtil.$(OBJEXT) \ + CUnicode.$(OBJEXT) LogOutputters.$(OBJEXT) XBase.$(OBJEXT) +libbase_a_OBJECTS = $(am_libbase_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CFunctionJob.Po $(DEPDIR)/CJobList.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CLog.Po $(DEPDIR)/CStopwatch.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CStringUtil.Po $(DEPDIR)/CUnicode.Po \ +@AMDEP_TRUE@ $(DEPDIR)/LogOutputters.Po $(DEPDIR)/XBase.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libbase_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libbase_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/base/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libbase.a: $(libbase_a_OBJECTS) $(libbase_a_DEPENDENCIES) + -rm -f libbase.a + $(libbase_a_AR) libbase.a $(libbase_a_OBJECTS) $(libbase_a_LIBADD) + $(RANLIB) libbase.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CFunctionJob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CJobList.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CLog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CStopwatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CStringUtil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CUnicode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/LogOutputters.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XBase.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/base/TMethodJob.h b/lib/base/TMethodJob.h new file mode 100644 index 0000000..54cd936 --- /dev/null +++ b/lib/base/TMethodJob.h @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMETHODJOB_H +#define CMETHODJOB_H + +#include "IJob.h" + +//! Use a function as a job +/*! +A job class that invokes a member function. +*/ +template +class TMethodJob : public IJob { +public: + //! run() invokes \c object->method(arg) + TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); + virtual ~TMethodJob(); + + // IJob overrides + virtual void run(); + +private: + T* m_object; + void (T::*m_method)(void*); + void* m_arg; +}; + +template +inline +TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : + m_object(object), + m_method(method), + m_arg(arg) +{ + // do nothing +} + +template +inline +TMethodJob::~TMethodJob() +{ + // do nothing +} + +template +inline +void +TMethodJob::run() +{ + if (m_object != NULL) { + (m_object->*m_method)(m_arg); + } +} + +#endif diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp new file mode 100644 index 0000000..022be30 --- /dev/null +++ b/lib/base/XBase.cpp @@ -0,0 +1,69 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XBase.h" +#include "CStringUtil.h" +#include +#include + +// +// XBase +// + +XBase::XBase() : + m_what() +{ + // do nothing +} + +XBase::XBase(const CString& msg) : + m_what(msg) +{ + // do nothing +} + +XBase::~XBase() +{ + // do nothing +} + +const char* +XBase::what() const +{ + if (m_what.empty()) { + m_what = getWhat(); + } + return m_what.c_str(); +} + +CString +XBase::format(const char* /*id*/, const char* fmt, ...) const throw() +{ + // FIXME -- lookup message string using id as an index. set + // fmt to that string if it exists. + + // format + CString result; + va_list args; + va_start(args, fmt); + try { + result = CStringUtil::vformat(fmt, args); + } + catch (...) { + // ignore + } + va_end(args); + + return result; +} diff --git a/lib/base/XBase.h b/lib/base/XBase.h new file mode 100644 index 0000000..e9161d7 --- /dev/null +++ b/lib/base/XBase.h @@ -0,0 +1,121 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XBASE_H +#define XBASE_H + +#include "CString.h" + +//! Exception base class +/*! +This is the base class of most exception types. +*/ +class XBase { +public: + //! Use getWhat() as the result of what() + XBase(); + //! Use \c msg as the result of what() + XBase(const CString& msg); + virtual ~XBase(); + + //! Reason for exception + virtual const char* what() const; + +protected: + //! Get a human readable string describing the exception + virtual CString getWhat() const throw() = 0; + + //! Format a string + /*! + Looks up a message format using \c id, using \c defaultFormat if + no format can be found, then replaces positional parameters in + the format string and returns the result. + */ + virtual CString format(const char* id, + const char* defaultFormat, ...) const throw(); + +private: + mutable CString m_what; +}; + +/*! +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() is not +declared. +*/ +#define XBASE_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ +} + +/*! +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() must be +implemented. +*/ +#define XBASE_SUBCLASS_WHAT(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ +} + +/*! +\def XBASE_SUBCLASS_FORMAT +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. what() is overridden +to call getWhat() when first called; getWhat() can format the +error message and can call what() to get the message passed to the +c'tor. +*/ +#define XBASE_SUBCLASS_FORMAT(name_, super_) \ +class name_ : public super_ { \ +private: \ + enum EState { kFirst, kFormat, kDone }; \ + \ +public: \ + name_() : super_(), m_state(kDone) { } \ + name_(const CString& msg) : super_(msg), m_state(kFirst) { } \ + \ + virtual const char* what() const \ + { \ + if (m_state == kFirst) { \ + m_state = kFormat; \ + m_formatted = getWhat(); \ + m_state = kDone; \ + } \ + if (m_state == kDone) { \ + return m_formatted.c_str(); \ + } \ + else { \ + return super_::what(); \ + } \ + } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ + \ +private: \ + mutable EState m_state; \ + mutable std::string m_formatted; \ +} + +#endif diff --git a/lib/base/base.dsp b/lib/base/base.dsp new file mode 100644 index 0000000..991f51c --- /dev/null +++ b/lib/base/base.dsp @@ -0,0 +1,174 @@ +# Microsoft Developer Studio Project File - Name="base" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=base - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "base.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "base.mak" CFG="base - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "base - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "base - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "base - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "base - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "base - Win32 Release" +# Name "base - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CFunctionJob.cpp +# End Source File +# Begin Source File + +SOURCE=.\CJobList.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLog.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStringUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.cpp +# End Source File +# Begin Source File + +SOURCE=.\LogOutputters.cpp +# End Source File +# Begin Source File + +SOURCE=.\XBase.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CFunctionJob.h +# End Source File +# Begin Source File + +SOURCE=.\CJobList.h +# End Source File +# Begin Source File + +SOURCE=.\CLog.h +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.h +# End Source File +# Begin Source File + +SOURCE=.\CString.h +# End Source File +# Begin Source File + +SOURCE=.\CStringUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.h +# End Source File +# Begin Source File + +SOURCE=.\IJob.h +# End Source File +# Begin Source File + +SOURCE=.\ILogOutputter.h +# End Source File +# Begin Source File + +SOURCE=.\LogOutputters.h +# End Source File +# Begin Source File + +SOURCE=.\TMethodJob.h +# End Source File +# Begin Source File + +SOURCE=.\XBase.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp new file mode 100644 index 0000000..8ada111 --- /dev/null +++ b/lib/client/CClient.cpp @@ -0,0 +1,754 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClient.h" +#include "CServerProxy.h" +#include "ISecondaryScreenFactory.h" +#include "CClipboard.h" +#include "CInputPacketStream.h" +#include "COutputPacketStream.h" +#include "CProtocolUtil.h" +#include "CSecondaryScreen.h" +#include "IServer.h" +#include "ProtocolTypes.h" +#include "XScreen.h" +#include "XSynergy.h" +#include "IDataSocket.h" +#include "ISocketFactory.h" +#include "XSocket.h" +#include "IStreamFilterFactory.h" +#include "CLock.h" +#include "CThread.h" +#include "CTimerThread.h" +#include "XMT.h" +#include "XThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CClient +// + +CClient::CClient(const CString& clientName) : + m_name(clientName), + m_screen(NULL), + m_server(NULL), + m_screenFactory(NULL), + m_socketFactory(NULL), + m_streamFilterFactory(NULL), + m_session(NULL), + m_active(false), + m_rejected(true), + m_status(kNotRunning) +{ + // do nothing +} + +CClient::~CClient() +{ + delete m_screenFactory; + delete m_socketFactory; + delete m_streamFilterFactory; +} + +void +CClient::setAddress(const CNetworkAddress& serverAddress) +{ + CLock lock(&m_mutex); + m_serverAddress = serverAddress; +} + +void +CClient::setScreenFactory(ISecondaryScreenFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_screenFactory; + m_screenFactory = adopted; +} + +void +CClient::setSocketFactory(ISocketFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_socketFactory; + m_socketFactory = adopted; +} + +void +CClient::setStreamFilterFactory(IStreamFilterFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_streamFilterFactory; + m_streamFilterFactory = adopted; +} + +void +CClient::exitMainLoop() +{ + m_screen->exitMainLoop(); +} + +void +CClient::addStatusJob(IJob* job) +{ + m_statusJobs.addJob(job); +} + +void +CClient::removeStatusJob(IJob* job) +{ + m_statusJobs.removeJob(job); +} + +bool +CClient::wasRejected() const +{ + return m_rejected; +} + +CClient::EStatus +CClient::getStatus(CString* msg) const +{ + CLock lock(&m_mutex); + if (msg != NULL) { + *msg = m_statusMessage; + } + return m_status; +} + +void +CClient::runStatusJobs() const +{ + m_statusJobs.runJobs(); +} + +void +CClient::setStatus(EStatus status, const char* msg) +{ + { + CLock lock(&m_mutex); + m_status = status; + if (m_status == kError) { + m_statusMessage = (msg == NULL) ? "Error" : msg; + } + else { + m_statusMessage = (msg == NULL) ? "" : msg; + } + } + runStatusJobs(); +} + +void +CClient::onError() +{ + setStatus(kError); + + // close down session but don't wait too long + deleteSession(3.0); +} + +void +CClient::onInfoChanged(const CClientInfo& info) +{ + LOG((CLOG_DEBUG "resolution changed")); + + CLock lock(&m_mutex); + if (m_server != NULL) { + m_server->onInfoChanged(info); + } +} + +bool +CClient::onGrabClipboard(ClipboardID id) +{ + CLock lock(&m_mutex); + if (m_server == NULL) { + // m_server can be NULL if the screen calls this method + // before we've gotten around to connecting to the server. + // we simply ignore the clipboard change in that case. + return false; + } + + // grab ownership + m_server->onGrabClipboard(id); + + // we now own the clipboard and it has not been sent to the server + m_ownClipboard[id] = true; + m_timeClipboard[id] = 0; + + // if we're not the active screen then send the clipboard now, + // otherwise we'll wait until we leave. + if (!m_active) { + sendClipboard(id); + } + + return true; +} + +void +CClient::onClipboardChanged(ClipboardID, const CString&) +{ + // ignore -- we'll check the clipboard when we leave +} + +void +CClient::open() +{ + // open the screen + try { + LOG((CLOG_DEBUG "opening screen")); + openSecondaryScreen(); + setStatus(kNotRunning); + } + catch (XScreenOpenFailure& e) { + // can't open screen + setStatus(kError, e.what()); + LOG((CLOG_DEBUG "failed to open screen")); + throw; + } +} + +void +CClient::mainLoop() +{ + { + CLock lock(&m_mutex); + + // check preconditions + assert(m_screen != NULL); + assert(m_server == NULL); + + // connection starts as unsuccessful + m_rejected = true; + } + + try { + setStatus(kNotRunning); + LOG((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); + + // start server interactions + { + CLock lock(&m_mutex); + m_session = new CThread(new TMethodJob( + this, &CClient::runSession)); + } + + // handle events + m_screen->mainLoop(); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + } + catch (XMT& e) { + LOG((CLOG_ERR "client error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + throw; + } + catch (XBase& e) { + LOG((CLOG_ERR "client error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + CLock lock(&m_mutex); + m_rejected = false; + } + catch (XThread&) { + setStatus(kNotRunning); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + throw; + } + catch (...) { + LOG((CLOG_ERR "client error: ")); + setStatus(kError); + + // clean up + deleteSession(); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); + throw; + } +} + +void +CClient::close() +{ + closeSecondaryScreen(); + LOG((CLOG_DEBUG "closed screen")); +} + +void +CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) +{ + { + CLock lock(&m_mutex); + m_active = true; + } + + m_screen->enter(xAbs, yAbs, mask); +} + +bool +CClient::leave() +{ + m_screen->leave(); + + CLock lock(&m_mutex); + m_active = false; + + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + sendClipboard(id); + } + } + + return true; +} + +void +CClient::setClipboard(ClipboardID id, const CString& data) +{ + // unmarshall + CClipboard clipboard; + clipboard.unmarshall(data, 0); + + // set screen's clipboard + m_screen->setClipboard(id, &clipboard); +} + +void +CClient::grabClipboard(ClipboardID id) +{ + // we no longer own the clipboard + { + CLock lock(&m_mutex); + m_ownClipboard[id] = false; + } + + m_screen->grabClipboard(id); +} + +void +CClient::setClipboardDirty(ClipboardID, bool) +{ + assert(0 && "shouldn't be called"); +} + +void +CClient::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) +{ + m_screen->keyDown(id, mask, button); +} + +void +CClient::keyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + m_screen->keyRepeat(id, mask, count, button); +} + +void +CClient::keyUp(KeyID id, KeyModifierMask mask, KeyButton button) +{ + m_screen->keyUp(id, mask, button); +} + +void +CClient::mouseDown(ButtonID id) +{ + m_screen->mouseDown(id); +} + +void +CClient::mouseUp(ButtonID id) +{ + m_screen->mouseUp(id); +} + +void +CClient::mouseMove(SInt32 x, SInt32 y) +{ + m_screen->mouseMove(x, y); +} + +void +CClient::mouseWheel(SInt32 delta) +{ + m_screen->mouseWheel(delta); +} + +void +CClient::screensaver(bool activate) +{ + m_screen->screensaver(activate); +} + +void +CClient::resetOptions() +{ + m_screen->resetOptions(); +} + +void +CClient::setOptions(const COptionsList& options) +{ + m_screen->setOptions(options); +} + +CString +CClient::getName() const +{ + return m_name; +} + +SInt32 +CClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); +} + +void +CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + m_screen->getShape(x, y, w, h); +} + +void +CClient::getCursorPos(SInt32& x, SInt32& y) const +{ + m_screen->getCursorPos(x, y); +} + +void +CClient::getCursorCenter(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CClient::openSecondaryScreen() +{ + assert(m_screen == NULL); + + // not active + m_active = false; + + // reset clipboard state + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_ownClipboard[id] = false; + m_timeClipboard[id] = 0; + } + + // create screen + LOG((CLOG_DEBUG1 "creating secondary screen")); + if (m_screenFactory != NULL) { + m_screen = m_screenFactory->create(this); + } + if (m_screen == NULL) { + throw XScreenOpenFailure(); + } + + // open screen + try { + LOG((CLOG_DEBUG1 "opening secondary screen")); + m_screen->open(); + } + catch (...) { + LOG((CLOG_DEBUG1 "destroying secondary screen")); + delete m_screen; + m_screen = NULL; + throw; + } +} + +void +CClient::closeSecondaryScreen() +{ + // close the secondary screen + try { + if (m_screen != NULL) { + LOG((CLOG_DEBUG1 "closing secondary screen")); + m_screen->close(); + } + } + catch (...) { + // ignore + } + + // clean up + LOG((CLOG_DEBUG1 "destroying secondary screen")); + delete m_screen; + m_screen = NULL; +} + +void +CClient::sendClipboard(ClipboardID id) +{ + // note -- m_mutex must be locked on entry + assert(m_screen != NULL); + assert(m_server != NULL); + + // get clipboard data. set the clipboard time to the last + // clipboard time before getting the data from the screen + // as the screen may detect an unchanged clipboard and + // avoid copying the data. + CClipboard clipboard; + if (clipboard.open(m_timeClipboard[id])) { + clipboard.close(); + } + m_screen->getClipboard(id, &clipboard); + + // check time + if (m_timeClipboard[id] == 0 || + clipboard.getTime() != m_timeClipboard[id]) { + // save new time + m_timeClipboard[id] = clipboard.getTime(); + + // marshall the data + CString data = clipboard.marshall(); + + // save and send data if different + if (data != m_dataClipboard[id]) { + m_dataClipboard[id] = data; + m_server->onClipboardChanged(id, data); + } + } +} + +void +CClient::runSession(void*) +{ + try { + LOG((CLOG_DEBUG "starting server proxy")); + runServer(); + m_screen->exitMainLoop(); + LOG((CLOG_DEBUG "stopping server proxy")); + } + catch (...) { + m_screen->exitMainLoop(); + LOG((CLOG_DEBUG "stopping server proxy")); + throw; + } +} + +void +CClient::deleteSession(double timeout) +{ + // get session thread object + CThread* thread; + { + CLock lock(&m_mutex); + thread = m_session; + m_session = NULL; + } + + // shut it down + if (thread != NULL) { + thread->cancel(); + thread->wait(timeout); + delete thread; + } +} + +void +CClient::runServer() +{ + IDataSocket* socket = NULL; + CServerProxy* proxy = NULL; + bool timedOut; + try { + // allow connect and handshake this much time to succeed + CTimerThread timer(15.0, &timedOut); + + // create socket and attempt to connect to server + LOG((CLOG_DEBUG1 "connecting to server")); + if (m_socketFactory != NULL) { + socket = m_socketFactory->create(); + } + assert(socket != NULL); + socket->connect(m_serverAddress); + + // create proxy + LOG((CLOG_INFO "connected to server")); + LOG((CLOG_DEBUG1 "negotiating with server")); + proxy = handshakeServer(socket); + } + catch (XThread&) { + if (timedOut) { + LOG((CLOG_ERR "connection timed out")); + setStatus(kError, "connection timed out"); + } + else { + // cancelled by some thread other than the timer + } + delete proxy; + delete socket; + throw; + } + catch (XSocketConnect& e) { + LOG((CLOG_ERR "connection failed: %s", e.what())); + setStatus(kError, e.what()); + delete socket; + return; + } + catch (XBase& e) { + LOG((CLOG_ERR "connection failed: %s", e.what())); + setStatus(kError, e.what()); + LOG((CLOG_INFO "disconnecting from server")); + delete socket; + return; + } + catch (...) { + LOG((CLOG_ERR "connection failed: ")); + setStatus(kError); + LOG((CLOG_INFO "disconnecting from server")); + delete socket; + return; + } + + // saver server proxy object + { + CLock lock(&m_mutex); + m_server = proxy; + } + + try { + // prepare for remote control + m_screen->remoteControl(); + + // process messages + bool rejected = true; + if (proxy != NULL) { + LOG((CLOG_DEBUG1 "communicating with server")); + setStatus(kRunning); + rejected = !proxy->mainLoop(); + setStatus(kNotRunning); + } + + // prepare for local control + m_screen->localControl(); + + // clean up + CLock lock(&m_mutex); + m_rejected = rejected; + m_server = NULL; + delete proxy; + LOG((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + } + catch (...) { + setStatus(kNotRunning); + m_screen->localControl(); + CLock lock(&m_mutex); + m_rejected = false; + m_server = NULL; + delete proxy; + LOG((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + throw; + } +} + +CServerProxy* +CClient::handshakeServer(IDataSocket* socket) +{ + // get the input and output streams + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; + + // attach filters + if (m_streamFilterFactory != NULL) { + input = m_streamFilterFactory->createInput(input, own); + output = m_streamFilterFactory->createOutput(output, own); + own = true; + } + + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + own = true; + + CServerProxy* proxy = NULL; + try { + // wait for hello from server + LOG((CLOG_DEBUG1 "wait for hello")); + SInt16 major, minor; + CProtocolUtil::readf(input, kMsgHello, &major, &minor); + + // check versions + LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); + if (major < kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { + throw XIncompatibleClient(major, minor); + } + + // say hello back + LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); + CProtocolUtil::writef(output, kMsgHelloBack, + kProtocolMajorVersion, + kProtocolMinorVersion, &m_name); + + // create server proxy + proxy = new CServerProxy(this, input, output); + + // negotiate + // FIXME + + return proxy; + } + catch (XIncompatibleClient& e) { + LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); + setStatus(kError, e.what()); + } + catch (XBase& e) { + LOG((CLOG_WARN "error communicating with server: %s", e.what())); + setStatus(kError, e.what()); + } + catch (...) { + // probably timed out + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + throw; + } + + // failed + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + + return NULL; +} diff --git a/lib/client/CClient.h b/lib/client/CClient.h new file mode 100644 index 0000000..5647e7e --- /dev/null +++ b/lib/client/CClient.h @@ -0,0 +1,206 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENT_H +#define CCLIENT_H + +#include "IScreenReceiver.h" +#include "IClient.h" +#include "IClipboard.h" +#include "CNetworkAddress.h" +#include "CMutex.h" +#include "CJobList.h" + +class CSecondaryScreen; +class CServerProxy; +class CThread; +class IDataSocket; +class IScreenReceiver; +class ISecondaryScreenFactory; +class ISocketFactory; +class IStreamFilterFactory; + +//! Synergy client +/*! +This class implements the top-level client algorithms for synergy. +*/ +class CClient : public IScreenReceiver, public IClient { +public: + enum EStatus { + kNotRunning, + kRunning, + kError, + kMaxStatus + }; + + /*! + This client will attempt to connect the server using \c clientName + as its name. + */ + CClient(const CString& clientName); + ~CClient(); + + //! @name manipulators + //@{ + + //! Set server address + /*! + Sets the server's address that the client should connect to. + */ + void setAddress(const CNetworkAddress& serverAddress); + + //! Set secondary screen factory + /*! + Sets the factory for creating secondary screens. This must be + set before calling open(). This object takes ownership of the + factory. + */ + void setScreenFactory(ISecondaryScreenFactory*); + + //! Set socket factory + /*! + Sets the factory used to create a socket to connect to the server. + This must be set before calling mainLoop(). This object takes + ownership of the factory. + */ + void setSocketFactory(ISocketFactory*); + + //! Set stream filter factory + /*! + Sets the factory used to filter the socket streams used to + communicate with the server. This object takes ownership + of the factory. + */ + void setStreamFilterFactory(IStreamFilterFactory*); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + void exitMainLoop(); + + //! Add a job to notify of status changes + /*! + The added job is run whenever the server's status changes in + certain externally visible ways. The client keeps ownership + of the job. + */ + void addStatusJob(IJob*); + + //! Remove a job to notify of status changes + /*! + Removes a previously added status notification job. A job can + remove itself when called but must not remove any other jobs. + The client keeps ownership of the job. + */ + void removeStatusJob(IJob*); + + //@} + //! @name accessors + //@{ + + //! + /*! + Returns true if the server rejected our connection. + */ + bool wasRejected() const; + + //! Get the status + /*! + Returns the current status and status message. + */ + EStatus getStatus(CString* = NULL) const; + + //@} + + // IScreenReceiver overrides + virtual void onError(); + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString&); + + // IClient overrides + virtual void open(); + virtual void mainLoop(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool dirty); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; + +private: + // notify status jobs of a change + void runStatusJobs() const; + + // set new status + void setStatus(EStatus, const char* msg = NULL); + + // open/close the secondary screen + void openSecondaryScreen(); + void closeSecondaryScreen(); + + // send the clipboard to the server + void sendClipboard(ClipboardID); + + // handle server messaging + void runSession(void*); + void deleteSession(double timeout = -1.0); + void runServer(); + CServerProxy* handshakeServer(IDataSocket*); + +private: + CMutex m_mutex; + CString m_name; + CSecondaryScreen* m_screen; + IScreenReceiver* m_server; + CNetworkAddress m_serverAddress; + ISecondaryScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; + CThread* m_session; + bool m_active; + bool m_rejected; + bool m_ownClipboard[kClipboardEnd]; + IClipboard::Time m_timeClipboard[kClipboardEnd]; + CString m_dataClipboard[kClipboardEnd]; + + // the status change jobs and status + CJobList m_statusJobs; + EStatus m_status; + CString m_statusMessage; +}; + +#endif diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp new file mode 100644 index 0000000..435cdc5 --- /dev/null +++ b/lib/client/CServerProxy.cpp @@ -0,0 +1,760 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServerProxy.h" +#include "CProtocolUtil.h" +#include "IClient.h" +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLock.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "XBase.h" +#include + +// +// CServerProxy +// + +CServerProxy::CServerProxy(IClient* client, + IInputStream* adoptedInput, IOutputStream* adoptedOutput) : + m_client(client), + m_input(adoptedInput), + m_output(adoptedOutput), + m_seqNum(0), + m_heartRate(kHeartRate) +{ + assert(m_client != NULL); + assert(m_input != NULL); + assert(m_output != NULL); + + // initialize modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) + m_modifierTranslationTable[id] = id; +} + +CServerProxy::~CServerProxy() +{ + delete m_input; + delete m_output; +} + +bool +CServerProxy::mainLoop() +{ + bool failedToConnect = false; + try { + // no compressed mouse motion yet + m_compressMouse = false; + + // not ignoring mouse motions + m_ignoreMouse = false; + + // reset sequence number + m_seqNum = 0; + + // handle messages from server + CStopwatch heartbeat; + for (;;) { + // if no input is pending then flush compressed mouse motion + if (getInputStream()->getSize() == 0) { + flushCompressedMouse(); + } + + // wait for a message + LOG((CLOG_DEBUG2 "waiting for message")); + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4, m_heartRate); + + // check if server hungup + if (n == 0) { + LOG((CLOG_NOTE "server disconnected")); + break; + } + + // check for time out + if (n == (UInt32)-1 || + (m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) { + // send heartbeat + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + heartbeat.reset(); + if (n == (UInt32)-1) { + // no message to process + continue; + } + } + + // verify we got an entire code + if (n != 4) { + // client sent an incomplete message + LOG((CLOG_ERR "incomplete message from server")); + break; + } + + // parse message + LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + mouseMove(); + } + + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } + + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } + + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } + + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } + + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } + + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } + + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } + + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } + + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screensaver(); + } + + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } + + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } + + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); + } + + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + break; + } + + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + SInt32 major, minor; + CProtocolUtil::readf(getInputStream(), + kMsgEIncompatible + 4, &major, &minor); + LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEBusy, 4) == 0) { + LOG((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str())); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + LOG((CLOG_ERR "server refused client with name \"%s\"", getName().c_str())); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + failedToConnect = true; + break; + } + + else { + // unknown message + LOG((CLOG_ERR "unknown message from server")); + LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); + failedToConnect = true; + break; + } + } + } + catch (XBase& e) { + LOG((CLOG_ERR "error: %s", e.what())); + } + catch (...) { + throw; + } + + return !failedToConnect; +} + +IClient* +CServerProxy::getClient() const +{ + return m_client; +} + +CString +CServerProxy::getName() const +{ + return m_client->getName(); +} + +IInputStream* +CServerProxy::getInputStream() const +{ + return m_input; +} + +IOutputStream* +CServerProxy::getOutputStream() const +{ + return m_output; +} + +void +CServerProxy::onError() +{ + // ignore +} + +void +CServerProxy::onInfoChanged(const CClientInfo& info) +{ + // ignore mouse motion until we receive acknowledgment of our info + // change message. + CLock lock(&m_mutex); + m_ignoreMouse = true; + + // send info update + sendInfo(info); +} + +bool +CServerProxy::onGrabClipboard(ClipboardID id) +{ + LOG((CLOG_DEBUG1 "sending clipboard %d changed", id)); + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum); + return true; +} + +void +CServerProxy::onClipboardChanged(ClipboardID id, const CString& data) +{ + CLock lock(&m_mutex); + LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data); +} + +void +CServerProxy::flushCompressedMouse() +{ + bool send = false; + SInt32 x = 0, y = 0; + { + CLock lock(&m_mutex); + if (m_compressMouse) { + m_compressMouse = false; + x = m_xMouse; + y = m_yMouse; + send = true; + } + } + + if (send) { + getClient()->mouseMove(x, y); + } +} + +void +CServerProxy::sendInfo(const CClientInfo& info) +{ + // note -- m_mutex should be locked on entry + LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); + CProtocolUtil::writef(getOutputStream(), kMsgDInfo, + info.m_x, info.m_y, + info.m_w, info.m_h, + info.m_zoneSize, + info.m_mx, info.m_my); +} + +KeyID +CServerProxy::translateKey(KeyID id) const +{ + static const KeyID s_translationTable[kKeyModifierIDLast][2] = { + { kKeyNone, kKeyNone }, + { kKeyShift_L, kKeyShift_R }, + { kKeyControl_L, kKeyControl_R }, + { kKeyAlt_L, kKeyAlt_R }, + { kKeyMeta_L, kKeyMeta_R }, + { kKeySuper_L, kKeySuper_R } + }; + + KeyModifierID id2 = kKeyModifierIDNull; + UInt32 side = 0; + switch (id) { + case kKeyShift_L: + id2 = kKeyModifierIDShift; + side = 0; + break; + + case kKeyShift_R: + id2 = kKeyModifierIDShift; + side = 1; + break; + + case kKeyControl_L: + id2 = kKeyModifierIDControl; + side = 0; + break; + + case kKeyControl_R: + id2 = kKeyModifierIDControl; + side = 1; + break; + + case kKeyAlt_L: + id2 = kKeyModifierIDAlt; + side = 0; + break; + + case kKeyAlt_R: + id2 = kKeyModifierIDAlt; + side = 1; + break; + + case kKeyMeta_L: + id2 = kKeyModifierIDMeta; + side = 0; + break; + + case kKeyMeta_R: + id2 = kKeyModifierIDMeta; + side = 1; + break; + + case kKeySuper_L: + id2 = kKeyModifierIDSuper; + side = 0; + break; + + case kKeySuper_R: + id2 = kKeyModifierIDSuper; + side = 1; + break; + } + + if (id2 != kKeyModifierIDNull) { + return s_translationTable[m_modifierTranslationTable[id2]][side]; + } + else { + return id; + } +} + +KeyModifierMask +CServerProxy::translateModifierMask(KeyModifierMask mask) const +{ + static const KeyModifierMask s_masks[kKeyModifierIDLast] = { + 0x0000, + KeyModifierShift, + KeyModifierControl, + KeyModifierAlt, + KeyModifierMeta, + KeyModifierSuper + }; + + KeyModifierMask newMask = mask & ~(KeyModifierShift | + KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper); + if ((mask & KeyModifierShift) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDShift]]; + } + if ((mask & KeyModifierControl) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDControl]]; + } + if ((mask & KeyModifierAlt) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDAlt]]; + } + if ((mask & KeyModifierMeta) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDMeta]]; + } + if ((mask & KeyModifierSuper) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]]; + } + return newMask; +} + +void +CServerProxy::enter() +{ + // parse + SInt16 x, y; + UInt16 mask; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), + kMsgCEnter + 4, &x, &y, &seqNum, &mask); + LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); + + // discard old compressed mouse motion, if any + { + CLock lock(&m_mutex); + m_compressMouse = false; + m_seqNum = seqNum; + } + + // forward + getClient()->enter(x, y, seqNum, static_cast(mask), false); +} + +void +CServerProxy::leave() +{ + // parse + LOG((CLOG_DEBUG1 "recv leave")); + + // send last mouse motion + flushCompressedMouse(); + + // forward + getClient()->leave(); +} + +void +CServerProxy::setClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), + kMsgDClipboard + 4, &id, &seqNum, &data); + LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->setClipboard(id, data); +} + +void +CServerProxy::grabClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + LOG((CLOG_DEBUG "recv grab clipboard %d", id)); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->grabClipboard(id); +} + +void +CServerProxy::keyDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, + &id, &mask, &button); + LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); + + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyDown(id2, mask2, button); +} + +void +CServerProxy::keyRepeat() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, count, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4, + &id, &mask, &count, &button); + LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button)); + + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyRepeat(id2, mask2, count, button); +} + +void +CServerProxy::keyUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button); + LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); + + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyUp(id2, mask2, button); +} + +void +CServerProxy::mouseDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id); + LOG((CLOG_DEBUG1 "recv mouse down id=%d", id)); + + // forward + getClient()->mouseDown(static_cast(id)); +} + +void +CServerProxy::mouseUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id); + LOG((CLOG_DEBUG1 "recv mouse up id=%d", id)); + + // forward + getClient()->mouseUp(static_cast(id)); +} + +void +CServerProxy::mouseMove() +{ + // parse + bool ignore; + SInt16 x, y; + CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y); + + { + // note if we should ignore the move + CLock lock(&m_mutex); + ignore = m_ignoreMouse; + + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) { + m_compressMouse = true; + } + + // if compressing then ignore the motion but record it + if (m_compressMouse) { + ignore = true; + m_xMouse = x; + m_yMouse = y; + } + } + LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); + + // forward + if (!ignore) { + getClient()->mouseMove(x, y); + } +} + +void +CServerProxy::mouseWheel() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt16 delta; + CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta); + LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); + + // forward + getClient()->mouseWheel(delta); +} + +void +CServerProxy::screensaver() +{ + // parse + SInt8 on; + CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on); + LOG((CLOG_DEBUG1 "recv screen saver on=%d", on)); + + // forward + getClient()->screensaver(on != 0); +} + +void +CServerProxy::resetOptions() +{ + // parse + LOG((CLOG_DEBUG1 "recv reset options")); + + // forward + getClient()->resetOptions(); + + CLock lock(&m_mutex); + + // reset heart rate + m_heartRate = kHeartRate; + + // reset modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) { + m_modifierTranslationTable[id] = id; + } + + // send heartbeat if necessary + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + } +} + +void +CServerProxy::setOptions() +{ + // parse + COptionsList options; + CProtocolUtil::readf(getInputStream(), kMsgDSetOptions + 4, &options); + LOG((CLOG_DEBUG1 "recv set options size=%d", options.size())); + + // forward + getClient()->setOptions(options); + + CLock lock(&m_mutex); + + // update modifier table + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + KeyModifierID id = kKeyModifierIDNull; + if (options[i] == kOptionModifierMapForShift) { + id = kKeyModifierIDShift; + } + else if (options[i] == kOptionModifierMapForControl) { + id = kKeyModifierIDControl; + } + else if (options[i] == kOptionModifierMapForAlt) { + id = kKeyModifierIDAlt; + } + else if (options[i] == kOptionModifierMapForMeta) { + id = kKeyModifierIDMeta; + } + else if (options[i] == kOptionModifierMapForSuper) { + id = kKeyModifierIDSuper; + } + else if (options[i] == kOptionHeartbeat) { + // update heart rate + m_heartRate = 1.0e-3 * static_cast(options[i + 1]); + + // send heartbeat if necessary + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + } + } + if (id != kKeyModifierIDNull) { + m_modifierTranslationTable[id] = + static_cast(options[i + 1]); + LOG((CLOG_DEBUG1 "modifier %d mapped to %d", id, m_modifierTranslationTable[id])); + } + } +} + +void +CServerProxy::queryInfo() +{ + // get current info + CClientInfo info; + getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getClient()->getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getClient()->getJumpZoneSize(); + + // send it + CLock lock(&m_mutex); + sendInfo(info); +} + +void +CServerProxy::infoAcknowledgment() +{ + // parse + LOG((CLOG_DEBUG1 "recv info acknowledgment")); + + // now allow mouse motion + CLock lock(&m_mutex); + m_ignoreMouse = false; +} diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h new file mode 100644 index 0000000..cbb58f1 --- /dev/null +++ b/lib/client/CServerProxy.h @@ -0,0 +1,136 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSERVERPROXY_H +#define CSERVERPROXY_H + +#include "IScreenReceiver.h" +#include "KeyTypes.h" +#include "CMutex.h" + +class IClient; +class IInputStream; +class IOutputStream; + +//! Proxy for server +/*! +This class acts a proxy for the server, converting calls into messages +to the server and messages from the server to calls on the client. +*/ +class CServerProxy : public IScreenReceiver { +public: + /*! \c adoptedInput is the stream from the server and + \c adoptedOutput is the stream to the server. This object + takes ownership of both and destroys them in the d'tor. + Messages from the server are converted to calls on \c client. + */ + CServerProxy(IClient* client, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CServerProxy(); + + //! @name manipulators + //@{ + + //! Run event loop + /*! + Run the event loop and return when the server disconnects or + requests the client to disconnect. Return true iff the server + didn't reject our connection. + + (cancellation point) + */ + bool mainLoop(); + + //@} + //! @name accessors + //@{ + + //! Get client + /*! + Returns the client passed to the c'tor. + */ + IClient* getClient() const; + + //! Get input stream + /*! + Return the input stream passed to the c'tor. + */ + IInputStream* getInputStream() const; + + //! Get output stream + /*! + Return the output stream passed to the c'tor. + */ + IOutputStream* getOutputStream() const; + + //@} + + // IScreenReceiver overrides + virtual void onError(); + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString& data); + +private: + + // get the client name (from the client) + CString getName() const; + + // if compressing mouse motion then send the last motion now + void flushCompressedMouse(); + + void sendInfo(const CClientInfo&); + + // modifier key translation + KeyID translateKey(KeyID) const; + KeyModifierMask translateModifierMask(KeyModifierMask) const; + + // message handlers + void enter(); + void leave(); + void setClipboard(); + void grabClipboard(); + void keyDown(); + void keyRepeat(); + void keyUp(); + void mouseDown(); + void mouseUp(); + void mouseMove(); + void mouseWheel(); + void screensaver(); + void resetOptions(); + void setOptions(); + void queryInfo(); + void infoAcknowledgment(); + +private: + CMutex m_mutex; + + IClient* m_client; + IInputStream* m_input; + IOutputStream* m_output; + + UInt32 m_seqNum; + + bool m_compressMouse; + SInt32 m_xMouse, m_yMouse; + + bool m_ignoreMouse; + + KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; + double m_heartRate; +}; + +#endif diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am new file mode 100644 index 0000000..9d6ba46 --- /dev/null +++ b/lib/client/Makefile.am @@ -0,0 +1,42 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + client.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libclient.a +libclient_a_SOURCES = \ + CClient.cpp \ + CServerProxy.cpp \ + CClient.h \ + CServerProxy.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) diff --git a/lib/client/Makefile.in b/lib/client/Makefile.in new file mode 100644 index 0000000..3e6f4d8 --- /dev/null +++ b/lib/client/Makefile.in @@ -0,0 +1,342 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + client.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libclient.a +libclient_a_SOURCES = \ + CClient.cpp \ + CServerProxy.cpp \ + CClient.h \ + CServerProxy.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) + +subdir = lib/client +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libclient_a_AR = $(AR) cru +libclient_a_LIBADD = +am_libclient_a_OBJECTS = CClient.$(OBJEXT) CServerProxy.$(OBJEXT) +libclient_a_OBJECTS = $(am_libclient_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClient.Po $(DEPDIR)/CServerProxy.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libclient_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libclient_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/client/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libclient.a: $(libclient_a_OBJECTS) $(libclient_a_DEPENDENCIES) + -rm -f libclient.a + $(libclient_a_AR) libclient.a $(libclient_a_OBJECTS) $(libclient_a_LIBADD) + $(RANLIB) libclient.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClient.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CServerProxy.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/client/client.dsp b/lib/client/client.dsp new file mode 100644 index 0000000..eb8e2e6 --- /dev/null +++ b/lib/client/client.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=client - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "client.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "client.mak" CFG="client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "client - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "client - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "client - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "client - Win32 Release" +# Name "client - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClient.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/common/BasicTypes.h b/lib/common/BasicTypes.h new file mode 100644 index 0000000..c717663 --- /dev/null +++ b/lib/common/BasicTypes.h @@ -0,0 +1,87 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef BASICTYPES_H +#define BASICTYPES_H + +#include "common.h" + +// +// pick types of particular sizes +// + +#if !defined(TYPE_OF_SIZE_1) +# if SIZEOF_CHAR == 1 +# define TYPE_OF_SIZE_1 char +# endif +#endif + +#if !defined(TYPE_OF_SIZE_2) +# if SIZEOF_INT == 2 +# define TYPE_OF_SIZE_2 int +# else +# define TYPE_OF_SIZE_2 short +# endif +#endif + +#if !defined(TYPE_OF_SIZE_4) +# if SIZEOF_INT == 4 +# define TYPE_OF_SIZE_4 int +# else +# define TYPE_OF_SIZE_4 long +# endif +#endif + +// +// verify existence of required types +// + +#if !defined(TYPE_OF_SIZE_1) +# error No 1 byte integer type +#endif +#if !defined(TYPE_OF_SIZE_2) +# error No 2 byte integer type +#endif +#if !defined(TYPE_OF_SIZE_4) +# error No 4 byte integer type +#endif + + +// +// make typedefs +// +// except for SInt8 and UInt8 these types are only guaranteed to be +// at least as big as indicated (in bits). that is, they may be +// larger than indicated. +// + +typedef signed TYPE_OF_SIZE_1 SInt8; +typedef signed TYPE_OF_SIZE_2 SInt16; +typedef signed TYPE_OF_SIZE_4 SInt32; + +typedef unsigned TYPE_OF_SIZE_1 UInt8; +typedef unsigned TYPE_OF_SIZE_2 UInt16; +typedef unsigned TYPE_OF_SIZE_4 UInt32; + +// +// clean up +// + +#undef TYPE_OF_SIZE_1 +#undef TYPE_OF_SIZE_2 +#undef TYPE_OF_SIZE_4 + +#endif + + diff --git a/lib/common/IInterface.h b/lib/common/IInterface.h new file mode 100644 index 0000000..97df31c --- /dev/null +++ b/lib/common/IInterface.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IINTERFACE_H +#define IINTERFACE_H + +#include "common.h" + +//! Base class of interfaces +/*! +This is the base class of all interface classes. An interface class has +only pure virtual methods. +*/ +class IInterface { +public: + //! Interface destructor does nothing + virtual ~IInterface() { } +}; + +#endif diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am new file mode 100644 index 0000000..cfaafeb --- /dev/null +++ b/lib/common/Makefile.am @@ -0,0 +1,44 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + common.dsp \ + BasicTypes.h \ + IInterface.h \ + Version.h \ + common.h \ + stdbitset.h \ + stddeque.h \ + stdfstream.h \ + stdistream.h \ + stdlist.h \ + stdmap.h \ + stdostream.h \ + stdpost.h \ + stdpre.h \ + stdset.h \ + stdsstream.h \ + stdstring.h \ + stdvector.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +INCLUDES = \ + $(NULL) diff --git a/lib/common/Makefile.in b/lib/common/Makefile.in new file mode 100644 index 0000000..8925b7c --- /dev/null +++ b/lib/common/Makefile.in @@ -0,0 +1,248 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + common.dsp \ + BasicTypes.h \ + IInterface.h \ + Version.h \ + common.h \ + stdbitset.h \ + stddeque.h \ + stdfstream.h \ + stdistream.h \ + stdlist.h \ + stdmap.h \ + stdostream.h \ + stdpost.h \ + stdpre.h \ + stdset.h \ + stdsstream.h \ + stdstring.h \ + stdvector.h \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +INCLUDES = \ + $(NULL) + +subdir = lib/common +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = +DIST_COMMON = Makefile.am Makefile.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/common/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: +tags: TAGS +TAGS: + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/common/Version.h b/lib/common/Version.h new file mode 100644 index 0000000..d7a67b1 --- /dev/null +++ b/lib/common/Version.h @@ -0,0 +1,45 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef VERSION_H +#define VERSION_H + +#include "common.h" + +// set version macro if not set yet +#if !defined(VERSION) +# define VERSION "1.0.14" +#endif + +// important strings +static const char* kApplication = "synergy"; +static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; +static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; +static const char* kWebsite = "http://synergy2.sourceforge.net/"; + +// build version. follows linux kernel style: an even minor number implies +// a release version, odd implies development version. +static const char* kVersion = VERSION; + +// application version +static const char* kAppVersion = "synergy " VERSION; + +// exit codes +static const int kExitSuccess = 0; // successful completion +static const int kExitFailed = 1; // general failure +static const int kExitTerminated = 2; // killed by signal +static const int kExitArgs = 3; // bad arguments +static const int kExitConfig = 4; // cannot read configuration + +#endif diff --git a/lib/common/common.dsp b/lib/common/common.dsp new file mode 100644 index 0000000..ca7f513 --- /dev/null +++ b/lib/common/common.dsp @@ -0,0 +1,162 @@ +# Microsoft Developer Studio Project File - Name="common" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=common - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "common.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "common.mak" CFG="common - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "common - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "common - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "common - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "common - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "common - Win32 Release" +# Name "common - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BasicTypes.h +# End Source File +# Begin Source File + +SOURCE=.\common.h +# End Source File +# Begin Source File + +SOURCE=.\IInterface.h +# End Source File +# Begin Source File + +SOURCE=.\stdbitset.h +# End Source File +# Begin Source File + +SOURCE=.\stddeque.h +# End Source File +# Begin Source File + +SOURCE=.\stdfstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdistream.h +# End Source File +# Begin Source File + +SOURCE=.\stdlist.h +# End Source File +# Begin Source File + +SOURCE=.\stdmap.h +# End Source File +# Begin Source File + +SOURCE=.\stdostream.h +# End Source File +# Begin Source File + +SOURCE=.\stdpost.h +# End Source File +# Begin Source File + +SOURCE=.\stdpre.h +# End Source File +# Begin Source File + +SOURCE=.\stdset.h +# End Source File +# Begin Source File + +SOURCE=.\stdsstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdstring.h +# End Source File +# Begin Source File + +SOURCE=.\stdvector.h +# End Source File +# Begin Source File + +SOURCE=.\Version.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/common/common.h b/lib/common/common.h new file mode 100644 index 0000000..74b8f1d --- /dev/null +++ b/lib/common/common.h @@ -0,0 +1,74 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +// this file should be included, directly or indirectly by every other. + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +// check if win32 platform +#if defined(_WIN32) +# define WINDOWS_LIKE 1 + + // VC++ specific +# if (_MSC_VER >= 1200) + // work around for statement scoping bug +# define for if (false) { } else for + + // turn off bonehead warnings +# pragma warning(disable: 4786) // identifier truncated in debug info +# pragma warning(disable: 4514) // unreferenced inline function removed + + // this one's a little too aggressive +# pragma warning(disable: 4127) // conditional expression is constant + + // emitted incorrectly under release build in some circumstances +# if defined(NDEBUG) +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4701) // variable maybe used uninitialized +# endif + +# endif // (_MSC_VER >= 1200) + + // VC++ has built-in sized types +# if defined(_MSC_VER) +# define TYPE_OF_SIZE_1 __int8 +# define TYPE_OF_SIZE_2 __int16 +# define TYPE_OF_SIZE_4 __int32 +# else +# define SIZE_OF_CHAR 1 +# define SIZE_OF_SHORT 2 +# define SIZE_OF_INT 4 +# define SIZE_OF_LONG 4 +# endif +#endif // defined(_WIN32) + +// unix-like if not like anything else +#if (!defined(WINDOWS_LIKE) || WINDOWS_LIKE == 0) +# define UNIX_LIKE 1 +#endif + +// define NULL +#ifndef NULL +#define NULL 0 +#endif + +// make assert available since we use it a lot +#include + +#endif diff --git a/lib/common/stdbitset.h b/lib/common/stdbitset.h new file mode 100644 index 0000000..529772c --- /dev/null +++ b/lib/common/stdbitset.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stddeque.h b/lib/common/stddeque.h new file mode 100644 index 0000000..06bdafd --- /dev/null +++ b/lib/common/stddeque.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdfstream.h b/lib/common/stdfstream.h new file mode 100644 index 0000000..9d1f5aa --- /dev/null +++ b/lib/common/stdfstream.h @@ -0,0 +1,18 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" +#include "stdistream.h" diff --git a/lib/common/stdistream.h b/lib/common/stdistream.h new file mode 100644 index 0000000..bd90357 --- /dev/null +++ b/lib/common/stdistream.h @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#if defined(HAVE_ISTREAM) +#include +#else +#include +#endif +#include "stdpost.h" + +#if defined(_MSC_VER) && _MSC_VER <= 1200 +// VC++6 istream has no overloads for __int* types, .NET does +inline +std::istream& operator>>(std::istream& s, SInt8& i) +{ return s >> (signed char&)i; } +inline +std::istream& operator>>(std::istream& s, SInt16& i) +{ return s >> (short&)i; } +inline +std::istream& operator>>(std::istream& s, SInt32& i) +{ return s >> (int&)i; } +inline +std::istream& operator>>(std::istream& s, UInt8& i) +{ return s >> (unsigned char&)i; } +inline +std::istream& operator>>(std::istream& s, UInt16& i) +{ return s >> (unsigned short&)i; } +inline +std::istream& operator>>(std::istream& s, UInt32& i) +{ return s >> (unsigned int&)i; } +#endif diff --git a/lib/common/stdlist.h b/lib/common/stdlist.h new file mode 100644 index 0000000..6f0df8e --- /dev/null +++ b/lib/common/stdlist.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdmap.h b/lib/common/stdmap.h new file mode 100644 index 0000000..da50161 --- /dev/null +++ b/lib/common/stdmap.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdostream.h b/lib/common/stdostream.h new file mode 100644 index 0000000..e7496f5 --- /dev/null +++ b/lib/common/stdostream.h @@ -0,0 +1,21 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#if defined(HAVE_OSTREAM) +#include +#else +#include +#endif +#include "stdpost.h" diff --git a/lib/common/stdpost.h b/lib/common/stdpost.h new file mode 100644 index 0000000..0f06b74 --- /dev/null +++ b/lib/common/stdpost.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/lib/common/stdpre.h b/lib/common/stdpre.h new file mode 100644 index 0000000..10fa7d5 --- /dev/null +++ b/lib/common/stdpre.h @@ -0,0 +1,27 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if defined(_MSC_VER) +#pragma warning(disable: 4786) // identifier truncated +#pragma warning(disable: 4514) // unreferenced inline +#pragma warning(disable: 4710) // not inlined +#pragma warning(disable: 4663) // C++ change, template specialization +#pragma warning(disable: 4503) // decorated name length too long +#pragma warning(push, 3) +#pragma warning(disable: 4018) // signed/unsigned mismatch +#pragma warning(disable: 4284) +#pragma warning(disable: 4146) // unary minus on unsigned value +#pragma warning(disable: 4127) // conditional expression is constant +#pragma warning(disable: 4701) // variable possibly used uninitialized +#endif diff --git a/lib/common/stdset.h b/lib/common/stdset.h new file mode 100644 index 0000000..aeb491b --- /dev/null +++ b/lib/common/stdset.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdsstream.h b/lib/common/stdsstream.h new file mode 100644 index 0000000..ed8e723 --- /dev/null +++ b/lib/common/stdsstream.h @@ -0,0 +1,371 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" + +#if defined(HAVE_SSTREAM) || !defined(__GNUC__) || (__GNUC__ >= 3) + +#include + +#elif defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 95) +// g++ 2.95 didn't ship with sstream. the following is a backport +// by Magnus Fromreide of the sstream in g++ 3.0. + +/* This is part of libio/iostream, providing -*- C++ -*- input/output. +Copyright (C) 2000 Free Software Foundation + +This file is part of the GNU IO Library. This library is free +software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this library; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +As a special exception, if you link this library with files +compiled with a GNU compiler to produce an executable, this does not cause +the resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why +the executable file might be covered by the GNU General Public License. */ + +/* Written by Magnus Fromreide (magfr@lysator.liu.se). */ +/* seekoff and ideas for overflow is largely borrowed from libstdc++-v3 */ + +#include +#include +#include + +namespace std +{ + class stringbuf : public streambuf + { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + stringbuf(int which=ios::in|ios::out) + : streambuf(), mode(static_cast(which)), + stream(NULL), stream_len(0) + { + stringbuf_init(); + } + + explicit + stringbuf(const string &str, int which=ios::in|ios::out) + : streambuf(), mode(static_cast(which)), + stream(NULL), stream_len(0) + { + if (mode & (ios::in|ios::out)) + { + stream_len = str.size(); + stream = new char_type[stream_len]; + str.copy(stream, stream_len); + } + stringbuf_init(); + } + + virtual + ~stringbuf() + { + delete[] stream; + } + + string + str() const + { + if (pbase() != 0) + return string(stream, pptr()-pbase()); + else + return string(); + } + + void + str(const string& str) + { + delete[] stream; + stream_len = str.size(); + stream = new char_type[stream_len]; + str.copy(stream, stream_len); + stringbuf_init(); + } + + protected: + // The buffer is already in gptr, so if it ends then it is out of data. + virtual int + underflow() + { + return EOF; + } + + virtual int + overflow(int c = EOF) + { + int res; + if (mode & ios::out) + { + if (c != EOF) + { + streamsize old_stream_len = stream_len; + stream_len += 1; + char_type* new_stream = new char_type[stream_len]; + memcpy(new_stream, stream, old_stream_len); + delete[] stream; + stream = new_stream; + stringbuf_sync(gptr()-eback(), pptr()-pbase()); + sputc(c); + res = c; + } + else + res = EOF; + } + else + res = 0; + return res; + } + + virtual streambuf* + setbuf(char_type* s, streamsize n) + { + if (n != 0) + { + delete[] stream; + stream = new char_type[n]; + memcpy(stream, s, n); + stream_len = n; + stringbuf_sync(0, 0); + } + return this; + } + + virtual pos_type + seekoff(off_type off, ios::seek_dir way, int which = ios::in | ios::out) + { + pos_type ret = pos_type(off_type(-1)); + bool testin = which & ios::in && mode & ios::in; + bool testout = which & ios::out && mode & ios::out; + bool testboth = testin && testout && way != ios::cur; + + if (stream_len && ((testin != testout) || testboth)) + { + char_type* beg = stream; + char_type* curi = NULL; + char_type* curo = NULL; + char_type* endi = NULL; + char_type* endo = NULL; + + if (testin) + { + curi = gptr(); + endi = egptr(); + } + if (testout) + { + curo = pptr(); + endo = epptr(); + } + + off_type newoffi = 0; + off_type newoffo = 0; + if (way == ios::beg) + { + newoffi = beg - curi; + newoffo = beg - curo; + } + else if (way == ios::end) + { + newoffi = endi - curi; + newoffo = endo - curo; + } + + if (testin && newoffi + off + curi - beg >= 0 && + endi - beg >= newoffi + off + curi - beg) + { + gbump(newoffi + off); + ret = pos_type(newoffi + off + curi); + } + if (testout && newoffo + off + curo - beg >= 0 && + endo - beg >= newoffo + off + curo - beg) + { + pbump(newoffo + off); + ret = pos_type(newoffo + off + curo); + } + } + return ret; + } + + virtual pos_type + seekpos(pos_type sp, int which = ios::in | ios::out) + { + pos_type ret = seekoff(sp, ios::beg, which); + return ret; + } + + private: + void + stringbuf_sync(streamsize i, streamsize o) + { + if (mode & ios::in) + setg(stream, stream + i, stream + stream_len); + if (mode & ios::out) + { + setp(stream, stream + stream_len); + pbump(o); + } + } + void + stringbuf_init() + { + if (mode & ios::ate) + stringbuf_sync(0, stream_len); + else + stringbuf_sync(0, 0); + } + + private: + ios::open_mode mode; + char_type* stream; + streamsize stream_len; + }; + + class istringstream : public istream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + istringstream(int which=ios::in) + : istream(&sb), sb(which | ios::in) + { } + + explicit + istringstream(const string& str, int which=ios::in) + : istream(&sb), sb(str, which | ios::in) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + void + str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; + + class ostringstream : public ostream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + ostringstream(int which=ios::out) + : ostream(&sb), sb(which | ios::out) + { } + + explicit + ostringstream(const string& str, int which=ios::out) + : ostream(&sb), sb(str, which | ios::out) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + + void str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; + + class stringstream : public iostream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + stringstream(int which=ios::out|ios::in) + : iostream(&sb), sb(which) + { } + + explicit + stringstream(const string& str, int which=ios::out|ios::in) + : iostream(&sb), sb(str, which) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + + void + str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; +}; + +#else /* not g++ 2.95 and no */ + +#error "Standard C++ library is missing required sstream header." + +#endif /* not g++ 2.95 and no */ + +#include "stdpost.h" +#include "stdistream.h" diff --git a/lib/common/stdstring.h b/lib/common/stdstring.h new file mode 100644 index 0000000..3d83c03 --- /dev/null +++ b/lib/common/stdstring.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/common/stdvector.h b/lib/common/stdvector.h new file mode 100644 index 0000000..1056bb4 --- /dev/null +++ b/lib/common/stdvector.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/http/CHTTPProtocol.cpp b/lib/http/CHTTPProtocol.cpp new file mode 100644 index 0000000..ae385d6 --- /dev/null +++ b/lib/http/CHTTPProtocol.cpp @@ -0,0 +1,658 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CHTTPProtocol.h" +#include "XHTTP.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLog.h" +#include "stdsstream.h" +#include +#include +#include + +// +// CHTTPRequest +// + +CHTTPRequest::CHTTPRequest() +{ + // do nothing +} + +CHTTPRequest::~CHTTPRequest() +{ + // do nothing +} + +void +CHTTPRequest::insertHeader(const CString& name, const CString& value) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + index->second->second = value; + } + else { + CHeaderList::iterator pos = m_headers.insert( + m_headers.end(), std::make_pair(name, value)); + m_headerByName.insert(std::make_pair(name, pos)); + } +} + +void +CHTTPRequest::appendHeader(const CString& name, const CString& value) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + index->second->second += ","; + index->second->second += value; + } + else { + CHeaderList::iterator pos = m_headers.insert( + m_headers.end(), std::make_pair(name, value)); + m_headerByName.insert(std::make_pair(name, pos)); + } +} + +void +CHTTPRequest::eraseHeader(const CString& name) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + m_headers.erase(index->second); + } +} + +bool +CHTTPRequest::isHeader(const CString& name) const +{ + return (m_headerByName.find(name) != m_headerByName.end()); +} + +CString +CHTTPRequest::getHeader(const CString& name) const +{ + CHeaderMap::const_iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + return index->second->second; + } + else { + return CString(); + } +} + + +// +// CHTTPProtocol +// + +CHTTPRequest* +CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) +{ + CString scratch; + + // note if we should limit the request size + const bool checkSize = (maxSize > 0); + + // parse request line by line + CHTTPRequest* request = new CHTTPRequest; + try { + CString line; + + // read request line. accept and discard leading empty lines. + do { + line = readLine(stream, scratch); + if (checkSize) { + if (line.size() + 2 > maxSize) { + throw XHTTP(413); + } + maxSize -= line.size() + 2; + } + } while (line.empty()); + + // parse request line: + { + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + CString version; + s >> request->m_method >> request->m_uri >> version; + if (!s || request->m_uri.empty() || version.find("HTTP/") != 0) { + LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + throw XHTTP(400); + } + + // parse version + char dot; + s.str(version); + s.clear(); + s.ignore(5); + s >> request->m_majorVersion; + s.get(dot); + s >> request->m_minorVersion; + if (!s || dot != '.') { + LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + throw XHTTP(400); + } + } + if (!isValidToken(request->m_method)) { + LOG((CLOG_DEBUG1 "invalid HTTP method: %s", line.c_str())); + throw XHTTP(400); + } + if (request->m_majorVersion < 1 || request->m_minorVersion < 0) { + LOG((CLOG_DEBUG1 "invalid HTTP version: %s", line.c_str())); + throw XHTTP(400); + } + + // parse headers + readHeaders(stream, request, false, scratch, + checkSize ? &maxSize : NULL); + + // HTTP/1.1 requests must have a Host header + if (request->m_majorVersion > 1 || + (request->m_majorVersion == 1 && request->m_minorVersion >= 1)) { + if (request->isHeader("Host") == 0) { + LOG((CLOG_DEBUG1 "Host header missing")); + throw XHTTP(400); + } + } + + // some methods may not have a body. ensure that the headers + // that indicate the body length do not exist for those methods + // and do exist for others. + if ((request->isHeader("Transfer-Encoding") || + request->isHeader("Content-Length")) == + (request->m_method == "GET" || + request->m_method == "HEAD")) { + LOG((CLOG_DEBUG1 "HTTP method (%s)/body mismatch", request->m_method.c_str())); + throw XHTTP(400); + } + + // prepare to read the body. the length of the body is + // determined using, in order: + // 1. Transfer-Encoding indicates a "chunked" transfer + // 2. Content-Length is present + // Content-Length is ignored for "chunked" transfers. + CString header; + if (!(header = request->getHeader("Transfer-Encoding")).empty()) { + // we only understand "chunked" encodings + if (!CStringUtil::CaselessCmp::equal(header, "chunked")) { + LOG((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", header.c_str())); + throw XHTTP(501); + } + + // chunked encoding + UInt32 oldSize; + do { + oldSize = request->m_body.size(); + request->m_body += readChunk(stream, scratch, + checkSize ? &maxSize : NULL); + } while (request->m_body.size() != oldSize); + + // read footer + readHeaders(stream, request, true, scratch, + checkSize ? &maxSize : NULL); + + // remove "chunked" from Transfer-Encoding and set the + // Content-Length. + std::ostringstream s; + s << std::dec << request->m_body.size(); + request->eraseHeader("Transfer-Encoding"); + request->insertHeader("Content-Length", s.str()); + } + else if (!(header = request->getHeader("Content-Length")).empty()) { + // parse content-length + UInt32 length; + { + std::istringstream s(header); + s.exceptions(std::ios::goodbit); + s >> length; + if (!s) { + LOG((CLOG_DEBUG1 "cannot parse Content-Length", header.c_str())); + throw XHTTP(400); + } + } + + // check against expected size + if (checkSize && length > maxSize) { + throw XHTTP(413); + } + + // use content length + request->m_body = readBlock(stream, length, scratch); + if (request->m_body.size() != length) { + // length must match size of body + LOG((CLOG_DEBUG1 "Content-Length/actual length mismatch (%d vs %d)", length, request->m_body.size())); + throw XHTTP(400); + } + } + } + catch (...) { + delete request; + throw; + } + + return request; +} + +void +CHTTPProtocol::reply(IOutputStream* stream, CHTTPReply& reply) +{ + // suppress body for certain replies + bool hasBody = true; + if ((reply.m_status / 100) == 1 || + reply.m_status == 204 || + reply.m_status == 304) { + hasBody = false; + } + + // adjust headers + for (CHTTPReply::CHeaderList::iterator + index = reply.m_headers.begin(); + index != reply.m_headers.end(); ) { + const CString& header = index->first; + + // remove certain headers + if (CStringUtil::CaselessCmp::equal(header, "Content-Length") || + CStringUtil::CaselessCmp::equal(header, "Date") || + CStringUtil::CaselessCmp::equal(header, "Transfer-Encoding")) { + // FIXME -- Transfer-Encoding should be left as-is if + // not "chunked" and if the version is 1.1 or up. + index = reply.m_headers.erase(index); + } + + // keep as-is + else { + ++index; + } + } + + // write reply header + std::ostringstream s; + s << "HTTP/" << reply.m_majorVersion << "." << + reply.m_minorVersion << " " << + reply.m_status << " " << + reply.m_reason << "\r\n"; + + // get date + // FIXME -- should use C++ locale stuff but VC++ time_put is broken. + // FIXME -- double check that VC++ is broken + char date[30]; + { + const char* oldLocale = setlocale(LC_TIME, "C"); + time_t t = time(NULL); +#if HAVE_GMTIME_R + struct tm tm; + struct tm* tmp = &tm; + gmtime_r(&t, tmp); +#else + struct tm* tmp = gmtime(&t); +#endif + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", tmp); + setlocale(LC_TIME, oldLocale); + } + + // write headers + s << "Date: " << date << "\r\n"; + for (CHTTPReply::CHeaderList::const_iterator + index = reply.m_headers.begin(); + index != reply.m_headers.end(); ++index) { + s << index->first << ": " << index->second << "\r\n"; + } + if (hasBody) { + s << "Content-Length: " << reply.m_body.size() << "\r\n"; + } + s << "Connection: close\r\n"; + + // write end of headers + s << "\r\n"; + + // write to stream + stream->write(s.str().data(), s.str().size()); + + // write body. replies to HEAD method never have a body (though + // they do have the Content-Length header). + if (hasBody && reply.m_method != "HEAD") { + stream->write(reply.m_body.data(), reply.m_body.size()); + } +} + +bool +CHTTPProtocol::parseFormData(const CHTTPRequest& request, CFormParts& parts) +{ + static const char formData[] = "multipart/form-data"; + static const char boundary[] = "boundary="; + static const char disposition[] = "Content-Disposition:"; + static const char nameAttr[] = "name="; + static const char quote[] = "\""; + + // find the Content-Type header + const CString contentType = request.getHeader("Content-Type"); + if (contentType.empty()) { + // missing required Content-Type header + return false; + } + + // parse type + CString::const_iterator index = std::search( + contentType.begin(), contentType.end(), + formData, formData + sizeof(formData) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == contentType.end()) { + // not form-data + return false; + } + index += sizeof(formData) - 1; + index = std::search(index, contentType.end(), + boundary, boundary + sizeof(boundary) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == contentType.end()) { + // no boundary + return false; + } + CString delimiter = contentType.c_str() + + (index - contentType.begin()) + + sizeof(boundary) - 1; + + // find first delimiter + const CString& body = request.m_body; + CString::size_type partIndex = body.find(delimiter); + if (partIndex == CString::npos) { + return false; + } + + // skip over it + partIndex += delimiter.size(); + + // prepend CRLF-- to delimiter + delimiter = "\r\n--" + delimiter; + + // parse parts until there are no more + for (;;) { + // is it the last part? + if (body.size() >= partIndex + 2 && + body[partIndex ] == '-' && + body[partIndex + 1] == '-') { + // found last part. ignore trailing data, if any. + return true; + } + + // find the end of this part + CString::size_type nextPart = body.find(delimiter, partIndex); + if (nextPart == CString::npos) { + // no terminator + return false; + } + + // find end of headers + CString::size_type endOfHeaders = body.find("\r\n\r\n", partIndex); + if (endOfHeaders == CString::npos || endOfHeaders > nextPart) { + // bad part + return false; + } + endOfHeaders += 2; + + // now find Content-Disposition + index = std::search(body.begin() + partIndex, + body.begin() + endOfHeaders, + disposition, + disposition + sizeof(disposition) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == contentType.begin() + endOfHeaders) { + // bad part + return false; + } + + // find the name in the Content-Disposition + CString::size_type endOfHeader = body.find("\r\n", + index - body.begin()); + if (endOfHeader >= endOfHeaders) { + // bad part + return false; + } + index = std::search(index, body.begin() + endOfHeader, + nameAttr, nameAttr + sizeof(nameAttr) - 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == body.begin() + endOfHeader) { + // no name + return false; + } + + // extract the name + CString name; + index += sizeof(nameAttr) - 1; + if (*index == quote[0]) { + // quoted name + ++index; + CString::size_type namePos = index - body.begin(); + index = std::search(index, body.begin() + endOfHeader, + quote, quote + 1, + CStringUtil::CaselessCmp::cmpEqual); + if (index == body.begin() + endOfHeader) { + // missing close quote + return false; + } + name = body.substr(namePos, index - body.begin() - namePos); + } + else { + // unquoted name + name = body.substr(index - body.begin(), + body.find_first_of(" \t\r\n")); + } + + // save part. add 2 to endOfHeaders to skip CRLF. + parts.insert(std::make_pair(name, body.substr(endOfHeaders + 2, + nextPart - (endOfHeaders + 2)))); + + // move to next part + partIndex = nextPart + delimiter.size(); + } + + // should've found the last delimiter inside the loop but we did not + return false; +} + +CString +CHTTPProtocol::readLine(IInputStream* stream, CString& tmpBuffer) +{ + // read up to and including a CRLF from stream, using whatever + // is in tmpBuffer as if it were at the head of the stream. + + for (;;) { + // scan tmpBuffer for CRLF + CString::size_type newline = tmpBuffer.find("\r\n"); + if (newline != CString::npos) { + // copy line without the CRLF + CString line = tmpBuffer.substr(0, newline); + + // discard line and CRLF from tmpBuffer + tmpBuffer.erase(0, newline + 2); + return line; + } + + // read more from stream + char buffer[4096]; + UInt32 n = stream->read(buffer, sizeof(buffer), -1.0); + if (n == 0) { + // stream is empty. return what's leftover. + CString line = tmpBuffer; + tmpBuffer.erase(); + return line; + } + + // append stream data + tmpBuffer.append(buffer, n); + } +} + +CString +CHTTPProtocol::readBlock(IInputStream* stream, + UInt32 numBytes, CString& tmpBuffer) +{ + CString data; + + // read numBytes from stream, using whatever is in tmpBuffer as + // if it were at the head of the stream. + if (tmpBuffer.size() > 0) { + // ignore stream if there's enough data in tmpBuffer + if (tmpBuffer.size() >= numBytes) { + data = tmpBuffer.substr(0, numBytes); + tmpBuffer.erase(0, numBytes); + return data; + } + + // move everything out of tmpBuffer into data + data = tmpBuffer; + tmpBuffer.erase(); + } + + // account for bytes read so far + assert(data.size() < numBytes); + numBytes -= data.size(); + + // read until we have all the requested data + while (numBytes > 0) { + // read max(4096, bytes_left) bytes into buffer + char buffer[4096]; + UInt32 n = sizeof(buffer); + if (n > numBytes) { + n = numBytes; + } + n = stream->read(buffer, n, -1.0); + + // if stream is empty then return what we've got so far + if (n == 0) { + break; + } + + // append stream data + data.append(buffer, n); + numBytes -= n; + } + + return data; +} + +CString +CHTTPProtocol::readChunk(IInputStream* stream, + CString& tmpBuffer, UInt32* maxSize) +{ + CString line; + + // get chunk header + line = readLine(stream, tmpBuffer); + + // parse chunk size + UInt32 size; + { + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + s >> std::hex >> size; + if (!s) { + LOG((CLOG_DEBUG1 "cannot parse chunk size", line.c_str())); + throw XHTTP(400); + } + } + if (size == 0) { + return CString(); + } + + // check size + if (maxSize != NULL) { + if (line.size() + 2 + size + 2 > *maxSize) { + throw XHTTP(413); + } + maxSize -= line.size() + 2 + size + 2; + } + + // read size bytes + CString data = readBlock(stream, size, tmpBuffer); + if (data.size() != size) { + LOG((CLOG_DEBUG1 "expected/actual chunk size mismatch", size, data.size())); + throw XHTTP(400); + } + + // read an discard CRLF + line = readLine(stream, tmpBuffer); + if (!line.empty()) { + LOG((CLOG_DEBUG1 "missing CRLF after chunk")); + throw XHTTP(400); + } + + return data; +} + +void +CHTTPProtocol::readHeaders(IInputStream* stream, + CHTTPRequest* request, bool isFooter, + CString& tmpBuffer, UInt32* maxSize) +{ + // parse headers. done with headers when we get a blank line. + CString name; + CString line = readLine(stream, tmpBuffer); + while (!line.empty()) { + // check size + if (maxSize != NULL) { + if (line.size() + 2 > *maxSize) { + throw XHTTP(413); + } + *maxSize -= line.size() + 2; + } + + // if line starts with space or tab then append it to the + // previous header. if there is no previous header then + // throw. + if (line[0] == ' ' || line[0] == '\t') { + if (name.empty()) { + LOG((CLOG_DEBUG1 "first header is a continuation")); + throw XHTTP(400); + } + request->appendHeader(name, line); + } + + // line should have the form: :[] + else { + // parse + CString value; + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + std::getline(s, name, ':'); + if (!s || !isValidToken(name)) { + LOG((CLOG_DEBUG1 "invalid header: %s", line.c_str())); + throw XHTTP(400); + } + std::getline(s, value); + + // check validity of name + if (isFooter) { + // FIXME -- only certain names are allowed in footers + // but which ones? + } + + request->appendHeader(name, value); + } + + // next header + line = readLine(stream, tmpBuffer); + } +} + +bool +CHTTPProtocol::isValidToken(const CString& token) +{ + return (token.find("()<>@,;:\\\"/[]?={} " + "\0\1\2\3\4\5\6\7" + "\10\11\12\13\14\15\16\17" + "\20\21\22\23\24\25\26\27" + "\30\31\32\33\34\35\36\37\177") == CString::npos); +} diff --git a/lib/http/CHTTPProtocol.h b/lib/http/CHTTPProtocol.h new file mode 100644 index 0000000..8c86645 --- /dev/null +++ b/lib/http/CHTTPProtocol.h @@ -0,0 +1,201 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CHTTPPROTOCOL_H +#define CHTTPPROTOCOL_H + +#include "CString.h" +#include "CStringUtil.h" +#include "BasicTypes.h" +#include "stdlist.h" +#include "stdmap.h" +#include "stdvector.h" + +class IInputStream; +class IOutputStream; + +//! HTTP request type +/*! +This class encapsulates an HTTP request. +*/ +class CHTTPRequest { +private: + typedef std::list > CHeaderList; +public: + //! Iterator on headers + /*! + An iterator on the headers. Each element is a std::pair; first is + the header name as a CString, second is the header value as a CString. + */ + typedef CHeaderList::const_iterator const_iterator; + + CHTTPRequest(); + ~CHTTPRequest(); + + //! @name manipulators + //@{ + + //! Insert header + /*! + Add a header by name replacing the existing header, if any. + Headers are sent in the order they're inserted. Replacing + a header does not change its original position in the order. + */ + void insertHeader(const CString& name, const CString& value); + + //! Append header + /*! + Append a header. Equivalent to insertHeader() if the header + doesn't exist, otherwise it appends a comma and the value to + the existing header. + */ + void appendHeader(const CString& name, const CString& value); + + //! Remove header + /*! + Remove a header by name. Does nothing if the header doesn't exist. + */ + void eraseHeader(const CString& name); + + //@} + //! @name accessors + //@{ + + //! Check header existence + /*! + Returns true iff the header exists. + */ + bool isHeader(const CString& name) const; + + //! Get header + /*! + Get a header by name. Returns the empty string if the header + doesn't exist. + */ + CString getHeader(const CString& name) const; + + // headers are iterated in the order they were added. + //! Get beginning header iterator + const_iterator begin() const { return m_headers.begin(); } + //! Get ending header iterator + const_iterator end() const { return m_headers.end(); } + + //@} + +public: + // note -- these members are public for convenience + //! The HTTP method + CString m_method; + //! The HTTP URI + CString m_uri; + //! The HTTP major version number + SInt32 m_majorVersion; + //! The HTTP minor version number + SInt32 m_minorVersion; + //! The HTTP body, after transfer decoding + CString m_body; + +private: + typedef std::map CHeaderMap; + + CHeaderList m_headers; + CHeaderMap m_headerByName; +}; + +//! HTTP reply type +/*! +This class encapsulates an HTTP reply. +*/ +class CHTTPReply { +public: + //! Header list + /*! + The type of the reply header list. Each pair is the header name + and value, respectively for first and second. + */ + typedef std::vector > CHeaderList; + + // note -- these members are public for convenience + //! The HTTP major version number + SInt32 m_majorVersion; + //! The HTTP minor version number + SInt32 m_minorVersion; + //! The HTTP status code + SInt32 m_status; + //! The HTTP reason phrase + CString m_reason; + //! The HTTP method + CString m_method; + //! The HTTP headers + CHeaderList m_headers; + //! The HTTP body + CString m_body; +}; + +//! HTTP protocol utilities +/*! +This class provides utility functions for HTTP. +*/ +class CHTTPProtocol { +public: + //! Multipart form parts + /*! + Each element is the contents of a multipart form part indexed by + it's name. + */ + typedef std::map CFormParts; + + //! Read HTTP request + /*! + Read and parse an HTTP request. The result is returned in a + CHTTPRequest which the client must delete. Throws an + XHTTP if there was a parse error. Throws an XIO exception + if there was a read error. If \c maxSize is greater than + zero and the request is larger than \c maxSize bytes then + throws XHTTP(413) (request entity too large). + */ + static CHTTPRequest* readRequest(IInputStream*, UInt32 maxSize = 0); + + //! Send HTTP response + /*! + Send an HTTP reply. The Content-Length and Date headers are set + automatically. + */ + static void reply(IOutputStream*, CHTTPReply&); + + //! Parse multipart form data + /*! + Parse a multipart/form-data body into its parts. Returns true + iff the entire body was correctly parsed. + */ + // FIXME -- name/value pairs insufficient to save part headers + static bool parseFormData(const CHTTPRequest&, + CFormParts& parts); + +private: + static CString readLine(IInputStream*, CString& tmpBuffer); + static CString readBlock(IInputStream*, + UInt32 numBytes, CString& tmpBuffer); + static CString readChunk(IInputStream*, CString& tmpBuffer, + UInt32* maxSize); + static void readHeaders(IInputStream*, + CHTTPRequest*, bool isFooter, + CString& tmpBuffer, + UInt32* maxSize); + + static bool isValidToken(const CString&); +}; + +#endif diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am new file mode 100644 index 0000000..c9abe53 --- /dev/null +++ b/lib/http/Makefile.am @@ -0,0 +1,39 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + http.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libhttp.a +libhttp_a_SOURCES = \ + CHTTPProtocol.cpp \ + XHTTP.cpp \ + CHTTPProtocol.h \ + XHTTP.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) diff --git a/lib/http/Makefile.in b/lib/http/Makefile.in new file mode 100644 index 0000000..6beef7f --- /dev/null +++ b/lib/http/Makefile.in @@ -0,0 +1,339 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + http.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libhttp.a +libhttp_a_SOURCES = \ + CHTTPProtocol.cpp \ + XHTTP.cpp \ + CHTTPProtocol.h \ + XHTTP.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) + +subdir = lib/http +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libhttp_a_AR = $(AR) cru +libhttp_a_LIBADD = +am_libhttp_a_OBJECTS = CHTTPProtocol.$(OBJEXT) XHTTP.$(OBJEXT) +libhttp_a_OBJECTS = $(am_libhttp_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CHTTPProtocol.Po $(DEPDIR)/XHTTP.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libhttp_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libhttp_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/http/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libhttp.a: $(libhttp_a_OBJECTS) $(libhttp_a_DEPENDENCIES) + -rm -f libhttp.a + $(libhttp_a_AR) libhttp.a $(libhttp_a_OBJECTS) $(libhttp_a_LIBADD) + $(RANLIB) libhttp.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CHTTPProtocol.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XHTTP.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/http/XHTTP.cpp b/lib/http/XHTTP.cpp new file mode 100644 index 0000000..9b14609 --- /dev/null +++ b/lib/http/XHTTP.cpp @@ -0,0 +1,135 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XHTTP.h" +#include "CHTTPProtocol.h" +#include "CStringUtil.h" +#include "stdsstream.h" + +// +// XHTTP +// + +XHTTP::XHTTP(SInt32 statusCode) : + XBase(), + m_status(statusCode), + m_reason(getReason(statusCode)) +{ + // do nothing +} + +XHTTP::XHTTP(SInt32 statusCode, const CString& reasonPhrase) : + XBase(), + m_status(statusCode), + m_reason(reasonPhrase) +{ + // do nothing +} + +XHTTP::~XHTTP() +{ + // do nothing +} + +SInt32 +XHTTP::getStatus() const +{ + return m_status; +} + +CString +XHTTP::getReason() const +{ + return m_reason; +} + +void +XHTTP::addHeaders(CHTTPReply&) const +{ + // do nothing +} + +CString +XHTTP::getWhat() const throw() +{ + const char* reason; + if (m_reason.empty()) { + reason = getReason(m_status); + } + else { + reason = m_reason.c_str(); + } + return format("XHTTP", "%{1} %{2}", + CStringUtil::print("%d", m_status).c_str(), + reason); +} + +const char* +XHTTP::getReason(SInt32 status) +{ + switch (status) { + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Moved Temporarily"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Time-out"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-URI Too Large"; + case 415: return "Unsupported Media Type"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Time-out"; + case 505: return "HTTP Version not supported"; + default: return ""; + } +} + + +// +// XHTTPAllow +// + +XHTTPAllow::XHTTPAllow(const CString& allowedMethods) : + XHTTP(405), + m_allowed(allowedMethods) +{ + // do nothing +} + +XHTTPAllow::~XHTTPAllow() +{ + // do nothing +} + +void +XHTTPAllow::addHeaders(CHTTPReply& reply) const +{ + reply.m_headers.push_back(std::make_pair(CString("Allow"), m_allowed)); +} diff --git a/lib/http/XHTTP.h b/lib/http/XHTTP.h new file mode 100644 index 0000000..2bd6ed7 --- /dev/null +++ b/lib/http/XHTTP.h @@ -0,0 +1,82 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XHTTP_H +#define XHTTP_H + +#include "BasicTypes.h" +#include "XBase.h" + +class CHTTPReply; + +//! Generic HTTP exception +class XHTTP : public XBase { +public: + /*! + Use the HTTP \c statusCode as the failure reason. + */ + XHTTP(SInt32 statusCode); + /*! + Use the HTTP \c statusCode as the failure reason. Use \c reasonPhrase + as the human readable reason for the failure. + */ + XHTTP(SInt32 statusCode, const CString& reasonPhrase); + ~XHTTP(); + + //@{ + //! @name accessors + + //! Get the HTTP status code + SInt32 getStatus() const; + + //! Get the reason phrase + CString getReason() const; + + //! Modify reply for error + /*! + Override to modify an HTTP reply to further describe the error. + */ + virtual void addHeaders(CHTTPReply&) const; + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + static const char* getReason(SInt32 status); + +private: + SInt32 m_status; + CString m_reason; +}; + +//! HTTP exception indicating an unsupported method +class XHTTPAllow : public XHTTP { +public: + /*! + \c allowedMethods is added as an `Allow' header to a reply in + addHeaders(). + */ + XHTTPAllow(const CString& allowedMethods); + ~XHTTPAllow(); + + // XHTTP overrides + virtual void addHeaders(CHTTPReply&) const; + +private: + CString m_allowed; +}; + +#endif diff --git a/lib/http/http.dsp b/lib/http/http.dsp new file mode 100644 index 0000000..feff3d0 --- /dev/null +++ b/lib/http/http.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=http - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "http.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "http.mak" CFG="http - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "http - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "http - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "http - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "http - Win32 Release" +# Name "http - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CHTTPProtocol.cpp +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CHTTPProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/io/CBufferedInputStream.cpp b/lib/io/CBufferedInputStream.cpp new file mode 100644 index 0000000..27b7cb6 --- /dev/null +++ b/lib/io/CBufferedInputStream.cpp @@ -0,0 +1,132 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CBufferedInputStream.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "CStopwatch.h" +#include "IJob.h" +#include "XIO.h" +#include + +// +// CBufferedInputStream +// + +CBufferedInputStream::CBufferedInputStream( + CMutex* mutex, IJob* adoptedCloseCB) : + m_mutex(mutex), + m_empty(mutex, true), + m_closeCB(adoptedCloseCB), + m_closed(false), + m_hungup(false) +{ + assert(m_mutex != NULL); +} + +CBufferedInputStream::~CBufferedInputStream() +{ + delete m_closeCB; +} + +void +CBufferedInputStream::write(const void* buffer, UInt32 n) +{ + if (!m_hungup && n > 0) { + m_buffer.write(buffer, n); + m_empty = (m_buffer.getSize() == 0); + m_empty.broadcast(); + } +} + +void +CBufferedInputStream::hangup() +{ + m_hungup = true; + m_empty.broadcast(); +} + +UInt32 +CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout) +{ + if (m_closed) { + throw XIOClosed(); + } + + // wait for data, hangup, or timeout + CStopwatch timer(true); + while (!m_hungup && m_empty == true) { + if (!m_empty.wait(timer, timeout)) { + // timed out + return (UInt32)-1; + } + } + + // read data + const UInt32 count = m_buffer.getSize(); + if (n > count) { + n = count; + } + if (n > 0) { + if (buffer != NULL) { + memcpy(buffer, m_buffer.peek(n), n); + } + m_buffer.pop(n); + } + + // update empty state + if (m_buffer.getSize() == 0) { + m_empty = true; + m_empty.broadcast(); + } + return n; +} + +UInt32 +CBufferedInputStream::getSizeNoLock() const +{ + return m_buffer.getSize(); +} + +void +CBufferedInputStream::close() +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + m_hungup = true; + m_buffer.pop(m_buffer.getSize()); + m_empty.broadcast(); + if (m_closeCB != NULL) { + m_closeCB->run(); + } +} + +UInt32 +CBufferedInputStream::read(void* buffer, UInt32 n, double timeout) +{ + CLock lock(m_mutex); + return readNoLock(buffer, n, timeout); +} + +UInt32 +CBufferedInputStream::getSize() const +{ + CLock lock(m_mutex); + return getSizeNoLock(); +} diff --git a/lib/io/CBufferedInputStream.h b/lib/io/CBufferedInputStream.h new file mode 100644 index 0000000..132d172 --- /dev/null +++ b/lib/io/CBufferedInputStream.h @@ -0,0 +1,96 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CBUFFEREDINPUTSTREAM_H +#define CBUFFEREDINPUTSTREAM_H + +#include "IInputStream.h" +#include "CStreamBuffer.h" +#include "CCondVar.h" + +class CMutex; +class IJob; + +//! Memory buffer input stream +/*! +This class provides an input stream that reads from a memory buffer. +It also provides a means for the owner to ensure thread safe access. +Typically, an owner object will make this object visible to clients +that need access to an IInputStream while using the CBufferedInputStream +methods to write data to the stream. +*/ +class CBufferedInputStream : public IInputStream { +public: + /*! + The \c mutex must not be NULL and will be used to ensure thread + safe access. If \c adoptedCloseCB is not NULL it will be called + when close() is called, allowing the creator to detect the close. + */ + CBufferedInputStream(CMutex* mutex, IJob* adoptedCloseCB); + ~CBufferedInputStream(); + + //! @name manipulators + //@{ + + //! Write data to stream + /*! + Write \c n bytes from \c buffer to the stream. The mutex must + be locked before calling this. + */ + void write(const void* buffer, UInt32 n); + + //! Hangup stream + /*! + Causes read() to always return immediately. If there is no + more data to read then it returns 0. Further writes are discarded. + The mutex must be locked before calling this. + */ + void hangup(); + + //! Read from stream + /*! + This is the same as read() but the mutex must be locked before + calling this. + */ + UInt32 readNoLock(void*, UInt32 n, double timeout); + + //@} + //! @name accessors + //@{ + + //! Get remaining size of stream + /*! + This is the same as getSize() but the mutex must be locked before + calling this. + */ + UInt32 getSizeNoLock() const; + + //@} + + // IInputStream overrides + // these all lock the mutex for their duration + virtual void close(); + virtual UInt32 read(void*, UInt32 n, double timeout); + virtual UInt32 getSize() const; + +private: + CMutex* m_mutex; + CCondVar m_empty; + IJob* m_closeCB; + CStreamBuffer m_buffer; + bool m_closed; + bool m_hungup; +}; + +#endif diff --git a/lib/io/CBufferedOutputStream.cpp b/lib/io/CBufferedOutputStream.cpp new file mode 100644 index 0000000..3406232 --- /dev/null +++ b/lib/io/CBufferedOutputStream.cpp @@ -0,0 +1,97 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CBufferedOutputStream.h" +#include "XIO.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "IJob.h" + +// +// CBufferedOutputStream +// + +CBufferedOutputStream::CBufferedOutputStream( + CMutex* mutex, IJob* adoptedCloseCB) : + m_mutex(mutex), + m_closeCB(adoptedCloseCB), + m_empty(mutex, true), + m_closed(false) +{ + assert(m_mutex != NULL); +} + +CBufferedOutputStream::~CBufferedOutputStream() +{ + delete m_closeCB; +} + +const void* +CBufferedOutputStream::peek(UInt32 n) +{ + return m_buffer.peek(n); +} + +void +CBufferedOutputStream::pop(UInt32 n) +{ + m_buffer.pop(n); + if (m_buffer.getSize() == 0) { + m_empty.broadcast(); + } +} + +UInt32 +CBufferedOutputStream::getSize() const +{ + return m_buffer.getSize(); +} + +void +CBufferedOutputStream::close() +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + m_buffer.pop(m_buffer.getSize()); + if (m_closeCB != NULL) { + m_closeCB->run(); + } +} + +UInt32 +CBufferedOutputStream::write(const void* buffer, UInt32 n) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_buffer.write(buffer, n); + return n; +} + +void +CBufferedOutputStream::flush() +{ + // wait until all data is written + CLock lock(m_mutex); + while (m_buffer.getSize() > 0) { + m_empty.wait(); + } +} diff --git a/lib/io/CBufferedOutputStream.h b/lib/io/CBufferedOutputStream.h new file mode 100644 index 0000000..675aaac --- /dev/null +++ b/lib/io/CBufferedOutputStream.h @@ -0,0 +1,88 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CBUFFEREDOUTPUTSTREAM_H +#define CBUFFEREDOUTPUTSTREAM_H + +#include "IOutputStream.h" +#include "CStreamBuffer.h" +#include "CCondVar.h" + +class CMutex; +class IJob; + +//! Memory buffer output stream +/*! +This class provides an output stream that writes to a memory buffer. +It also provides a means for the owner to ensure thread safe access. +Typically, an owner object will make this object visible to clients +that need access to an IOutputStream while using the CBufferedOutputStream +methods to read the data written to the stream. +*/ +class CBufferedOutputStream : public IOutputStream { +public: + /*! + The \c mutex must not be NULL and will be used to ensure thread + safe access. If \c adoptedCloseCB is not NULL it will be called + when close() is called, allowing the creator to detect the close. + */ + CBufferedOutputStream(CMutex* mutex, IJob* adoptedCloseCB); + ~CBufferedOutputStream(); + + //! @name manipulators + //@{ + + //! Read data without removing from buffer + /*! + Returns a buffer of \c n bytes (which must be <= getSize()). The + caller must not modify the buffer nor delete it. The mutex must + be locked before calling this. + */ + const void* peek(UInt32 n); + + //! Discard data + /*! + Discards the next \c n bytes. If \c n >= getSize() then the buffer + is cleared. The mutex must be locked before calling this. + */ + void pop(UInt32 n); + + //@} + //! @name accessors + //@{ + + //! Get size of buffer + /*! + Returns the number of bytes in the buffer. The mutex must be locked + before calling this. + */ + UInt32 getSize() const; + + //@} + + // IOutputStream overrides + // these all lock the mutex for their duration + virtual void close(); + virtual UInt32 write(const void*, UInt32 n); + virtual void flush(); + +private: + CMutex* m_mutex; + IJob* m_closeCB; + CCondVar m_empty; + CStreamBuffer m_buffer; + bool m_closed; +}; + +#endif diff --git a/lib/io/CInputStreamFilter.cpp b/lib/io/CInputStreamFilter.cpp new file mode 100644 index 0000000..6d058b4 --- /dev/null +++ b/lib/io/CInputStreamFilter.cpp @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CInputStreamFilter.h" + +// +// CInputStreamFilter +// + +CInputStreamFilter::CInputStreamFilter(IInputStream* stream, bool adopted) : + m_stream(stream), + m_adopted(adopted) +{ + assert(m_stream != NULL); +} + +CInputStreamFilter::~CInputStreamFilter() +{ + if (m_adopted) { + delete m_stream; + } +} + +IInputStream* +CInputStreamFilter::getStream() const +{ + return m_stream; +} diff --git a/lib/io/CInputStreamFilter.h b/lib/io/CInputStreamFilter.h new file mode 100644 index 0000000..9274a81 --- /dev/null +++ b/lib/io/CInputStreamFilter.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CINPUTSTREAMFILTER_H +#define CINPUTSTREAMFILTER_H + +#include "IInputStream.h" + +//! A filtering input stream +/*! +This class wraps an input stream. Subclasses provide indirect access +to the stream, typically performing some filtering. +*/ +class CInputStreamFilter : public IInputStream { +public: + /*! + Create a wrapper around \c stream. Iff \c adoptStream is true then + this object takes ownership of the stream and will delete it in the + d'tor. + */ + CInputStreamFilter(IInputStream* stream, bool adoptStream = true); + ~CInputStreamFilter(); + + // IInputStream overrides + virtual void close() = 0; + virtual UInt32 read(void*, UInt32 n, double timeout) = 0; + virtual UInt32 getSize() const = 0; + +protected: + //! Get the stream + /*! + Returns the stream passed to the c'tor. + */ + IInputStream* getStream() const; + +private: + IInputStream* m_stream; + bool m_adopted; +}; + +#endif diff --git a/lib/io/COutputStreamFilter.cpp b/lib/io/COutputStreamFilter.cpp new file mode 100644 index 0000000..d90465a --- /dev/null +++ b/lib/io/COutputStreamFilter.cpp @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COutputStreamFilter.h" + +// +// COutputStreamFilter +// + +COutputStreamFilter::COutputStreamFilter(IOutputStream* stream, bool adopted) : + m_stream(stream), + m_adopted(adopted) +{ + assert(m_stream != NULL); +} + +COutputStreamFilter::~COutputStreamFilter() +{ + if (m_adopted) { + delete m_stream; + } +} + +IOutputStream* +COutputStreamFilter::getStream() const +{ + return m_stream; +} diff --git a/lib/io/COutputStreamFilter.h b/lib/io/COutputStreamFilter.h new file mode 100644 index 0000000..027e32d --- /dev/null +++ b/lib/io/COutputStreamFilter.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COUTPUTSTREAMFILTER_H +#define COUTPUTSTREAMFILTER_H + +#include "IOutputStream.h" + +//! A filtering output stream +/*! +This class wraps an output stream. Subclasses provide indirect access +to the stream, typically performing some filtering. +*/ +class COutputStreamFilter : public IOutputStream { +public: + /*! + Create a wrapper around \c stream. Iff \c adoptStream is true then + this object takes ownership of the stream and will delete it in the + d'tor. + */ + COutputStreamFilter(IOutputStream* stream, bool adoptStream = true); + ~COutputStreamFilter(); + + // IOutputStream overrides + virtual void close() = 0; + virtual UInt32 write(const void*, UInt32 count) = 0; + virtual void flush() = 0; + +protected: + //! Get the stream + /*! + Returns the stream passed to the c'tor. + */ + IOutputStream* getStream() const; + +private: + IOutputStream* m_stream; + bool m_adopted; +}; + +#endif diff --git a/lib/io/CStreamBuffer.cpp b/lib/io/CStreamBuffer.cpp new file mode 100644 index 0000000..2f10cac --- /dev/null +++ b/lib/io/CStreamBuffer.cpp @@ -0,0 +1,136 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CStreamBuffer.h" + +// +// CStreamBuffer +// + +const UInt32 CStreamBuffer::kChunkSize = 4096; + +CStreamBuffer::CStreamBuffer() : + m_size(0), + m_headUsed(0) +{ + // do nothing +} + +CStreamBuffer::~CStreamBuffer() +{ + // do nothing +} + +const void* +CStreamBuffer::peek(UInt32 n) +{ + assert(n <= m_size); + + // reserve space in first chunk + ChunkList::iterator head = m_chunks.begin(); + head->reserve(n + m_headUsed); + + // consolidate chunks into the first chunk until it has n bytes + ChunkList::iterator scan = head; + ++scan; + while (head->size() - m_headUsed < n && scan != m_chunks.end()) { + head->insert(head->end(), scan->begin(), scan->end()); + scan = m_chunks.erase(scan); + } + + return reinterpret_cast(&(head->begin()[m_headUsed])); +} + +void +CStreamBuffer::pop(UInt32 n) +{ + // discard all chunks if n is greater than or equal to m_size + if (n >= m_size) { + m_size = 0; + m_headUsed = 0; + m_chunks.clear(); + return; + } + + // update size + m_size -= n; + + // discard chunks until more than n bytes would've been discarded + ChunkList::iterator scan = m_chunks.begin(); + assert(scan != m_chunks.end()); + while (scan->size() - m_headUsed <= n) { + n -= scan->size() - m_headUsed; + m_headUsed = 0; + scan = m_chunks.erase(scan); + assert(scan != m_chunks.end()); + } + + // remove left over bytes from the head chunk + if (n > 0) { + m_headUsed += n; + } +} + +void +CStreamBuffer::write(const void* vdata, UInt32 n) +{ + assert(vdata != NULL); + + // ignore if no data, otherwise update size + if (n == 0) { + return; + } + m_size += n; + + // cast data to bytes + const UInt8* data = reinterpret_cast(vdata); + + // point to last chunk if it has space, otherwise append an empty chunk + ChunkList::iterator scan = m_chunks.end(); + if (scan != m_chunks.begin()) { + --scan; + if (scan->size() >= kChunkSize) { + ++scan; + } + } + if (scan == m_chunks.end()) { + scan = m_chunks.insert(scan, Chunk()); + } + + // append data in chunks + while (n > 0) { + // choose number of bytes for next chunk + assert(scan->size() <= kChunkSize); + UInt32 count = kChunkSize - scan->size(); + if (count > n) + count = n; + + // transfer data + scan->insert(scan->end(), data, data + count); + n -= count; + data += count; + + // append another empty chunk if we're not done yet + if (n > 0) { + ++scan; + scan = m_chunks.insert(scan, Chunk()); + } + } +} + +UInt32 +CStreamBuffer::getSize() const +{ + return m_size; +} diff --git a/lib/io/CStreamBuffer.h b/lib/io/CStreamBuffer.h new file mode 100644 index 0000000..fafcc72 --- /dev/null +++ b/lib/io/CStreamBuffer.h @@ -0,0 +1,78 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTREAMBUFFER_H +#define CSTREAMBUFFER_H + +#include "BasicTypes.h" +#include "stdlist.h" +#include "stdvector.h" + +//! FIFO of bytes +/*! +This class maintains a FIFO (first-in, last-out) buffer of bytes. +*/ +class CStreamBuffer { +public: + CStreamBuffer(); + ~CStreamBuffer(); + + //! @name manipulators + //@{ + + //! Read data without removing from buffer + /*! + Return a pointer to memory with the next \c n bytes in the buffer + (which must be <= getSize()). The caller must not modify the returned + memory nor delete it. + */ + const void* peek(UInt32 n); + + //! Discard data + /*! + Discards the next \c n bytes. If \c n >= getSize() then the buffer + is cleared. + */ + void pop(UInt32 n); + + //! Write data to buffer + /*! + Appends \c n bytes from \c data to the buffer. + */ + void write(const void* data, UInt32 n); + + //@} + //! @name accessors + //@{ + + //! Get size of buffer + /*! + Returns the number of bytes in the buffer. + */ + UInt32 getSize() const; + + //@} + +private: + static const UInt32 kChunkSize; + + typedef std::vector Chunk; + typedef std::list ChunkList; + + ChunkList m_chunks; + UInt32 m_size; + UInt32 m_headUsed; +}; + +#endif diff --git a/lib/io/IInputStream.h b/lib/io/IInputStream.h new file mode 100644 index 0000000..a1b250d --- /dev/null +++ b/lib/io/IInputStream.h @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IINPUTSTREAM_H +#define IINPUTSTREAM_H + +#include "IInterface.h" +#include "BasicTypes.h" + +//! Input stream interface +/*! +Defines the interface for all input streams. +*/ +class IInputStream : public IInterface { +public: + //! @name manipulators + //@{ + + //! Close the stream + /*! + Closes the stream. Attempting to read() after close() throws + XIOClosed and getSize() always returns zero. + */ + virtual void close() = 0; + + //! Read from stream + /*! + Read up to \c n bytes into buffer, returning the number read. + Blocks for up to \c timeout seconds if no data is available but does + not wait if any data is available, even if less than \c n bytes. + If \c timeout < 0 then it blocks indefinitely until data is available. + If \c buffer is NULL then the data is discarded. Returns (UInt32)-1 if + it times out and 0 if no data is available and the other end of the + stream has hungup. + + (cancellation point) + */ + virtual UInt32 read(void* buffer, UInt32 n, double timeout) = 0; + + //@} + //! @name accessors + //@{ + + //! Get remaining size of stream + /*! + Returns a conservative estimate of the available bytes to read + (i.e. a number not greater than the actual number of bytes). + Some streams may not be able to determine this and will always + return zero. + */ + virtual UInt32 getSize() const = 0; + + //@} +}; + +#endif diff --git a/lib/io/IOutputStream.h b/lib/io/IOutputStream.h new file mode 100644 index 0000000..5540ce0 --- /dev/null +++ b/lib/io/IOutputStream.h @@ -0,0 +1,58 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IOUTPUTSTREAM_H +#define IOUTPUTSTREAM_H + +#include "IInterface.h" +#include "BasicTypes.h" + +//! Output stream interface +/*! +Defines the interface for all output streams. +*/ +class IOutputStream : public IInterface { +public: + //! @name manipulators + //@{ + + //! Close the stream + /*! + Closes the stream. Attempting to write() after close() throws + XIOClosed. + */ + virtual void close() = 0; + + //! Write to stream + /*! + Write \c n bytes from \c buffer to the stream. If this can't + complete immeditely it will block. If cancelled, an indeterminate + amount of data may have been written. + + (cancellation point) + */ + virtual UInt32 write(const void* buffer, UInt32 n) = 0; + + //! Flush the stream + /*! + Waits until all buffered data has been written to the stream. + + (cancellation point) + */ + virtual void flush() = 0; + + //@} +}; + +#endif diff --git a/lib/io/IStreamFilterFactory.h b/lib/io/IStreamFilterFactory.h new file mode 100644 index 0000000..2f459ef --- /dev/null +++ b/lib/io/IStreamFilterFactory.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISTREAMFILTERFACTORY_H +#define ISTREAMFILTERFACTORY_H + +#include "IInterface.h" + +class CInputStreamFilter; +class COutputStreamFilter; +class IInputStream; +class IOutputStream; + +//! Stream filter factory interface +/*! +This interface provides factory methods to create stream filters. +*/ +class IStreamFilterFactory : public IInterface { +public: + //! Create input filter + /*! + Create and return an input stream filter. The caller must delete the + returned object. + */ + virtual CInputStreamFilter* + createInput(IInputStream*, bool adoptStream) = 0; + + //! Create output filter + /*! + Create and return an output stream filter. The caller must delete the + returned object. + */ + virtual COutputStreamFilter* + createOutput(IOutputStream*, bool adoptStream) = 0; +}; + +#endif diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am new file mode 100644 index 0000000..8bac4c8 --- /dev/null +++ b/lib/io/Makefile.am @@ -0,0 +1,49 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + io.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libio.a +libio_a_SOURCES = \ + CBufferedInputStream.cpp \ + CBufferedOutputStream.cpp \ + CInputStreamFilter.cpp \ + COutputStreamFilter.cpp \ + CStreamBuffer.cpp \ + XIO.cpp \ + CBufferedInputStream.h \ + CBufferedOutputStream.h \ + CInputStreamFilter.h \ + COutputStreamFilter.h \ + CStreamBuffer.h \ + IInputStream.h \ + IOutputStream.h \ + IStreamFilterFactory.h \ + XIO.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + $(NULL) diff --git a/lib/io/Makefile.in b/lib/io/Makefile.in new file mode 100644 index 0000000..2113fb9 --- /dev/null +++ b/lib/io/Makefile.in @@ -0,0 +1,360 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + io.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libio.a +libio_a_SOURCES = \ + CBufferedInputStream.cpp \ + CBufferedOutputStream.cpp \ + CInputStreamFilter.cpp \ + COutputStreamFilter.cpp \ + CStreamBuffer.cpp \ + XIO.cpp \ + CBufferedInputStream.h \ + CBufferedOutputStream.h \ + CInputStreamFilter.h \ + COutputStreamFilter.h \ + CStreamBuffer.h \ + IInputStream.h \ + IOutputStream.h \ + IStreamFilterFactory.h \ + XIO.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + $(NULL) + +subdir = lib/io +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libio_a_AR = $(AR) cru +libio_a_LIBADD = +am_libio_a_OBJECTS = CBufferedInputStream.$(OBJEXT) \ + CBufferedOutputStream.$(OBJEXT) CInputStreamFilter.$(OBJEXT) \ + COutputStreamFilter.$(OBJEXT) CStreamBuffer.$(OBJEXT) \ + XIO.$(OBJEXT) +libio_a_OBJECTS = $(am_libio_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CBufferedInputStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CBufferedOutputStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CInputStreamFilter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/COutputStreamFilter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CStreamBuffer.Po $(DEPDIR)/XIO.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libio_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libio_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/io/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libio.a: $(libio_a_OBJECTS) $(libio_a_DEPENDENCIES) + -rm -f libio.a + $(libio_a_AR) libio.a $(libio_a_OBJECTS) $(libio_a_LIBADD) + $(RANLIB) libio.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CBufferedInputStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CBufferedOutputStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CInputStreamFilter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/COutputStreamFilter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CStreamBuffer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XIO.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/io/XIO.cpp b/lib/io/XIO.cpp new file mode 100644 index 0000000..710c419 --- /dev/null +++ b/lib/io/XIO.cpp @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XIO.h" + +// +// XIOClosed +// + +CString +XIOClosed::getWhat() const throw() +{ + return format("XIOClosed", "already closed"); +} + + +// +// XIOEndOfStream +// + +CString +XIOEndOfStream::getWhat() const throw() +{ + return format("XIOEndOfStream", "reached end of stream"); +} diff --git a/lib/io/XIO.h b/lib/io/XIO.h new file mode 100644 index 0000000..31871bf --- /dev/null +++ b/lib/io/XIO.h @@ -0,0 +1,42 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XIO_H +#define XIO_H + +#include "XBase.h" + +//! Generic I/O exception +XBASE_SUBCLASS(XIO, XBase); + +//! I/O closing exception +/*! +Thrown if a stream cannot be closed. +*/ +XBASE_SUBCLASS(XIOClose, XIO); + +//! I/O already closed exception +/*! +Thrown when attempting to close or perform I/O on an already closed. +stream. +*/ +XBASE_SUBCLASS_WHAT(XIOClosed, XIO); + +//! I/O end of stream exception +/*! +Thrown when attempting to read beyond the end of a stream. +*/ +XBASE_SUBCLASS_WHAT(XIOEndOfStream, XIO); + +#endif diff --git a/lib/io/io.dsp b/lib/io/io.dsp new file mode 100644 index 0000000..a46f158 --- /dev/null +++ b/lib/io/io.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=io - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "io.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "io.mak" CFG="io - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "io - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "io - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "io - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "io - Win32 Release" +# Name "io - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CBufferedInputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\XIO.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CBufferedInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\IInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IStreamFilterFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XIO.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/mt/CCondVar.cpp b/lib/mt/CCondVar.cpp new file mode 100644 index 0000000..d13aedf --- /dev/null +++ b/lib/mt/CCondVar.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CCondVar.h" +#include "CStopwatch.h" +#include "CArch.h" + +// +// CCondVarBase +// + +CCondVarBase::CCondVarBase(CMutex* mutex) : + m_mutex(mutex) +{ + assert(m_mutex != NULL); + m_cond = ARCH->newCondVar(); +} + +CCondVarBase::~CCondVarBase() +{ + ARCH->closeCondVar(m_cond); +} + +void +CCondVarBase::lock() const +{ + m_mutex->lock(); +} + +void +CCondVarBase::unlock() const +{ + m_mutex->unlock(); +} + +void +CCondVarBase::signal() +{ + ARCH->signalCondVar(m_cond); +} + +void +CCondVarBase::broadcast() +{ + ARCH->broadcastCondVar(m_cond); +} + +bool +CCondVarBase::wait(CStopwatch& timer, double timeout) const +{ + // check timeout against timer + if (timeout >= 0.0) { + timeout -= timer.getTime(); + if (timeout < 0.0) + return false; + } + return wait(timeout); +} + +bool +CCondVarBase::wait(double timeout) const +{ + return ARCH->waitCondVar(m_cond, m_mutex->m_mutex, timeout); +} + +CMutex* +CCondVarBase::getMutex() const +{ + return m_mutex; +} diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h new file mode 100644 index 0000000..78fbfe9 --- /dev/null +++ b/lib/mt/CCondVar.h @@ -0,0 +1,226 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCONDVAR_H +#define CCONDVAR_H + +#include "CMutex.h" +#include "BasicTypes.h" + +class CStopwatch; + +//! Generic condition variable +/*! +This class provides functionality common to all condition variables +but doesn't provide the actual variable storage. A condition variable +is a multiprocessing primitive that can be waited on. Every condition +variable has an associated mutex. +*/ +class CCondVarBase { +public: + /*! + \c mutex must not be NULL. All condition variables have an + associated mutex. The mutex needn't be unique to one condition + variable. + */ + CCondVarBase(CMutex* mutex); + ~CCondVarBase(); + + //! @name manipulators + //@{ + + //! Lock the condition variable's mutex + /*! + Lock the condition variable's mutex. The condition variable should + be locked before reading or writing it. It must be locked for a + call to wait(). Locks are not recursive; locking a locked mutex + will deadlock the thread. + */ + void lock() const; + + //! Unlock the condition variable's mutex + void unlock() const; + + //! Signal the condition variable + /*! + Wake up one waiting thread, if there are any. Which thread gets + woken is undefined. + */ + void signal(); + + //! Signal the condition variable + /*! + Wake up all waiting threads, if any. + */ + void broadcast(); + + //@} + //! @name accessors + //@{ + + //! Wait on the condition variable + /*! + Wait on the condition variable. If \c timeout < 0 then wait until + signalled, otherwise up to \c timeout seconds or until signalled, + whichever comes first. Returns true if the object was signalled + during the wait, false otherwise. + + The proper way to wait for a condition is: + \code + cv.lock(); + while (cv-expr) { + cv.wait(); + } + cv.unlock(); + \endcode + where \c cv-expr involves the value of \c cv and is false when the + condition is satisfied. + + (cancellation point) + */ + bool wait(double timeout = -1.0) const; + + //! Wait on the condition variable + /*! + Same as \c wait(double) but use \c timer to compare against \timeout. + Since clients normally wait on condition variables in a loop, clients + can use this to avoid recalculating \c timeout on each iteration. + Passing a stopwatch with a negative \c timeout is pointless (it will + never time out) but permitted. + + (cancellation point) + */ + bool wait(CStopwatch& timer, double timeout) const; + + //! Get the mutex + /*! + Get the mutex passed to the c'tor. + */ + CMutex* getMutex() const; + + //@} + +private: + // not implemented + CCondVarBase(const CCondVarBase&); + CCondVarBase& operator=(const CCondVarBase&); + +private: + CMutex* m_mutex; + CArchCond m_cond; +}; + +//! Condition variable +/*! +A condition variable with storage for type \c T. +*/ +template +class CCondVar : public CCondVarBase { +public: + //! Initialize using \c value + CCondVar(CMutex* mutex, const T& value); + //! Initialize using another condition variable's value + CCondVar(const CCondVar&); + ~CCondVar(); + + //! @name manipulators + //@{ + + //! Assigns the value of \c cv to this + /*! + Set the variable's value. The condition variable should be locked + before calling this method. + */ + CCondVar& operator=(const CCondVar& cv); + + //! Assigns \c value to this + /*! + Set the variable's value. The condition variable should be locked + before calling this method. + */ + CCondVar& operator=(const T& v); + + //@} + //! @name accessors + //@{ + + //! Get the variable's value + /*! + Get the variable's value. The condition variable should be locked + before calling this method. + */ + operator const T&() const; + + //@} + +private: + T m_data; +}; + +template +inline +CCondVar::CCondVar( + CMutex* mutex, + const T& data) : + CCondVarBase(mutex), + m_data(data) +{ + // do nothing +} + +template +inline +CCondVar::CCondVar( + const CCondVar& cv) : + CCondVarBase(cv.getMutex()), + m_data(cv.m_data) +{ + // do nothing +} + +template +inline +CCondVar::~CCondVar() +{ + // do nothing +} + +template +inline +CCondVar& +CCondVar::operator=( + const CCondVar& cv) +{ + m_data = cv.m_data; + return *this; +} + +template +inline +CCondVar& +CCondVar::operator=( + const T& data) +{ + m_data = data; + return *this; +} + +template +inline +CCondVar::operator const T&() const +{ + return m_data; +} + +#endif diff --git a/lib/mt/CLock.cpp b/lib/mt/CLock.cpp new file mode 100644 index 0000000..c943395 --- /dev/null +++ b/lib/mt/CLock.cpp @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CLock.h" +#include "CCondVar.h" +#include "CMutex.h" + +// +// CLock +// + +CLock::CLock(const CMutex* mutex) : + m_mutex(mutex) +{ + m_mutex->lock(); +} + +CLock::CLock(const CCondVarBase* cv) : + m_mutex(cv->getMutex()) +{ + m_mutex->lock(); +} + +CLock::~CLock() +{ + m_mutex->unlock(); +} diff --git a/lib/mt/CLock.h b/lib/mt/CLock.h new file mode 100644 index 0000000..f00e0cd --- /dev/null +++ b/lib/mt/CLock.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CLOCK_H +#define CLOCK_H + +#include "common.h" + +class CMutex; +class CCondVarBase; + +//! Mutual exclusion lock utility +/*! +This class locks a mutex or condition variable in the c'tor and unlocks +it in the d'tor. It's easier and safer than manually locking and +unlocking since unlocking must usually be done no matter how a function +exits (including by unwinding due to an exception). +*/ +class CLock { +public: + //! Lock the mutex \c mutex + CLock(const CMutex* mutex); + //! Lock the condition variable \c cv + CLock(const CCondVarBase* cv); + //! Unlock the mutex or condition variable + ~CLock(); + +private: + // not implemented + CLock(const CLock&); + CLock& operator=(const CLock&); + +private: + const CMutex* m_mutex; +}; + +#endif diff --git a/lib/mt/CMutex.cpp b/lib/mt/CMutex.cpp new file mode 100644 index 0000000..0d2bab3 --- /dev/null +++ b/lib/mt/CMutex.cpp @@ -0,0 +1,53 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMutex.h" +#include "CArch.h" + +// +// CMutex +// + +CMutex::CMutex() +{ + m_mutex = ARCH->newMutex(); +} + +CMutex::CMutex(const CMutex&) +{ + m_mutex = ARCH->newMutex(); +} + +CMutex::~CMutex() +{ + ARCH->closeMutex(m_mutex); +} + +CMutex& +CMutex::operator=(const CMutex&) +{ + return *this; +} + +void +CMutex::lock() const +{ + ARCH->lockMutex(m_mutex); +} + +void +CMutex::unlock() const +{ + ARCH->unlockMutex(m_mutex); +} diff --git a/lib/mt/CMutex.h b/lib/mt/CMutex.h new file mode 100644 index 0000000..c11eeee --- /dev/null +++ b/lib/mt/CMutex.h @@ -0,0 +1,78 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMUTEX_H +#define CMUTEX_H + +#include "IArchMultithread.h" + +//! Mutual exclusion +/*! +A non-recursive mutual exclusion object. Only one thread at a time can +hold a lock on a mutex. Any thread that attempts to lock a locked mutex +will block until the mutex is unlocked. At that time, if any threads are +blocked, exactly one waiting thread will acquire the lock and continue +running. A thread may not lock a mutex it already owns the lock on; if +it tries it will deadlock itself. +*/ +class CMutex { +public: + CMutex(); + //! Equivalent to default c'tor + /*! + Copy c'tor doesn't copy anything. It just makes it possible to + copy objects that contain a mutex. + */ + CMutex(const CMutex&); + ~CMutex(); + + //! @name manipulators + //@{ + + //! Does nothing + /*! + This does nothing. It just makes it possible to assign objects + that contain a mutex. + */ + CMutex& operator=(const CMutex&); + + //@} + //! @name accessors + //@{ + + //! Lock the mutex + /*! + Locks the mutex, which must not have been previously locked by the + calling thread. This blocks if the mutex is already locked by another + thread. + + (cancellation point) + */ + void lock() const; + + //! Unlock the mutex + /*! + Unlocks the mutex, which must have been previously locked by the + calling thread. + */ + void unlock() const; + + //@} + +private: + friend class CCondVarBase; + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp new file mode 100644 index 0000000..fcf6832 --- /dev/null +++ b/lib/mt/CThread.cpp @@ -0,0 +1,184 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CThread.h" +#include "XMT.h" +#include "XThread.h" +#include "CLog.h" +#include "IJob.h" +#include "CArch.h" + +// +// CThread +// + +CThread::CThread(IJob* job) +{ + m_thread = ARCH->newThread(&CThread::threadFunc, job); + if (m_thread == NULL) { + // couldn't create thread + delete job; + throw XMTThreadUnavailable(); + } +} + +CThread::CThread(const CThread& thread) +{ + m_thread = ARCH->copyThread(thread.m_thread); +} + +CThread::CThread(CArchThread adoptedThread) +{ + m_thread = adoptedThread; +} + +CThread::~CThread() +{ + ARCH->closeThread(m_thread); +} + +CThread& +CThread::operator=(const CThread& thread) +{ + // copy given thread and release ours + CArchThread copy = ARCH->copyThread(thread.m_thread); + ARCH->closeThread(m_thread); + + // cut over + m_thread = copy; + + return *this; +} + +void +CThread::exit(void* result) +{ + throw XThreadExit(result); +} + +void +CThread::cancel() +{ + ARCH->cancelThread(m_thread); +} + +void +CThread::setPriority(int n) +{ + ARCH->setPriorityOfThread(m_thread, n); +} + +CThread +CThread::getCurrentThread() +{ + return CThread(ARCH->newCurrentThread()); +} + +void +CThread::testCancel() +{ + ARCH->testCancelThread(); +} + +bool +CThread::wait(double timeout) const +{ + return ARCH->wait(m_thread, timeout); +} + +CThread::EWaitResult +CThread::waitForEvent(double timeout) const +{ + // IArchMultithread EWaitResults map directly to our EWaitResults + static const EWaitResult s_map[] = { + kEvent, + kExit, + kTimeout + }; + return s_map[ARCH->waitForEvent(m_thread, timeout)]; +} + +void* +CThread::getResult() const +{ + if (wait()) + return ARCH->getResultOfThread(m_thread); + else + return NULL; +} + +IArchMultithread::ThreadID +CThread::getID() const +{ + return ARCH->getIDOfThread(m_thread); +} + +bool +CThread::operator==(const CThread& thread) const +{ + return ARCH->isSameThread(m_thread, thread.m_thread); +} + +bool +CThread::operator!=(const CThread& thread) const +{ + return !ARCH->isSameThread(m_thread, thread.m_thread); +} + +void* +CThread::threadFunc(void* vjob) +{ + // get this thread's id for logging + IArchMultithread::ThreadID id; + { + CArchThread thread = ARCH->newCurrentThread(); + id = ARCH->getIDOfThread(thread); + ARCH->closeThread(thread); + } + + // get job + IJob* job = reinterpret_cast(vjob); + + // run job + void* result = NULL; + try { + // go + LOG((CLOG_DEBUG1 "thread 0x%08x entry", id)); + job->run(); + LOG((CLOG_DEBUG1 "thread 0x%08x exit", id)); + } + + catch (XThreadCancel&) { + // client called cancel() + LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id)); + delete job; + throw; + } + catch (XThreadExit& e) { + // client called exit() + result = e.m_result; + LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); + } + catch (...) { + LOG((CLOG_DEBUG1 "exception on thread 0x%08x", id)); + delete job; + throw; + } + + // done with job + delete job; + + // return exit result + return result; +} diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h new file mode 100644 index 0000000..7398261 --- /dev/null +++ b/lib/mt/CThread.h @@ -0,0 +1,225 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTHREAD_H +#define CTHREAD_H + +#include "IArchMultithread.h" + +class IJob; + +//! Thread handle +/*! +Creating a CThread creates a new context of execution (i.e. thread) that +runs simulatenously with the calling thread. A CThread is only a handle +to a thread; deleting a CThread does not cancel or destroy the thread it +refers to and multiple CThread objects can refer to the same thread. + +Threads can terminate themselves but cannot be forced to terminate by +other threads. However, other threads can signal a thread to terminate +itself by cancelling it. And a thread can wait (block) on another thread +to terminate. + +Most functions that can block for an arbitrary time are cancellation +points. A cancellation point is a function that can be interrupted by +a request to cancel the thread. Cancellation points are noted in the +documentation. +*/ +// note -- do not derive from this class +class CThread { +public: + //! Result of waitForEvent() + enum EWaitResult { + kEvent, //!< An event is pending + kExit, //!< Thread exited + kTimeout //!< Wait timed out + }; + + //! Run \c adoptedJob in a new thread + /*! + Create and start a new thread executing the \c adoptedJob. The + new thread takes ownership of \c adoptedJob and will delete it. + */ + CThread(IJob* adoptedJob); + + //! Duplicate a thread handle + /*! + Make a new thread object that refers to an existing thread. + This does \b not start a new thread. + */ + CThread(const CThread&); + + //! Release a thread handle + /*! + Release a thread handle. This does not terminate the thread. A thread + will keep running until the job completes or calls exit() or allows + itself to be cancelled. + */ + ~CThread(); + + //! @name manipulators + //@{ + + //! Assign thread handle + /*! + Assign a thread handle. This has no effect on the threads, it simply + makes this thread object refer to another thread. It does \b not + start a new thread. + */ + CThread& operator=(const CThread&); + + //! Terminate the calling thread + /*! + Terminate the calling thread. This function does not return but + the stack is unwound and automatic objects are destroyed, as if + exit() threw an exception (which is, in fact, what it does). The + argument is saved as the result returned by getResult(). If you + have \c catch(...) blocks then you should add the following before + each to avoid catching the exit: + \code + catch(CThreadExit&) { throw; } + \endcode + or add the \c RETHROW_XTHREAD macro to the \c catch(...) block. + */ + static void exit(void*); + + //! Cancel thread + /*! + Cancel the thread. cancel() never waits for the thread to + terminate; it just posts the cancel and returns. A thread will + terminate when it enters a cancellation point with cancellation + enabled. If cancellation is disabled then the cancel is + remembered but not acted on until the first call to a + cancellation point after cancellation is enabled. + + A cancellation point is a function that can act on cancellation. + A cancellation point does not return if there's a cancel pending. + Instead, it unwinds the stack and destroys automatic objects, as + if cancel() threw an exception (which is, in fact, what it does). + Threads must take care to unlock and clean up any resources they + may have, especially mutexes. They can \c catch(XThreadCancel) to + do that then rethrow the exception or they can let it happen + automatically by doing clean up in the d'tors of automatic + objects (like CLock). Clients are strongly encouraged to do the latter. + During cancellation, further cancel() calls are ignored (i.e. + a thread cannot be interrupted by a cancel during cancellation). + + Clients that \c catch(XThreadCancel) must always rethrow the + exception. Clients that \c catch(...) must either rethrow the + exception or include a \c catch(XThreadCancel) handler that + rethrows. The \c RETHROW_XTHREAD macro may be useful for that. + */ + void cancel(); + + //! Change thread priority + /*! + Change the priority of the thread. Normal priority is 0, 1 is + the next lower, etc. -1 is the next higher, etc. but boosting + the priority may not be permitted and will be silenty ignored. + */ + void setPriority(int n); + + //@} + //! @name accessors + //@{ + + //! Get current thread's handle + /*! + Return a CThread object representing the calling thread. + */ + static CThread getCurrentThread(); + + //! Test for cancellation + /*! + testCancel() does nothing but is a cancellation point. Call + this to make a function itself a cancellation point. If the + thread was cancelled and cancellation is enabled this will + cause the thread to unwind the stack and terminate. + + (cancellation point) + */ + static void testCancel(); + + //! Wait for thread to terminate + /*! + Waits for the thread to terminate (by exit() or cancel() or + by returning from the thread job) for up to \c timeout seconds, + returning true if the thread terminated and false otherwise. + This returns immediately with false if called by a thread on + itself and immediately with true if the thread has already + terminated. This will wait forever if \c timeout < 0.0. + + (cancellation point) + */ + bool wait(double timeout = -1.0) const; + + //! Wait for an event (win32) + /*! + Wait for the message queue to contain a message or for the thread + to exit for up to \c timeout seconds. This returns immediately if + any message is available (including messages that were already in + the queue during the last call to \c GetMessage() or + \c PeekMessage() or waitForEvent(). Returns kEvent if a message + is available, kExit if the thread exited, and kTimeout otherwise. + This will wait forever if \c timeout < 0.0. + + This method is available under win32 only. + + (cancellation point) + */ + EWaitResult waitForEvent(double timeout = -1.0) const; + + //! Get the exit result + /*! + Returns the exit result. This does an implicit wait(). It returns + NULL immediately if called by a thread on itself or on a thread that + was cancelled. + + (cancellation point) + */ + void* getResult() const; + + //! Get the thread id + /*! + Returns an integer id for this thread. This id must not be used to + check if two CThread objects refer to the same thread. Use + operator==() for that. + */ + IArchMultithread::ThreadID + getID() const; + + //! Compare thread handles + /*! + Returns true if two CThread objects refer to the same thread. + */ + bool operator==(const CThread&) const; + + //! Compare thread handles + /*! + Returns true if two CThread objects do not refer to the same thread. + */ + bool operator!=(const CThread&) const; + + //@} + +private: + CThread(CArchThread); + + static void* threadFunc(void*); + +private: + CArchThread m_thread; +}; + +#endif diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp new file mode 100644 index 0000000..2ac6352 --- /dev/null +++ b/lib/mt/CTimerThread.cpp @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTimerThread.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CLog.h" +#include "CArch.h" + +// +// CTimerThread +// + +CTimerThread::CTimerThread(double timeout, bool* timedOut) : + m_timeout(timeout), + m_timedOut(timedOut) +{ + if (m_timedOut != NULL) { + *m_timedOut = false; + } + if (m_timeout >= 0.0) { + m_callingThread = new CThread(CThread::getCurrentThread()); + m_timingThread = new CThread(new TMethodJob( + this, &CTimerThread::timer)); + } + else { + m_callingThread = NULL; + m_timingThread = NULL; + } +} + +CTimerThread::~CTimerThread() +{ + if (m_timingThread != NULL) { + LOG((CLOG_DEBUG1 "cancelling timeout")); + m_timingThread->cancel(); + m_timingThread->wait(); + LOG((CLOG_DEBUG1 "cancelled timeout")); + delete m_timingThread; + delete m_callingThread; + } +} + +void +CTimerThread::timer(void*) +{ + LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); + ARCH->sleep(m_timeout); + LOG((CLOG_DEBUG1 "timeout")); + if (m_timedOut != NULL) { + *m_timedOut = true; + } + m_callingThread->cancel(); +} diff --git a/lib/mt/CTimerThread.h b/lib/mt/CTimerThread.h new file mode 100644 index 0000000..1a0a2b5 --- /dev/null +++ b/lib/mt/CTimerThread.h @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTIMERTHREAD_H +#define CTIMERTHREAD_H + +#include "common.h" + +class CThread; + +//! A timer thread +/*! +An object of this class cancels the thread that called the c'tor unless +the object is destroyed before a given timeout. +*/ +class CTimerThread { +public: + //! Cancel calling thread after \c timeout seconds + /*! + Cancels the calling thread after \c timeout seconds unless destroyed + before then. If \c timeout is less than zero then it never times + out and this is a no-op. If \c timedOutFlag is not NULL then it's + set to false in the c'tor and to true if the timeout exipires before + it's cancelled. + */ + CTimerThread(double timeout, bool* timedOutFlag = NULL); + //! Cancel the timer thread + ~CTimerThread(); + +private: + void timer(void*); + + // not implemented + CTimerThread(const CTimerThread&); + CTimerThread& operator=(const CTimerThread&); + +private: + double m_timeout; + bool* m_timedOut; + CThread* m_callingThread; + CThread* m_timingThread; +}; + +#endif + diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am new file mode 100644 index 0000000..6eda91d --- /dev/null +++ b/lib/mt/Makefile.am @@ -0,0 +1,46 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + mt.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libmt.a +libmt_a_SOURCES = \ + CLock.cpp \ + CMutex.cpp \ + CCondVar.cpp \ + CThread.cpp \ + CTimerThread.cpp \ + XMT.cpp \ + CCondVar.h \ + CLock.h \ + CMutex.h \ + CThread.h \ + CTimerThread.h \ + XMT.h \ + XThread.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + $(NULL) diff --git a/lib/mt/Makefile.in b/lib/mt/Makefile.in new file mode 100644 index 0000000..8a51b70 --- /dev/null +++ b/lib/mt/Makefile.in @@ -0,0 +1,353 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + mt.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libmt.a +libmt_a_SOURCES = \ + CLock.cpp \ + CMutex.cpp \ + CCondVar.cpp \ + CThread.cpp \ + CTimerThread.cpp \ + XMT.cpp \ + CCondVar.h \ + CLock.h \ + CMutex.h \ + CThread.h \ + CTimerThread.h \ + XMT.h \ + XThread.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + $(NULL) + +subdir = lib/mt +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libmt_a_AR = $(AR) cru +libmt_a_LIBADD = +am_libmt_a_OBJECTS = CLock.$(OBJEXT) CMutex.$(OBJEXT) CCondVar.$(OBJEXT) \ + CThread.$(OBJEXT) CTimerThread.$(OBJEXT) XMT.$(OBJEXT) +libmt_a_OBJECTS = $(am_libmt_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CCondVar.Po $(DEPDIR)/CLock.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CMutex.Po $(DEPDIR)/CThread.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTimerThread.Po $(DEPDIR)/XMT.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libmt_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libmt_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/mt/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libmt.a: $(libmt_a_OBJECTS) $(libmt_a_DEPENDENCIES) + -rm -f libmt.a + $(libmt_a_AR) libmt.a $(libmt_a_OBJECTS) $(libmt_a_LIBADD) + $(RANLIB) libmt.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CCondVar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CLock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CMutex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CThread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTimerThread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XMT.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/mt/XMT.cpp b/lib/mt/XMT.cpp new file mode 100644 index 0000000..b10d2d7 --- /dev/null +++ b/lib/mt/XMT.cpp @@ -0,0 +1,25 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XMT.h" + +// +// XMTThreadUnavailable +// + +CString +XMTThreadUnavailable::getWhat() const throw() +{ + return format("XMTThreadUnavailable", "cannot create thread"); +} diff --git a/lib/mt/XMT.h b/lib/mt/XMT.h new file mode 100644 index 0000000..f66428e --- /dev/null +++ b/lib/mt/XMT.h @@ -0,0 +1,29 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XMT_H +#define XMT_H + +#include "XBase.h" + +//! Generic multithreading exception +XBASE_SUBCLASS(XMT, XBase); + +//! Thread creation exception +/*! +Thrown when a thread cannot be created. +*/ +XBASE_SUBCLASS_WHAT(XMTThreadUnavailable, XMT); + +#endif diff --git a/lib/mt/XThread.h b/lib/mt/XThread.h new file mode 100644 index 0000000..30dff1d --- /dev/null +++ b/lib/mt/XThread.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XTHREAD_H +#define XTHREAD_H + +#include "XArch.h" + +//! Thread exception to exit +/*! +Thrown by CThread::exit() to exit a thread. Clients of CThread +must not throw this type but must rethrow it if caught (by +XThreadExit, XThread, or ...). +*/ +class XThreadExit : public XThread { +public: + //! \c result is the result of the thread + XThreadExit(void* result) : m_result(result) { } + ~XThreadExit() { } + +public: + void* m_result; +}; + +#endif diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp new file mode 100644 index 0000000..a11135e --- /dev/null +++ b/lib/mt/mt.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=mt - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mt.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mt.mak" CFG="mt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "mt - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mt - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "mt - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "mt - Win32 Release" +# Name "mt - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CCondVar.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLock.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMutex.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\XMT.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CCondVar.h +# End Source File +# Begin Source File + +SOURCE=.\CLock.h +# End Source File +# Begin Source File + +SOURCE=.\CMutex.h +# End Source File +# Begin Source File + +SOURCE=.\CThread.h +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.h +# End Source File +# Begin Source File + +SOURCE=.\XMT.h +# End Source File +# Begin Source File + +SOURCE=.\XThread.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp new file mode 100644 index 0000000..c60b96a --- /dev/null +++ b/lib/net/CNetworkAddress.cpp @@ -0,0 +1,166 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CNetworkAddress.h" +#include "XSocket.h" +#include "CArch.h" +#include "XArch.h" +#include + +// +// CNetworkAddress +// + +CNetworkAddress::CNetworkAddress() : + m_address(NULL) +{ + // note -- make no calls to CNetwork socket interface here; + // we're often called prior to CNetwork::init(). +} + +CNetworkAddress::CNetworkAddress(int port) +{ + if (port == 0) { + throw XSocketAddress(XSocketAddress::kBadPort, "", port); + } + + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); +} + +CNetworkAddress::CNetworkAddress(const CNetworkAddress& addr) : + m_address(ARCH->copyAddr(addr.m_address)), + m_hostname(addr.m_hostname) +{ + // do nothing +} + +CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : + m_hostname(hostname_) +{ + if (port == 0) { + throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, port); + } + + // check for port suffix + CString hostname(m_hostname); + CString::size_type i = hostname.rfind(':'); + if (i != CString::npos && i + 1 < hostname.size()) { + // found a colon. see if it looks like an IPv6 address. + bool colonNotation = false; + bool dotNotation = false; + bool doubleColon = false; + for (CString::size_type j = 0; j < i; ++j) { + if (hostname[j] == ':') { + colonNotation = true; + dotNotation = false; + if (hostname[j + 1] == ':') { + doubleColon = true; + } + } + else if (hostname[j] == '.' && colonNotation) { + dotNotation = true; + } + } + + // port suffix is ambiguous with IPv6 notation if there's + // a double colon and the end of the address is not in dot + // notation. in that case we assume it's not a port suffix. + // the user can replace the double colon with zeros to + // disambiguate. + if ((!doubleColon || dotNotation) || !colonNotation) { + char* end; + long suffixPort = strtol(hostname.c_str() + i + 1, &end, 10); + if (end == hostname.c_str() + i + 1 || *end != '\0' || + suffixPort <= 0 || suffixPort > 65535) { + // bogus port + throw XSocketAddress(XSocketAddress::kBadPort, + m_hostname, port); + } + else { + // good port + port = static_cast(suffixPort); + hostname.erase(i); + } + } + } + + // if hostname is empty then use wildcard address + if (hostname.empty()) { + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); + } + else { + // look up name + try { + m_address = ARCH->nameToAddr(hostname); + ARCH->setAddrPort(m_address, port); + } + catch (XArchNetworkNameUnknown&) { + throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); + } + catch (XArchNetworkNameNoAddress&) { + throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); + } + catch (XArchNetworkName&) { + throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); + } + } +} + +CNetworkAddress::~CNetworkAddress() +{ + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } +} + +CNetworkAddress& +CNetworkAddress::operator=(const CNetworkAddress& addr) +{ + CArchNetAddress newAddr = NULL; + if (addr.m_address != NULL) { + newAddr = ARCH->copyAddr(addr.m_address); + } + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } + m_hostname = addr.m_hostname; + m_address = newAddr; + return *this; +} + +bool +CNetworkAddress::isValid() const +{ + return (m_address != NULL); +} + +const CArchNetAddress& +CNetworkAddress::getAddress() const +{ + return m_address; +} + +int +CNetworkAddress::getPort() const +{ + return (m_address == NULL) ? 0 : ARCH->getAddrPort(m_address); +} + +CString +CNetworkAddress::getHostname() const +{ + return m_hostname; +} diff --git a/lib/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h new file mode 100644 index 0000000..0289827 --- /dev/null +++ b/lib/net/CNetworkAddress.h @@ -0,0 +1,91 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CNETWORKADDRESS_H +#define CNETWORKADDRESS_H + +#include "CString.h" +#include "BasicTypes.h" +#include "IArchNetwork.h" + +//! Network address type +/*! +This class represents a network address. +*/ +class CNetworkAddress { +public: + /*! + Constructs the invalid address + */ + CNetworkAddress(); + + /*! + Construct the wildcard address with the given port. \c port must + not be zero. + */ + CNetworkAddress(int port); + + /*! + Construct the network address for the given \c hostname and \c port. + If \c hostname can be parsed as a numerical address then that's how + it's used, otherwise the host name is looked up. If the lookup fails + then this throws XSocketAddress. If \c hostname ends in ":[0-9]+" then + that suffix is extracted and used as the port, overridding the port + parameter. Neither the extracted port or \c port may be zero. + */ + CNetworkAddress(const CString& hostname, int port); + + CNetworkAddress(const CNetworkAddress&); + + ~CNetworkAddress(); + + CNetworkAddress& operator=(const CNetworkAddress&); + + //! @name accessors + //@{ + + //! Check address validity + /*! + Returns true if this is not the invalid address. + */ + bool isValid() const; + + //! Get address + /*! + Returns the address in the platform's native network address + structure. + */ + const CArchNetAddress& getAddress() const; + + //! Get port + /*! + Returns the port passed to the c'tor as a suffix to the hostname, + if that existed, otherwise as passed directly to the c'tor. + */ + int getPort() const; + + //! Get hostname + /*! + Returns the hostname passed to the c'tor sans the port suffix. + */ + CString getHostname() const; + + //@} + +private: + CArchNetAddress m_address; + CString m_hostname; +}; + +#endif diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp new file mode 100644 index 0000000..1177516 --- /dev/null +++ b/lib/net/CTCPListenSocket.cpp @@ -0,0 +1,98 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTCPListenSocket.h" +#include "CTCPSocket.h" +#include "CNetworkAddress.h" +#include "XIO.h" +#include "XSocket.h" +#include "CThread.h" +#include "CArch.h" +#include "XArch.h" + +// +// CTCPListenSocket +// + +CTCPListenSocket::CTCPListenSocket() +{ + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); + } +} + +CTCPListenSocket::~CTCPListenSocket() +{ + try { + ARCH->closeSocket(m_socket); + } + catch (...) { + // ignore + } +} + +void +CTCPListenSocket::bind(const CNetworkAddress& addr) +{ + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + ARCH->listenOnSocket(m_socket); + } + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); + } +} + +IDataSocket* +CTCPListenSocket::accept() +{ + // accept asynchronously so we can check for cancellation + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLIN; + for (;;) { + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0 && + (pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) { + return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); + } + } + catch (XArchNetwork&) { + // ignore and retry + } + } +} + +void +CTCPListenSocket::close() +{ + if (m_socket == NULL) { + throw XIOClosed(); + } + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); + } +} diff --git a/lib/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h new file mode 100644 index 0000000..d511637 --- /dev/null +++ b/lib/net/CTCPListenSocket.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTCPLISTENSOCKET_H +#define CTCPLISTENSOCKET_H + +#include "IListenSocket.h" +#include "IArchNetwork.h" + +//! TCP listen socket +/*! +A listen socket using TCP. +*/ +class CTCPListenSocket : public IListenSocket { +public: + CTCPListenSocket(); + ~CTCPListenSocket(); + + // ISocket overrides + virtual void bind(const CNetworkAddress&); + virtual void close(); + + // IListenSocket overrides + virtual IDataSocket* accept(); + +private: + CArchSocket m_socket; +}; + +#endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp new file mode 100644 index 0000000..e74fc4f --- /dev/null +++ b/lib/net/CTCPSocket.cpp @@ -0,0 +1,379 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTCPSocket.h" +#include "CBufferedInputStream.h" +#include "CBufferedOutputStream.h" +#include "CNetworkAddress.h" +#include "XIO.h" +#include "XSocket.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CArch.h" +#include "XArch.h" + +// +// CTCPSocket +// + +CTCPSocket::CTCPSocket() +{ + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); + } + init(); +} + +CTCPSocket::CTCPSocket(CArchSocket socket) : + m_socket(socket) +{ + assert(m_socket != NULL); + + init(); + + // socket starts in connected state + m_connected = kReadWrite; + + // start handling socket + m_thread = new CThread(new TMethodJob( + this, &CTCPSocket::ioThread)); +} + +CTCPSocket::~CTCPSocket() +{ + try { + close(); + } + catch (...) { + // ignore + } + + // clean up + delete m_input; + delete m_output; + delete m_mutex; +} + +void +CTCPSocket::bind(const CNetworkAddress& addr) +{ + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + } + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); + } +} + +void +CTCPSocket::close() +{ + // see if buffers should be flushed + bool doFlush = false; + { + CLock lock(m_mutex); + doFlush = (m_thread != NULL && (m_connected & kWrite) != 0); + } + + // flush buffers + if (doFlush) { + m_output->flush(); + } + + // cause ioThread to exit + if (m_socket != NULL) { + CLock lock(m_mutex); + try { + ARCH->closeSocketForRead(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + try { + ARCH->closeSocketForWrite(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + m_connected = kClosed; + } + + // wait for thread + if (m_thread != NULL) { + m_thread->wait(); + delete m_thread; + m_thread = NULL; + } + + // close socket + if (m_socket != NULL) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); + } + } +} + +void +CTCPSocket::connect(const CNetworkAddress& addr) +{ + do { + // connect asynchronously so we can check for cancellation. + // we can't wrap setting and resetting the blocking flag in + // the c'tor/d'tor of a class (to make resetting automatic) + // because setBlockingOnSocket() can throw and it might be + // called while unwinding the stack due to a throw. + try { + ARCH->setBlockingOnSocket(m_socket, false); + ARCH->connectSocket(m_socket, addr.getAddress()); + ARCH->setBlockingOnSocket(m_socket, true); + + // connected + break; + } + catch (XArchNetworkConnecting&) { + // connection is in progress + ARCH->setBlockingOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + ARCH->setBlockingOnSocket(m_socket, true); + throw XSocketConnect(e.what()); + } + + // wait for connection or failure + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLOUT; + for (;;) { + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { + // connection failed + ARCH->throwErrorOnSocket(m_socket); + } + if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) { + // connection may have failed or succeeded + ARCH->throwErrorOnSocket(m_socket); + + // connected! + break; + } + } + } + catch (XArchNetwork& e) { + throw XSocketConnect(e.what()); + } + } + } while (false); + + // start servicing the socket + m_connected = kReadWrite; + m_thread = new CThread(new TMethodJob( + this, &CTCPSocket::ioThread)); +} + +IInputStream* +CTCPSocket::getInputStream() +{ + return m_input; +} + +IOutputStream* +CTCPSocket::getOutputStream() +{ + return m_output; +} + +void +CTCPSocket::init() +{ + m_mutex = new CMutex; + m_thread = NULL; + m_connected = kClosed; + m_input = new CBufferedInputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::closeInput)); + m_output = new CBufferedOutputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::closeOutput)); + + // turn off Nagle algorithm. we send lots of very short messages + // that should be sent without (much) delay. for example, the + // mouse motion messages are much less useful if they're delayed. +// FIXME -- the client should do this + try { + ARCH->setNoDelayOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork&) { + // ignore + } + throw XSocketCreate(e.what()); + } +} + +void +CTCPSocket::ioThread(void*) +{ + try { + ioService(); + ioCleanup(); + } + catch (...) { + ioCleanup(); + throw; + } +} + +void +CTCPSocket::ioCleanup() +{ + try { + m_input->close(); + } + catch (...) { + // ignore + } + try { + m_output->close(); + } + catch (...) { + // ignore + } +} + +void +CTCPSocket::ioService() +{ + assert(m_socket != NULL); + + // now service the connection + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + for (;;) { + { + // choose events to poll for + CLock lock(m_mutex); + pfds[0].m_events = 0; + if (m_connected == 0) { + return; + } + if ((m_connected & kRead) != 0) { + // still open for reading + pfds[0].m_events |= IArchNetwork::kPOLLIN; + } + if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { + // data queued for writing + pfds[0].m_events |= IArchNetwork::kPOLLOUT; + } + } + + try { + // check for status + const int status = ARCH->pollSocket(pfds, 1, 0.01); + + // transfer data and handle errors + if (status == 1) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { + // stream is no good anymore so bail + CLock lock(m_mutex); + m_input->hangup(); + return; + } + + // read some data + if (pfds[0].m_revents & IArchNetwork::kPOLLIN) { + UInt8 buffer[4096]; + size_t n = ARCH->readSocket(m_socket, + buffer, sizeof(buffer)); + CLock lock(m_mutex); + if (n > 0) { + m_input->write(buffer, n); + } + else { + // stream hungup + m_input->hangup(); + m_connected &= ~kRead; + } + } + + // write some data + if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) { + CLock lock(m_mutex); + + // get amount of data to write + UInt32 n = m_output->getSize(); + + // write data + const void* buffer = m_output->peek(n); + size_t n2 = ARCH->writeSocket(m_socket, buffer, n); + + // discard written data + if (n2 > 0) { + m_output->pop(n2); + } + } + } + } + catch (XArchNetwork&) { + // socket has failed + return; + } + } +} + +void +CTCPSocket::closeInput(void*) +{ + // note -- m_mutex should already be locked + try { + ARCH->closeSocketForRead(m_socket); + m_connected &= ~kRead; + } + catch (XArchNetwork&) { + // ignore + } +} + +void +CTCPSocket::closeOutput(void*) +{ + // note -- m_mutex should already be locked + try { + ARCH->closeSocketForWrite(m_socket); + m_connected &= ~kWrite; + } + catch (XArchNetwork&) { + // ignore + } +} diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h new file mode 100644 index 0000000..bfbade8 --- /dev/null +++ b/lib/net/CTCPSocket.h @@ -0,0 +1,66 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTCPSOCKET_H +#define CTCPSOCKET_H + +#include "IDataSocket.h" +#include "BasicTypes.h" +#include "IArchNetwork.h" + +class CMutex; +class CThread; +class CBufferedInputStream; +class CBufferedOutputStream; + +//! TCP data socket +/*! +A data socket using TCP. +*/ +class CTCPSocket : public IDataSocket { +public: + CTCPSocket(); + CTCPSocket(CArchSocket); + ~CTCPSocket(); + + // ISocket overrides + virtual void bind(const CNetworkAddress&); + virtual void close(); + + // IDataSocket overrides + virtual void connect(const CNetworkAddress&); + virtual IInputStream* getInputStream(); + virtual IOutputStream* getOutputStream(); + +private: + void init(); + void ioThread(void*); + void ioCleanup(); + void ioService(); + void closeInput(void*); + void closeOutput(void*); + +private: + enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; + + CArchSocket m_socket; + CBufferedInputStream* m_input; + CBufferedOutputStream* m_output; + + CMutex* m_mutex; + CThread* m_thread; + UInt32 m_connected; +}; + +#endif diff --git a/lib/net/CTCPSocketFactory.cpp b/lib/net/CTCPSocketFactory.cpp new file mode 100644 index 0000000..f590efa --- /dev/null +++ b/lib/net/CTCPSocketFactory.cpp @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CTCPSocketFactory.h" +#include "CTCPSocket.h" +#include "CTCPListenSocket.h" + +// +// CTCPSocketFactory +// + +CTCPSocketFactory::CTCPSocketFactory() +{ + // do nothing +} + +CTCPSocketFactory::~CTCPSocketFactory() +{ + // do nothing +} + +IDataSocket* +CTCPSocketFactory::create() const +{ + return new CTCPSocket; +} + +IListenSocket* +CTCPSocketFactory::createListen() const +{ + return new CTCPListenSocket; +} diff --git a/lib/net/CTCPSocketFactory.h b/lib/net/CTCPSocketFactory.h new file mode 100644 index 0000000..2b946d1 --- /dev/null +++ b/lib/net/CTCPSocketFactory.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CTCPSOCKETFACTORY_H +#define CTCPSOCKETFACTORY_H + +#include "ISocketFactory.h" + +//! Socket factory for TCP sockets +class CTCPSocketFactory : public ISocketFactory { +public: + CTCPSocketFactory(); + virtual ~CTCPSocketFactory(); + + // ISocketFactory overrides + virtual IDataSocket* create() const; + virtual IListenSocket* createListen() const; +}; + +#endif diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h new file mode 100644 index 0000000..0193e1d --- /dev/null +++ b/lib/net/IDataSocket.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IDATASOCKET_H +#define IDATASOCKET_H + +#include "ISocket.h" + +class IInputStream; +class IOutputStream; + +//! Data stream socket interface +/*! +This interface defines the methods common to all network sockets that +represent a full-duplex data stream. +*/ +class IDataSocket : public ISocket { +public: + //! @name manipulators + //@{ + + //! Connect socket + /*! + Attempt to connect to a remote endpoint. This waits until the + connection is established or fails. If it fails it throws an + XSocketConnect exception. + + (cancellation point) + */ + virtual void connect(const CNetworkAddress&) = 0; + + //! Get input stream + /*! + Returns the input stream for reading from the socket. Closing this + stream will shutdown the socket for reading. + */ + virtual IInputStream* getInputStream() = 0; + //! Get output stream + /*! + Returns the output stream for writing to the socket. Closing this + stream will shutdown the socket for writing. + */ + virtual IOutputStream* getOutputStream() = 0; + + //@} + + // ISocket overrides + virtual void bind(const CNetworkAddress&) = 0; + virtual void close() = 0; +}; + +#endif diff --git a/lib/net/IListenSocket.h b/lib/net/IListenSocket.h new file mode 100644 index 0000000..7a7ddaa --- /dev/null +++ b/lib/net/IListenSocket.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ILISTENSOCKET_H +#define ILISTENSOCKET_H + +#include "ISocket.h" + +class IDataSocket; + +//! Listen socket interface +/*! +This interface defines the methods common to all network sockets that +listen for incoming connections. +*/ +class IListenSocket : public ISocket { +public: + //! @name manipulators + //@{ + + //! Accept connection + /*! + Wait for and accept a connection, returning a socket representing + the full-duplex data stream. + + (cancellation point) + */ + virtual IDataSocket* accept() = 0; + + //@} + + // ISocket overrides + virtual void bind(const CNetworkAddress&) = 0; + virtual void close() = 0; +}; + +#endif diff --git a/lib/net/ISocket.h b/lib/net/ISocket.h new file mode 100644 index 0000000..623a25a --- /dev/null +++ b/lib/net/ISocket.h @@ -0,0 +1,46 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISOCKET_H +#define ISOCKET_H + +#include "IInterface.h" + +class CNetworkAddress; + +//! Generic socket interface +/*! +This interface defines the methods common to all network sockets. +*/ +class ISocket : public IInterface { +public: + //! @name manipulators + //@{ + + //! Bind socket to address + /*! + Binds the socket to a particular address. + */ + virtual void bind(const CNetworkAddress&) = 0; + + //! Close socket + /*! + Closes the socket. This should flush the output stream. + */ + virtual void close() = 0; + + //@} +}; + +#endif diff --git a/lib/net/ISocketFactory.h b/lib/net/ISocketFactory.h new file mode 100644 index 0000000..dbf9ae2 --- /dev/null +++ b/lib/net/ISocketFactory.h @@ -0,0 +1,42 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISOCKETFACTORY_H +#define ISOCKETFACTORY_H + +#include "IInterface.h" + +class IDataSocket; +class IListenSocket; + +//! Socket factory +/*! +This interface defines the methods common to all factories used to +create sockets. +*/ +class ISocketFactory : public IInterface { +public: + //! @name accessors + //@{ + + //! Create data socket + virtual IDataSocket* create() const = 0; + + //! Create listen socket + virtual IListenSocket* createListen() const = 0; + + //@} +}; + +#endif diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am new file mode 100644 index 0000000..efeb519 --- /dev/null +++ b/lib/net/Makefile.am @@ -0,0 +1,49 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + net.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libnet.a +libnet_a_SOURCES = \ + CNetworkAddress.cpp \ + CTCPListenSocket.cpp \ + CTCPSocket.cpp \ + CTCPSocketFactory.cpp \ + XSocket.cpp \ + CNetworkAddress.h \ + CTCPListenSocket.h \ + CTCPSocket.h \ + CTCPSocketFactory.h \ + IDataSocket.h \ + IListenSocket.h \ + ISocket.h \ + ISocketFactory.h \ + XSocket.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) diff --git a/lib/net/Makefile.in b/lib/net/Makefile.in new file mode 100644 index 0000000..a2f4eff --- /dev/null +++ b/lib/net/Makefile.in @@ -0,0 +1,357 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + net.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libnet.a +libnet_a_SOURCES = \ + CNetworkAddress.cpp \ + CTCPListenSocket.cpp \ + CTCPSocket.cpp \ + CTCPSocketFactory.cpp \ + XSocket.cpp \ + CNetworkAddress.h \ + CTCPListenSocket.h \ + CTCPSocket.h \ + CTCPSocketFactory.h \ + IDataSocket.h \ + IListenSocket.h \ + ISocket.h \ + ISocketFactory.h \ + XSocket.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + $(NULL) + +subdir = lib/net +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libnet_a_AR = $(AR) cru +libnet_a_LIBADD = +am_libnet_a_OBJECTS = CNetworkAddress.$(OBJEXT) \ + CTCPListenSocket.$(OBJEXT) CTCPSocket.$(OBJEXT) \ + CTCPSocketFactory.$(OBJEXT) XSocket.$(OBJEXT) +libnet_a_OBJECTS = $(am_libnet_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CNetworkAddress.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTCPListenSocket.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTCPSocket.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CTCPSocketFactory.Po $(DEPDIR)/XSocket.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libnet_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libnet_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/net/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libnet.a: $(libnet_a_OBJECTS) $(libnet_a_DEPENDENCIES) + -rm -f libnet.a + $(libnet_a_AR) libnet.a $(libnet_a_OBJECTS) $(libnet_a_LIBADD) + $(RANLIB) libnet.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CNetworkAddress.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTCPListenSocket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTCPSocket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CTCPSocketFactory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XSocket.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp new file mode 100644 index 0000000..cf8b3ca --- /dev/null +++ b/lib/net/XSocket.cpp @@ -0,0 +1,111 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XSocket.h" +#include "CStringUtil.h" + +// +// XSocketAddress +// + +XSocketAddress::XSocketAddress(EError error, + const CString& hostname, int port) throw() : + m_error(error), + m_hostname(hostname), + m_port(port) +{ + // do nothing +} + +XSocketAddress::EError +XSocketAddress::getError() const throw() +{ + return m_error; +} + +CString +XSocketAddress::getHostname() const throw() +{ + return m_hostname; +} + +int +XSocketAddress::getPort() const throw() +{ + return m_port; +} + +CString +XSocketAddress::getWhat() const throw() +{ + static const char* s_errorID[] = { + "XSocketAddressUnknown", + "XSocketAddressNotFound", + "XSocketAddressNoAddress", + "XSocketAddressBadPort" + }; + static const char* s_errorMsg[] = { + "unknown error for: %{1}:%{2}", + "address not found for: %{1}", + "no address for: %{1}", + "invalid port" // m_port may not be set to the bad port + }; + return format(s_errorID[m_error], s_errorMsg[m_error], + m_hostname.c_str(), + CStringUtil::print("%d", m_port).c_str()); +} + + +// +// XSocketIOClose +// + +CString +XSocketIOClose::getWhat() const throw() +{ + return format("XSocketIOClose", "close: %{1}", what()); +} + + +// +// XSocketBind +// + +CString +XSocketBind::getWhat() const throw() +{ + return format("XSocketBind", "cannot bind address: %{1}", what()); +} + + +// +// XSocketConnect +// + +CString +XSocketConnect::getWhat() const throw() +{ + return format("XSocketConnect", "cannot connect socket: %{1}", what()); +} + + +// +// XSocketCreate +// + +CString +XSocketCreate::getWhat() const throw() +{ + return format("XSocketCreate", "cannot create socket: %{1}", what()); +} diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h new file mode 100644 index 0000000..c366303 --- /dev/null +++ b/lib/net/XSocket.h @@ -0,0 +1,95 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XSOCKET_H +#define XSOCKET_H + +#include "XIO.h" +#include "XBase.h" +#include "CString.h" +#include "BasicTypes.h" + +//! Generic socket exception +XBASE_SUBCLASS(XSocket, XBase); + +//! Socket bad address exception +/*! +Thrown when attempting to create an invalid network address. +*/ +class XSocketAddress : public XSocket { +public: + //! Failure codes + enum EError { + kUnknown, //!< Unknown error + kNotFound, //!< The hostname is unknown + kNoAddress, //!< The hostname is valid but has no IP address + kBadPort //!< The port is invalid + }; + + XSocketAddress(EError, const CString& hostname, int port) throw(); + + //! @name accessors + //@{ + + //! Get the error code + EError getError() const throw(); + //! Get the hostname + CString getHostname() const throw(); + //! Get the port + int getPort() const throw(); + + //@} + +protected: + // XBase overrides + virtual CString getWhat() const throw(); + +private: + EError m_error; + CString m_hostname; + int m_port; +}; + +//! I/O closing exception +/*! +Thrown if a stream cannot be closed. +*/ +XBASE_SUBCLASS_FORMAT(XSocketIOClose, XIOClose); + +//! Socket cannot bind address exception +/*! +Thrown when a socket cannot be bound to an address. +*/ +XBASE_SUBCLASS_FORMAT(XSocketBind, XSocket); + +//! Socket address in use exception +/*! +Thrown when a socket cannot be bound to an address because the address +is already in use. +*/ +XBASE_SUBCLASS(XSocketAddressInUse, XSocketBind); + +//! Cannot connect socket exception +/*! +Thrown when a socket cannot connect to a remote endpoint. +*/ +XBASE_SUBCLASS_FORMAT(XSocketConnect, XSocket); + +//! Cannot create socket exception +/*! +Thrown when a socket cannot be created (by the operating system). +*/ +XBASE_SUBCLASS_FORMAT(XSocketCreate, XSocket); + +#endif diff --git a/lib/net/net.dsp b/lib/net/net.dsp new file mode 100644 index 0000000..0a5fdd5 --- /dev/null +++ b/lib/net/net.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=net - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "net.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "net.mak" CFG="net - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "net - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "net - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "net - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "net - Win32 Release" +# Name "net - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CNetworkAddress.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSocket.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CNetworkAddress.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XSocket.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp new file mode 100644 index 0000000..5f4c767 --- /dev/null +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -0,0 +1,205 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboard.h" +#include "CMSWindowsClipboardTextConverter.h" +#include "CMSWindowsClipboardUTF16Converter.h" +#include "CLog.h" +#include "CArchMiscWindows.h" + +// +// CMSWindowsClipboard +// + +UINT CMSWindowsClipboard::s_ownershipFormat = 0; + +CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : + m_window(window), + m_time(0) +{ + // add converters, most desired first + m_converters.push_back(new CMSWindowsClipboardUTF16Converter); + if (CArchMiscWindows::isWindows95Family()) { + // windows nt family converts to/from unicode automatically. + // let it do so to avoid text encoding issues. + m_converters.push_back(new CMSWindowsClipboardTextConverter); + } +} + +CMSWindowsClipboard::~CMSWindowsClipboard() +{ + clearConverters(); +} + +bool +CMSWindowsClipboard::emptyUnowned() +{ + LOG((CLOG_DEBUG "empty clipboard")); + + // empty the clipboard (and take ownership) + if (!EmptyClipboard()) { + LOG((CLOG_DEBUG "failed to grab clipboard")); + return false; + } + + return true; +} + +bool +CMSWindowsClipboard::empty() +{ + if (!emptyUnowned()) { + return false; + } + + // mark clipboard as being owned by synergy + HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1); + SetClipboardData(getOwnershipFormat(), data); + + return true; +} + +void +CMSWindowsClipboard::add(EFormat format, const CString& data) +{ + LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); + + // convert data to win32 form + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IMSWindowsClipboardConverter* converter = *index; + + // skip converters for other formats + if (converter->getFormat() == format) { + HANDLE win32Data = converter->fromIClipboard(data); + if (win32Data != NULL) { + UINT win32Format = converter->getWin32Format(); + if (SetClipboardData(win32Format, win32Data) == NULL) { + // free converted data if we couldn't put it on + // the clipboard + GlobalFree(win32Data); + } + } + } + } +} + +bool +CMSWindowsClipboard::open(Time time) const +{ + LOG((CLOG_DEBUG "open clipboard")); + + if (!OpenClipboard(m_window)) { + LOG((CLOG_WARN "failed to open clipboard")); + return false; + } + + m_time = time; + + return true; +} + +void +CMSWindowsClipboard::close() const +{ + LOG((CLOG_DEBUG "close clipboard")); + CloseClipboard(); +} + +IClipboard::Time +CMSWindowsClipboard::getTime() const +{ + return m_time; +} + +bool +CMSWindowsClipboard::has(EFormat format) const +{ + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IMSWindowsClipboardConverter* converter = *index; + if (converter->getFormat() == format) { + if (IsClipboardFormatAvailable(converter->getWin32Format())) { + return true; + } + } + } + return false; +} + +CString +CMSWindowsClipboard::get(EFormat format) const +{ + // find the converter for the first clipboard format we can handle + IMSWindowsClipboardConverter* converter = NULL; + UINT win32Format = EnumClipboardFormats(0); + while (converter == NULL && win32Format != 0) { + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getWin32Format() == win32Format && + converter->getFormat() == format) { + break; + } + converter = NULL; + } + win32Format = EnumClipboardFormats(win32Format); + } + + // if no converter then we don't recognize any formats + if (converter == NULL) { + return CString(); + } + + // get a handle to the clipboard data + HANDLE win32Data = GetClipboardData(converter->getWin32Format()); + if (win32Data == NULL) { + return CString(); + } + + // convert + return converter->toIClipboard(win32Data); +} + +void +CMSWindowsClipboard::clearConverters() +{ + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; + } + m_converters.clear(); +} + +bool +CMSWindowsClipboard::isOwnedBySynergy() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0); +} + +UINT +CMSWindowsClipboard::getOwnershipFormat() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + + // return the format + return s_ownershipFormat; +} diff --git a/lib/platform/CMSWindowsClipboard.h b/lib/platform/CMSWindowsClipboard.h new file mode 100644 index 0000000..e9b59fb --- /dev/null +++ b/lib/platform/CMSWindowsClipboard.h @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARD_H +#define CMSWINDOWSCLIPBOARD_H + +#include "IClipboard.h" +#include "stdvector.h" +#define WIN32_LEAN_AND_MEAN +#include + +class IMSWindowsClipboardConverter; + +//! Microsoft windows clipboard implementation +class CMSWindowsClipboard : public IClipboard { +public: + CMSWindowsClipboard(HWND window); + virtual ~CMSWindowsClipboard(); + + //! Empty clipboard without ownership + /*! + Take ownership of the clipboard and clear all data from it. + This must be called between a successful open() and close(). + Return false if the clipboard ownership could not be taken; + the clipboard should not be emptied in this case. Unlike + empty(), isOwnedBySynergy() will return false when emptied + this way. This is useful when synergy wants to put data on + clipboard but pretend (to itself) that some other app did it. + When using empty(), synergy assumes the data came from the + server and doesn't need to be sent back. emptyUnowned() + makes synergy send the data to the server. + */ + bool emptyUnowned(); + + //! Test if clipboard is owned by synergy + static bool isOwnedBySynergy(); + + // IClipboard overrides + virtual bool empty(); + virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + +private: + void clearConverters(); + + UINT convertFormatToWin32(EFormat) const; + HANDLE convertTextToWin32(const CString& data) const; + CString convertTextFromWin32(HANDLE) const; + + static UINT getOwnershipFormat(); + +private: + typedef std::vector ConverterList; + + HWND m_window; + mutable Time m_time; + ConverterList m_converters; + static UINT s_ownershipFormat; +}; + +//! Clipboard format converter interface +/*! +This interface defines the methods common to all win32 clipboard format +converters. +*/ +class IMSWindowsClipboardConverter : public IInterface { +public: + // accessors + + // return the clipboard format this object converts from/to + virtual IClipboard::EFormat + getFormat() const = 0; + + // return the atom representing the win32 clipboard format that + // this object converts from/to + virtual UINT getWin32Format() const = 0; + + // convert from the IClipboard format to the win32 clipboard format. + // the input data must be in the IClipboard format returned by + // getFormat(). the return data will be in the win32 clipboard + // format returned by getWin32Format(), allocated by GlobalAlloc(). + virtual HANDLE fromIClipboard(const CString&) const = 0; + + // convert from the win32 clipboard format to the IClipboard format + // (i.e., the reverse of fromIClipboard()). + virtual CString toIClipboard(HANDLE data) const = 0; +}; + +#endif diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp new file mode 100644 index 0000000..730f342 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp @@ -0,0 +1,145 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardAnyTextConverter.h" + +// +// CMSWindowsClipboardAnyTextConverter +// + +CMSWindowsClipboardAnyTextConverter::CMSWindowsClipboardAnyTextConverter() +{ + // do nothing +} + +CMSWindowsClipboardAnyTextConverter::~CMSWindowsClipboardAnyTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +CMSWindowsClipboardAnyTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +HANDLE +CMSWindowsClipboardAnyTextConverter::fromIClipboard(const CString& data) const +{ + // convert linefeeds and then convert to desired encoding + CString text = doFromIClipboard(convertLinefeedToWin32(data)); + UInt32 size = text.size(); + + // copy to memory handle + HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size); + if (gData != NULL) { + // get a pointer to the allocated memory + char* dst = (char*)GlobalLock(gData); + if (dst != NULL) { + memcpy(dst, text.data(), size); + GlobalUnlock(gData); + } + else { + GlobalFree(gData); + gData = NULL; + } + } + + return gData; +} + +CString +CMSWindowsClipboardAnyTextConverter::toIClipboard(HANDLE data) const +{ + // get datator + const char* src = (const char*)GlobalLock(data); + UInt32 srcSize = (UInt32)GlobalSize(data); + if (src == NULL || srcSize <= 1) { + return CString(); + } + + // convert text + CString text = doToIClipboard(CString(src, srcSize)); + + // release handle + GlobalUnlock(data); + + // convert newlines + return convertLinefeedToUnix(text); +} + +CString +CMSWindowsClipboardAnyTextConverter::convertLinefeedToWin32( + const CString& src) const +{ + // note -- we assume src is a valid UTF-8 string + + // count newlines in string + UInt32 numNewlines = 0; + UInt32 n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (*scan == '\n') { + ++numNewlines; + } + } + if (numNewlines == 0) { + return src; + } + + // allocate new string + CString dst; + dst.reserve(src.size() + numNewlines); + + // copy string, converting newlines + n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] == '\n') { + dst += '\r'; + } + dst += scan[0]; + } + + return dst; +} + +CString +CMSWindowsClipboardAnyTextConverter::convertLinefeedToUnix( + const CString& src) const +{ + // count newlines in string + UInt32 numNewlines = 0; + UInt32 n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] == '\r' && scan[1] == '\n') { + ++numNewlines; + } + } + if (numNewlines == 0) { + return src; + } + + // allocate new string + CString dst; + dst.reserve(src.size()); + + // copy string, converting newlines + n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] != '\r' || scan[1] != '\n') { + dst += scan[0]; + } + } + + return dst; +} diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.h b/lib/platform/CMSWindowsClipboardAnyTextConverter.h new file mode 100644 index 0000000..254099f --- /dev/null +++ b/lib/platform/CMSWindowsClipboardAnyTextConverter.h @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H +#define CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H + +#include "CMSWindowsClipboard.h" + +//! Convert to/from some text encoding +class CMSWindowsClipboardAnyTextConverter : + public IMSWindowsClipboardConverter { +public: + CMSWindowsClipboardAnyTextConverter(); + virtual ~CMSWindowsClipboardAnyTextConverter(); + + // IMSWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual UINT getWin32Format() const = 0; + virtual HANDLE fromIClipboard(const CString&) const; + virtual CString toIClipboard(HANDLE) const; + +protected: + //! Convert from IClipboard format + /*! + Do UTF-8 conversion only. Memory handle allocation and + linefeed conversion is done by this class. doFromIClipboard() + must include the nul terminator in the returned string (not + including the CString's nul terminator). + */ + virtual CString doFromIClipboard(const CString&) const = 0; + + //! Convert to IClipboard format + /*! + Do UTF-8 conversion only. Memory handle allocation and + linefeed conversion is done by this class. + */ + virtual CString doToIClipboard(const CString&) const = 0; + +private: + CString convertLinefeedToWin32(const CString&) const; + CString convertLinefeedToUnix(const CString&) const; +}; + +#endif diff --git a/lib/platform/CMSWindowsClipboardTextConverter.cpp b/lib/platform/CMSWindowsClipboardTextConverter.cpp new file mode 100644 index 0000000..a735094 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardTextConverter.cpp @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardTextConverter.h" +#include "CUnicode.h" + +// +// CMSWindowsClipboardTextConverter +// + +CMSWindowsClipboardTextConverter::CMSWindowsClipboardTextConverter() +{ + // do nothing +} + +CMSWindowsClipboardTextConverter::~CMSWindowsClipboardTextConverter() +{ + // do nothing +} + +UINT +CMSWindowsClipboardTextConverter::getWin32Format() const +{ + return CF_TEXT; +} + +CString +CMSWindowsClipboardTextConverter::doFromIClipboard(const CString& data) const +{ + // convert and add nul terminator + return CUnicode::UTF8ToText(data) += '\0'; +} + +CString +CMSWindowsClipboardTextConverter::doToIClipboard(const CString& data) const +{ + // convert and truncate at first nul terminator + CString dst = CUnicode::textToUTF8(data); + CString::size_type n = dst.find('\0'); + if (n != CString::npos) { + dst.erase(n); + } + return dst; +} diff --git a/lib/platform/CMSWindowsClipboardTextConverter.h b/lib/platform/CMSWindowsClipboardTextConverter.h new file mode 100644 index 0000000..6f00d47 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardTextConverter.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDTEXTCONVERTER_H +#define CMSWINDOWSCLIPBOARDTEXTCONVERTER_H + +#include "CMSWindowsClipboardAnyTextConverter.h" + +//! Convert to/from locale text encoding +class CMSWindowsClipboardTextConverter : + public CMSWindowsClipboardAnyTextConverter { +public: + CMSWindowsClipboardTextConverter(); + virtual ~CMSWindowsClipboardTextConverter(); + + // IMSWindowsClipboardConverter overrides + virtual UINT getWin32Format() const; + +protected: + // CMSWindowsClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; +}; + +#endif diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.cpp b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp new file mode 100644 index 0000000..81b85c6 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardUTF16Converter.h" +#include "CUnicode.h" + +// +// CMSWindowsClipboardUTF16Converter +// + +CMSWindowsClipboardUTF16Converter::CMSWindowsClipboardUTF16Converter() +{ + // do nothing +} + +CMSWindowsClipboardUTF16Converter::~CMSWindowsClipboardUTF16Converter() +{ + // do nothing +} + +UINT +CMSWindowsClipboardUTF16Converter::getWin32Format() const +{ + return CF_UNICODETEXT; +} + +CString +CMSWindowsClipboardUTF16Converter::doFromIClipboard(const CString& data) const +{ + // convert and add nul terminator + return CUnicode::UTF8ToUTF16(data).append(sizeof(wchar_t), 0); +} + +CString +CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const +{ + // convert and strip nul terminator + CString dst = CUnicode::UTF16ToUTF8(data); + CString::size_type n = dst.find('\0'); + if (n != CString::npos) { + dst.erase(n); + } + return dst; +} diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.h b/lib/platform/CMSWindowsClipboardUTF16Converter.h new file mode 100644 index 0000000..51f477f --- /dev/null +++ b/lib/platform/CMSWindowsClipboardUTF16Converter.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDUTF16CONVERTER_H +#define CMSWINDOWSCLIPBOARDUTF16CONVERTER_H + +#include "CMSWindowsClipboardAnyTextConverter.h" + +//! Convert to/from UTF-16 encoding +class CMSWindowsClipboardUTF16Converter : + public CMSWindowsClipboardAnyTextConverter { +public: + CMSWindowsClipboardUTF16Converter(); + virtual ~CMSWindowsClipboardUTF16Converter(); + + // IMSWindowsClipboardConverter overrides + virtual UINT getWin32Format() const; + +protected: + // CMSWindowsClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; +}; + +#endif diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp new file mode 100644 index 0000000..d68612d --- /dev/null +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -0,0 +1,1816 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsPrimaryScreen.h" +#include "CMSWindowsScreen.h" +#include "IPrimaryScreenReceiver.h" +#include "XScreen.h" +#include "CLog.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// +// map virtual key id to a name +// + +static const char* g_buttonToName[] = { + "button 0", + "Left Button", + "Middle Button", + "Right Button", + "X Button 1", + "X Button 2" +}; +static const char* g_vkToName[] = { + "vk 0x00", + "Left Button", + "Right Button", + "VK_CANCEL", + "Middle Button", + "vk 0x05", + "vk 0x06", + "vk 0x07", + "VK_BACK", + "VK_TAB", + "vk 0x0a", + "vk 0x0b", + "VK_CLEAR", + "VK_RETURN", + "vk 0x0e", + "vk 0x0f", + "VK_SHIFT", + "VK_CONTROL", + "VK_MENU", + "VK_PAUSE", + "VK_CAPITAL", + "VK_KANA", + "vk 0x16", + "VK_JUNJA", + "VK_FINAL", + "VK_KANJI", + "vk 0x1a", + "VK_ESCAPE", + "VK_CONVERT", + "VK_NONCONVERT", + "VK_ACCEPT", + "VK_MODECHANGE", + "VK_SPACE", + "VK_PRIOR", + "VK_NEXT", + "VK_END", + "VK_HOME", + "VK_LEFT", + "VK_UP", + "VK_RIGHT", + "VK_DOWN", + "VK_SELECT", + "VK_PRINT", + "VK_EXECUTE", + "VK_SNAPSHOT", + "VK_INSERT", + "VK_DELETE", + "VK_HELP", + "VK_0", + "VK_1", + "VK_2", + "VK_3", + "VK_4", + "VK_5", + "VK_6", + "VK_7", + "VK_8", + "VK_9", + "vk 0x3a", + "vk 0x3b", + "vk 0x3c", + "vk 0x3d", + "vk 0x3e", + "vk 0x3f", + "vk 0x40", + "VK_A", + "VK_B", + "VK_C", + "VK_D", + "VK_E", + "VK_F", + "VK_G", + "VK_H", + "VK_I", + "VK_J", + "VK_K", + "VK_L", + "VK_M", + "VK_N", + "VK_O", + "VK_P", + "VK_Q", + "VK_R", + "VK_S", + "VK_T", + "VK_U", + "VK_V", + "VK_W", + "VK_X", + "VK_Y", + "VK_Z", + "VK_LWIN", + "VK_RWIN", + "VK_APPS", + "vk 0x5e", + "vk 0x5f", + "VK_NUMPAD0", + "VK_NUMPAD1", + "VK_NUMPAD2", + "VK_NUMPAD3", + "VK_NUMPAD4", + "VK_NUMPAD5", + "VK_NUMPAD6", + "VK_NUMPAD7", + "VK_NUMPAD8", + "VK_NUMPAD9", + "VK_MULTIPLY", + "VK_ADD", + "VK_SEPARATOR", + "VK_SUBTRACT", + "VK_DECIMAL", + "VK_DIVIDE", + "VK_F1", + "VK_F2", + "VK_F3", + "VK_F4", + "VK_F5", + "VK_F6", + "VK_F7", + "VK_F8", + "VK_F9", + "VK_F10", + "VK_F11", + "VK_F12", + "VK_F13", + "VK_F14", + "VK_F15", + "VK_F16", + "VK_F17", + "VK_F18", + "VK_F19", + "VK_F20", + "VK_F21", + "VK_F22", + "VK_F23", + "VK_F24", + "vk 0x88", + "vk 0x89", + "vk 0x8a", + "vk 0x8b", + "vk 0x8c", + "vk 0x8d", + "vk 0x8e", + "vk 0x8f", + "VK_NUMLOCK", + "VK_SCROLL", + "vk 0x92", + "vk 0x93", + "vk 0x94", + "vk 0x95", + "vk 0x96", + "vk 0x97", + "vk 0x98", + "vk 0x99", + "vk 0x9a", + "vk 0x9b", + "vk 0x9c", + "vk 0x9d", + "vk 0x9e", + "vk 0x9f", + "VK_LSHIFT", + "VK_RSHIFT", + "VK_LCONTROL", + "VK_RCONTROL", + "VK_LMENU", + "VK_RMENU", + "VK_BROWSER_BACK", + "VK_BROWSER_FORWARD", + "VK_BROWSER_REFRESH", + "VK_BROWSER_STOP", + "VK_BROWSER_SEARCH", + "VK_BROWSER_FAVORITES", + "VK_BROWSER_HOME", + "VK_VOLUME_MUTE", + "VK_VOLUME_DOWN", + "VK_VOLUME_UP", + "VK_MEDIA_NEXT_TRACK", + "VK_MEDIA_PREV_TRACK", + "VK_MEDIA_STOP", + "VK_MEDIA_PLAY_PAUSE", + "VK_LAUNCH_MAIL", + "VK_LAUNCH_MEDIA_SELECT", + "VK_LAUNCH_APP1", + "VK_LAUNCH_APP2", + "vk 0xb8", + "vk 0xb9", + "vk 0xba", + "vk 0xbb", + "vk 0xbc", + "vk 0xbd", + "vk 0xbe", + "vk 0xbf", + "vk 0xc0", + "vk 0xc1", + "vk 0xc2", + "vk 0xc3", + "vk 0xc4", + "vk 0xc5", + "vk 0xc6", + "vk 0xc7", + "vk 0xc8", + "vk 0xc9", + "vk 0xca", + "vk 0xcb", + "vk 0xcc", + "vk 0xcd", + "vk 0xce", + "vk 0xcf", + "vk 0xd0", + "vk 0xd1", + "vk 0xd2", + "vk 0xd3", + "vk 0xd4", + "vk 0xd5", + "vk 0xd6", + "vk 0xd7", + "vk 0xd8", + "vk 0xd9", + "vk 0xda", + "vk 0xdb", + "vk 0xdc", + "vk 0xdd", + "vk 0xde", + "vk 0xdf", + "vk 0xe0", + "vk 0xe1", + "vk 0xe2", + "vk 0xe3", + "vk 0xe4", + "VK_PROCESSKEY", + "vk 0xe6", + "vk 0xe7", + "vk 0xe8", + "vk 0xe9", + "vk 0xea", + "vk 0xeb", + "vk 0xec", + "vk 0xed", + "vk 0xee", + "vk 0xef", + "vk 0xf0", + "vk 0xf1", + "vk 0xf2", + "vk 0xf3", + "vk 0xf4", + "vk 0xf5", + "VK_ATTN", + "VK_CRSEL", + "VK_EXSEL", + "VK_EREOF", + "VK_PLAY", + "VK_ZOOM", + "VK_NONAME", + "VK_PA1", + "VK_OEM_CLEAR", + "vk 0xff" +}; + +// +// CMSWindowsPrimaryScreen +// + +CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( + IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) : + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_threadID(0), + m_mark(0), + m_markReceived(0), + m_lowLevel(false), + m_cursorThread(0) +{ + assert(m_receiver != NULL); + + // load the hook library + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary == NULL) { + LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing")); + throw XScreenOpenFailure(); + } + m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); + m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); + m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay"); + m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); + m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); + m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); + m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); + if (m_setSides == NULL || + m_setZone == NULL || + m_setRelay == NULL || + m_install == NULL || + m_uninstall == NULL || + m_init == NULL || + m_cleanup == NULL) { + LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll")); + FreeLibrary(m_hookLibrary); + throw XScreenOpenFailure(); + } + + // create screen + m_screen = new CMSWindowsScreen(receiver, this); +} + +CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() +{ + assert(m_hookLibrary != NULL); + + delete m_screen; + FreeLibrary(m_hookLibrary); +} + +void +CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) +{ + m_setSides(activeSides); +} + +void +CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + // warp mouse + warpCursorNoFlush(x, y); + + // remove all input events before and including warp + MSG msg; + while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST, + SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) { + // do nothing + } + + // save position as last position + m_x = x; + m_y = y; +} + +void +CMSWindowsPrimaryScreen::resetOptions() +{ + // no options +} + +void +CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/) +{ + // no options +} + +UInt32 +CMSWindowsPrimaryScreen::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + +KeyModifierMask +CMSWindowsPrimaryScreen::getToggleMask() const +{ + KeyModifierMask mask = 0; + + // get key state from our shadow state + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) + mask |= KeyModifierScrollLock; + + return mask; +} + +bool +CMSWindowsPrimaryScreen::isLockedToScreen() const +{ + // use shadow keyboard state in m_keys and m_buttons + for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + if ((m_buttons[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_buttonToName[i])); + return true; + } + } + for (UInt32 i = 0; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { + if ((m_keys[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i])); + return true; + } + } + + // not locked + return false; +} + +IScreen* +CMSWindowsPrimaryScreen::getScreen() const +{ + return m_screen; +} + +void +CMSWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + +bool +CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) +{ + assert(event != NULL); + + const MSG* msg = &event->m_msg; + + // check if windows key is up but we think it's down. if so then + // synthesize a key release for it. we have to do this because + // if the user presses and releases a windows key without pressing + // any other key when its down then windows will eat the key + // release. if we don't detect that an synthesize the release + // then the user will be locked to the screen and the client won't + // take the usual windows key release action (which on windows is + // to show the start menu). + // + // we can use GetKeyState() to check the state of the windows keys + // because, event though the key release is not reported to us, + // the event is processed and the keyboard state updated by the + // system. since the key could go up at any time we'll check the + // state on every event. only check on windows 95 family since + // NT family reports the key release as usual. obviously we skip + // this if the event is for the windows key itself. + if (m_is95Family) { + if ((m_keys[VK_LWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_LWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_LWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + bool altgr; + KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); + updateKey(wParam, false); + } + if ((m_keys[VK_RWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_RWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_RWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + bool altgr; + KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); + updateKey(wParam, false); + } + } + + // handle event + switch (msg->message) { + case SYNERGY_MSG_MARK: + m_markReceived = msg->wParam; + return true; + + case SYNERGY_MSG_KEY: + // ignore message if posted prior to last mark change + if (!ignore()) { + WPARAM wParam = msg->wParam; + LPARAM lParam = msg->lParam; + + // check for ctrl+alt+del emulation + if ((wParam == VK_PAUSE || wParam == VK_CANCEL) && + (m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0) { + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + wParam = VK_DELETE; + lParam &= 0xffff0000; + lParam |= 0x00000001; + } + + // process key normally + bool altgr; + KeyModifierMask mask; + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + if (key != kKeyNone && key != kKeyMultiKey) { + if ((lParam & 0x80000000) == 0) { + // key press + + // if AltGr required for this key then make sure + // the ctrl and alt keys are *not* down on the + // client. windows simulates AltGr with ctrl and + // alt for some inexplicable reason and clients + // will get confused if they see mode switch and + // ctrl and alt. we'll also need to put ctrl and + // alt back the way there were after we simulate + // the key. + bool ctrlL = ((m_keys[VK_LCONTROL] & 0x80) != 0); + bool ctrlR = ((m_keys[VK_RCONTROL] & 0x80) != 0); + bool altL = ((m_keys[VK_LMENU] & 0x80) != 0); + bool altR = ((m_keys[VK_RMENU] & 0x80) != 0); + if (altgr) { + KeyID key; + KeyButton button; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (ctrlR) { + key = kKeyControl_R; + button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (altL) { + key = kKeyAlt_L; + button = mapKeyToScanCode(VK_LMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (altR) { + key = kKeyAlt_R; + button = mapKeyToScanCode(VK_RMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + } + + // send key + const bool wasDown = ((lParam & 0x40000000) != 0); + SInt32 repeat = (SInt32)(lParam & 0xffff); + if (!wasDown) { + LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyDown(key, mask, button); + if (repeat > 0) { + --repeat; + } + } + if (repeat >= 1) { + LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); + m_receiver->onKeyRepeat(key, mask, repeat, button); + } + + // restore ctrl and alt state + if (altgr) { + KeyID key; + KeyButton button; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (ctrlR) { + key = kKeyControl_R; + button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (altL) { + key = kKeyAlt_L; + button = mapKeyToScanCode(VK_LMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } + if (altR) { + key = kKeyAlt_R; + button = mapKeyToScanCode(VK_RMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } + } + } + else { + // key release. if the key isn't down according to + // our table then we never got the key press event + // for it. if it's not a modifier key then we'll + // synthesize the press first. only do this on + // the windows 95 family, which eats certain special + // keys like alt+tab, ctrl+esc, etc. + if (m_is95Family && !isModifier(msg->wParam) && + (m_keys[msg->wParam] & 0x80) == 0) { + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyDown(key, mask, button); + updateKey(msg->wParam, true); + } + + // do key up + LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); + } + } + else { + LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam)); + } + } + + // keep our shadow key state up to date + updateKey(msg->wParam, ((msg->lParam & 0x80000000) == 0)); + + return true; + + case SYNERGY_MSG_MOUSE_BUTTON: { + // get which button + bool pressed = false; + const ButtonID button = mapButton(msg->wParam, msg->lParam); + + // ignore message if posted prior to last mark change + if (!ignore()) { + switch (msg->wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: + LOG((CLOG_DEBUG1 "event: button press button=%d", button)); + if (button != kButtonNone) { + m_receiver->onMouseDown(button); + } + pressed = true; + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: + LOG((CLOG_DEBUG1 "event: button release button=%d", button)); + if (button != kButtonNone) { + m_receiver->onMouseUp(button); + } + pressed = false; + break; + } + } + + // keep our shadow key state up to date + if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { + if (pressed) { + m_buttons[button] |= 0x80; + } + else { + m_buttons[button] &= ~0x80; + } + } + + return true; + } + + case SYNERGY_MSG_MOUSE_WHEEL: + // ignore message if posted prior to last mark change + if (!ignore()) { + LOG((CLOG_DEBUG1 "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); + m_receiver->onMouseWheel(msg->wParam); + } + return true; + + case SYNERGY_MSG_PRE_WARP: + { + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + + // we warped the mouse. discard events until we find the + // matching post warp event. see warpCursorNoFlush() for + // where the events are sent. we discard the matching + // post warp event and can be sure we've skipped the warp + // event. + MSG msg; + do { + GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE, + SYNERGY_MSG_POST_WARP); + } while (msg.message != SYNERGY_MSG_POST_WARP); + + return true; + } + + case SYNERGY_MSG_POST_WARP: + LOG((CLOG_WARN "unmatched post warp")); + return true; + + case SYNERGY_MSG_MOUSE_MOVE: + // ignore message if posted prior to last mark change + if (!ignore()) { + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = static_cast(msg->wParam) - m_x; + SInt32 y = static_cast(msg->lParam) - m_y; + + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + + if (!isActive()) { + // motion on primary screen + if (x != 0 || y != 0) { + m_receiver->onMouseMovePrimary(m_x, m_y); + } + } + else { + // motion on secondary screen. warp mouse back to + // center. + if (x != 0 || y != 0) { + // back to center + warpCursorNoFlush(m_xCenter, m_yCenter); + + // examine the motion. if it's about the distance + // from the center of the screen to an edge then + // it's probably a bogus motion that we want to + // ignore (see warpCursorNoFlush() for a further + // description). + static SInt32 bogusZoneSize = 10; + SInt32 x0, y0, w0, h0; + m_screen->getShape(x0, y0, w0, h0); + if (-x + bogusZoneSize > m_xCenter - x0 || + x + bogusZoneSize > x0 + w0 - m_xCenter || + -y + bogusZoneSize > m_yCenter - y0 || + y + bogusZoneSize > y0 + h0 - m_yCenter) { + LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y)); + } + else { + // send motion + m_receiver->onMouseMoveSecondary(x, y); + } + } + } + } + return true; + } + + return false; +} + +bool +CMSWindowsPrimaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_DISPLAYCHANGE: + // recompute center pixel of primary screen + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // warp mouse to center if active + if (isActive()) { + warpCursorToCenter(); + } + + // tell hook about resize if not active + else { + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + } + return true; + } + + return false; +} + +void +CMSWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) +{ + m_receiver->onOneShotTimerExpired(id); +} + +SInt32 +CMSWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CMSWindowsPrimaryScreen::postCreateWindow(HWND) +{ + // install hooks + switch (m_install()) { + case kHOOK_FAILED: + // FIXME -- can't install hook so we won't work; report error + m_lowLevel = false; + break; + + case kHOOK_OKAY: + m_lowLevel = false; + break; + + case kHOOK_OKAY_LL: + m_lowLevel = true; + break; + } + + if (!isActive()) { + // watch jump zones + m_setRelay(false); + + // all messages prior to now are invalid + nextMark(); + } +} + +void +CMSWindowsPrimaryScreen::preDestroyWindow(HWND) +{ + // uninstall hooks + m_uninstall(); +} + +void +CMSWindowsPrimaryScreen::onAccessibleDesktop() +{ + // get the current keyboard state + updateKeys(); +} + +void +CMSWindowsPrimaryScreen::onPreMainLoop() +{ + // must call mainLoop() from same thread as open() + assert(m_threadID == GetCurrentThreadId()); +} + +void +CMSWindowsPrimaryScreen::onPreOpen() +{ + // initialize hook library + m_threadID = GetCurrentThreadId(); + if (m_init(m_threadID) == 0) { + LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); + throw XScreenOpenFailure(); + } +} + +void +CMSWindowsPrimaryScreen::onPostOpen() +{ + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // set jump zones + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPostClose() +{ + m_cleanup(); + m_threadID = 0; +} + +void +CMSWindowsPrimaryScreen::onPreEnter() +{ + // show cursor if we hid it + if (m_cursorThread != 0) { + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, TRUE); + } + ShowCursor(TRUE); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, FALSE); + } + m_cursorThread = 0; + } + + // enable ctrl+alt+del, alt+tab, etc + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0); + } + + // watch jump zones + m_setRelay(false); +} + +void +CMSWindowsPrimaryScreen::onPostEnter() +{ + // all messages prior to now are invalid + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPreLeave() +{ + // all messages prior to now are invalid + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPostLeave(bool success) +{ + if (success) { + // relay all mouse and keyboard events + m_setRelay(true); + + // disable ctrl+alt+del, alt+tab, etc + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); + } + + // hide the cursor if using low level hooks + if (m_lowLevel) { + HWND hwnd = GetForegroundWindow(); + m_cursorThread = GetWindowThreadProcessId(hwnd, NULL); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, TRUE); + } + ShowCursor(FALSE); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, FALSE); + } + } + } +} + +void +CMSWindowsPrimaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } + + // we don't ever want our window to activate + EnableWindow(m_window, FALSE); +} + +void +CMSWindowsPrimaryScreen::destroyWindow() +{ + // close the desktop and the window + m_screen->closeDesktop(); +} + +bool +CMSWindowsPrimaryScreen::showWindow() +{ + // we don't need a window to capture input but we need a window + // to hide the cursor when using low-level hooks. do not try to + // take the activation; we want the currently active window to + // stay active. + if (m_lowLevel) { + SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, + SWP_NOACTIVATE); + ShowWindow(m_window, SW_SHOWNA); + } + return true; +} + +void +CMSWindowsPrimaryScreen::hideWindow() +{ + // hide our window + if (m_lowLevel) { + ShowWindow(m_window, SW_HIDE); + } +} + +void +CMSWindowsPrimaryScreen::warpCursorToCenter() +{ + warpCursor(m_xCenter, m_yCenter); +} + +void +CMSWindowsPrimaryScreen::warpCursorNoFlush(SInt32 x, SInt32 y) +{ + // send an event that we can recognize before the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y); + + // warp mouse. hopefully this inserts a mouse motion event + // between the previous message and the following message. + SetCursorPos(x, y); + + // yield the CPU. there's a race condition when warping: + // a hardware mouse event occurs + // the mouse hook is not called because that process doesn't have the CPU + // we send PRE_WARP, SetCursorPos(), send POST_WARP + // we process all of those events and update m_x, m_y + // we finish our time slice + // the hook is called + // the hook sends us a mouse event from the pre-warp position + // we get the CPU + // we compute a bogus warp + // we need the hook to process all mouse events that occur + // before we warp before we do the warp but i'm not sure how + // to guarantee that. yielding the CPU here may reduce the + // chance of undesired behavior. we'll also check for very + // large motions that look suspiciously like about half width + // or height of the screen. + ARCH->sleep(0.0); + + // send an event that we can recognize after the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); +} + +void +CMSWindowsPrimaryScreen::nextMark() +{ + // next mark + ++m_mark; + + // mark point in message queue where the mark was changed + PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); +} + +bool +CMSWindowsPrimaryScreen::ignore() const +{ + return (m_mark != m_markReceived); +} + +// map virtual keys to synergy key enumeration. use extended keyboard +// bit to distinguish some keys. +static const KeyID g_virtualKey[][2] = +{ + /* 0x00 */ kKeyNone, kKeyNone, // reserved + /* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON + /* 0x02 */ kKeyNone, kKeyNone, // VK_RBUTTON + /* 0x03 */ kKeyNone, kKeyBreak, // VK_CANCEL + /* 0x04 */ kKeyNone, kKeyNone, // VK_MBUTTON + /* 0x05 */ kKeyNone, kKeyNone, // undefined + /* 0x06 */ kKeyNone, kKeyNone, // undefined + /* 0x07 */ kKeyNone, kKeyNone, // undefined + /* 0x08 */ kKeyBackSpace, kKeyNone, // VK_BACK + /* 0x09 */ kKeyTab, kKeyNone, // VK_TAB + /* 0x0a */ kKeyNone, kKeyNone, // undefined + /* 0x0b */ kKeyNone, kKeyNone, // undefined + /* 0x0c */ kKeyClear, kKeyClear, // VK_CLEAR + /* 0x0d */ kKeyReturn, kKeyKP_Enter, // VK_RETURN + /* 0x0e */ kKeyNone, kKeyNone, // undefined + /* 0x0f */ kKeyNone, kKeyNone, // undefined + /* 0x10 */ kKeyShift_L, kKeyShift_R, // VK_SHIFT + /* 0x11 */ kKeyControl_L, kKeyControl_R, // VK_CONTROL + /* 0x12 */ kKeyAlt_L, kKeyAlt_R, // VK_MENU + /* 0x13 */ kKeyPause, kKeyNone, // VK_PAUSE + /* 0x14 */ kKeyCapsLock, kKeyNone, // VK_CAPITAL + /* 0x15 */ kKeyNone, kKeyNone, // VK_KANA + /* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL + /* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA + /* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL + /* 0x19 */ kKeyNone, kKeyNone, // VK_KANJI + /* 0x1a */ kKeyNone, kKeyNone, // undefined + /* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE + /* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT + /* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT + /* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT + /* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE + /* 0x20 */ kKeyNone, kKeyNone, // VK_SPACE + /* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR + /* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT + /* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END + /* 0x24 */ kKeyKP_Home, kKeyHome, // VK_HOME + /* 0x25 */ kKeyKP_Left, kKeyLeft, // VK_LEFT + /* 0x26 */ kKeyKP_Up, kKeyUp, // VK_UP + /* 0x27 */ kKeyKP_Right, kKeyRight, // VK_RIGHT + /* 0x28 */ kKeyKP_Down, kKeyDown, // VK_DOWN + /* 0x29 */ kKeySelect, kKeySelect, // VK_SELECT + /* 0x2a */ kKeyNone, kKeyNone, // VK_PRINT + /* 0x2b */ kKeyExecute, kKeyExecute, // VK_EXECUTE + /* 0x2c */ kKeyPrint, kKeyPrint, // VK_SNAPSHOT + /* 0x2d */ kKeyKP_Insert, kKeyInsert, // VK_INSERT + /* 0x2e */ kKeyKP_Delete, kKeyDelete, // VK_DELETE + /* 0x2f */ kKeyHelp, kKeyHelp, // VK_HELP + /* 0x30 */ kKeyNone, kKeyNone, // VK_0 + /* 0x31 */ kKeyNone, kKeyNone, // VK_1 + /* 0x32 */ kKeyNone, kKeyNone, // VK_2 + /* 0x33 */ kKeyNone, kKeyNone, // VK_3 + /* 0x34 */ kKeyNone, kKeyNone, // VK_4 + /* 0x35 */ kKeyNone, kKeyNone, // VK_5 + /* 0x36 */ kKeyNone, kKeyNone, // VK_6 + /* 0x37 */ kKeyNone, kKeyNone, // VK_7 + /* 0x38 */ kKeyNone, kKeyNone, // VK_8 + /* 0x39 */ kKeyNone, kKeyNone, // VK_9 + /* 0x3a */ kKeyNone, kKeyNone, // undefined + /* 0x3b */ kKeyNone, kKeyNone, // undefined + /* 0x3c */ kKeyNone, kKeyNone, // undefined + /* 0x3d */ kKeyNone, kKeyNone, // undefined + /* 0x3e */ kKeyNone, kKeyNone, // undefined + /* 0x3f */ kKeyNone, kKeyNone, // undefined + /* 0x40 */ kKeyNone, kKeyNone, // undefined + /* 0x41 */ kKeyNone, kKeyNone, // VK_A + /* 0x42 */ kKeyNone, kKeyNone, // VK_B + /* 0x43 */ kKeyNone, kKeyNone, // VK_C + /* 0x44 */ kKeyNone, kKeyNone, // VK_D + /* 0x45 */ kKeyNone, kKeyNone, // VK_E + /* 0x46 */ kKeyNone, kKeyNone, // VK_F + /* 0x47 */ kKeyNone, kKeyNone, // VK_G + /* 0x48 */ kKeyNone, kKeyNone, // VK_H + /* 0x49 */ kKeyNone, kKeyNone, // VK_I + /* 0x4a */ kKeyNone, kKeyNone, // VK_J + /* 0x4b */ kKeyNone, kKeyNone, // VK_K + /* 0x4c */ kKeyNone, kKeyNone, // VK_L + /* 0x4d */ kKeyNone, kKeyNone, // VK_M + /* 0x4e */ kKeyNone, kKeyNone, // VK_N + /* 0x4f */ kKeyNone, kKeyNone, // VK_O + /* 0x50 */ kKeyNone, kKeyNone, // VK_P + /* 0x51 */ kKeyNone, kKeyNone, // VK_Q + /* 0x52 */ kKeyNone, kKeyNone, // VK_R + /* 0x53 */ kKeyNone, kKeyNone, // VK_S + /* 0x54 */ kKeyNone, kKeyNone, // VK_T + /* 0x55 */ kKeyNone, kKeyNone, // VK_U + /* 0x56 */ kKeyNone, kKeyNone, // VK_V + /* 0x57 */ kKeyNone, kKeyNone, // VK_W + /* 0x58 */ kKeyNone, kKeyNone, // VK_X + /* 0x59 */ kKeyNone, kKeyNone, // VK_Y + /* 0x5a */ kKeyNone, kKeyNone, // VK_Z + /* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN + /* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN + /* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS + /* 0x5e */ kKeyNone, kKeyNone, // undefined + /* 0x5f */ kKeyNone, kKeyNone, // undefined + /* 0x60 */ kKeyKP_0, kKeyNone, // VK_NUMPAD0 + /* 0x61 */ kKeyKP_1, kKeyNone, // VK_NUMPAD1 + /* 0x62 */ kKeyKP_2, kKeyNone, // VK_NUMPAD2 + /* 0x63 */ kKeyKP_3, kKeyNone, // VK_NUMPAD3 + /* 0x64 */ kKeyKP_4, kKeyNone, // VK_NUMPAD4 + /* 0x65 */ kKeyKP_5, kKeyNone, // VK_NUMPAD5 + /* 0x66 */ kKeyKP_6, kKeyNone, // VK_NUMPAD6 + /* 0x67 */ kKeyKP_7, kKeyNone, // VK_NUMPAD7 + /* 0x68 */ kKeyKP_8, kKeyNone, // VK_NUMPAD8 + /* 0x69 */ kKeyKP_9, kKeyNone, // VK_NUMPAD9 + /* 0x6a */ kKeyKP_Multiply, kKeyNone, // VK_MULTIPLY + /* 0x6b */ kKeyKP_Add, kKeyNone, // VK_ADD + /* 0x6c */ kKeyKP_Separator,kKeyKP_Separator,// VK_SEPARATOR + /* 0x6d */ kKeyKP_Subtract, kKeyNone, // VK_SUBTRACT + /* 0x6e */ kKeyKP_Decimal, kKeyNone, // VK_DECIMAL + /* 0x6f */ kKeyNone, kKeyKP_Divide, // VK_DIVIDE + /* 0x70 */ kKeyF1, kKeyNone, // VK_F1 + /* 0x71 */ kKeyF2, kKeyNone, // VK_F2 + /* 0x72 */ kKeyF3, kKeyNone, // VK_F3 + /* 0x73 */ kKeyF4, kKeyNone, // VK_F4 + /* 0x74 */ kKeyF5, kKeyNone, // VK_F5 + /* 0x75 */ kKeyF6, kKeyNone, // VK_F6 + /* 0x76 */ kKeyF7, kKeyNone, // VK_F7 + /* 0x77 */ kKeyF8, kKeyNone, // VK_F8 + /* 0x78 */ kKeyF9, kKeyNone, // VK_F9 + /* 0x79 */ kKeyF10, kKeyNone, // VK_F10 + /* 0x7a */ kKeyF11, kKeyNone, // VK_F11 + /* 0x7b */ kKeyF12, kKeyNone, // VK_F12 + /* 0x7c */ kKeyF13, kKeyF13, // VK_F13 + /* 0x7d */ kKeyF14, kKeyF14, // VK_F14 + /* 0x7e */ kKeyF15, kKeyF15, // VK_F15 + /* 0x7f */ kKeyF16, kKeyF16, // VK_F16 + /* 0x80 */ kKeyF17, kKeyF17, // VK_F17 + /* 0x81 */ kKeyF18, kKeyF18, // VK_F18 + /* 0x82 */ kKeyF19, kKeyF19, // VK_F19 + /* 0x83 */ kKeyF20, kKeyF20, // VK_F20 + /* 0x84 */ kKeyF21, kKeyF21, // VK_F21 + /* 0x85 */ kKeyF22, kKeyF22, // VK_F22 + /* 0x86 */ kKeyF23, kKeyF23, // VK_F23 + /* 0x87 */ kKeyF24, kKeyF24, // VK_F24 + /* 0x88 */ kKeyNone, kKeyNone, // unassigned + /* 0x89 */ kKeyNone, kKeyNone, // unassigned + /* 0x8a */ kKeyNone, kKeyNone, // unassigned + /* 0x8b */ kKeyNone, kKeyNone, // unassigned + /* 0x8c */ kKeyNone, kKeyNone, // unassigned + /* 0x8d */ kKeyNone, kKeyNone, // unassigned + /* 0x8e */ kKeyNone, kKeyNone, // unassigned + /* 0x8f */ kKeyNone, kKeyNone, // unassigned + /* 0x90 */ kKeyNumLock, kKeyNumLock, // VK_NUMLOCK + /* 0x91 */ kKeyScrollLock, kKeyNone, // VK_SCROLL + /* 0x92 */ kKeyNone, kKeyNone, // unassigned + /* 0x93 */ kKeyNone, kKeyNone, // unassigned + /* 0x94 */ kKeyNone, kKeyNone, // unassigned + /* 0x95 */ kKeyNone, kKeyNone, // unassigned + /* 0x96 */ kKeyNone, kKeyNone, // unassigned + /* 0x97 */ kKeyNone, kKeyNone, // unassigned + /* 0x98 */ kKeyNone, kKeyNone, // unassigned + /* 0x99 */ kKeyNone, kKeyNone, // unassigned + /* 0x9a */ kKeyNone, kKeyNone, // unassigned + /* 0x9b */ kKeyNone, kKeyNone, // unassigned + /* 0x9c */ kKeyNone, kKeyNone, // unassigned + /* 0x9d */ kKeyNone, kKeyNone, // unassigned + /* 0x9e */ kKeyNone, kKeyNone, // unassigned + /* 0x9f */ kKeyNone, kKeyNone, // unassigned + /* 0xa0 */ kKeyShift_L, kKeyShift_L, // VK_LSHIFT + /* 0xa1 */ kKeyShift_R, kKeyShift_R, // VK_RSHIFT + /* 0xa2 */ kKeyControl_L, kKeyControl_L, // VK_LCONTROL + /* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL + /* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU + /* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU + /* 0xa6 */ kKeyNone, kKeyWWWBack, // VK_BROWSER_BACK + /* 0xa7 */ kKeyNone, kKeyWWWForward, // VK_BROWSER_FORWARD + /* 0xa8 */ kKeyNone, kKeyWWWRefresh, // VK_BROWSER_REFRESH + /* 0xa9 */ kKeyNone, kKeyWWWStop, // VK_BROWSER_STOP + /* 0xaa */ kKeyNone, kKeyWWWSearch, // VK_BROWSER_SEARCH + /* 0xab */ kKeyNone, kKeyWWWFavorites, // VK_BROWSER_FAVORITES + /* 0xac */ kKeyNone, kKeyWWWHome, // VK_BROWSER_HOME + /* 0xad */ kKeyNone, kKeyAudioMute, // VK_VOLUME_MUTE + /* 0xae */ kKeyNone, kKeyAudioDown, // VK_VOLUME_DOWN + /* 0xaf */ kKeyNone, kKeyAudioUp, // VK_VOLUME_UP + /* 0xb0 */ kKeyNone, kKeyAudioNext, // VK_MEDIA_NEXT_TRACK + /* 0xb1 */ kKeyNone, kKeyAudioPrev, // VK_MEDIA_PREV_TRACK + /* 0xb2 */ kKeyNone, kKeyAudioStop, // VK_MEDIA_STOP + /* 0xb3 */ kKeyNone, kKeyAudioPlay, // VK_MEDIA_PLAY_PAUSE + /* 0xb4 */ kKeyNone, kKeyAppMail, // VK_LAUNCH_MAIL + /* 0xb5 */ kKeyNone, kKeyAppMedia, // VK_LAUNCH_MEDIA_SELECT + /* 0xb6 */ kKeyNone, kKeyAppUser1, // VK_LAUNCH_APP1 + /* 0xb7 */ kKeyNone, kKeyAppUser2, // VK_LAUNCH_APP2 + /* 0xb8 */ kKeyNone, kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, kKeyNone, // unassigned + /* 0xba */ kKeyNone, kKeyNone, // OEM specific + /* 0xbb */ kKeyNone, kKeyNone, // OEM specific + /* 0xbc */ kKeyNone, kKeyNone, // OEM specific + /* 0xbd */ kKeyNone, kKeyNone, // OEM specific + /* 0xbe */ kKeyNone, kKeyNone, // OEM specific + /* 0xbf */ kKeyNone, kKeyNone, // OEM specific + /* 0xc0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xc1 */ kKeyNone, kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, kKeyNone, // unassigned + /* 0xca */ kKeyNone, kKeyNone, // unassigned + /* 0xcb */ kKeyNone, kKeyNone, // unassigned + /* 0xcc */ kKeyNone, kKeyNone, // unassigned + /* 0xcd */ kKeyNone, kKeyNone, // unassigned + /* 0xce */ kKeyNone, kKeyNone, // unassigned + /* 0xcf */ kKeyNone, kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, kKeyNone, // unassigned + /* 0xda */ kKeyNone, kKeyNone, // unassigned + /* 0xdb */ kKeyNone, kKeyNone, // OEM specific + /* 0xdc */ kKeyNone, kKeyNone, // OEM specific + /* 0xdd */ kKeyNone, kKeyNone, // OEM specific + /* 0xde */ kKeyNone, kKeyNone, // OEM specific + /* 0xdf */ kKeyNone, kKeyNone, // OEM specific + /* 0xe0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe5 */ kKeyNone, kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe7 */ kKeyNone, kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, kKeyNone, // OEM specific + /* 0xea */ kKeyNone, kKeyNone, // OEM specific + /* 0xeb */ kKeyNone, kKeyNone, // OEM specific + /* 0xec */ kKeyNone, kKeyNone, // OEM specific + /* 0xed */ kKeyNone, kKeyNone, // OEM specific + /* 0xee */ kKeyNone, kKeyNone, // OEM specific + /* 0xef */ kKeyNone, kKeyNone, // OEM specific + /* 0xf0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf5 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf6 */ kKeyNone, kKeyNone, // VK_ATTN + /* 0xf7 */ kKeyNone, kKeyNone, // VK_CRSEL + /* 0xf8 */ kKeyNone, kKeyNone, // VK_EXSEL + /* 0xf9 */ kKeyNone, kKeyNone, // VK_EREOF + /* 0xfa */ kKeyNone, kKeyNone, // VK_PLAY + /* 0xfb */ kKeyNone, kKeyNone, // VK_ZOOM + /* 0xfc */ kKeyNone, kKeyNone, // reserved + /* 0xfd */ kKeyNone, kKeyNone, // VK_PA1 + /* 0xfe */ kKeyNone, kKeyNone, // VK_OEM_CLEAR + /* 0xff */ kKeyNone, kKeyNone // reserved +}; + +KeyID +CMSWindowsPrimaryScreen::mapKey( + WPARAM vkCode, + LPARAM info, + KeyModifierMask* maskOut, + bool* altgr) +{ + // note: known microsoft bugs + // Q72583 -- MapVirtualKey() maps keypad keys incorrectly + // 95,98: num pad vk code -> invalid scan code + // 95,98,NT4: num pad scan code -> bad vk code except + // SEPARATOR, MULTIPLY, SUBTRACT, ADD + + assert(maskOut != NULL); + assert(altgr != NULL); + + // get the scan code and the extended keyboard flag + UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); + int extended = ((info & 0x01000000) == 0) ? 0 : 1; + LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); + + // handle some keys via table lookup + char c = 0; + KeyID id = g_virtualKey[vkCode][extended]; + if (id == kKeyNone) { + // not in table + + // save the control state then clear it. ToAscii() maps ctrl+letter + // to the corresponding control code and ctrl+backspace to delete. + // we don't want that translation so we clear the control modifier + // state. however, if we want to simulate AltGr (which is ctrl+alt) + // then we must not clear it. + BYTE lControl = m_keys[VK_LCONTROL]; + BYTE rControl = m_keys[VK_RCONTROL]; + BYTE control = m_keys[VK_CONTROL]; + BYTE lMenu = m_keys[VK_LMENU]; + BYTE menu = m_keys[VK_MENU]; + if ((control & 0x80) == 0 || (menu & 0x80) == 0) { + m_keys[VK_LCONTROL] = 0; + m_keys[VK_RCONTROL] = 0; + m_keys[VK_CONTROL] = 0; + } + else { + m_keys[VK_LCONTROL] = 0x80; + m_keys[VK_CONTROL] = 0x80; + m_keys[VK_LMENU] = 0x80; + m_keys[VK_MENU] = 0x80; + } + + // convert to ascii + WORD ascii; + int result = ToAscii(vkCode, scanCode, m_keys, &ascii, + ((menu & 0x80) == 0) ? 0 : 1); + + // restore control state + m_keys[VK_LCONTROL] = lControl; + m_keys[VK_RCONTROL] = rControl; + m_keys[VK_CONTROL] = control; + m_keys[VK_LMENU] = lMenu; + m_keys[VK_MENU] = menu; + + // if result is less than zero then it was a dead key. leave it + // there. + if (result < 0) { + id = kKeyMultiKey; + } + + // if result is 1 then the key was succesfully converted + else if (result == 1) { + c = static_cast(ascii & 0xff); + if (ascii >= 0x80) { + // character is not really ASCII. instead it's some + // character in the current ANSI code page. try to + // convert that to a Unicode character. if we fail + // then use the single byte character as is. + char src = c; + wchar_t unicode; + if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, + &src, 1, &unicode, 1) > 0) { + id = static_cast(unicode); + } + else { + id = static_cast(ascii & 0x00ff); + } + } + else { + id = static_cast(ascii & 0x00ff); + } + } + + // if result is 2 then a previous dead key could not be composed. + else if (result == 2) { + // if the two characters are the same and this is a key release + // then this event is the dead key being released. we put the + // dead key back in that case, otherwise we discard both key + // events because we can't compose the character. alternatively + // we could generate key events for both keys. + if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || + (info & 0x80000000) == 0) { + // cannot compose key + return kKeyNone; + } + + // get the scan code of the dead key and the shift state + // required to generate it. + vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); + + // set shift state required to generate key + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + if (vkCode & 0x0100) { + keys[VK_SHIFT] = 0x80; + } + if (vkCode & 0x0200) { + keys[VK_CONTROL] = 0x80; + } + if (vkCode & 0x0400) { + keys[VK_MENU] = 0x80; + } + + // strip shift state off of virtual key code + vkCode &= 0x00ff; + + // get the scan code for the key + scanCode = MapVirtualKey(vkCode, 0); + + // put it back + ToAscii(vkCode, scanCode, keys, &ascii, 0); + id = kKeyMultiKey; + } + } + + // set mask + *altgr = false; + if (id != kKeyNone && id != kKeyMultiKey && c != 0) { + // note if key requires AltGr. VkKeyScan() can have a problem + // with some characters. there are two problems in particular. + // first, typing a dead key then pressing space will cause + // VkKeyScan() to return 0xffff. second, certain characters + // may map to multiple virtual keys and we might get the wrong + // one. if that happens then we might not get the right + // modifier mask. AltGr+9 on the french keyboard layout (^) + // has this problem. in the first case, we'll assume AltGr is + // required (only because it solves the problems we've seen + // so far). in the second, we'll use whatever the keyboard + // state says. + WORD virtualKeyAndModifierState = VkKeyScan(c); + if (virtualKeyAndModifierState == 0xffff) { + // there is no mapping. assume AltGr. + LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); + *altgr = true; + } + else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { + // we didn't get the key that was actually pressed + LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0) { + *altgr = true; + } + } + else { + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + if ((modifierState & 6) == 6) { + // key requires ctrl and alt == AltGr + *altgr = true; + } + } + } + + // map modifier key + KeyModifierMask mask = 0; + if (((m_keys[VK_LSHIFT] | + m_keys[VK_RSHIFT] | + m_keys[VK_SHIFT]) & 0x80) != 0) { + mask |= KeyModifierShift; + } + if (*altgr) { + mask |= KeyModifierModeSwitch; + } + else { + if (((m_keys[VK_LCONTROL] | + m_keys[VK_RCONTROL] | + m_keys[VK_CONTROL]) & 0x80) != 0) { + mask |= KeyModifierControl; + } + if (((m_keys[VK_LMENU] | + m_keys[VK_RMENU] | + m_keys[VK_MENU]) & 0x80) != 0) { + mask |= KeyModifierAlt; + } + } + if (((m_keys[VK_LWIN] | + m_keys[VK_RWIN]) & 0x80) != 0) { + mask |= KeyModifierSuper; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + mask |= KeyModifierScrollLock; + } + *maskOut = mask; + + return id; +} + +ButtonID +CMSWindowsPrimaryScreen::mapButton(WPARAM msg, LPARAM button) const +{ + switch (msg) { + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCLBUTTONUP: + return kButtonLeft; + + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONUP: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONDBLCLK: + case WM_NCMBUTTONUP: + return kButtonMiddle; + + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONUP: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONUP: + return kButtonRight; + + case WM_XBUTTONDOWN: + case WM_XBUTTONDBLCLK: + case WM_XBUTTONUP: + case WM_NCXBUTTONDOWN: + case WM_NCXBUTTONDBLCLK: + case WM_NCXBUTTONUP: + switch (button) { + case XBUTTON1: + return kButtonExtra0 + 0; + + case XBUTTON2: + return kButtonExtra0 + 1; + } + return kButtonNone; + + default: + return kButtonNone; + } +} + +void +CMSWindowsPrimaryScreen::updateKeys() +{ + // not using GetKeyboardState() because that doesn't seem to give + // up-to-date results. i don't know why that is or why GetKeyState() + // should give different results. + + // clear key and button state + memset(m_keys, 0, sizeof(m_keys)); + memset(m_buttons, 0, sizeof(m_buttons)); + + // we only care about the modifier key states. other keys and the + // mouse buttons should be up. + // sometimes these seem to be out of date so, to avoid getting + // locked to a screen unexpectedly, just assume the non-toggle + // modifier keys are up. +/* + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT) & 0x80); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT) & 0x80); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT) & 0x80); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL) & 0x80); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL) & 0x80); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL) & 0x80); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU) & 0x80); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU) & 0x80); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU) & 0x80); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN) & 0x80); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN) & 0x80); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS) & 0x80); +*/ + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); +} + +void +CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) +{ + if (press) { + switch (vkCode) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[vkCode] |= 0x80; + m_keys[VK_SHIFT] |= 0x80; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[vkCode] |= 0x80; + m_keys[VK_CONTROL] |= 0x80; + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[vkCode] |= 0x80; + m_keys[VK_MENU] |= 0x80; + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[vkCode] |= 0x80; + break; + + default: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[vkCode] |= 0x80; + break; + } + + // special case: we detect ctrl+alt+del being pressed on some + // systems but we don't detect the release of those keys. so + // if ctrl, alt, and del are down then mark them up. + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0 && + (m_keys[VK_DELETE] & 0x80) != 0) { + m_keys[VK_LCONTROL] &= ~0x80; + m_keys[VK_RCONTROL] &= ~0x80; + m_keys[VK_CONTROL] &= ~0x80; + m_keys[VK_LMENU] &= ~0x80; + m_keys[VK_RMENU] &= ~0x80; + m_keys[VK_MENU] &= ~0x80; + m_keys[VK_DELETE] &= ~0x80; + } + } + else { + switch (vkCode) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + } + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[vkCode] &= ~0x80; + m_keys[vkCode] ^= 0x01; + break; + + default: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[vkCode] &= ~0x80; + break; + } + } +} + +bool +CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const + +{ + switch (vkCode) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LWIN: + case VK_RWIN: + return true; + + default: + return false; + } +} + +KeyButton +CMSWindowsPrimaryScreen::mapKeyToScanCode(UINT vk1, UINT vk2) const +{ + KeyButton button = static_cast(MapVirtualKey(vk1, 0)); + if (button == 0) { + button = static_cast(MapVirtualKey(vk2, 0)); + } + return button; +} diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h new file mode 100644 index 0000000..c318d12 --- /dev/null +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -0,0 +1,140 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSPRIMARYSCREEN_H +#define CMSWINDOWSPRIMARYSCREEN_H + +#include "CPrimaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" +#include "CSynergyHook.h" +#include "MouseTypes.h" +#include "CString.h" + +class CMSWindowsScreen; +class IScreenReceiver; +class IPrimaryScreenReceiver; + +//! Microsoft windows primary screen implementation +class CMSWindowsPrimaryScreen : + public CPrimaryScreen, public IMSWindowsScreenEventHandler { +public: + typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); + + CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); + virtual ~CMSWindowsPrimaryScreen(); + + // CPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual UInt32 addOneShotTimer(double timeout); + virtual KeyModifierMask getToggleMask() const; + virtual bool isLockedToScreen() const; + virtual IScreen* getScreen() const; + + // IMSWindowsScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + virtual void onAccessibleDesktop(); + +protected: + // CPrimaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void onPostLeave(bool); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); + +private: + void enterNoWarp(); + + // warp cursor without discarding queued events + void warpCursorNoFlush(SInt32 x, SInt32 y); + + // discard posted messages + void nextMark(); + + // test if event should be ignored + bool ignore() const; + + // key and button queries + KeyID mapKey(WPARAM keycode, LPARAM info, + KeyModifierMask* maskOut, bool* altgr); + ButtonID mapButton(WPARAM msg, LPARAM button) const; + void updateKey(UINT vkCode, bool press); + bool isModifier(UINT vkCode) const; + KeyButton mapKeyToScanCode(UINT vk1, UINT vk2) const; + +private: + IPrimaryScreenReceiver* m_receiver; + CMSWindowsScreen* m_screen; + + // true if windows 95/98/me + bool m_is95Family; + + // the main loop's thread id + DWORD m_threadID; + + // my window + HWND m_window; + + // used to discard queued messages that are no longer needed + UInt32 m_mark; + UInt32 m_markReceived; + + // map of key state + BYTE m_keys[256]; + + // map of button state + BYTE m_buttons[1 + kButtonExtra0 + 1]; + + // last mouse position + SInt32 m_x, m_y; + + // position of center pixel of screen + SInt32 m_xCenter, m_yCenter; + + // hook library stuff + HINSTANCE m_hookLibrary; + InitFunc m_init; + CleanupFunc m_cleanup; + InstallFunc m_install; + UninstallFunc m_uninstall; + SetSidesFunc m_setSides; + SetZoneFunc m_setZone; + SetRelayFunc m_setRelay; + bool m_lowLevel; + + // stuff for hiding the cursor + DWORD m_cursorThread; +}; + +#endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp new file mode 100644 index 0000000..0b37d99 --- /dev/null +++ b/lib/platform/CMSWindowsScreen.cpp @@ -0,0 +1,820 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsScreen.h" +#include "CMSWindowsClipboard.h" +#include "CMSWindowsScreenSaver.h" +#include "CClipboard.h" +#include "IMSWindowsScreenEventHandler.h" +#include "IScreenReceiver.h" +#include "CThread.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CLog.h" +#include "CString.h" +#include "CStringUtil.h" +#include "CArchMiscWindows.h" +#include +#include +#include + +// +// add backwards compatible multihead support (and suppress bogus warning) +// +#pragma warning(push) +#pragma warning(disable: 4706) // assignment within conditional +#define COMPILE_MULTIMON_STUBS +#include +#pragma warning(pop) + +// +// CMSWindowsScreen +// + +HINSTANCE CMSWindowsScreen::s_instance = NULL; +CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; + +CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, + IMSWindowsScreenEventHandler* eventHandler) : + m_receiver(receiver), + m_eventHandler(eventHandler), + m_class(NULL), + m_icon(NULL), + m_cursor(NULL), + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_window(NULL), + m_x(0), m_y(0), + m_w(0), m_h(0), + m_multimon(false), + m_threadID(0), + m_lastThreadID(0), + m_nextClipboardWindow(NULL), + m_ownClipboard(false), + m_timer(0), + m_desk(NULL), + m_deskName(), + m_hookLibrary(NULL), + m_installScreensaver(NULL), + m_uninstallScreensaver(NULL), + m_screensaver(NULL), + m_screensaverNotify(false), + m_inaccessibleDesktop(false) +{ + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + + s_screen = this; + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); +} + +CMSWindowsScreen::~CMSWindowsScreen() +{ + assert(s_screen != NULL); + assert(m_class == 0); + + s_screen = NULL; +} + +void +CMSWindowsScreen::init(HINSTANCE instance) +{ + s_instance = instance; +} + +HWND +CMSWindowsScreen::openDesktop() +{ + // save thread id + m_threadID = GetCurrentThreadId(); + + // get the input desktop and switch to it + if (!switchDesktop(openInputDesktop())) { + return NULL; + } + + // poll input desktop to see if it changes (onPreDispatch() + // handles WM_TIMER). this is also used for polling other + // stuff. + m_timer = SetTimer(NULL, 0, 200, NULL); + + return m_window; +} + +void +CMSWindowsScreen::closeDesktop() +{ + // remove timer + if (m_timer != 0) { + KillTimer(NULL, m_timer); + m_timer = 0; + } + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + m_oneShotTimer = 0; + } + + // disconnect from desktop + switchDesktop(NULL); + + // clear thread id + m_threadID = 0; + + assert(m_window == NULL); + assert(m_desk == NULL); +} + +UInt32 +CMSWindowsScreen::addOneShotTimer(double timeout) +{ + // FIXME -- support multiple one-shot timers + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + } + m_oneShotTimer = SetTimer(NULL, 0, + static_cast(1000.0 * timeout), NULL); + return 0; +} + +bool +CMSWindowsScreen::isMultimon() const +{ + return m_multimon; +} + +HINSTANCE +CMSWindowsScreen::getInstance() +{ + return s_instance; +} + +void +CMSWindowsScreen::open() +{ + assert(s_instance != NULL); + assert(m_class == 0); + + LOG((CLOG_DEBUG "opening display")); + + // create the transparent cursor + createBlankCursor(); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + m_class = RegisterClassEx(&classInfo); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_screensaver = new CMSWindowsScreenSaver(); + + // load the hook library and get the screen saver functions + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary != NULL) { + m_installScreensaver = (InstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = (UninstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "uninstallScreenSaver"); + if (m_installScreensaver == NULL || m_uninstallScreensaver == NULL) { + // disable if either install or uninstall is unavailable + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + } + } +} + +void +CMSWindowsScreen::mainLoop() +{ + // must call mainLoop() from same thread as openDesktop() + assert(m_threadID == GetCurrentThreadId()); + + // event loop + CEvent event; + event.m_result = 0; + for (;;) { + // wait for an event in a cancellable way + if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) { + continue; + } + if (!PeekMessage(&event.m_msg, NULL, 0, 0, PM_REMOVE)) { + continue; + } + + // handle quit message + if (event.m_msg.message == WM_QUIT) { + if (event.m_msg.wParam == 0) { + // force termination + CThread::getCurrentThread().cancel(); + } + else { + // just exit the main loop + break; + } + } + + // dispatch message + if (!onPreDispatch(&event)) { + TranslateMessage(&event.m_msg); + DispatchMessage(&event.m_msg); + } + } +} + +void +CMSWindowsScreen::exitMainLoop() +{ + // close down cleanly + PostThreadMessage(m_threadID, WM_QUIT, 1, 0); +} + +void +CMSWindowsScreen::close() +{ + assert(s_instance != NULL); + + // done with hook library + if (m_hookLibrary != NULL) { + FreeLibrary(m_hookLibrary); + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + m_hookLibrary = NULL; + } + + // done with screen saver + delete m_screensaver; + m_screensaver = NULL; + + // unregister the window class + if (m_class != 0) { + UnregisterClass((LPCTSTR)m_class, s_instance); + m_class = 0; + } + + // delete resources + if (m_cursor != NULL) { + DestroyCursor(m_cursor); + m_cursor = NULL; + } + + LOG((CLOG_DEBUG "closed display")); +} + +bool +CMSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src) +{ + CMSWindowsClipboard dst(m_window); + if (src != NULL) { + // save clipboard data + return CClipboard::copy(&dst, src); + } + else { + // assert clipboard ownership + if (!dst.open(0)) { + return false; + } + dst.empty(); + dst.close(); + return true; + } +} + +void +CMSWindowsScreen::checkClipboards() +{ + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received")); + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } +} + +void +CMSWindowsScreen::openScreensaver(bool notify) +{ + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + else { + m_screensaver->disable(); + } +} + +void +CMSWindowsScreen::closeScreensaver() +{ + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + else { + m_screensaver->enable(); + } + } + m_screensaverNotify = false; +} + +void +CMSWindowsScreen::screensaver(bool activate) +{ + assert(m_screensaver != NULL); + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CMSWindowsScreen::syncDesktop() +{ + // change calling thread's desktop + if (!m_is95Family) { + if (SetThreadDesktop(m_desk) == 0) { +// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError())); + } + } + + // attach input queues if not already attached. this has a habit + // of sucking up more and more CPU each time it's called (even if + // the threads are already attached). since we only expect one + // thread to call this more than once we can save just the last + // attached thread. + DWORD threadID = GetCurrentThreadId(); + if (threadID != m_lastThreadID && threadID != m_threadID) { + m_lastThreadID = threadID; + AttachThreadInput(threadID, m_threadID, TRUE); + } +} + +bool +CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const +{ + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); + return true; +} + +void +CMSWindowsScreen::getShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const +{ + assert(m_class != 0); + + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + POINT pos; + if (GetCursorPos(&pos)) { + x = pos.x; + y = pos.y; + } + else { + getCursorCenter(x, y); + } +} + +void +CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = GetSystemMetrics(SM_CXSCREEN) >> 1; + y = GetSystemMetrics(SM_CYSCREEN) >> 1; +} + +void +CMSWindowsScreen::updateScreenShape() +{ + // get shape + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // check for multiple monitors + m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || + m_h != GetSystemMetrics(SM_CYSCREEN)); +} + +bool +CMSWindowsScreen::onPreDispatch(const CEvent* event) +{ + // handle event + const MSG* msg = &event->m_msg; + switch (msg->message) { + case SYNERGY_MSG_SCREEN_SAVER: + { + // activating or deactivating? + bool activate = (msg->wParam != 0); + + // ignore this message if there are any other screen saver + // messages already in the queue. this is important because + // our checkStarted() function has a deliberate delay, so it + // can't respond to events at full CPU speed and will fall + // behind if a lot of screen saver events are generated. + // that can easily happen because windows will continually + // send SC_SCREENSAVE until the screen saver starts, even if + // the screen saver is disabled! + MSG msg; + if (!PeekMessage(&msg, NULL, SYNERGY_MSG_SCREEN_SAVER, + SYNERGY_MSG_SCREEN_SAVER, PM_NOREMOVE)) { + if (activate) { + if (m_screensaver->checkStarted( + SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { + m_eventHandler->onScreensaver(true); + } + } + else { + m_eventHandler->onScreensaver(false); + } + } + return true; + } + + case WM_TIMER: + if (msg->wParam == m_timer) { + // if current desktop is not the input desktop then switch to it. + // windows 95 doesn't support multiple desktops so don't bother + // to check under it. + if (!m_is95Family) { + HDESK desk = openInputDesktop(); + if (desk != NULL) { + if (isCurrentDesktop(desk)) { + CloseDesktop(desk); + } + else if (m_screensaver->isActive()) { + // don't switch desktops when the screensaver is + // active. we'd most likely switch to the + // screensaver desktop which would have the side + // effect of forcing the screensaver to stop. + CloseDesktop(desk); + } + else { + switchDesktop(desk); + } + + // if the desktop was inaccessible then notify the + // event handler of that. + if (m_inaccessibleDesktop) { + LOG((CLOG_DEBUG "desktop is now accessible")); + m_inaccessibleDesktop = false; + m_eventHandler->onAccessibleDesktop(); + } + } + else if (!m_inaccessibleDesktop) { + // the desktop has become inaccessible + m_inaccessibleDesktop = true; + LOG((CLOG_DEBUG "desktop is now inaccessible")); + } + } + + // let client do timer related stuff. ignore the return + // value though since the event has been handled here. + m_eventHandler->onPreDispatch(event); + } + else if (msg->wParam == m_oneShotTimer) { + // one shot timer expired + KillTimer(NULL, m_oneShotTimer); + m_oneShotTimer = 0; + m_eventHandler->onOneShotTimerExpired(0); + } + + return true; + } + + // let client handle the event + return m_eventHandler->onPreDispatch(event); +} + +bool +CMSWindowsScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_QUERYENDSESSION: + if (m_is95Family) { + event->m_result = TRUE; + return true; + } + break; + + case WM_ENDSESSION: + if (m_is95Family) { + if (msg.wParam == TRUE && msg.lParam == 0) { + exitMainLoop(); + } + return true; + } + break; + + case WM_PAINT: + ValidateRect(msg.hwnd, NULL); + return true; + + case WM_DRAWCLIPBOARD: + LOG((CLOG_DEBUG "clipboard was taken")); + + // first pass it on + if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + + // now notify client that somebody changed the clipboard (unless + // we're the owner). + if (!CMSWindowsClipboard::isOwnedBySynergy()) { + LOG((CLOG_DEBUG "clipboard changed: foreign owned")); + if (m_ownClipboard) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership")); + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + else { + LOG((CLOG_DEBUG "clipboard changed: synergy owned")); + m_ownClipboard = true; + } + return true; + + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)msg.wParam) { + m_nextClipboardWindow = (HWND)msg.lParam; + LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow)); + } + else if (m_nextClipboardWindow != NULL) { + LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", msg.message, msg.wParam, msg.lParam)); + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + return true; + + case WM_DISPLAYCHANGE: + { + // screen resolution may have changed. get old shape. + SInt32 xOld, yOld, wOld, hOld; + getShape(xOld, yOld, wOld, hOld); + + // update shape + updateScreenShape(); + + // collect new screen info + CClientInfo info; + getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = m_eventHandler->getJumpZoneSize(); + + // do nothing if resolution hasn't changed + if (info.m_x != xOld || info.m_y != yOld || + info.m_w != wOld || info.m_h != hOld) { + // forward event + m_eventHandler->onEvent(event); + + // send new screen info + m_receiver->onInfoChanged(info); + } + + return true; + } + } + + return m_eventHandler->onEvent(event); +} + +void +CMSWindowsScreen::createBlankCursor() +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; +} + +bool +CMSWindowsScreen::switchDesktop(HDESK desk) +{ + // assume we don't own the clipboard until later + m_ownClipboard = false; + + // destroy old window + if (m_window != NULL) { + // first remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // let client clean up before we destroy the window + m_eventHandler->preDestroyWindow(m_window); + + // now destroy window + DestroyWindow(m_window); + m_window = NULL; + + // done with desk + if (!m_is95Family) { + CloseDesktop(m_desk); + } + m_desk = NULL; + m_deskName = ""; + } + + // if no new desktop then we're done + if (desk == NULL) { + LOG((CLOG_DEBUG "disconnecting desktop")); + return true; + } + + // uninstall screen saver hooks + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + + // set the desktop. can only do this when there are no windows + // and hooks on the current desktop owned by this thread. + if (SetThreadDesktop(desk) == 0) { + LOG((CLOG_ERR "failed to set desktop: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | + WS_EX_TOOLWINDOW, + (LPCTSTR)m_class, + "Synergy", + WS_POPUP, + 0, 0, 1, 1, + NULL, NULL, + getInstance(), + NULL); + if (m_window == NULL) { + LOG((CLOG_ERR "failed to create window: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // reinstall screen saver hooks + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // check if we own the clipboard + m_ownClipboard = CMSWindowsClipboard::isOwnedBySynergy(); + + // save new desktop + m_desk = desk; + m_deskName = getDesktopName(m_desk); + LOG((CLOG_DEBUG "switched to desktop \"%s\"", m_deskName.c_str())); + + // let client prepare the window + m_eventHandler->postCreateWindow(m_window); + + return true; +} + +HDESK +CMSWindowsScreen::openInputDesktop() const +{ + if (m_is95Family) { + // there's only one desktop on windows 95 et al. + return GetThreadDesktop(GetCurrentThreadId()); + } + else { + return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, + DESKTOP_CREATEWINDOW | + DESKTOP_HOOKCONTROL | + GENERIC_WRITE); + } +} + +CString +CMSWindowsScreen::getDesktopName(HDESK desk) const +{ + if (desk == NULL) { + return CString(); + } + else if (m_is95Family) { + return "desktop"; + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR)); + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + CString result(name); + return result; + } +} + +bool +CMSWindowsScreen::isCurrentDesktop(HDESK desk) const +{ + // don't allocate space for current desktop name on heap since + // we do this a lot and we never save the name. + TCHAR* name; + if (desk == NULL) { + name = _T(""); + } + else if (m_is95Family) { + name = _T("desktop"); + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + name = (TCHAR*)alloca(size + sizeof(TCHAR)); + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + } + return (_tcsicmp(name, m_deskName.c_str()) == 0); +} + +LRESULT CALLBACK +CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + assert(s_screen != NULL); + + CEvent event; + event.m_msg.hwnd = hwnd; + event.m_msg.message = msg; + event.m_msg.wParam = wParam; + event.m_msg.lParam = lParam; + event.m_result = 0; + + if (s_screen->onEvent(&event)) { + return event.m_result; + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); + } +} diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h new file mode 100644 index 0000000..f4af0ca --- /dev/null +++ b/lib/platform/CMSWindowsScreen.h @@ -0,0 +1,204 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSCREEN_H +#define CMSWINDOWSSCREEN_H + +#include "IScreen.h" +#include "CSynergyHook.h" +#include "CMutex.h" +#include "CString.h" +#define WIN32_LEAN_AND_MEAN +#include + +class CMSWindowsScreenSaver; +class CThread; + +// Microsoft windows event +class CEvent { +public: + MSG m_msg; + LRESULT m_result; +}; + +class IScreenReceiver; +class IMSWindowsScreenEventHandler; + +//! Implementation of IScreen for Microsoft Windows +class CMSWindowsScreen : public IScreen { +public: + CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*); + virtual ~CMSWindowsScreen(); + + //! @name manipulators + //@{ + + //! Initialize + /*! + Saves the application's HINSTANCE. This \b must be called by + WinMain with the HINSTANCE it was passed. + */ + static void init(HINSTANCE); + + //! Open desktop + /*! + Open the desktop and create and return the window. Returns NULL + on failure. + */ + HWND openDesktop(); + + //! Close desktop + /*! + Close the window and desktop. + */ + void closeDesktop(); + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to the receiver's + \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + + //@} + //! @name accessors + //@{ + + //! Test for multiple monitors + /*! + Returns true iff the system appears to have multiple monitors. + */ + bool isMultimon() const; + + //! Get instance + /*! + Returns the application instance handle passed to init(). + */ + static HINSTANCE getInstance(); + + //@} + + // IScreen overrides + // note -- this class expects the hook DLL to have been loaded + // and initialized before open() is called. + void open(); + void mainLoop(); + void exitMainLoop(); + void close(); + bool setClipboard(ClipboardID, const IClipboard*); + void checkClipboards(); + void openScreensaver(bool notify); + void closeScreensaver(); + void screensaver(bool activate); + void syncDesktop(); + bool getClipboard(ClipboardID, IClipboard*) const; + void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + void getCursorPos(SInt32&, SInt32&) const; + void getCursorCenter(SInt32&, SInt32&) const; + +private: + // update screen size cache + void updateScreenShape(); + + // internal pre-dispatch event processing + bool onPreDispatch(const CEvent* event); + + // internal (post-dispatch) event processing + bool onEvent(CEvent* event); + + // create the transparent cursor + void createBlankCursor(); + + // switch to the given desktop. this destroys the window and unhooks + // all hooks, switches the desktop, then creates the window and rehooks + // all hooks (because you can't switch the thread's desktop if it has + // any windows or hooks). + bool switchDesktop(HDESK desk); + + // get the input desktop. caller must CloseDesktop() the result. + // do not call under windows 95/98/me. + HDESK openInputDesktop() const; + + // get the desktop's name. do not call under windows 95/98/me. + CString getDesktopName(HDESK) const; + + // returns true iff desk is the current desk. do not call under + // windows 95/98/me. + bool isCurrentDesktop(HDESK desk) const; + + // our window proc + static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); + +private: + static HINSTANCE s_instance; + + IScreenReceiver* m_receiver; + IMSWindowsScreenEventHandler* m_eventHandler; + + ATOM m_class; + HICON m_icon; + HCURSOR m_cursor; + + // true if windows 95/98/me + bool m_is95Family; + + // our window + HWND m_window; + + // screen shape + SInt32 m_x, m_y; + SInt32 m_w, m_h; + + // true if system appears to have multiple monitors + bool m_multimon; + + // the main loop's thread id + DWORD m_threadID; + + // the thread id of the last attached thread + DWORD m_lastThreadID; + + // clipboard stuff + HWND m_nextClipboardWindow; + bool m_ownClipboard; + + // the timer used to check for desktop switching + UINT m_timer; + + // the one shot timer + UINT m_oneShotTimer; + + // the current desk and it's name + HDESK m_desk; + CString m_deskName; + + // screen saver stuff + HINSTANCE m_hookLibrary; + InstallScreenSaverFunc m_installScreensaver; + UninstallScreenSaverFunc m_uninstallScreensaver; + CMSWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + + // true when the current desktop is inaccessible. while + // the desktop is inaccessible we won't receive user input + // and we'll lose track of the keyboard state. when the + // desktop becomes accessible again we'll notify the event + // handler of that. + bool m_inaccessibleDesktop; + + static CMSWindowsScreen* s_screen; +}; + +#endif diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp new file mode 100644 index 0000000..14e0bac --- /dev/null +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -0,0 +1,377 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsScreenSaver.h" +#include "CThread.h" +#include "CLog.h" +#include "TMethodJob.h" +#include "CArch.h" +#include +#include + +#if !defined(SPI_GETSCREENSAVERRUNNING) +#define SPI_GETSCREENSAVERRUNNING 114 +#endif + +// +// CMSWindowsScreenSaver +// + +CMSWindowsScreenSaver::CMSWindowsScreenSaver() : + m_process(NULL), + m_threadID(0), + m_watch(NULL) +{ + // detect OS + m_is95Family = false; + m_is95 = false; + m_isNT = false; + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + if (GetVersionEx(&info)) { + m_is95Family = (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT && + info.dwMajorVersion <= 4) { + m_isNT = true; + } + else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && + info.dwMajorVersion == 4 && + info.dwMinorVersion == 0) { + m_is95 = true; + } + } + + // check if screen saver is enabled + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); +} + +CMSWindowsScreenSaver::~CMSWindowsScreenSaver() +{ + unwatchProcess(); +} + +bool +CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) +{ + // screen saver may have started. look for it and get + // the process. if we can't find it then assume it + // didn't really start. we wait a moment before + // looking to give the screen saver a chance to start. + // this shouldn't be a problem since we only get here + // if the screen saver wants to kick in, meaning that + // the system is idle or the user deliberately started + // the screen saver. + Sleep(250); + + // set parameters common to all screen saver handling + m_threadID = GetCurrentThreadId(); + m_msg = msg; + m_wParam = wParam; + m_lParam = lParam; + + // we handle the screen saver differently for the windows + // 95 and nt families. + if (m_is95Family) { + // on windows 95 we wait for the screen saver process + // to terminate. get the process. + DWORD processID = findScreenSaver(); + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); + if (process == NULL) { + // didn't start + LOG((CLOG_DEBUG "can't open screen saver process")); + return false; + } + + // watch for the process to exit + watchProcess(process); + } + else { + // on the windows nt family we wait for the desktop to + // change until it's neither the Screen-Saver desktop + // nor a desktop we can't open (the login desktop). + // since windows will send the request-to-start-screen- + // saver message even when the screen saver is disabled + // we first check that the screen saver is indeed active + // before watching for it to stop. + if (!isActive()) { + LOG((CLOG_DEBUG "can't open screen saver desktop")); + return false; + } + + watchDesktop(); + } + + return true; +} + +void +CMSWindowsScreenSaver::enable() +{ + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0); +} + +void +CMSWindowsScreenSaver::disable() +{ + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0); +} + +void +CMSWindowsScreenSaver::activate() +{ + // don't activate if already active + if (!isActive()) { + HWND hwnd = GetForegroundWindow(); + if (hwnd != NULL) { + PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0); + } + else { + // no foreground window. pretend we got the event instead. + DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0); + } + } +} + +void +CMSWindowsScreenSaver::deactivate() +{ + bool killed = false; + if (!m_is95Family) { + // NT runs screen saver in another desktop + HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, + DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); + if (desktop != NULL) { + EnumDesktopWindows(desktop, + &CMSWindowsScreenSaver::killScreenSaverFunc, + reinterpret_cast(&killed)); + CloseDesktop(desktop); + } + } + + // if above failed or wasn't tried, try the windows 95 way + if (!killed) { + // find screen saver window and close it + HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); + if (hwnd == NULL) { + // win2k may use a different class + hwnd = FindWindow("Default Screen Saver", NULL); + } + if (hwnd != NULL) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + } + } + + // force timer to restart + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, + !m_wasEnabled, 0, SPIF_SENDWININICHANGE); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, + m_wasEnabled, 0, SPIF_SENDWININICHANGE); +} + +bool +CMSWindowsScreenSaver::isActive() const +{ + if (m_is95) { + return (FindWindow("WindowsScreenSaverClass", NULL) != NULL); + } + else if (m_isNT) { + // screen saver runs on a separate desktop + HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED); + if (desktop == NULL && GetLastError() != ERROR_ACCESS_DENIED) { + // desktop doesn't exist so screen saver is not running + return false; + } + + // desktop exists. this should indicate that the screen saver + // is running but an OS bug can cause a valid handle to be + // returned even if the screen saver isn't running (Q230117). + // we'll try to enumerate the windows on the desktop and, if + // there are any, we assume the screen saver is running. (note + // that if we don't have permission to enumerate then we'll + // assume that the screen saver is not running.) that'd be + // easy enough except there's another OS bug (Q198590) that can + // cause EnumDesktopWindows() to enumerate the windows of + // another desktop if the requested desktop has no windows. to + // work around that we have to verify that the enumerated + // windows are, in fact, on the expected desktop. + CFindScreenSaverInfo info; + info.m_desktop = desktop; + info.m_window = NULL; + EnumDesktopWindows(desktop, + &CMSWindowsScreenSaver::findScreenSaverFunc, + reinterpret_cast(&info)); + + // done with desktop + CloseDesktop(desktop); + + // screen saver is running if a window was found + return (info.m_window != NULL); + } + else { + BOOL running; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0); + return (running != FALSE); + } +} + +BOOL CALLBACK +CMSWindowsScreenSaver::findScreenSaverFunc(HWND hwnd, LPARAM arg) +{ + CFindScreenSaverInfo* info = reinterpret_cast(arg); + + if (info->m_desktop != NULL) { + DWORD threadID = GetWindowThreadProcessId(hwnd, NULL); + HDESK desktop = GetThreadDesktop(threadID); + if (desktop != NULL && desktop != info->m_desktop) { + // stop enumerating -- wrong desktop + return FALSE; + } + } + + // found a window + info->m_window = hwnd; + + // don't need to enumerate further + return FALSE; +} + +BOOL CALLBACK +CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) +{ + if (IsWindowVisible(hwnd)) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + *reinterpret_cast(arg) = true; + } + return TRUE; +} + +DWORD +CMSWindowsScreenSaver::findScreenSaver() +{ + // try windows 95 way + HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); + + // get process ID of process that owns the window, if found + if (hwnd != NULL) { + DWORD processID; + GetWindowThreadProcessId(hwnd, &processID); + return processID; + } + + // not found + return 0; +} + +void +CMSWindowsScreenSaver::watchDesktop() +{ + // stop watching previous process/desktop + unwatchProcess(); + + // watch desktop in another thread + LOG((CLOG_DEBUG "watching screen saver desktop")); + m_watch = new CThread(new TMethodJob(this, + &CMSWindowsScreenSaver::watchDesktopThread)); +} + +void +CMSWindowsScreenSaver::watchProcess(HANDLE process) +{ + // stop watching previous process/desktop + unwatchProcess(); + + // watch new process in another thread + if (process != NULL) { + LOG((CLOG_DEBUG "watching screen saver process")); + m_process = process; + m_watch = new CThread(new TMethodJob(this, + &CMSWindowsScreenSaver::watchProcessThread)); + } +} + +void +CMSWindowsScreenSaver::unwatchProcess() +{ + if (m_watch != NULL) { + LOG((CLOG_DEBUG "stopped watching screen saver process/desktop")); + m_watch->cancel(); + m_watch->wait(); + delete m_watch; + m_watch = NULL; + } + if (m_process != NULL) { + CloseHandle(m_process); + m_process = NULL; + } +} + +void +CMSWindowsScreenSaver::watchDesktopThread(void*) +{ + DWORD reserved = 0; + TCHAR* name = NULL; + + for (;;) { + // wait a bit + ARCH->sleep(0.2); + + // get current desktop + HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ); + if (desk == NULL) { + // can't open desktop so keep waiting + continue; + } + + // get current desktop name length + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + + // allocate more space for the name, if necessary + if (size > reserved) { + reserved = size; + name = (TCHAR*)alloca(reserved + sizeof(TCHAR)); + } + + // get current desktop name + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + + // compare name to screen saver desktop name + if (_tcsicmp(name, TEXT("Screen-saver")) == 0) { + // still the screen saver desktop so keep waiting + continue; + } + + // send screen saver deactivation message + PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); + return; + } +} + +void +CMSWindowsScreenSaver::watchProcessThread(void*) +{ + for (;;) { + CThread::testCancel(); + if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) { + // process terminated + LOG((CLOG_DEBUG "screen saver died")); + + // send screen saver deactivation message + PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); + return; + } + } +} diff --git a/lib/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h new file mode 100644 index 0000000..b4c5fea --- /dev/null +++ b/lib/platform/CMSWindowsScreenSaver.h @@ -0,0 +1,82 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSCREENSAVER_H +#define CMSWINDOWSSCREENSAVER_H + +#include "IScreenSaver.h" +#define WIN32_LEAN_AND_MEAN +#include + +class CThread; + +//! Microsoft windows screen saver implementation +class CMSWindowsScreenSaver : public IScreenSaver { +public: + CMSWindowsScreenSaver(); + virtual ~CMSWindowsScreenSaver(); + + //! @name manipulators + //@{ + + //! Check if screen saver started + /*! + Check if the screen saver really started. Returns false if it + hasn't, true otherwise. When the screen saver stops, \c msg will + be posted to the current thread's message queue with the given + parameters. + */ + bool checkStarted(UINT msg, WPARAM, LPARAM); + + //@} + + // IScreenSaver overrides + virtual void enable(); + virtual void disable(); + virtual void activate(); + virtual void deactivate(); + virtual bool isActive() const; + +private: + class CFindScreenSaverInfo { + public: + HDESK m_desktop; + HWND m_window; + }; + + static BOOL CALLBACK findScreenSaverFunc(HWND hwnd, LPARAM lParam); + static BOOL CALLBACK killScreenSaverFunc(HWND hwnd, LPARAM lParam); + + DWORD findScreenSaver(); + void watchDesktop(); + void watchProcess(HANDLE process); + void unwatchProcess(); + void watchDesktopThread(void*); + void watchProcessThread(void*); + +private: + bool m_is95Family; + bool m_is95; + bool m_isNT; + BOOL m_wasEnabled; + + HANDLE m_process; + CThread* m_watch; + DWORD m_threadID; + UINT m_msg; + WPARAM m_wParam; + LPARAM m_lParam; +}; + +#endif diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp new file mode 100644 index 0000000..cc61733 --- /dev/null +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -0,0 +1,1604 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsSecondaryScreen.h" +#include "CMSWindowsScreen.h" +#include "XScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "CArchMiscWindows.h" +#include + +// these are only defined when WINVER >= 0x0500 +#if !defined(SPI_GETMOUSESPEED) +#define SPI_GETMOUSESPEED 112 +#endif +#if !defined(SPI_SETMOUSESPEED) +#define SPI_SETMOUSESPEED 113 +#endif + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// multimedia keys +#if !defined(VK_BROWSER_BACK) +#define VK_BROWSER_BACK 0xA6 +#define VK_BROWSER_FORWARD 0xA7 +#define VK_BROWSER_REFRESH 0xA8 +#define VK_BROWSER_STOP 0xA9 +#define VK_BROWSER_SEARCH 0xAA +#define VK_BROWSER_FAVORITES 0xAB +#define VK_BROWSER_HOME 0xAC +#define VK_VOLUME_MUTE 0xAD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0xB0 +#define VK_MEDIA_PREV_TRACK 0xB1 +#define VK_MEDIA_STOP 0xB2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define VK_LAUNCH_MAIL 0xB4 +#define VK_LAUNCH_MEDIA_SELECT 0xB5 +#define VK_LAUNCH_APP1 0xB6 +#define VK_LAUNCH_APP2 0xB7 +#endif + +// +// CMSWindowsSecondaryScreen +// + +// a list of modifier key info +const CMSWindowsSecondaryScreen::CModifierInfo + CMSWindowsSecondaryScreen::s_modifier[] = { + { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, + { KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false }, + { KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false }, + // note -- no keys for KeyModifierMeta + { KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, + { KeyModifierCapsLock, VK_CAPITAL, 0, true }, + { KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true }, + { KeyModifierScrollLock,VK_SCROLL, 0, true } +}; + +CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( + IScreenReceiver* receiver) : + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_window(NULL), + m_mask(0) +{ + m_screen = new CMSWindowsScreen(receiver, this); +} + +CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() +{ + assert(m_window == NULL); + + delete m_screen; +} + +void +CMSWindowsSecondaryScreen::keyDown(KeyID key, + KeyModifierMask mask, KeyButton button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + synthesizeCtrlAltDel(); + return; + } + + // get the sequence of keys to simulate key press and the final + // modifier state. + Keystrokes keys; + UINT virtualKey; + m_mask = mapKey(keys, virtualKey, key, mask, kPress); + if (keys.empty()) { + // do nothing if there are no associated keys (i.e. lookup failed) + return; + } + + // generate key events + doKeystrokes(keys, 1); + + // do not record button down if button or virtual key is 0 (invalid) + if (button != 0 && virtualKey != 0) { + // note that key is now down + m_serverKeyMap[button] = virtualKey; + m_keys[virtualKey] |= 0x80; + m_fakeKeys[virtualKey] |= 0x80; + switch (virtualKey) { + case VK_LSHIFT: + case VK_RSHIFT: + m_keys[VK_SHIFT] |= 0x80; + m_fakeKeys[VK_SHIFT] |= 0x80; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + m_keys[VK_CONTROL] |= 0x80; + m_fakeKeys[VK_CONTROL] |= 0x80; + break; + + case VK_LMENU: + case VK_RMENU: + m_keys[VK_MENU] |= 0x80; + m_fakeKeys[VK_MENU] |= 0x80; + break; + } + } +} + +void +CMSWindowsSecondaryScreen::keyRepeat(KeyID key, + KeyModifierMask mask, SInt32 count, KeyButton button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + Keystrokes keys; + UINT virtualKey; + m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); + if (keys.empty()) { + return; + } + + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (virtualKey != index->second) { + // replace key up with previous keycode but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_virtualKey == virtualKey) { + index2->m_virtualKey = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] = false; + m_fakeKeys[index->second] = false; + + // map server key to new key + index->second = virtualKey; + + // note that new key is now down + m_keys[index->second] = true; + m_fakeKeys[index->second] = true; + } + + // generate key events + doKeystrokes(keys, count); +} + +void +CMSWindowsSecondaryScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + UINT virtualKey = index->second; + + // get the sequence of keys to simulate key release and the final + // modifier state. + Keystrokes keys; + m_mask = mapKeyRelease(keys, virtualKey); + + // generate key events + doKeystrokes(keys, 1); + + // note that key is now up + m_serverKeyMap.erase(index); + m_keys[virtualKey] &= ~0x80; + m_fakeKeys[virtualKey] &= ~0x80; + switch (virtualKey) { + case VK_LSHIFT: + if ((m_keys[VK_RSHIFT] & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + m_fakeKeys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_RSHIFT: + if ((m_keys[VK_LSHIFT] & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + m_fakeKeys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_LCONTROL: + if ((m_keys[VK_RCONTROL] & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + m_fakeKeys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_RCONTROL: + if ((m_keys[VK_LCONTROL] & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + m_fakeKeys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_LMENU: + if ((m_keys[VK_RMENU] & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + m_fakeKeys[VK_MENU] &= ~0x80; + } + break; + + case VK_RMENU: + if ((m_keys[VK_LMENU] & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + m_fakeKeys[VK_MENU] &= ~0x80; + } + break; + } +} + +void +CMSWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // map button id to button flag + DWORD data; + DWORD flags = mapButton(button, true, &data); + + // send event + if (flags != 0) { + mouse_event(flags, 0, 0, data, 0); + } +} + +void +CMSWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + + // map button id to button flag + DWORD data; + DWORD flags = mapButton(button, false, &data); + + // send event + if (flags != 0) { + mouse_event(flags, 0, 0, data, 0); + } +} + +void +CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + warpCursor(x, y); +} + +void +CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) +{ + CLock lock(&m_mutex); + m_screen->syncDesktop(); + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); +} + +void +CMSWindowsSecondaryScreen::resetOptions() +{ + CSecondaryScreen::resetOptions(); +} + +void +CMSWindowsSecondaryScreen::setOptions(const COptionsList& options) +{ + CSecondaryScreen::setOptions(options); +} + +IScreen* +CMSWindowsSecondaryScreen::getScreen() const +{ + return m_screen; +} + +void +CMSWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + +bool +CMSWindowsSecondaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CMSWindowsSecondaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_ACTIVATEAPP: + if (msg.wParam == FALSE) { + // some other app activated. hide the hider window. + ShowWindow(m_window, SW_HIDE); + } + break; + } + + return false; +} + +void +CMSWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) +{ + // ignore +} + +SInt32 +CMSWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void +CMSWindowsSecondaryScreen::postCreateWindow(HWND window) +{ + m_window = window; + + // update key state + updateKeys(); + + // hide cursor if this screen isn't active + if (!isActive()) { + SInt32 x, y; + getScreen()->getCursorCenter(x, y); + showWindow(x, y); + } +} + +void +CMSWindowsSecondaryScreen::preDestroyWindow(HWND) +{ + // do nothing +} + +void +CMSWindowsSecondaryScreen::onAccessibleDesktop() +{ + // get the current keyboard state + updateKeys(); +} + +void +CMSWindowsSecondaryScreen::onPreMainLoop() +{ + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::onPreOpen() +{ + assert(m_window == NULL); +} + +void +CMSWindowsSecondaryScreen::onPreEnter() +{ + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::onPreLeave() +{ + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } +} + +void +CMSWindowsSecondaryScreen::destroyWindow() +{ + // release keys that are logically pressed + releaseKeys(); + + // close the desktop and the window + m_screen->closeDesktop(); + m_window = NULL; +} + +void +CMSWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) +{ + // move hider window under the given position + MoveWindow(m_window, x, y, 1, 1, FALSE); + + // raise and show the hider window + ShowWindow(m_window, SW_SHOWNA); + + // now warp the mouse + warpCursor(x, y); +} + +void +CMSWindowsSecondaryScreen::hideWindow() +{ + ShowWindow(m_window, SW_HIDE); +} + +void +CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_screen->isMultimon(); + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + } + + // move the mouse directly to target position if motion is simple + if (simple) { + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65536.0 * x) / w), + (DWORD)((65536.0 * y) / h), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back a forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. + // + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // get current mouse position + POINT pos; + GetCursorPos(&pos); + + // move relative to mouse position + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsSecondaryScreen::updateKeys() +{ + // clear key state + memset(m_keys, 0, sizeof(m_keys)); + memset(m_fakeKeys, 0, sizeof(m_keys)); + + // we only care about the modifier key states + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); + + // copy over lock states to m_fakeKeys + m_fakeKeys[VK_CAPITAL] = static_cast(m_keys[VK_CAPITAL] & 0x01); + m_fakeKeys[VK_NUMLOCK] = static_cast(m_keys[VK_NUMLOCK] & 0x01); + m_fakeKeys[VK_SCROLL] = static_cast(m_keys[VK_SCROLL] & 0x01); + + // update active modifier mask + m_mask = 0; + if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { + m_mask |= KeyModifierShift; + } + if ((m_keys[VK_LCONTROL] & 0x80) != 0 || + (m_keys[VK_RCONTROL] & 0x80) != 0) { + m_mask |= KeyModifierControl; + } + if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { + m_mask |= KeyModifierAlt; + } + // note -- no keys for KeyModifierMeta + if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { + m_mask |= KeyModifierSuper; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + m_mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + m_mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + m_mask |= KeyModifierScrollLock; + } + // note -- do not save KeyModifierModeSwitch in m_mask + LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); +} + +void +CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) +{ + // toggle modifiers that don't match the desired state + if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { + toggleKey(VK_CAPITAL, KeyModifierCapsLock); + } + if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { + toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); + } + if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { + toggleKey(VK_SCROLL, KeyModifierScrollLock); + } +} + +KeyModifierMask +CMSWindowsSecondaryScreen::getToggleState() const +{ + return (m_mask & (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); +} + +// map special KeyID keys to virtual key codes. if the key is an +// extended key then the entry is the virtual key code | 0x100. +// unmapped keys have a 0 entry. +static const UINT g_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, VK_BROWSER_BACK|0x100, VK_BROWSER_FORWARD|0x100, + /* 0xa8 */ VK_BROWSER_REFRESH|0x100, VK_BROWSER_STOP|0x100, + /* 0xaa */ VK_BROWSER_SEARCH|0x100, VK_BROWSER_FAVORITES|0x100, + /* 0xac */ VK_BROWSER_HOME|0x100, VK_VOLUME_MUTE|0x100, + /* 0xae */ VK_VOLUME_DOWN|0x100, VK_VOLUME_UP|0x100, + /* 0xb0 */ VK_MEDIA_NEXT_TRACK|0x100, VK_MEDIA_PREV_TRACK|0x100, + /* 0xb2 */ VK_MEDIA_STOP|0x100, VK_MEDIA_PLAY_PAUSE|0x100, + /* 0xb4 */ VK_LAUNCH_MAIL|0x100, VK_LAUNCH_MEDIA_SELECT|0x100, + /* 0xb6 */ VK_LAUNCH_APP1|0x100, VK_LAUNCH_APP2|0x100, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_mapEE00[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_mapEF00[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, + /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, + /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100, + /* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100, + /* 0x64 */ 0, 0, 0, VK_APPS|0x100, + /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK|0x100, + /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, + /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, + /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, + /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, + /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, + /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, + /* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, + /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, + /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, + /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0, + /* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100, + /* 0xec */ VK_RWIN|0x100, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 +}; + +DWORD +CMSWindowsSecondaryScreen::mapButton(ButtonID button, + bool press, DWORD* inData) const +{ + DWORD dummy; + DWORD* data = (inData != NULL) ? inData : &dummy; + + // the system will swap the meaning of left/right for us if + // the user has configured a left-handed mouse but we don't + // want it to swap since we want the handedness of the + // server's mouse. so pre-swap for a left-handed mouse. + if (GetSystemMetrics(SM_SWAPBUTTON)) { + switch (button) { + case kButtonLeft: + button = kButtonRight; + break; + + case kButtonRight: + button = kButtonLeft; + break; + } + } + + // map button id to button flag and button data + *data = 0; + switch (button) { + case kButtonLeft: + return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; + + case kButtonMiddle: + return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; + + case kButtonRight: + return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + + case kButtonExtra0 + 0: + *data = XBUTTON1; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + + case kButtonExtra0 + 1: + *data = XBUTTON2; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + + default: + return 0; + } +} + +KeyModifierMask +CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, + KeyID id, KeyModifierMask mask, EKeyAction action) const +{ + virtualKey = 0; + + // check for special keys + if ((id & 0xfffff000) == 0xe000) { + if ((id & 0xff00) == 0xe000) { + virtualKey = g_mapE000[id & 0xff]; + } + else if ((id & 0xff00) == 0xee00) { + virtualKey = g_mapEE00[id & 0xff]; + } + else if ((id & 0xff00) == 0xef00) { + virtualKey = g_mapEF00[id & 0xff]; + } + if (virtualKey == 0) { + LOG((CLOG_DEBUG2 "unknown special key")); + return m_mask; + } + } + + // special handling of VK_SNAPSHOT + if ((virtualKey & 0xff) == VK_SNAPSHOT) { + // ignore key repeats on print screen + if (action != kRepeat) { + // get event flags + DWORD flags = 0; + if (isExtendedKey(virtualKey)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (action != kPress) { + flags |= KEYEVENTF_KEYUP; + } + + // active window or fullscreen? + BYTE scan = 0; + if ((mask & KeyModifierAlt) == 0) { + scan = 1; + } + + // send event + keybd_event(static_cast(virtualKey & 0xff), scan, flags, 0); + } + return m_mask; + } + + // handle other special keys + if (virtualKey != 0) { + // compute the final desired modifier mask. special keys use + // the desired modifiers as given except we keep the caps lock, + // num lock, and scroll lock as is. + KeyModifierMask outMask = (m_mask & + (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); + outMask |= (mask & + (KeyModifierShift | + KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper)); + + // strip out extended key flag + UINT virtualKey2 = (virtualKey & ~0x100); + + // check numeric keypad. note that virtual keys do not distinguish + // between the keypad and non-keypad movement keys. however, the + // virtual keys do distinguish between keypad numbers and operators + // (e.g. add, multiply) and their main keyboard counterparts. + // therefore, we can ignore the num-lock state for movement virtual + // keys but not for numeric keys. + if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { + // set required shift state based on current numlock state + if ((outMask & KeyModifierNumLock) == 0) { + if ((m_mask & KeyModifierNumLock) == 0) { + LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); + outMask |= KeyModifierNumLock; + } + else { + LOG((CLOG_DEBUG2 "turn on shift for keypad key")); + outMask |= KeyModifierShift; + } + } + } + + // check for left tab. that requires the shift key. + if (id == kKeyLeftTab) { + mask |= KeyModifierShift; + } + + // now generate the keystrokes and return the resulting modifier mask + LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); + return mapToKeystrokes(keys, virtualKey, m_mask, outMask, action); + } + + // determine the thread that'll receive this event + // FIXME -- we can't be sure we'll get the right thread here + HWND targetWindow = GetForegroundWindow(); + DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL); + + // figure out the code page for the target thread. i'm just + // guessing here. get the target thread's keyboard layout, + // extract the language id from that, and choose the code page + // based on that language. + HKL hkl = GetKeyboardLayout(targetThread); + LANGID langID = static_cast(LOWORD(hkl)); + UINT codePage = getCodePageFromLangID(langID); + LOG((CLOG_DEBUG2 "using code page %d and language id 0x%04x for thread 0x%08x", codePage, langID, targetThread)); + + // regular characters are complicated by dead keys. it may not be + // possible to generate a desired character directly. we may need + // to generate a dead key first then some other character. the + // app receiving the events will compose these two characters into + // a single precomposed character. + // + // as best as i can tell this is the simplest way to convert a + // character into its uncomposed version. along the way we'll + // discover if the key cannot be handled at all. we convert + // from wide char to multibyte, then from multibyte to wide char + // forcing conversion to composite characters, then from wide + // char back to multibyte without making precomposed characters. + // + // after the first conversion to multibyte we see if we can map + // the key. if so then we don't bother trying to decompose dead + // keys. + BOOL error; + char multiByte[2 * MB_LEN_MAX]; + wchar_t unicode[2]; + unicode[0] = static_cast(id & 0x0000ffff); + int nChars = WideCharToMultiByte(codePage, + WC_COMPOSITECHECK | WC_DEFAULTCHAR, + unicode, 1, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); + return m_mask; + } + virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + if (virtualKey != static_cast(-1)) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + if ((MapVirtualKey(virtualKey, 2) & 0x80000000u) != 0) { + // it looks like this character is a dead key but + // MapVirtualKey() will claim it's a dead key even if it's + // not (though i don't think it ever claims it's not when + // it is). we need a backup test to ensure that this is + // really a dead key. we could use ToAscii() for this but + // that keeps state and it's a hassle to restore that state. + // OemKeyScan() appears to do the trick. if the character + // cannot be generated with a single keystroke then it + // returns 0xffffffff. + if (OemKeyScan(multiByte[0]) != 0xffffffffu) { + // character mapped to a dead key but we want the + // character for real so send a space key afterwards. + LOG((CLOG_DEBUG2 "character mapped to dead key")); + Keystroke keystroke; + keystroke.m_virtualKey = VK_SPACE; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + + // ignore the release of this key since we already + // handled it in mapCharacter(). + virtualKey = 0; + } + } + return m_mask; + } + nChars = MultiByteToWideChar(codePage, + MB_COMPOSITE | MB_ERR_INVALID_CHARS, + multiByte, nChars, + unicode, 2); + if (nChars == 0) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); + return m_mask; + } + nChars = WideCharToMultiByte(codePage, + 0, + unicode, nChars, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); + return m_mask; + } + + // we expect one or two characters in multiByte. if there are two + // then the *second* is a dead key. process the dead key if there. + // FIXME -- we assume each character is one byte here + if (nChars > 2) { + LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id)); + return m_mask; + } + if (nChars == 2) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); + mapCharacter(keys, multiByte[1], hkl, m_mask, mask, action); + } + + // process character + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + + // non-special key cannot modify the modifier mask + return m_mask; +} + +KeyModifierMask +CMSWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, + UINT virtualKey) const +{ + // add key release + Keystroke keystroke; + keystroke.m_virtualKey = virtualKey; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // if this is a modifier keycode then update the current modifier mask + const CModifierInfo* modifier = getModifierInfo(virtualKey); + if (modifier != NULL) { + if (modifier->m_isToggle) { + // toggle keys modify the state on release + return (m_mask ^ modifier->m_mask); + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any (except virtualKey) are + // pressed. + bool down = false; + if (virtualKey != (modifier->m_virtualKey & 0xff) && + (m_keys[modifier->m_virtualKey & 0xff] & 0x80) != 0) { + down = true; + } + if (modifier->m_virtualKey2 != 0 && + virtualKey != (modifier->m_virtualKey2 & 0xff) && + (m_keys[modifier->m_virtualKey2 & 0xff] & 0x80) != 0) { + down = true; + } + if (!down) { + return (m_mask & ~modifier->m_mask); + } + } + } + + return m_mask; +} + +UINT +CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, + char c, HKL hkl, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const +{ + // translate the character into its virtual key and its required + // modifier state. + SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + + // get virtual key + UINT virtualKey = LOBYTE(virtualKeyAndModifierState); + if (LOBYTE(virtualKeyAndModifierState) == 0xff) { + LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); + return static_cast(-1); + } + + // get the required modifier state + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + + // compute the final desired modifier mask. this is the + // desired modifier mask except that the system might require + // that certain modifiers be up or down in order to generate + // the character. to start with, we know that we want to keep + // the caps lock, num lock, scroll lock modifiers as is. also, + // the system never requires the meta or super modifiers so we + // can set those however we like. + KeyModifierMask outMask = (currentMask & + (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); + outMask |= (desiredMask & + (KeyModifierMeta | + KeyModifierSuper)); + + // win32 does not permit ctrl and alt used together to + // modify a character because ctrl and alt together mean + // AltGr. if the desired mask has both ctrl and alt then + // strip them both out. + if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + desiredMask &= ~(KeyModifierControl | KeyModifierAlt); + } + + // strip out the desired shift state. we're forced to use + // a particular shift state to generate the desired character. + outMask &= ~KeyModifierShift; + + // use the required modifiers. if AltGr is required then + // modifierState will indicate control and alt. + if ((modifierState & 1) != 0) { + outMask |= KeyModifierShift; + } + if ((modifierState & 2) != 0) { + outMask |= KeyModifierControl; + } + if ((modifierState & 4) != 0) { + outMask |= KeyModifierAlt; + } + + // use desired modifiers + if ((desiredMask & KeyModifierControl) != 0) { + outMask |= KeyModifierControl; + } + if ((desiredMask & KeyModifierAlt) != 0) { + outMask |= KeyModifierAlt; + } + + // handle combination of caps-lock and shift. if caps-lock is + // off locally then use shift as necessary. if caps-lock is on + // locally then it reverses the meaning of shift for keys that + // are subject to case conversion. + if ((outMask & KeyModifierCapsLock) != 0) { + // there doesn't seem to be a simple way to test if a + // character respects the caps lock key. for normal + // characters it's easy enough but CharLower() and + // CharUpper() don't map dead keys even though they + // do respect caps lock for some unfathomable reason. + // first check the easy way. if that doesn't work + // then see if it's a dead key. + unsigned char uc = static_cast(c); + if (CharLower((LPTSTR)uc) != CharUpper((LPTSTR)uc) || + (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + LOG((CLOG_DEBUG2 "flip shift")); + outMask ^= KeyModifierShift; + } + } + + // now generate the keystrokes. ignore the resulting modifier + // mask since it can't have changed (because we don't call this + // method for modifier keys). + LOG((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", (unsigned char)c, virtualKey, outMask)); + mapToKeystrokes(keys, virtualKey, currentMask, outMask, action); + + return virtualKey; +} + +KeyModifierMask +CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, + UINT virtualKey, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const +{ + const CModifierInfo* modifier = getModifierInfo(virtualKey); + + // add the key events required to get to the desired modifier state. + // also save the key events required to restore the current state. + // if the key is a modifier key then skip this because modifiers + // should not modify modifiers. + Keystrokes undo; + Keystroke keystroke; + if (desiredMask != currentMask && modifier == NULL) { + const unsigned int s_numModifiers = sizeof(s_modifier) / + sizeof(s_modifier[0]); + for (unsigned int i = 0; i < s_numModifiers; ++i) { + KeyModifierMask bit = s_modifier[i].m_mask; + if ((desiredMask & bit) != (currentMask & bit)) { + if ((desiredMask & bit) != 0) { + // modifier is not active but should be. if the + // modifier is a toggle then toggle it on with a + // press/release, otherwise activate it with a + // press. + keystroke.m_virtualKey = s_modifier[i].m_virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + if (s_modifier[i].m_isToggle) { + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + else { + keystroke.m_press = false; + undo.push_back(keystroke); + } + } + + else { + // modifier is active but should not be. if the + // modifier is a toggle then toggle it off with a + // press/release, otherwise deactivate it with a + // release. we must check each keycode for the + // modifier if not a toggle. + if (s_modifier[i].m_isToggle) { + keystroke.m_virtualKey = s_modifier[i].m_virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + else { + UINT key = s_modifier[i].m_virtualKey; + if ((m_keys[key & 0xff] & 0x80) != 0) { + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + key = s_modifier[i].m_virtualKey2; + if (key != 0 && (m_keys[key & 0xff] & 0x80) != 0) { + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + } + } + } + } + } + + // add the key event + keystroke.m_virtualKey = virtualKey; + switch (action) { + case kPress: + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRelease: + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRepeat: + keystroke.m_press = true; + keystroke.m_repeat = true; + keys.push_back(keystroke); + break; + } + + // if this is a dead key press then send a release immediately. + // the dead key may not be processed correctly if its release + // event comes after we release the modifiers. + if (action == kPress && + (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + keystroke.m_press = false; + keys.push_back(keystroke); + } + + // add key events to restore the modifier state. apply events in + // the reverse order that they're stored in undo. + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + // if the key is a modifier key then compute the modifier mask after + // this key is pressed. toggle keys modify the state on release. + // other keys set the modifier bit on press. + KeyModifierMask mask = currentMask; + if (action == kPress) { + if (modifier != NULL && !modifier->m_isToggle) { + mask |= modifier->m_mask; + } + } + + LOG((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", currentMask, mask)); + return mask; +} + +void +CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) { + return; + } + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + sendKeyEvent(k->m_virtualKey, k->m_press); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + sendKeyEvent(k->m_virtualKey, k->m_press); + + // next key + ++k; + } + } +} + +const CMSWindowsSecondaryScreen::CModifierInfo* +CMSWindowsSecondaryScreen::getModifierInfo(UINT virtualKey) const +{ + // note if the key is a modifier. strip out extended key flag from + // virtual key before lookup. + switch (virtualKey & ~0x100) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + return s_modifier + 0; + + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + return s_modifier + 1; + + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + return s_modifier + 2; + + case VK_LWIN: + case VK_RWIN: + return s_modifier + 3; + + case VK_CAPITAL: + return s_modifier + 4; + + case VK_NUMLOCK: + return s_modifier + 5; + + case VK_SCROLL: + return s_modifier + 6; + + default: + return NULL; + } +} + +void +CMSWindowsSecondaryScreen::releaseKeys() +{ + // release keys that we've synthesized a press for and only those + // keys. we don't want to synthesize a release on a key the user + // is still physically pressing. + + CLock lock(&m_mutex); + + m_screen->syncDesktop(); + + // release left/right modifier keys first. if the platform doesn't + // support them then they won't be set and the non-side-distinuishing + // key will retain its state. if the platform does support them then + // the non-side-distinguishing will be reset. + if ((m_fakeKeys[VK_LSHIFT] & 0x80) != 0) { + sendKeyEvent(VK_LSHIFT, false); + m_fakeKeys[VK_SHIFT] = 0; + m_fakeKeys[VK_LSHIFT] = 0; + } + if ((m_fakeKeys[VK_RSHIFT] & 0x80) != 0) { + sendKeyEvent(VK_RSHIFT, false); + m_fakeKeys[VK_SHIFT] = 0; + m_fakeKeys[VK_RSHIFT] = 0; + } + if ((m_fakeKeys[VK_LCONTROL] & 0x80) != 0) { + sendKeyEvent(VK_LCONTROL, false); + m_fakeKeys[VK_CONTROL] = 0; + m_fakeKeys[VK_LCONTROL] = 0; + } + if ((m_fakeKeys[VK_RCONTROL] & 0x80) != 0) { + sendKeyEvent(VK_RCONTROL, false); + m_fakeKeys[VK_CONTROL] = 0; + m_fakeKeys[VK_RCONTROL] = 0; + } + if ((m_fakeKeys[VK_LMENU] & 0x80) != 0) { + sendKeyEvent(VK_LMENU, false); + m_fakeKeys[VK_MENU] = 0; + m_fakeKeys[VK_LMENU] = 0; + } + if ((m_fakeKeys[VK_RMENU] & 0x80) != 0) { + sendKeyEvent(VK_RMENU, false); + m_fakeKeys[VK_MENU] = 0; + m_fakeKeys[VK_RMENU] = 0; + } + + // now check all the other keys + for (UInt32 i = 0; i < sizeof(m_fakeKeys) / sizeof(m_fakeKeys[0]); ++i) { + if ((m_fakeKeys[i] & 0x80) != 0) { + sendKeyEvent(i, false); + m_fakeKeys[i] = 0; + } + } +} + +void +CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) +{ + // send key events to simulate a press and release + sendKeyEvent(virtualKey, true); + sendKeyEvent(virtualKey, false); + + // toggle shadow state + m_mask ^= mask; + m_keys[virtualKey & 0xff] ^= 0x01; + m_fakeKeys[virtualKey & 0xff] ^= 0x01; +} + +UINT +CMSWindowsSecondaryScreen::virtualKeyToScanCode(UINT& virtualKey) const +{ + // try mapping given virtual key + UINT code = MapVirtualKey(virtualKey & 0xff, 0); + if (code != 0) { + return code; + } + + // no dice. if the virtual key distinguishes between left/right + // then try the one that doesn't distinguish sides. windows (or + // keyboard drivers) are inconsistent in their treatment of these + // virtual keys. the following behaviors have been observed: + // + // win2k (gateway desktop): + // MapVirtualKey(vk, 0): + // VK_SHIFT == VK_LSHIFT != VK_RSHIFT + // VK_CONTROL == VK_LCONTROL == VK_RCONTROL + // VK_MENU == VK_LMENU == VK_RMENU + // MapVirtualKey(sc, 3): + // VK_LSHIFT and VK_RSHIFT mapped independently + // VK_LCONTROL is mapped but not VK_RCONTROL + // VK_LMENU is mapped but not VK_RMENU + // + // win me (sony vaio laptop): + // MapVirtualKey(vk, 0): + // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped + // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped + // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped + // MapVirtualKey(sc, 3): + // all scan codes unmapped (function apparently unimplemented) + switch (virtualKey & 0xff) { + case VK_LSHIFT: + case VK_RSHIFT: + virtualKey = VK_SHIFT; + return MapVirtualKey(VK_SHIFT, 0); + + case VK_LCONTROL: + case VK_RCONTROL: + virtualKey = VK_CONTROL; + return MapVirtualKey(VK_CONTROL, 0); + + case VK_LMENU: + case VK_RMENU: + virtualKey = VK_MENU; + return MapVirtualKey(VK_MENU, 0); + + default: + return 0; + } +} + +bool +CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) const +{ + // see if we've already encoded the extended flag + if ((virtualKey & 0x100) != 0) { + return true; + } + + // check known virtual keys + switch (virtualKey & 0xff) { + case VK_NUMLOCK: + case VK_RCONTROL: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + return true; + + default: + return false; + } +} + +void +CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) +{ + DWORD flags = 0; + if (isExtendedKey(virtualKey)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (!press) { + flags |= KEYEVENTF_KEYUP; + } + const UINT code = virtualKeyToScanCode(virtualKey); + keybd_event(static_cast(virtualKey & 0xff), + static_cast(code), flags, 0); + LOG((CLOG_DEBUG1 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); +} + +UINT +CMSWindowsSecondaryScreen::getCodePageFromLangID(LANGID langid) const +{ + // construct a locale id from the language id + LCID lcid = MAKELCID(langid, SORT_DEFAULT); + + // get the ANSI code page for this locale + char data[6]; + if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, data, 6) == 0) { + // can't get code page + LOG((CLOG_DEBUG1 "can't find code page for langid 0x%04x", langid)); + return CP_ACP; + } + + // convert stringified code page into a number + UINT codePage = static_cast(atoi(data)); + if (codePage == 0) { + // parse failed + LOG((CLOG_DEBUG1 "can't parse code page %s for langid 0x%04x", data, langid)); + return CP_ACP; + } + + return codePage; +} + +void +CMSWindowsSecondaryScreen::synthesizeCtrlAltDel() +{ + LOG((CLOG_DEBUG "emulating ctrl+alt+del")); + if (!m_is95Family) { + // to fake ctrl+alt+del on the NT family we broadcast a suitable + // hotkey to all windows on the winlogon desktop. however, we + // the current thread must be on that desktop to do the broadcast + // and we can't switch just any thread because some own windows + // or hooks. so start a new thread to do the real work. + CThread cad(new CFunctionJob( + &CMSWindowsSecondaryScreen::ctrlAltDelThread)); + cad.wait(); + } + else { + Keystrokes keys; + UINT virtualKey; + KeyID key = kKeyDelete; + KeyModifierMask mask = KeyModifierControl | KeyModifierAlt; + + // get the sequence of keys to simulate ctrl+alt+del + mapKey(keys, virtualKey, key, mask, kPress); + if (!keys.empty()) { + // generate key events + doKeystrokes(keys, 1); + } + } +} + +void +CMSWindowsSecondaryScreen::ctrlAltDelThread(void*) +{ + // get the Winlogon desktop at whatever privilege we can + HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); + if (desk != NULL) { + if (SetThreadDesktop(desk)) { + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, + MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE)); + } + else { + LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError())); + } + CloseDesktop(desk); + } + else { + LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError())); + } +} diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h new file mode 100644 index 0000000..ed1bdf9 --- /dev/null +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -0,0 +1,166 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSECONDARYSCREEN_H +#define CMSWINDOWSSECONDARYSCREEN_H + +// ensure that we get SendInput() +#if _WIN32_WINNT <= 0x400 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x401 +#endif + +#include "CSecondaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" +#include "CMutex.h" +#include "CString.h" +#include "stdmap.h" +#include "stdvector.h" + +class CMSWindowsScreen; +class IScreenReceiver; + +//! Microsoft windows secondary screen implementation +class CMSWindowsSecondaryScreen : + public CSecondaryScreen, public IMSWindowsScreenEventHandler { +public: + CMSWindowsSecondaryScreen(IScreenReceiver*); + virtual ~CMSWindowsSecondaryScreen(); + + // CSecondaryScreen overrides + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void mouseWheel(SInt32 delta); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual IScreen* getScreen() const; + + // IMSWindowsScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + virtual void onAccessibleDesktop(); + +protected: + // CSecondaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void createWindow(); + virtual void destroyWindow(); + virtual void showWindow(SInt32 x, SInt32 y); + virtual void hideWindow(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void updateKeys(); + virtual void releaseKeys(); + virtual void setToggleState(KeyModifierMask); + virtual KeyModifierMask getToggleState() const; + +private: + enum EKeyAction { kPress, kRelease, kRepeat }; + class Keystroke { + public: + UINT m_virtualKey; + bool m_press; + bool m_repeat; + }; + class CModifierInfo { + public: + KeyModifierMask m_mask; + UINT m_virtualKey; + UINT m_virtualKey2; + bool m_isToggle; + }; + typedef std::vector Keystrokes; + typedef std::map ServerKeyMap; + + // open/close desktop (for windows 95/98/me) + bool openDesktop(); + void closeDesktop(); + + // make desk the thread desktop (for windows NT/2000/XP) + bool switchDesktop(HDESK desk); + + // returns true iff there appear to be multiple monitors + bool isMultimon() const; + + // key and button queries and operations + DWORD mapButton(ButtonID button, + bool press, DWORD* data) const; + KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, + KeyModifierMask, EKeyAction) const; + KeyModifierMask mapKeyRelease(Keystrokes& keys, UINT virtualKey) const; + UINT mapCharacter(Keystrokes& keys, + char c, HKL hkl, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, + EKeyAction action) const; + KeyModifierMask mapToKeystrokes(Keystrokes& keys, + UINT virtualKey, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, + EKeyAction action) const; + void doKeystrokes(const Keystrokes&, SInt32 count); + const CModifierInfo* getModifierInfo(UINT virtualKey) const; + + void toggleKey(UINT virtualKey, KeyModifierMask mask); + UINT virtualKeyToScanCode(UINT& virtualKey) const; + bool isExtendedKey(UINT virtualKey) const; + void sendKeyEvent(UINT virtualKey, bool press); + + UINT getCodePageFromLangID(LANGID) const; + + // generate a fake ctrl+alt+del + void synthesizeCtrlAltDel(); + + // thread that generates fake ctrl+alt+del + static void ctrlAltDelThread(void*); + +private: + CMutex m_mutex; + CMSWindowsScreen* m_screen; + + // true if windows 95/98/me + bool m_is95Family; + + // our window + HWND m_window; + + // virtual key states as set by us or the user + BYTE m_keys[256]; + + // virtual key states as set by us + BYTE m_fakeKeys[256]; + + // current active modifiers + KeyModifierMask m_mask; + + // map server key buttons to local virtual keys + ServerKeyMap m_serverKeyMap; + + // modifier table + static const CModifierInfo s_modifier[]; +}; + +#endif diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp new file mode 100644 index 0000000..904d46b --- /dev/null +++ b/lib/platform/CSynergyHook.cpp @@ -0,0 +1,842 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CSynergyHook.h" +#include "ProtocolTypes.h" +#include + +// +// debugging compile flag. when not zero the server doesn't grab +// the keyboard when the mouse leaves the server screen. this +// makes it possible to use the debugger (via the keyboard) when +// all user input would normally be caught by the hook procedures. +// +#define NO_GRAB_KEYBOARD 0 + +// +// extra mouse wheel stuff +// + +enum EWheelSupport { + kWheelNone, + kWheelOld, + kWheelWin2000, + kWheelModern +}; + +// declare extended mouse hook struct. useable on win2k +typedef struct tagMOUSEHOOKSTRUCTWin2000 { + MOUSEHOOKSTRUCT mhs; + DWORD mouseData; +} MOUSEHOOKSTRUCTWin2000; + +#if !defined(SM_MOUSEWHEELPRESENT) +#define SM_MOUSEWHEELPRESENT 75 +#endif + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + + +// +// globals +// + +#pragma comment(linker, "-section:shared,rws") +#pragma data_seg("shared") +// all data in this shared section *must* be initialized + +static HINSTANCE g_hinstance = NULL; +static DWORD g_processID = 0; +static EWheelSupport g_wheelSupport = kWheelNone; +static UINT g_wmMouseWheel = 0; +static DWORD g_threadID = 0; +static HHOOK g_keyboard = NULL; +static HHOOK g_mouse = NULL; +static HHOOK g_getMessage = NULL; +static HANDLE g_hookThreadLL = NULL; +static DWORD g_hookThreadIDLL = 0; +static HANDLE g_hookEventLL = NULL; +static HHOOK g_keyboardLL = NULL; +static HHOOK g_mouseLL = NULL; +static bool g_screenSaver = false; +static bool g_relay = false; +static UInt32 g_zoneSides = 0; +static SInt32 g_zoneSize = 0; +static SInt32 g_xScreen = 0; +static SInt32 g_yScreen = 0; +static SInt32 g_wScreen = 0; +static SInt32 g_hScreen = 0; +static HCURSOR g_cursor = NULL; +static DWORD g_cursorThread = 0; + +#pragma data_seg() + +// keep linker quiet about floating point stuff. we don't use any +// floating point operations but our includes may define some +// (unused) floating point values. +#ifndef _DEBUG +extern "C" int _fltused=0; +#endif + + +// +// internal functions +// + +static +void +hideCursor(DWORD thread) +{ + // we should be running the context of the window who's cursor + // we want to hide so we shouldn't have to attach thread input + // but we'll check to make sure. + g_cursorThread = thread; + if (g_cursorThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, TRUE); + g_cursor = SetCursor(NULL); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, FALSE); + } +} + +static +void +restoreCursor() +{ + // restore the show cursor in the window we hid it last + if (g_cursor != NULL && g_cursorThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, TRUE); + SetCursor(g_cursor); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, FALSE); + } + g_cursor = NULL; + g_cursorThread = 0; +} + +static +bool +keyboardHookHandler(WPARAM wParam, LPARAM lParam) +{ + // forward message to our window. do this whether or not we're + // forwarding events to clients because this'll keep our thread's + // key state table up to date. that's important for querying + // the scroll lock toggle state. + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam); + + if (g_relay) { + // let certain keys pass through + switch (wParam) { + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // pass event on. we want to let these through to + // the window proc because otherwise the keyboard + // lights may not stay synchronized. + break; + + default: + // discard event + return true; + } + } + + return false; +} + +static +bool +mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) +{ + switch (wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: + // always relay the event. eat it if relaying. + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_BUTTON, wParam, data); + return g_relay; + + case WM_MOUSEWHEEL: + if (g_relay) { + // relay event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, data, 0); + } + return g_relay; + + case WM_NCMOUSEMOVE: + case WM_MOUSEMOVE: + if (g_relay) { + // we want the cursor to be hidden at all times so we + // hide the cursor on whatever window has it. but then + // we have to show the cursor whenever we leave that + // window (or at some later time before we stop relaying). + // so check the window with the cursor. if it's not the + // same window that had it before then show the cursor + // in the last window and hide it in this window. + DWORD thread = GetCurrentThreadId(); + if (thread != g_cursorThread) { + restoreCursor(); + hideCursor(thread); + } + + // relay and eat event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + return true; + } + else { + // check for mouse inside jump zone + bool inside = false; + if (!inside && (g_zoneSides & kLeftMask) != 0) { + inside = (x < g_xScreen + g_zoneSize); + } + if (!inside && (g_zoneSides & kRightMask) != 0) { + inside = (x >= g_xScreen + g_wScreen - g_zoneSize); + } + if (!inside && (g_zoneSides & kTopMask) != 0) { + inside = (y < g_yScreen + g_zoneSize); + } + if (!inside && (g_zoneSides & kBottomMask) != 0) { + inside = (y >= g_yScreen + g_hScreen - g_zoneSize); + } + + // relay the event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + + // if inside then eat the event + return inside; + } + } + + // pass the event + return false; +} + +static +LRESULT CALLBACK +keyboardHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // handle the message + if (keyboardHookHandler(wParam, lParam)) { + return 1; + } + } + + return CallNextHookEx(g_keyboard, code, wParam, lParam); +} + +static +LRESULT CALLBACK +mouseHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // decode message + const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + SInt32 w = 0; + if (wParam == WM_MOUSEWHEEL) { + // win2k and other systems supporting WM_MOUSEWHEEL in + // the mouse hook are gratuitously different (and poorly + // documented). if a low-level mouse hook is in place + // it should capture these events so we'll never see + // them. + switch (g_wheelSupport) { + case kWheelModern: + w = static_cast(LOWORD(info->dwExtraInfo)); + break; + + case kWheelWin2000: { + const MOUSEHOOKSTRUCTWin2000* info2k = + (const MOUSEHOOKSTRUCTWin2000*)lParam; + w = static_cast(HIWORD(info2k->mouseData)); + break; + } + } + } + + // handle the message. note that we don't handle X buttons + // here. that's okay because they're only supported on + // win2k and winxp and up and on those platforms we'll get + // get the mouse events through the low level hook. + if (mouseHookHandler(wParam, x, y, w)) { + return 1; + } + } + + return CallNextHookEx(g_mouse, code, wParam, lParam); +} + +static +LRESULT CALLBACK +getMessageHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_screenSaver) { + MSG* msg = reinterpret_cast(lParam); + if (msg->message == WM_SYSCOMMAND && + msg->wParam == SC_SCREENSAVE) { + // broadcast screen saver started message + PostThreadMessage(g_threadID, + SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); + } + } + if (g_relay) { + MSG* msg = reinterpret_cast(lParam); + if (msg->message == g_wmMouseWheel) { + // post message to our window + PostThreadMessage(g_threadID, + SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0); + + // zero out the delta in the message so it's (hopefully) + // ignored + msg->wParam = 0; + } + } + } + + return CallNextHookEx(g_getMessage, code, wParam, lParam); +} + +#if (_WIN32_WINNT >= 0x0400) + +// +// low-level keyboard hook -- this allows us to capture and handle +// alt+tab, alt+esc, ctrl+esc, and windows key hot keys. on the down +// side, key repeats are not reported to us. +// + +static +LRESULT CALLBACK +keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // decode the message + KBDLLHOOKSTRUCT* info = reinterpret_cast(lParam); + WPARAM wParam = info->vkCode; + LPARAM lParam = 1; // repeat code + lParam |= (info->scanCode << 16); // scan code + if (info->flags & LLKHF_EXTENDED) { + lParam |= (1lu << 24); // extended key + } + if (info->flags & LLKHF_ALTDOWN) { + lParam |= (1lu << 29); // context code + } + if (info->flags & LLKHF_UP) { + lParam |= (1lu << 31); // transition + } + // FIXME -- bit 30 should be set if key was already down but + // we don't know that info. as a result we'll never generate + // key repeat events. + + // handle the message + if (keyboardHookHandler(wParam, lParam)) { + return 1; + } + } + + return CallNextHookEx(g_keyboardLL, code, wParam, lParam); +} + +// +// low-level mouse hook -- this allows us to capture and handle mouse +// events very early. the earlier the better. +// + +static +LRESULT CALLBACK +mouseLLHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // decode the message + MSLLHOOKSTRUCT* info = reinterpret_cast(lParam); + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + SInt32 w = (SInt32)HIWORD(info->mouseData); + + // handle the message + if (mouseHookHandler(wParam, x, y, w)) { + return 1; + } + } + + return CallNextHookEx(g_mouseLL, code, wParam, lParam); +} + +static +DWORD WINAPI +getLowLevelProc(void*) +{ + // thread proc for low-level keyboard/mouse hooks. this does + // nothing but install the hook, process events, and uninstall + // the hook. + + // force this thread to have a message queue + MSG msg; + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + +#if !NO_GRAB_KEYBOARD + // install low-level keyboard hook + g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, + &keyboardLLHook, + g_hinstance, + 0); + if (g_keyboardLL == NULL) { + // indicate failure and exit + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); + return 1; + } +#else + // keep compiler quiet + &keyboardLLHook; +#endif + + // install low-level mouse hook + g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, + &mouseLLHook, + g_hinstance, + 0); + if (g_mouseLL == NULL) { + // indicate failure and exit + if (g_keyboardLL != NULL) { + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + } + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); + return 1; + } + + // ready + SetEvent(g_hookEventLL); + + // message loop + bool done = false; + while (!done) { + switch (GetMessage(&msg, NULL, 0, 0)) { + case -1: + break; + + case 0: + done = true; + break; + + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + } + + // uninstall hook + UnhookWindowsHookEx(g_mouseLL); + UnhookWindowsHookEx(g_keyboardLL); + g_mouseLL = NULL; + g_keyboardLL = NULL; + + return 0; +} + +#else // (_WIN32_WINNT < 0x0400) + +static +DWORD WINAPI +getLowLevelProc(void*) +{ + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); + return 1; +} + +#endif + +static +EWheelSupport +getWheelSupport() +{ + // get operating system + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + if (!GetVersionEx(&info)) { + return kWheelNone; + } + + // see if modern wheel is present + if (GetSystemMetrics(SM_MOUSEWHEELPRESENT)) { + // note if running on win2k + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT && + info.dwMajorVersion == 5 && + info.dwMinorVersion == 0) { + return kWheelWin2000; + } + return kWheelModern; + } + + // not modern. see if we've got old-style support. + UINT wheelSupportMsg = RegisterWindowMessage(MSH_WHEELSUPPORT); + HWND wheelSupportWindow = FindWindow(MSH_WHEELMODULE_CLASS, + MSH_WHEELMODULE_TITLE); + if (wheelSupportWindow != NULL && wheelSupportMsg != 0) { + if (SendMessage(wheelSupportWindow, wheelSupportMsg, 0, 0) != 0) { + g_wmMouseWheel = RegisterWindowMessage(MSH_MOUSEWHEEL); + if (g_wmMouseWheel != 0) { + return kWheelOld; + } + } + } + + // assume modern. we don't do anything special in this case + // except respond to WM_MOUSEWHEEL messages. GetSystemMetrics() + // can apparently return FALSE even if a mouse wheel is present + // though i'm not sure exactly when it does that (WinME returns + // FALSE for my logitech USB trackball). + return kWheelModern; +} + + +// +// external functions +// + +BOOL WINAPI +DllMain(HINSTANCE instance, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls(instance); + if (g_processID == 0) { + g_hinstance = instance; + g_processID = GetCurrentProcessId(); + } + } + else if (reason == DLL_PROCESS_DETACH) { + if (g_processID == GetCurrentProcessId()) { + if (g_keyboard != NULL || + g_mouse != NULL || + g_getMessage != NULL) { + uninstall(); + uninstallScreenSaver(); + } + g_processID = 0; + g_hinstance = NULL; + } + } + return TRUE; +} + +extern "C" { + +int +init(DWORD threadID) +{ + assert(g_hinstance != NULL); + + // try to open process that last called init() to see if it's + // still running or if it died without cleaning up. + if (g_processID != 0 && g_processID != GetCurrentProcessId()) { + HANDLE process = OpenProcess(STANDARD_RIGHTS_REQUIRED, + FALSE, g_processID); + if (process != NULL) { + // old process (probably) still exists so refuse to + // reinitialize this DLL (and thus steal it from the + // old process). + CloseHandle(process); + return 0; + } + + // clean up after old process. the system should've already + // removed the hooks so we just need to reset our state. + g_hinstance = GetModuleHandle("synrgyhk"); + g_processID = GetCurrentProcessId(); + g_wheelSupport = kWheelNone; + g_threadID = 0; + g_keyboard = NULL; + g_mouse = NULL; + g_getMessage = NULL; + g_hookThreadLL = NULL; + g_hookThreadIDLL = 0; + g_hookEventLL = NULL; + g_keyboardLL = NULL; + g_mouseLL = NULL; + g_screenSaver = false; + } + + // save thread id. we'll post messages to this thread's + // message queue. + g_threadID = threadID; + + // set defaults + g_relay = false; + g_zoneSides = 0; + g_zoneSize = 0; + g_xScreen = 0; + g_yScreen = 0; + g_wScreen = 0; + g_hScreen = 0; + g_cursor = NULL; + g_cursorThread = 0; + + return 1; +} + +int +cleanup(void) +{ + assert(g_hinstance != NULL); + + if (g_processID == GetCurrentProcessId()) { + g_threadID = 0; + } + + return 1; +} + +EHookResult +install() +{ + assert(g_hinstance != NULL); + assert(g_keyboard == NULL); + assert(g_mouse == NULL); + assert(g_getMessage == NULL || g_screenSaver); + + // must be initialized + if (g_threadID == 0) { + return kHOOK_FAILED; + } + + // check for mouse wheel support + g_wheelSupport = getWheelSupport(); + + // install GetMessage hook (unless already installed) + if (g_wheelSupport == kWheelOld && g_getMessage == NULL) { + g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, + &getMessageHook, + g_hinstance, + 0); + } + + // install low-level keyboard/mouse hooks, if possible. since these + // hooks are called in the context of the installing thread and that + // thread must have a message loop but we don't want the caller's + // message loop to do the work, we'll fire up a separate thread + // just for the hooks. note that low-level hooks are only available + // on windows NT SP3 and above. + g_hookEventLL = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_hookEventLL != NULL) { + g_hookThreadLL = CreateThread(NULL, 0, &getLowLevelProc, 0, + CREATE_SUSPENDED, &g_hookThreadIDLL); + if (g_hookThreadLL != NULL) { + // start the thread and wait for it to initialize + ResumeThread(g_hookThreadLL); + WaitForSingleObject(g_hookEventLL, INFINITE); + ResetEvent(g_hookEventLL); + + // the thread clears g_hookThreadIDLL if it failed + if (g_hookThreadIDLL == 0) { + CloseHandle(g_hookThreadLL); + g_hookThreadLL = NULL; + } + } + if (g_hookThreadLL == NULL) { + CloseHandle(g_hookEventLL); + g_hookEventLL = NULL; + } + } + + // install non-low-level hooks if the low-level hooks are not installed + if (g_hookThreadLL == NULL) { +#if !NO_GRAB_KEYBOARD + g_keyboard = SetWindowsHookEx(WH_KEYBOARD, + &keyboardHook, + g_hinstance, + 0); +#else + // keep compiler quiet + &keyboardHook; +#endif + g_mouse = SetWindowsHookEx(WH_MOUSE, + &mouseHook, + g_hinstance, + 0); + } + + // check for any failures. uninstall all hooks on failure. + if (g_hookThreadLL == NULL && +#if !NO_GRAB_KEYBOARD + (g_keyboard == NULL || g_mouse == NULL)) { +#else + (g_mouse == NULL)) { +#endif + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + } + if (g_mouse != NULL) { + UnhookWindowsHookEx(g_mouse); + g_mouse = NULL; + } + if (g_getMessage != NULL && !g_screenSaver) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + g_threadID = NULL; + return kHOOK_FAILED; + } + + return (g_hookThreadLL == NULL) ? kHOOK_OKAY : kHOOK_OKAY_LL; +} + +int +uninstall(void) +{ + assert(g_hinstance != NULL); + + // uninstall hooks + if (g_hookThreadLL != NULL) { + PostThreadMessage(g_hookThreadIDLL, WM_QUIT, 0, 0); + WaitForSingleObject(g_hookThreadLL, INFINITE); + CloseHandle(g_hookEventLL); + CloseHandle(g_hookThreadLL); + g_hookEventLL = NULL; + g_hookThreadLL = NULL; + g_hookThreadIDLL = 0; + } + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + } + if (g_mouse != NULL) { + UnhookWindowsHookEx(g_mouse); + g_mouse = NULL; + } + if (g_getMessage != NULL && !g_screenSaver) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + g_wheelSupport = kWheelNone; + + // show the cursor + restoreCursor(); + + return 1; +} + +int +installScreenSaver(void) +{ + assert(g_hinstance != NULL); + + // must be initialized + if (g_threadID == 0) { + return 0; + } + + // generate screen saver messages + g_screenSaver = true; + + // install hook unless it's already installed + if (g_getMessage == NULL) { + g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, + &getMessageHook, + g_hinstance, + 0); + } + + return (g_getMessage != NULL) ? 1 : 0; +} + +int +uninstallScreenSaver(void) +{ + assert(g_hinstance != NULL); + + // uninstall hook unless the mouse wheel hook is installed + if (g_getMessage != NULL && g_wheelSupport != kWheelOld) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + + // screen saver hook is no longer installed + g_screenSaver = false; + + return 1; +} + +void +setSides(UInt32 sides) +{ + g_zoneSides = sides; +} + +void +setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize) +{ + g_zoneSize = jumpZoneSize; + g_xScreen = x; + g_yScreen = y; + g_wScreen = w; + g_hScreen = h; +} + +void +setRelay(int enable) +{ + if ((enable != 0) == g_relay) { + // no change + return; + } + g_relay = (enable != 0); + if (!g_relay) { + restoreCursor(); + } +} + +} diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h new file mode 100644 index 0000000..03e72fa --- /dev/null +++ b/lib/platform/CSynergyHook.h @@ -0,0 +1,75 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSYNERGYHOOK_H +#define CSYNERGYHOOK_H + +#include "BasicTypes.h" + +#if WINDOWS_LIKE +#define WIN32_LEAN_AND_MEAN +#include +#else +#error CSynergyHook is a win32 specific file +#endif + +#if defined(SYNRGYHK_EXPORTS) +#define CSYNERGYHOOK_API __declspec(dllexport) +#else +#define CSYNERGYHOOK_API __declspec(dllimport) +#endif + +#define SYNERGY_MSG_MARK WM_APP + 0x0011 // mark id; +#define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data +#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; +#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0014 // delta; +#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0015 // x; y +#define SYNERGY_MSG_POST_WARP WM_APP + 0x0016 // ; +#define SYNERGY_MSG_PRE_WARP WM_APP + 0x0017 // x; y +#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; +#define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY +#define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP + +extern "C" { + +enum EHookResult { + kHOOK_FAILED, + kHOOK_OKAY, + kHOOK_OKAY_LL +}; + +typedef int (*InitFunc)(DWORD targetQueueThreadID); +typedef int (*CleanupFunc)(void); +typedef EHookResult (*InstallFunc)(void); +typedef int (*UninstallFunc)(void); +typedef int (*InstallScreenSaverFunc)(void); +typedef int (*UninstallScreenSaverFunc)(void); +typedef void (*SetSidesFunc)(UInt32); +typedef void (*SetZoneFunc)(SInt32, SInt32, SInt32, SInt32, SInt32); +typedef void (*SetRelayFunc)(int); + +CSYNERGYHOOK_API int init(DWORD); +CSYNERGYHOOK_API int cleanup(void); +CSYNERGYHOOK_API EHookResult install(void); +CSYNERGYHOOK_API int uninstall(void); +CSYNERGYHOOK_API int installScreenSaver(void); +CSYNERGYHOOK_API int uninstallScreenSaver(void); +CSYNERGYHOOK_API void setSides(UInt32 sides); +CSYNERGYHOOK_API void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 jumpZoneSize); +CSYNERGYHOOK_API void setRelay(int enable); // relay iff enable != 0 + +} + +#endif diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp new file mode 100644 index 0000000..0606c3e --- /dev/null +++ b/lib/platform/CXWindowsClipboard.cpp @@ -0,0 +1,1492 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboard.h" +#include "CXWindowsClipboardTextConverter.h" +#include "CXWindowsClipboardUCS2Converter.h" +#include "CXWindowsClipboardUTF8Converter.h" +#include "CXWindowsUtil.h" +#include "CThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "CArch.h" +#include "stdvector.h" +#include +#include + +// +// CXWindowsClipboard +// + +CXWindowsClipboard::CXWindowsClipboard(Display* display, + Window window, ClipboardID id) : + m_display(display), + m_window(window), + m_id(id), + m_open(false), + m_time(0), + m_owner(false), + m_timeOwned(0), + m_timeLost(0) +{ + // get some atoms + m_atomTargets = XInternAtom(m_display, "TARGETS", False); + m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); + m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); + m_atomInteger = XInternAtom(m_display, "INTEGER", False); + m_atomAtom = XInternAtom(m_display, "ATOM", False); + m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); + m_atomData = XInternAtom(m_display, "CLIP_TEMPORARY", False); + m_atomINCR = XInternAtom(m_display, "INCR", False); + m_atomMotifClipLock = XInternAtom(m_display, "_MOTIF_CLIP_LOCK", False); + m_atomMotifClipHeader = XInternAtom(m_display, "_MOTIF_CLIP_HEADER", False); + m_atomMotifClipAccess = XInternAtom(m_display, + "_MOTIF_CLIP_LOCK_ACCESS_VALID", False); + m_atomGDKSelection = XInternAtom(m_display, "GDK_SELECTION", False); + + // set selection atom based on clipboard id + switch (id) { + case kClipboardClipboard: + m_selection = XInternAtom(m_display, "CLIPBOARD", False); + break; + + case kClipboardSelection: + default: + m_selection = XA_PRIMARY; + break; + } + + // add converters, most desired first + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "text/plain;charset=UTF-8")); + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "UTF8_STRING")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/plain;charset=ISO-10646-UCS-2")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/unicode")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "text/plain")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "STRING")); + + // we have no data + clearCache(); +} + +CXWindowsClipboard::~CXWindowsClipboard() +{ + clearReplies(); + clearConverters(); +} + +void +CXWindowsClipboard::lost(Time time) +{ + LOG((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time)); + if (m_owner) { + m_owner = false; + m_timeLost = time; + clearCache(); + } +} + +void +CXWindowsClipboard::addRequest(Window owner, Window requestor, + Atom target, ::Time time, Atom property) +{ + // must be for our window and we must have owned the selection + // at the given time. + bool success = false; + if (owner == m_window) { + LOG((CLOG_DEBUG1 "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); + if (wasOwnedAtTime(time)) { + if (target == m_atomMultiple) { + // add a multiple request. property may not be None + // according to ICCCM. + if (property != None) { + success = insertMultipleReply(requestor, time, property); + } + } + else { + addSimpleRequest(requestor, target, time, property); + + // addSimpleRequest() will have already handled failure + success = true; + } + } + else { + LOG((CLOG_DEBUG1 "failed, not owned at time %d", time)); + } + } + + if (!success) { + // send failure + LOG((CLOG_DEBUG1 "failed")); + insertReply(new CReply(requestor, target, time)); + } + + // send notifications that are pending + pushReplies(); +} + +bool +CXWindowsClipboard::addSimpleRequest(Window requestor, + Atom target, ::Time time, Atom property) +{ + // obsolete requestors may supply a None property. in + // that case we use the target as the property to store + // the conversion. + if (property == None) { + property = target; + } + + // handle targets + CString data; + Atom type = None; + int format = 0; + if (target == m_atomTargets) { + type = getTargetsData(data, &format); + } + else if (target == m_atomTimestamp) { + type = getTimestampData(data, &format); + } + else { + IXWindowsClipboardConverter* converter = getConverter(target); + if (converter != NULL) { + IClipboard::EFormat clipboardFormat = converter->getFormat(); + if (m_added[clipboardFormat]) { + try { + data = converter->fromIClipboard(m_data[clipboardFormat]); + format = converter->getDataSize(); + type = converter->getAtom(); + } + catch (...) { + // ignore -- cannot convert + } + } + } + } + + if (type != None) { + // success + LOG((CLOG_DEBUG1 "success")); + insertReply(new CReply(requestor, target, time, + property, data, type, format)); + return true; + } + else { + // failure + LOG((CLOG_DEBUG1 "failed")); + insertReply(new CReply(requestor, target, time)); + return false; + } +} + +bool +CXWindowsClipboard::processRequest(Window requestor, + ::Time /*time*/, Atom property) +{ + CReplyMap::iterator index = m_replies.find(requestor); + if (index == m_replies.end()) { + // unknown requestor window + return false; + } + LOG((CLOG_DEBUG1 "received property %d delete from 0x08%x", property, requestor)); + + // find the property in the known requests. it should be the + // first property but we'll check 'em all if we have to. + CReplyList& replies = index->second; + for (CReplyList::iterator index2 = replies.begin(); + index2 != replies.end(); ++index2) { + CReply* reply = *index2; + if (reply->m_replied && reply->m_property == property) { + // if reply is complete then remove it and start the + // next one. + pushReplies(index, replies, index2); + return true; + } + } + + return false; +} + +bool +CXWindowsClipboard::destroyRequest(Window requestor) +{ + CReplyMap::iterator index = m_replies.find(requestor); + if (index == m_replies.end()) { + // unknown requestor window + return false; + } + + // destroy all replies for this window + clearReplies(index->second); + m_replies.erase(index); + + // note -- we don't stop watching the window for events because + // we're called in response to the window being destroyed. + + return true; +} + +Window +CXWindowsClipboard::getWindow() const +{ + return m_window; +} + +Atom +CXWindowsClipboard::getSelection() const +{ + return m_selection; +} + +bool +CXWindowsClipboard::empty() +{ + assert(m_open); + + LOG((CLOG_DEBUG "empty clipboard %d", m_id)); + + // assert ownership of clipboard + XSetSelectionOwner(m_display, m_selection, m_window, m_time); + if (XGetSelectionOwner(m_display, m_selection) != m_window) { + LOG((CLOG_DEBUG "failed to grab clipboard %d", m_id)); + return false; + } + + // clear all data. since we own the data now, the cache is up + // to date. + clearCache(); + m_cached = true; + + // FIXME -- actually delete motif clipboard items? + // FIXME -- do anything to motif clipboard properties? + + // save time + m_timeOwned = m_time; + m_timeLost = 0; + + // we're the owner now + m_owner = true; + LOG((CLOG_DEBUG "grabbed clipboard %d", m_id)); + + return true; +} + +void +CXWindowsClipboard::add(EFormat format, const CString& data) +{ + assert(m_open); + assert(m_owner); + + LOG((CLOG_DEBUG "add %d bytes to clipboard %d format: %d", data.size(), m_id, format)); + + m_data[format] = data; + m_added[format] = true; + + // FIXME -- set motif clipboard item? +} + +bool +CXWindowsClipboard::open(Time time) const +{ + assert(!m_open); + + LOG((CLOG_DEBUG "open clipboard %d", m_id)); + + // assume not motif + m_motif = false; + + // lock clipboard + if (m_id == kClipboardClipboard) { + if (!motifLockClipboard()) { + return false; + } + + // check if motif owns the selection. unlock motif clipboard + // if it does not. + m_motif = motifOwnsClipboard(); + LOG((CLOG_DEBUG1 "motif does %sown clipboard", m_motif ? "" : "not ")); + if (!m_motif) { + motifUnlockClipboard(); + } + } + + // now open + m_open = true; + m_time = time; + + // be sure to flush the cache later if it's dirty + m_checkCache = true; + + return true; +} + +void +CXWindowsClipboard::close() const +{ + assert(m_open); + + LOG((CLOG_DEBUG "close clipboard %d", m_id)); + + // unlock clipboard + if (m_motif) { + motifUnlockClipboard(); + } + + m_motif = false; + m_open = false; +} + +IClipboard::Time +CXWindowsClipboard::getTime() const +{ + checkCache(); + return m_timeOwned; +} + +bool +CXWindowsClipboard::has(EFormat format) const +{ + assert(m_open); + + fillCache(); + return m_added[format]; +} + +CString +CXWindowsClipboard::get(EFormat format) const +{ + assert(m_open); + + fillCache(); + return m_data[format]; +} + +void +CXWindowsClipboard::clearConverters() +{ + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; + } + m_converters.clear(); +} + +IXWindowsClipboardConverter* +CXWindowsClipboard::getConverter(Atom target, bool onlyIfNotAdded) const +{ + IXWindowsClipboardConverter* converter = NULL; + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getAtom() == target) { + break; + } + } + if (converter == NULL) { + LOG((CLOG_DEBUG1 " no converter for target %d", target)); + return NULL; + } + + // optionally skip already handled targets + if (onlyIfNotAdded) { + if (m_added[converter->getFormat()]) { + LOG((CLOG_DEBUG1 " skipping handled format %d", converter->getFormat())); + return NULL; + } + } + + return converter; +} + +void +CXWindowsClipboard::checkCache() const +{ + if (!m_checkCache) { + return; + } + m_checkCache = false; + + // get the time the clipboard ownership was taken by the current + // owner. + if (m_motif) { + m_timeOwned = motifGetTime(); + } + else { + m_timeOwned = icccmGetTime(); + } + + // if we can't get the time then use the time passed to us + if (m_timeOwned == 0) { + m_timeOwned = m_time; + } + + // if the cache is dirty then flush it + if (m_timeOwned != m_cacheTime) { + clearCache(); + } +} + +void +CXWindowsClipboard::clearCache() const +{ + const_cast(this)->doClearCache(); +} + +void +CXWindowsClipboard::doClearCache() +{ + m_checkCache = false; + m_cached = false; + for (SInt32 index = 0; index < kNumFormats; ++index) { + m_data[index] = ""; + m_added[index] = false; + } +} + +void +CXWindowsClipboard::fillCache() const +{ + // get the selection data if not already cached + checkCache(); + if (!m_cached) { + const_cast(this)->doFillCache(); + } +} + +void +CXWindowsClipboard::doFillCache() +{ + if (m_motif) { + motifFillCache(); + } + else { + icccmFillCache(); + } + m_checkCache = false; + m_cached = true; + m_cacheTime = m_timeOwned; +} + +void +CXWindowsClipboard::icccmFillCache() +{ + LOG((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); + + // see if we can get the list of available formats from the selection. + // if not then use a default list of formats. note that some clipboard + // owners are broken and report TARGETS as the type of the TARGETS data + // instead of the correct type ATOM; allow either. + const Atom atomTargets = m_atomTargets; + Atom target; + CString data; + if (!icccmGetSelection(atomTargets, &target, &data) || + (target != m_atomAtom && target != m_atomTargets)) { + LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); + data = ""; + + target = XA_STRING; + data.append(reinterpret_cast(&target), sizeof(target)); + } + + // try each converter in order (because they're in order of + // preference). + const Atom* targets = reinterpret_cast(data.data()); + const UInt32 numTargets = data.size() / sizeof(Atom); + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip already handled targets + if (m_added[converter->getFormat()]) { + continue; + } + + // see if atom is in target list + Atom target = None; + for (UInt32 i = 0; i < numTargets; ++i) { + if (converter->getAtom() == targets[i]) { + target = targets[i]; + break; + } + } + if (target == None) { + continue; + } + + // get the data + Atom actualTarget; + CString targetData; + if (!icccmGetSelection(target, &actualTarget, &targetData)) { + LOG((CLOG_DEBUG1 " no data for target %d", target)); + continue; + } + + // add to clipboard and note we've done it + IClipboard::EFormat format = converter->getFormat(); + m_data[format] = converter->toIClipboard(targetData); + m_added[format] = true; + LOG((CLOG_DEBUG " added format %d for target %d", format, target)); + } +} + +bool +CXWindowsClipboard::icccmGetSelection(Atom target, + Atom* actualTarget, CString* data) const +{ + assert(actualTarget != NULL); + assert(data != NULL); + + // request data conversion + CICCCMGetClipboard getter(m_window, m_time, m_atomData); + if (!getter.readClipboard(m_display, m_selection, + target, actualTarget, data)) { + LOG((CLOG_DEBUG1 "can't get data for selection target %d", target)); + LOGC(getter.m_error, (CLOG_WARN "ICCCM violation by clipboard owner")); + return false; + } + else if (*actualTarget == None) { + LOG((CLOG_DEBUG1 "selection conversion failed for target %d", target)); + return false; + } + return true; +} + +IClipboard::Time +CXWindowsClipboard::icccmGetTime() const +{ + Atom actualTarget; + CString data; + if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) && + actualTarget == m_atomInteger) { + Time time = *reinterpret_cast(data.data()); + LOG((CLOG_DEBUG1 "got ICCCM time %d", time)); + return time; + } + else { + // no timestamp + LOG((CLOG_DEBUG1 "can't get ICCCM time")); + return 0; + } +} + +bool +CXWindowsClipboard::motifLockClipboard() const +{ + // fail if anybody owns the lock (even us, so this is non-recursive) + Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); + if (lockOwner != None) { + LOG((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); + return false; + } + + // try to grab the lock + // FIXME -- is this right? there's a race condition here -- + // A grabs successfully, B grabs successfully, A thinks it + // still has the grab until it gets a SelectionClear. + Time time = CXWindowsUtil::getCurrentTime(m_display, m_window); + XSetSelectionOwner(m_display, m_atomMotifClipLock, m_window, time); + lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); + if (lockOwner != m_window) { + LOG((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); + return false; + } + + LOG((CLOG_DEBUG1 "locked motif clipboard")); + return true; +} + +void +CXWindowsClipboard::motifUnlockClipboard() const +{ + LOG((CLOG_DEBUG1 "unlocked motif clipboard")); + + // fail if we don't own the lock + Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); + if (lockOwner != m_window) { + return; + } + + // release lock + Time time = CXWindowsUtil::getCurrentTime(m_display, m_window); + XSetSelectionOwner(m_display, m_atomMotifClipLock, None, time); +} + +bool +CXWindowsClipboard::motifOwnsClipboard() const +{ + // get the current selection owner + // FIXME -- this can't be right. even if the window is destroyed + // Motif will still have a valid clipboard. how can we tell if + // some other client owns CLIPBOARD? + Window owner = XGetSelectionOwner(m_display, m_selection); + if (owner == None) { + return false; + } + + // get the Motif clipboard header property from the root window + Atom target; + SInt32 format; + CString data; + Window root = RootWindow(m_display, DefaultScreen(m_display)); + if (!CXWindowsUtil::getWindowProperty(m_display, root, + m_atomMotifClipHeader, + &data, &target, &format, False)) { + return false; + } + + // check the owner window against the current clipboard owner + const CMotifClipHeader* header = + reinterpret_cast(data.data()); + if (data.size() >= sizeof(CMotifClipHeader) && + header->m_id == kMotifClipHeader) { + if (header->m_selectionOwner == owner) { + return true; + } + } + + return false; +} + +void +CXWindowsClipboard::motifFillCache() +{ + LOG((CLOG_DEBUG "Motif fill clipboard %d", m_id)); + + // get the Motif clipboard header property from the root window + Atom target; + SInt32 format; + CString data; + Window root = RootWindow(m_display, DefaultScreen(m_display)); + if (!CXWindowsUtil::getWindowProperty(m_display, root, + m_atomMotifClipHeader, + &data, &target, &format, False)) { + return; + } + + // check that the header is okay + const CMotifClipHeader* header = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipHeader) || + header->m_id != kMotifClipHeader || + header->m_numItems < 1) { + return; + } + + // get the Motif item property from the root window + char name[18 + 20]; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", header->m_item); + Atom atomItem = XInternAtom(m_display, name, False); + data = ""; + if (!CXWindowsUtil::getWindowProperty(m_display, root, + atomItem, &data, + &target, &format, False)) { + return; + } + + // check that the item is okay + const CMotifClipItem* item = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipItem) || + item->m_id != kMotifClipItem || + item->m_numFormats - item->m_numDeletedFormats < 1) { + return; + } + + // format list is after static item structure elements + const SInt32 numFormats = item->m_numFormats - item->m_numDeletedFormats; + const SInt32* formats = reinterpret_cast(item->m_size + + reinterpret_cast(data.data())); + + // get the available formats + typedef std::map CMotifFormatMap; + CMotifFormatMap motifFormats; + for (SInt32 i = 0; i < numFormats; ++i) { + // get Motif format property from the root window + sprintf(name, "_MOTIF_CLIP_ITEM_%d", formats[i]); + Atom atomFormat = XInternAtom(m_display, name, False); + CString data; + if (!CXWindowsUtil::getWindowProperty(m_display, root, + atomFormat, &data, + &target, &format, False)) { + continue; + } + + // check that the format is okay + const CMotifClipFormat* motifFormat = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipFormat) || + motifFormat->m_id != kMotifClipFormat || + motifFormat->m_length < 0 || + motifFormat->m_type == None || + motifFormat->m_deleted != 0) { + continue; + } + + // save it + motifFormats.insert(std::make_pair(motifFormat->m_type, data)); + } + const UInt32 numMotifFormats = motifFormats.size(); + + // try each converter in order (because they're in order of + // preference). + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip already handled targets + if (m_added[converter->getFormat()]) { + continue; + } + + // see if atom is in target list + CMotifFormatMap::const_iterator index2 = + motifFormats.find(converter->getAtom()); + if (index2 == motifFormats.end()) { + continue; + } + + // get format + const CMotifClipFormat* motifFormat = + reinterpret_cast( + index2->second.data()); + const Atom target = motifFormat->m_type; + + // get the data (finally) + Atom actualTarget; + CString targetData; + if (!motifGetSelection(motifFormat, &actualTarget, &targetData)) { + LOG((CLOG_DEBUG1 " no data for target %d", target)); + continue; + } + + // add to clipboard and note we've done it + IClipboard::EFormat format = converter->getFormat(); + m_data[format] = converter->toIClipboard(targetData); + m_added[format] = true; + LOG((CLOG_DEBUG " added format %d for target %d", format, target)); + } +} + +bool +CXWindowsClipboard::motifGetSelection(const CMotifClipFormat* format, + Atom* actualTarget, CString* data) const +{ + // if the current clipboard owner and the owner indicated by the + // motif clip header are the same then transfer via a property on + // the root window, otherwise transfer as a normal ICCCM client. + if (!motifOwnsClipboard()) { + return icccmGetSelection(format->m_type, actualTarget, data); + } + + // use motif way + // FIXME -- this isn't right. it'll only work if the data is + // already stored on the root window and only if it fits in a + // property. motif has some scheme for transferring part by + // part that i don't know. + char name[18 + 20]; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", format->m_data); + Atom target = XInternAtom(m_display, name, False); + Window root = RootWindow(m_display, DefaultScreen(m_display)); + return CXWindowsUtil::getWindowProperty(m_display, root, + target, data, + actualTarget, NULL, False); +} + +IClipboard::Time +CXWindowsClipboard::motifGetTime() const +{ + return icccmGetTime(); +} + +bool +CXWindowsClipboard::insertMultipleReply(Window requestor, + ::Time time, Atom property) +{ + // get the requested targets + Atom target; + SInt32 format; + CString data; + if (!CXWindowsUtil::getWindowProperty(m_display, requestor, + property, &data, &target, &format, False)) { + // can't get the requested targets + return false; + } + + // fail if the requested targets isn't of the correct form + if (format != 32 || + target != m_atomAtomPair) { + return false; + } + + // data is a list of atom pairs: target, property + const Atom* targets = reinterpret_cast(data.data()); + const UInt32 numTargets = data.size() / sizeof(Atom); + + // add replies for each target + bool changed = false; + for (UInt32 i = 0; i < numTargets; i += 2) { + const Atom target = targets[i + 0]; + const Atom property = targets[i + 1]; + if (!addSimpleRequest(requestor, target, time, property)) { + // note that we can't perform the requested conversion + static const Atom none = None; + data.replace(i * sizeof(Atom), sizeof(Atom), + reinterpret_cast(&none), + sizeof(Atom)); + changed = true; + } + } + + // update the targets property if we changed it + if (changed) { + CXWindowsUtil::setWindowProperty(m_display, requestor, + property, data.data(), data.size(), + target, format); + } + + // add reply for MULTIPLE request + insertReply(new CReply(requestor, m_atomMultiple, + time, property, CString(), None, 32)); + + return true; +} + +void +CXWindowsClipboard::insertReply(CReply* reply) +{ + assert(reply != NULL); + + // note -- we must respond to requests in order if requestor,target,time + // are the same, otherwise we can use whatever order we like with one + // exception: each reply in a MULTIPLE reply must be handled in order + // as well. those replies will almost certainly not share targets so + // we can't simply use requestor,target,time as map index. + // + // instead we'll use just the requestor. that's more restrictive than + // necessary but we're guaranteed to do things in the right order. + // note that we could also include the time in the map index and still + // ensure the right order. but since that'll just make it harder to + // find the right reply when handling property notify events we stick + // to just the requestor. + + const bool newWindow = (m_replies.count(reply->m_requestor) == 0); + m_replies[reply->m_requestor].push_back(reply); + + // adjust requestor's event mask if we haven't done so already. we + // want events in case the window is destroyed or any of its + // properties change. + if (newWindow) { + // note errors while we adjust event masks + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + + // get and save the current event mask + XWindowAttributes attr; + XGetWindowAttributes(m_display, reply->m_requestor, &attr); + m_eventMasks[reply->m_requestor] = attr.your_event_mask; + + // add the events we want + XSelectInput(m_display, reply->m_requestor, attr.your_event_mask | + StructureNotifyMask | PropertyChangeMask); + + // if we failed then the window has already been destroyed + if (error) { + m_replies.erase(reply->m_requestor); + delete reply; + } + } +} + +void +CXWindowsClipboard::pushReplies() +{ + // send the first reply for each window if that reply hasn't + // been sent yet. + for (CReplyMap::iterator index = m_replies.begin(); + index != m_replies.end(); ++index) { + assert(!index->second.empty()); + if (!index->second.front()->m_replied) { + pushReplies(index, index->second, index->second.begin()); + } + } +} + +void +CXWindowsClipboard::pushReplies(CReplyMap::iterator mapIndex, + CReplyList& replies, CReplyList::iterator index) +{ + CReply* reply = *index; + while (sendReply(reply)) { + // reply is complete. discard it and send the next reply, + // if any. + index = replies.erase(index); + delete reply; + if (index == replies.end()) { + break; + } + reply = *index; + } + + // if there are no more replies in the list then remove the list + // and stop watching the requestor for events. + if (replies.empty()) { + CXWindowsUtil::CErrorLock lock(m_display); + Window requestor = mapIndex->first; + XSelectInput(m_display, requestor, m_eventMasks[requestor]); + m_replies.erase(mapIndex); + m_eventMasks.erase(requestor); + } +} + +bool +CXWindowsClipboard::sendReply(CReply* reply) +{ + assert(reply != NULL); + + // bail out immediately if reply is done + if (reply->m_done) { + LOG((CLOG_DEBUG1 "clipboard: finished reply to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + return true; + } + + // start in failed state if property is None + bool failed = (reply->m_property == None); + if (!failed) { + LOG((CLOG_DEBUG1 "clipboard: setting property on 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + + // send using INCR if already sending incrementally or if reply + // is too large, otherwise just send it. + const UInt32 maxRequestSize = 3 * XMaxRequestSize(m_display); + const bool useINCR = (reply->m_data.size() > maxRequestSize); + + // send INCR reply if incremental and we haven't replied yet + if (useINCR && !reply->m_replied) { + UInt32 size = reply->m_data.size(); + if (!CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + &size, 4, m_atomINCR, 32)) { + failed = true; + } + } + + // send more INCR reply or entire non-incremental reply + else { + // how much more data should we send? + UInt32 size = reply->m_data.size() - reply->m_ptr; + if (size > maxRequestSize) + size = maxRequestSize; + + // send it + if (!CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + reply->m_data.data() + reply->m_ptr, + size, + reply->m_type, reply->m_format)) { + failed = true; + } + else { + reply->m_ptr += size; + + // we've finished the reply if we just sent the zero + // size incremental chunk or if we're not incremental. + reply->m_done = (size == 0 || !useINCR); + } + } + } + + // if we've failed then delete the property and say we're done. + // if we haven't replied yet then we can send a failure notify, + // otherwise we've failed in the middle of an incremental + // transfer; i don't know how to cancel that so i'll just send + // the final zero-length property. + // FIXME -- how do you gracefully cancel an incremental transfer? + if (failed) { + LOG((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + reply->m_done = true; + if (reply->m_property != None) { + CXWindowsUtil::CErrorLock lock(m_display); + XDeleteProperty(m_display, reply->m_requestor, reply->m_property); + } + + if (!reply->m_replied) { + sendNotify(reply->m_requestor, m_selection, + reply->m_target, None, + reply->m_time); + + // don't wait for any reply (because we're not expecting one) + return true; + } + else { + static const char dummy = 0; + CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + &dummy, + 0, + reply->m_type, reply->m_format); + + // wait for delete notify + return false; + } + } + + // send notification if we haven't yet + if (!reply->m_replied) { + LOG((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + reply->m_replied = true; + + // dump every property on the requestor window to the debug2 + // log. we've seen what appears to be a bug in lesstif and + // knowing the properties may help design a workaround, if + // it becomes necessary. + if (CLOG->getFilter() >= CLog::kDEBUG2) { + CXWindowsUtil::CErrorLock lock(m_display); + int n; + Atom* props = XListProperties(m_display, reply->m_requestor, &n); + LOG((CLOG_DEBUG2 "properties of 0x%08x:", reply->m_requestor)); + for (int i = 0; i < n; ++i) { + Atom target; + CString data; + char* name = XGetAtomName(m_display, props[i]); + if (!CXWindowsUtil::getWindowProperty(m_display, + reply->m_requestor, + props[i], &data, &target, NULL, False)) { + LOG((CLOG_DEBUG2 " %s: ", name)); + } + else { + // if there are any non-ascii characters in string + // then print the binary data. + static const char* hex = "0123456789abcdef"; + for (CString::size_type j = 0; j < data.size(); ++j) { + if (data[j] < 32 || data[j] > 126) { + CString tmp; + tmp.reserve(data.size() * 3); + for (j = 0; j < data.size(); ++j) { + unsigned char v = (unsigned char)data[j]; + tmp += hex[v >> 16]; + tmp += hex[v & 15]; + tmp += ' '; + } + data = tmp; + break; + } + } + char* type = XGetAtomName(m_display, target); + LOG((CLOG_DEBUG2 " %s (%s): %s", name, type, data.c_str())); + if (type != NULL) { + XFree(type); + } + } + if (name != NULL) { + XFree(name); + } + } + if (props != NULL) { + XFree(props); + } + } + + sendNotify(reply->m_requestor, m_selection, + reply->m_target, reply->m_property, + reply->m_time); + } + + // wait for delete notify + return false; +} + +void +CXWindowsClipboard::clearReplies() +{ + for (CReplyMap::iterator index = m_replies.begin(); + index != m_replies.end(); ++index) { + clearReplies(index->second); + } + m_replies.clear(); + m_eventMasks.clear(); +} + +void +CXWindowsClipboard::clearReplies(CReplyList& replies) +{ + for (CReplyList::iterator index = replies.begin(); + index != replies.end(); ++index) { + delete *index; + } + replies.clear(); +} + +void +CXWindowsClipboard::sendNotify(Window requestor, + Atom selection, Atom target, Atom property, Time time) +{ + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = m_display; + event.xselection.requestor = requestor; + event.xselection.selection = selection; + event.xselection.target = target; + event.xselection.property = property; + event.xselection.time = time; + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, requestor, False, 0, &event); +} + +bool +CXWindowsClipboard::wasOwnedAtTime(::Time time) const +{ + // not owned if we've never owned the selection + checkCache(); + if (m_timeOwned == 0) { + return false; + } + + // if time is CurrentTime then return true if we still own the + // selection and false if we do not. else if we still own the + // selection then get the current time, otherwise use + // m_timeLost as the end time. + Time lost = m_timeLost; + if (m_timeLost == 0) { + if (time == CurrentTime) { + return true; + } + else { + lost = CXWindowsUtil::getCurrentTime(m_display, m_window); + } + } + else { + if (time == CurrentTime) { + return false; + } + } + + // compare time to range + Time duration = lost - m_timeOwned; + Time when = time - m_timeOwned; + return (/*when >= 0 &&*/ when < duration); +} + +Atom +CXWindowsClipboard::getTargetsData(CString& data, int* format) const +{ + assert(format != NULL); + + // add standard targets + Atom atom; + atom = m_atomTargets; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + atom = m_atomMultiple; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + atom = m_atomTimestamp; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + + // add targets we can convert to + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip formats we don't have + if (m_added[converter->getFormat()]) { + atom = converter->getAtom(); + data.append(reinterpret_cast(&atom), sizeof(Atom)); + } + } + + *format = 32; + return m_atomAtom; +} + +Atom +CXWindowsClipboard::getTimestampData(CString& data, int* format) const +{ + assert(format != NULL); + + assert(sizeof(m_timeOwned) == 4); + checkCache(); + data.append(reinterpret_cast(&m_timeOwned), 4); + *format = 32; + return m_atomInteger; +} + + +// +// CXWindowsClipboard::CICCCMGetClipboard +// + +CXWindowsClipboard::CICCCMGetClipboard::CICCCMGetClipboard( + Window requestor, Time time, Atom property) : + m_requestor(requestor), + m_time(time), + m_property(property), + m_incr(false), + m_failed(false), + m_done(false), + m_reading(false), + m_data(NULL), + m_actualTarget(NULL), + m_error(false) +{ + // do nothing +} + +CXWindowsClipboard::CICCCMGetClipboard::~CICCCMGetClipboard() +{ + // do nothing +} + +bool +CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, + Atom selection, Atom target, Atom* actualTarget, CString* data) +{ + assert(actualTarget != NULL); + assert(data != NULL); + + LOG((CLOG_DEBUG1 "request selection=%d, target=%d, window=%x", selection, target, m_requestor)); + + // save output pointers + m_actualTarget = actualTarget; + m_data = data; + + // assume failure + *m_actualTarget = None; + *m_data = ""; + + // delete target property + XDeleteProperty(display, m_requestor, m_property); + + // select window for property changes + XWindowAttributes attr; + XGetWindowAttributes(display, m_requestor, &attr); + XSelectInput(display, m_requestor, + attr.your_event_mask | PropertyChangeMask); + + // request data conversion + XConvertSelection(display, selection, target, + m_property, m_requestor, m_time); + + // synchronize with server before we start following timeout countdown + XSync(display, False); + + // Xlib inexplicably omits the ability to wait for an event with + // a timeout. (it's inexplicable because there's no portable way + // to do it.) we'll poll until we have what we're looking for or + // a timeout expires. we use a timeout so we don't get locked up + // by badly behaved selection owners. + XEvent xevent; + std::vector events; + CStopwatch timeout(true); + static const double s_timeout = 0.25; // FIXME -- is this too short? + bool noWait = false; + while (!m_done && !m_failed) { + // fail if timeout has expired + if (timeout.getTime() >= s_timeout) { + m_failed = true; + break; + } + + // process events if any otherwise sleep + if (noWait || XPending(display) > 0) { + while (!m_done && !m_failed && (noWait || XPending(display) > 0)) { + XNextEvent(display, &xevent); + if (!processEvent(display, &xevent)) { + // not processed so save it + events.push_back(xevent); + } + else { + // reset timer since we've made some progress + timeout.reset(); + + // don't sleep anymore, just block waiting for events. + // we're assuming here that the clipboard owner will + // complete the protocol correctly. if we continue to + // sleep we'll get very bad performance. + noWait = true; + } + } + } + else { + ARCH->sleep(0.01); + } + } + + // put unprocessed events back + for (UInt32 i = events.size(); i > 0; --i) { + XPutBackEvent(display, &events[i - 1]); + } + + // restore mask + XSelectInput(display, m_requestor, attr.your_event_mask); + + // return success or failure + LOG((CLOG_DEBUG1 "request %s", m_failed ? "failed" : "succeeded")); + return !m_failed; +} + +bool +CXWindowsClipboard::CICCCMGetClipboard::processEvent( + Display* display, XEvent* xevent) +{ + // process event + switch (xevent->type) { + case DestroyNotify: + if (xevent->xdestroywindow.window == m_requestor) { + m_failed = true; + return true; + } + + // not interested + return false; + + case SelectionNotify: + if (xevent->xselection.requestor == m_requestor) { + // done if we can't convert + if (xevent->xselection.property == None) { + m_done = true; + return true; + } + + // proceed if conversion successful + else if (xevent->xselection.property == m_property) { + m_reading = true; + break; + } + } + + // otherwise not interested + return false; + + case PropertyNotify: + // proceed if conversion successful and we're receiving more data + if (xevent->xproperty.window == m_requestor && + xevent->xproperty.atom == m_property && + xevent->xproperty.state == PropertyNewValue) { + if (!m_reading) { + // we haven't gotten the SelectionNotify yet + return true; + } + break; + } + + // otherwise not interested + return false; + + default: + // not interested + return false; + } + + // get the data from the property + Atom target; + const CString::size_type oldSize = m_data->size(); + if (!CXWindowsUtil::getWindowProperty(display, m_requestor, + m_property, m_data, &target, NULL, True)) { + // unable to read property + m_failed = true; + return true; + } + + // note if incremental. if we're already incremental then the + // selection owner is busted. if the INCR property has no size + // then the selection owner is busted. + if (target == XInternAtom(display, "INCR", False)) { + if (m_incr) { + m_failed = true; + m_error = true; + } + else if (m_data->size() == oldSize) { + m_failed = true; + m_error = true; + } + else { + m_incr = true; + + // discard INCR data + *m_data = ""; + } + } + + // handle incremental chunks + else if (m_incr) { + // if first incremental chunk then save target + if (oldSize == 0) { + LOG((CLOG_DEBUG1 " INCR first chunk, target %d", target)); + *m_actualTarget = target; + } + + // secondary chunks must have the same target + else { + if (target != *m_actualTarget) { + LOG((CLOG_WARN " INCR target mismatch")); + m_failed = true; + m_error = true; + } + } + + // note if this is the final chunk + if (m_data->size() == oldSize) { + LOG((CLOG_DEBUG1 " INCR final chunk: %d bytes total", m_data->size())); + m_done = true; + } + } + + // not incremental; save the target. + else { + LOG((CLOG_DEBUG1 " target %d", target)); + *m_actualTarget = target; + m_done = true; + } + + // this event has been processed + LOGC(!m_incr, (CLOG_DEBUG1 " got data, %d bytes", m_data->size())); + return true; +} + + +// +// CXWindowsClipboard::CReply +// + +CXWindowsClipboard::CReply::CReply(Window requestor, Atom target, ::Time time) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(None), + m_replied(false), + m_done(false), + m_data(), + m_type(None), + m_format(32), + m_ptr(0) +{ + // do nothing +} + +CXWindowsClipboard::CReply::CReply(Window requestor, Atom target, ::Time time, + Atom property, const CString& data, Atom type, int format) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(property), + m_replied(false), + m_done(false), + m_data(data), + m_type(type), + m_format(format), + m_ptr(0) +{ + // do nothing +} diff --git a/lib/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h new file mode 100644 index 0000000..25c221b --- /dev/null +++ b/lib/platform/CXWindowsClipboard.h @@ -0,0 +1,372 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARD_H +#define CXWINDOWSCLIPBOARD_H + +#include "IClipboard.h" +#include "ClipboardTypes.h" +#include "stdmap.h" +#include "stdlist.h" +#include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class IXWindowsClipboardConverter; + +//! X11 clipboard implementation +class CXWindowsClipboard : public IClipboard { +public: + /*! + Use \c window as the window that owns or interacts with the + clipboard identified by \c id. + */ + CXWindowsClipboard(Display*, Window window, ClipboardID id); + virtual ~CXWindowsClipboard(); + + //! Notify clipboard was lost + /*! + Tells clipboard it lost ownership at the given time. + */ + void lost(Time); + + //! Add clipboard request + /*! + Adds a selection request to the request list. If the given + owner window isn't this clipboard's window then this simply + sends a failure event to the requestor. + */ + void addRequest(Window owner, + Window requestor, Atom target, + ::Time time, Atom property); + + //! Process clipboard request + /*! + Continues processing a selection request. Returns true if the + request was handled, false if the request was unknown. + */ + bool processRequest(Window requestor, + ::Time time, Atom property); + + //! Cancel clipboard request + /*! + Terminate a selection request. Returns true iff the request + was known and handled. + */ + bool destroyRequest(Window requestor); + + //! Get window + /*! + Returns the clipboard's window (passed the c'tor). + */ + Window getWindow() const; + + //! Get selection atom + /*! + Returns the selection atom that identifies the clipboard to X11 + (e.g. XA_PRIMARY). + */ + Atom getSelection() const; + + // IClipboard overrides + virtual bool empty(); + virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + +private: + // remove all converters from our list + void clearConverters(); + + // get the converter for a clipboard format. returns NULL if no + // suitable converter. iff onlyIfNotAdded is true then also + // return NULL if a suitable converter was found but we already + // have data of the converter's clipboard format. + IXWindowsClipboardConverter* + getConverter(Atom target, + bool onlyIfNotAdded = false) const; + + // convert target atom to clipboard format + EFormat getFormat(Atom target) const; + + // add a non-MULTIPLE request. does not verify that the selection + // was owned at the given time. returns true if the conversion + // could be performed, false otherwise. in either case, the + // reply is inserted. + bool addSimpleRequest( + Window requestor, Atom target, + ::Time time, Atom property); + + // if not already checked then see if the cache is stale and, if so, + // clear it. this has the side effect of updating m_timeOwned. + void checkCache() const; + + // clear the cache, resetting the cached flag and the added flag for + // each format. + void clearCache() const; + void doClearCache(); + + // cache all formats of the selection + void fillCache() const; + void doFillCache(); + + // + // helper classes + // + + // read an ICCCM conforming selection + class CICCCMGetClipboard { + public: + CICCCMGetClipboard(Window requestor, Time time, Atom property); + ~CICCCMGetClipboard(); + + // convert the given selection to the given type. returns + // true iff the conversion was successful or the conversion + // cannot be performed (in which case *actualTarget == None). + bool readClipboard(Display* display, + Atom selection, Atom target, + Atom* actualTarget, CString* data); + + private: + bool processEvent(Display* display, XEvent* event); + + private: + Window m_requestor; + Time m_time; + Atom m_property; + bool m_incr; + bool m_failed; + bool m_done; + + // true iff we've received the selection notify + bool m_reading; + + // the converted selection data + CString* m_data; + + // the actual type of the data. if this is None then the + // selection owner cannot convert to the requested type. + Atom* m_actualTarget; + + public: + // true iff the selection owner didn't follow ICCCM conventions + bool m_error; + }; + + // Motif structure IDs + enum { kMotifClipFormat = 1, kMotifClipItem, kMotifClipHeader }; + + // _MOTIF_CLIP_HEADER structure + class CMotifClipHeader { + public: + SInt32 m_id; // kMotifClipHeader + SInt32 m_pad1[3]; + SInt32 m_item; + SInt32 m_pad2[4]; + SInt32 m_numItems; + SInt32 m_pad3[3]; + Window m_selectionOwner; + SInt32 m_pad4[2]; + }; + + // Motif clip item structure + class CMotifClipItem { + public: + SInt32 m_id; // kMotifClipItem + SInt32 m_pad1[5]; + SInt32 m_size; + SInt32 m_numFormats; + SInt32 m_numDeletedFormats; + SInt32 m_pad2[6]; + }; + + // Motif clip format structure + class CMotifClipFormat { + public: + SInt32 m_id; // kMotifClipFormat + SInt32 m_pad1[6]; + SInt32 m_length; + SInt32 m_data; + Atom m_type; + SInt32 m_pad2[1]; + int m_deleted; + SInt32 m_pad3[4]; + }; + + // stores data needed to respond to a selection request + class CReply { + public: + CReply(Window, Atom target, ::Time); + CReply(Window, Atom target, ::Time, Atom property, + const CString& data, Atom type, int format); + + public: + // information about the request + Window m_requestor; + Atom m_target; + ::Time m_time; + Atom m_property; + + // true iff we've sent the notification for this reply + bool m_replied; + + // true iff the reply has sent its last message + bool m_done; + + // the data to send and its type and format + CString m_data; + Atom m_type; + int m_format; + + // index of next byte in m_data to send + UInt32 m_ptr; + }; + typedef std::list CReplyList; + typedef std::map CReplyMap; + typedef std::map CReplyEventMask; + + // ICCCM interoperability methods + void icccmFillCache(); + bool icccmGetSelection(Atom target, + Atom* actualTarget, CString* data) const; + Time icccmGetTime() const; + + // motif interoperability methods + bool motifLockClipboard() const; + void motifUnlockClipboard() const; + bool motifOwnsClipboard() const; + void motifFillCache(); + bool motifGetSelection(const CMotifClipFormat*, + Atom* actualTarget, CString* data) const; + Time motifGetTime() const; + + // reply methods + bool insertMultipleReply(Window, ::Time, Atom); + void insertReply(CReply*); + void pushReplies(); + void pushReplies(CReplyMap::iterator, + CReplyList&, CReplyList::iterator); + bool sendReply(CReply*); + void clearReplies(); + void clearReplies(CReplyList&); + void sendNotify(Window requestor, Atom selection, + Atom target, Atom property, Time time); + bool wasOwnedAtTime(::Time) const; + + // data conversion methods + Atom getTargetsData(CString&, int* format) const; + Atom getTimestampData(CString&, int* format) const; + +private: + typedef std::vector ConverterList; + + Display* m_display; + Window m_window; + ClipboardID m_id; + Atom m_selection; + mutable bool m_open; + mutable Time m_time; + bool m_owner; + mutable Time m_timeOwned; + Time m_timeLost; + + // true iff open and clipboard owned by a motif app + mutable bool m_motif; + + // the added/cached clipboard data + mutable bool m_checkCache; + bool m_cached; + Time m_cacheTime; + bool m_added[kNumFormats]; + CString m_data[kNumFormats]; + + // conversion request replies + CReplyMap m_replies; + CReplyEventMask m_eventMasks; + + // clipboard format converters + ConverterList m_converters; + + // atoms we'll need + Atom m_atomTargets; + Atom m_atomMultiple; + Atom m_atomTimestamp; + Atom m_atomInteger; + Atom m_atomAtom; + Atom m_atomAtomPair; + Atom m_atomData; + Atom m_atomINCR; + Atom m_atomMotifClipLock; + Atom m_atomMotifClipHeader; + Atom m_atomMotifClipAccess; + Atom m_atomGDKSelection; +}; + +//! Clipboard format converter interface +/*! +This interface defines the methods common to all X11 clipboard format +converters. +*/ +class IXWindowsClipboardConverter : public IInterface { +public: + //! @name accessors + //@{ + + //! Get clipboard format + /*! + Return the clipboard format this object converts from/to. + */ + virtual IClipboard::EFormat + getFormat() const = 0; + + //! Get X11 format atom + /*! + Return the atom representing the X selection format that + this object converts from/to. + */ + virtual Atom getAtom() const = 0; + + //! Get X11 property datum size + /*! + Return the size (in bits) of data elements returned by + toIClipboard(). + */ + virtual int getDataSize() const = 0; + + //! Convert from IClipboard format + /*! + Convert from the IClipboard format to the X selection format. + The input data must be in the IClipboard format returned by + getFormat(). The return data will be in the X selection + format returned by getAtom(). + */ + virtual CString fromIClipboard(const CString&) const = 0; + + //! Convert to IClipboard format + /*! + Convert from the X selection format to the IClipboard format + (i.e., the reverse of fromIClipboard()). + */ + virtual CString toIClipboard(const CString&) const = 0; + + //@} +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardTextConverter.cpp b/lib/platform/CXWindowsClipboardTextConverter.cpp new file mode 100644 index 0000000..bd1a520 --- /dev/null +++ b/lib/platform/CXWindowsClipboardTextConverter.cpp @@ -0,0 +1,74 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardTextConverter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardTextConverter +// + +CXWindowsClipboardTextConverter::CXWindowsClipboardTextConverter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardTextConverter::~CXWindowsClipboardTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardTextConverter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardTextConverter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardTextConverter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToText(data); +} + +CString +CXWindowsClipboardTextConverter::toIClipboard(const CString& data) const +{ + // convert to UTF-8 + bool errors; + CString utf8 = CUnicode::textToUTF8(data, &errors); + + // if there were decoding errors then, to support old applications + // that don't understand UTF-8 but can report the exact binary + // UTF-8 representation, see if the data appears to be UTF-8. if + // so then use it as is. + if (errors && CUnicode::isUTF8(data)) { + return data; + } + + return utf8; +} diff --git a/lib/platform/CXWindowsClipboardTextConverter.h b/lib/platform/CXWindowsClipboardTextConverter.h new file mode 100644 index 0000000..3840b7d --- /dev/null +++ b/lib/platform/CXWindowsClipboardTextConverter.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDTEXTCONVERTER_H +#define CXWINDOWSCLIPBOARDTEXTCONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from locale text encoding +class CXWindowsClipboardTextConverter : public IXWindowsClipboardConverter { +public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ + CXWindowsClipboardTextConverter(Display* display, const char* name); + virtual ~CXWindowsClipboardTextConverter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardUCS2Converter.cpp b/lib/platform/CXWindowsClipboardUCS2Converter.cpp new file mode 100644 index 0000000..86b8d13 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUCS2Converter.cpp @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardUCS2Converter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardUCS2Converter +// + +CXWindowsClipboardUCS2Converter::CXWindowsClipboardUCS2Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUCS2Converter::~CXWindowsClipboardUCS2Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUCS2Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUCS2Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUCS2Converter::getDataSize() const +{ + return 16; +} + +CString +CXWindowsClipboardUCS2Converter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToUCS2(data); +} + +CString +CXWindowsClipboardUCS2Converter::toIClipboard(const CString& data) const +{ + return CUnicode::UCS2ToUTF8(data); +} diff --git a/lib/platform/CXWindowsClipboardUCS2Converter.h b/lib/platform/CXWindowsClipboardUCS2Converter.h new file mode 100644 index 0000000..e848e30 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUCS2Converter.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDUCS2CONVERTER_H +#define CXWINDOWSCLIPBOARDUCS2CONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from UCS-2 encoding +class CXWindowsClipboardUCS2Converter : public IXWindowsClipboardConverter { +public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ + CXWindowsClipboardUCS2Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUCS2Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardUTF8Converter.cpp b/lib/platform/CXWindowsClipboardUTF8Converter.cpp new file mode 100644 index 0000000..7edc850 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUTF8Converter.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardUTF8Converter.h" + +// +// CXWindowsClipboardUTF8Converter +// + +CXWindowsClipboardUTF8Converter::CXWindowsClipboardUTF8Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUTF8Converter::~CXWindowsClipboardUTF8Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUTF8Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUTF8Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUTF8Converter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardUTF8Converter::fromIClipboard(const CString& data) const +{ + return data; +} + +CString +CXWindowsClipboardUTF8Converter::toIClipboard(const CString& data) const +{ + return data; +} diff --git a/lib/platform/CXWindowsClipboardUTF8Converter.h b/lib/platform/CXWindowsClipboardUTF8Converter.h new file mode 100644 index 0000000..5ac8b15 --- /dev/null +++ b/lib/platform/CXWindowsClipboardUTF8Converter.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDUTF8CONVERTER_H +#define CXWINDOWSCLIPBOARDUTF8CONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from UTF-8 encoding +class CXWindowsClipboardUTF8Converter : public IXWindowsClipboardConverter { +public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ + CXWindowsClipboardUTF8Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUTF8Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp new file mode 100644 index 0000000..360bea8 --- /dev/null +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -0,0 +1,1044 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsPrimaryScreen.h" +#include "CXWindowsScreen.h" +#include "CXWindowsUtil.h" +#include "IPrimaryScreenReceiver.h" +#include "XScreen.h" +#include "CThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# define XK_MISCELLANY +# define XK_XKB_KEYS +# include +#endif +#include "CArch.h" + +// +// CXWindowsPrimaryScreen +// + +CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( + IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) : + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), + m_window(None), + m_im(NULL), + m_ic(NULL), + m_lastKeycode(0) +{ + m_screen = new CXWindowsScreen(receiver, this); +} + +CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() +{ + assert(m_window == None); + delete m_screen; +} + +void +CXWindowsPrimaryScreen::reconfigure(UInt32) +{ + // do nothing +} + +void +CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + CDisplayLock display(m_screen); + + // warp mouse + warpCursorNoFlush(display, x, y); + + // remove all input events before and including warp + XEvent event; + while (XCheckMaskEvent(display, PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) { + // do nothing + } + + // save position as last position + m_x = x; + m_y = y; +} + +void +CXWindowsPrimaryScreen::resetOptions() +{ + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +} + +void +CXWindowsPrimaryScreen::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } + } +} + +UInt32 +CXWindowsPrimaryScreen::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + +KeyModifierMask +CXWindowsPrimaryScreen::getToggleMask() const +{ + CDisplayLock display(m_screen); + + // query the pointer to get the keyboard state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + return 0; + } + + // convert to KeyModifierMask + KeyModifierMask mask = 0; + if (state & m_numLockMask) { + mask |= KeyModifierNumLock; + } + if (state & m_capsLockMask) { + mask |= KeyModifierCapsLock; + } + if (state & m_scrollLockMask) { + mask |= KeyModifierScrollLock; + } + + return mask; +} + +bool +CXWindowsPrimaryScreen::isLockedToScreen() const +{ + CDisplayLock display(m_screen); + + // query the pointer to get the button state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + if ((state & (Button1Mask | Button2Mask | Button3Mask | + Button4Mask | Button5Mask)) != 0) { + LOG((CLOG_DEBUG "locked by mouse button")); + return true; + } + } + + // get logical keyboard state + char keyMap[32]; + memset(keyMap, 0, sizeof(keyMap)); + XQueryKeymap(display, keyMap); + + // locked if any key is down + for (unsigned int i = 0; i < sizeof(keyMap); ++i) { + if (keyMap[i] != 0) { + for (unsigned int j = 0; j < 8; ++j) { + if ((keyMap[i] & (1 << j)) != 0) { + const KeyCode keycode = 8 * i + j; + const KeySym keysym = XKeycodeToKeysym(display, + keycode, 0); + // if any key is half-duplex then it'll be down when + // toggled on but shouldn't count as a reason to lock + // to the screen. + if (m_numLockHalfDuplex && keysym == XK_Num_Lock) { + continue; + } + if (m_capsLockHalfDuplex && keysym == XK_Caps_Lock) { + continue; + } + + // non-half-duplex key down + char* name = XKeysymToString(keysym); + if (name == NULL) { + LOG((CLOG_DEBUG "locked by keycode %d", keycode)); + } + else { + LOG((CLOG_DEBUG "locked by \"%s\"", name)); + } + return true; + } + } + } + } + + // not locked + return false; +} + +IScreen* +CXWindowsPrimaryScreen::getScreen() const +{ + return m_screen; +} + +void +CXWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + +bool +CXWindowsPrimaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CXWindowsPrimaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + XEvent& xevent = event->m_event; + + // let input methods try to handle event first + if (m_ic != NULL) { + // XFilterEvent() may eat the event and generate a new KeyPress + // event with a keycode of 0 because there isn't an actual key + // associated with the keysym. but the KeyRelease may pass + // through XFilterEvent() and keep its keycode. this means + // there's a mismatch between KeyPress and KeyRelease keycodes. + // since we use the keycode on the client to detect when a key + // is released this won't do. so we remember the keycode on + // the most recent KeyPress (and clear it on a matching + // KeyRelease) so we have a keycode for a synthesized KeyPress. + if (xevent.type == KeyPress && xevent.xkey.keycode != 0) { + m_lastKeycode = xevent.xkey.keycode; + } + else if (xevent.type == KeyRelease && + xevent.xkey.keycode == m_lastKeycode) { + m_lastKeycode = 0; + } + + // now filter the event + if (XFilterEvent(&xevent, None)) { + return true; + } + } + + // handle event + switch (xevent.type) { + case CreateNotify: + { + // select events on new window + CDisplayLock display(m_screen); + selectEvents(display, xevent.xcreatewindow.window); + } + return true; + + case MappingNotify: + // keyboard mapping changed + updateKeys(); + return true; + + case KeyPress: + { + LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyBreak) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + } + + // get which button. see call to XFilterEvent() above + // for more info. + KeyCode keycode = xevent.xkey.keycode; + if (keycode == 0) { + keycode = m_lastKeycode; + } + + // handle key + m_receiver->onKeyDown(key, mask, + static_cast(keycode)); + if (key == kKeyCapsLock && m_capsLockHalfDuplex) { + m_receiver->onKeyUp(key, mask | KeyModifierCapsLock, + static_cast(keycode)); + } + else if (key == kKeyNumLock && m_numLockHalfDuplex) { + m_receiver->onKeyUp(key, mask | KeyModifierNumLock, + static_cast(keycode)); + } + } + } + return true; + + case KeyRelease: + { + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + // check if this is a key repeat by getting the next + // KeyPress event that has the same key and time as + // this release event, if any. first prepare the + // filter info. + CKeyEventInfo filter; + filter.m_event = KeyPress; + filter.m_window = xevent.xkey.window; + filter.m_time = xevent.xkey.time; + filter.m_keycode = xevent.xkey.keycode; + + // now check for event + bool hasPress; + { + XEvent xevent2; + CDisplayLock display(m_screen); + hasPress = (XCheckIfEvent(display, &xevent2, + &CXWindowsPrimaryScreen::findKeyEvent, + (XPointer)&filter) == True); + } + + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyBreak) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del and ignore autorepeat + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + hasPress = false; + } + + + if (!hasPress) { + // no press event follows so it's a plain release + LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + if (key == kKeyCapsLock && m_capsLockHalfDuplex) { + m_receiver->onKeyDown(key, mask, + static_cast(xevent.xkey.keycode)); + } + else if (key == kKeyNumLock && m_numLockHalfDuplex) { + m_receiver->onKeyDown(key, mask, + static_cast(xevent.xkey.keycode)); + } + m_receiver->onKeyUp(key, mask, + static_cast(xevent.xkey.keycode)); + } + else { + // found a press event following so it's a repeat. + // we could attempt to count the already queued + // repeats but we'll just send a repeat of 1. + // note that we discard the press event. + LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + m_receiver->onKeyRepeat(key, mask, 1, + static_cast(xevent.xkey.keycode)); + } + } + } + return true; + + case ButtonPress: + { + LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_receiver->onMouseDown(button); + } + } + return true; + + case ButtonRelease: + { + LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_receiver->onMouseUp(button); + } + else if (xevent.xbutton.button == 4) { + // wheel forward (away from user) + m_receiver->onMouseWheel(120); + } + else if (xevent.xbutton.button == 5) { + // wheel backward (toward user) + m_receiver->onMouseWheel(-120); + } + } + return true; + + case MotionNotify: + { + LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = xevent.xmotion.x_root - m_x; + SInt32 y = xevent.xmotion.y_root - m_y; + + // save position to compute delta of next motion + m_x = xevent.xmotion.x_root; + m_y = xevent.xmotion.y_root; + + if (xevent.xmotion.send_event) { + // we warped the mouse. discard events until we + // find the matching sent event. see + // warpCursorNoFlush() for where the events are + // sent. we discard the matching sent event and + // can be sure we've skipped the warp event. + CDisplayLock display(m_screen); + do { + XMaskEvent(display, PointerMotionMask, &xevent); + } while (!xevent.xmotion.send_event); + } + else if (!isActive()) { + // motion on primary screen + m_receiver->onMouseMovePrimary(m_x, m_y); + } + else { + // motion on secondary screen. warp mouse back to + // center. + // + // my lombard (powerbook g3) running linux and + // using the adbmouse driver has two problems: + // first, the driver only sends motions of +/-2 + // pixels and, second, it seems to discard some + // physical input after a warp. the former isn't a + // big deal (we're just limited to every other + // pixel) but the latter is a PITA. to work around + // it we only warp when the mouse has moved more + // than s_size pixels from the center. + static const SInt32 s_size = 32; + if (xevent.xmotion.x_root - m_xCenter < -s_size || + xevent.xmotion.x_root - m_xCenter > s_size || + xevent.xmotion.y_root - m_yCenter < -s_size || + xevent.xmotion.y_root - m_yCenter > s_size) { + CDisplayLock display(m_screen); + warpCursorNoFlush(display, m_xCenter, m_yCenter); + } + + // send event if mouse moved. do this after warping + // back to center in case the motion takes us onto + // the primary screen. if we sent the event first + // in that case then the warp would happen after + // warping to the primary screen's enter position, + // effectively overriding it. + if (x != 0 || y != 0) { + m_receiver->onMouseMoveSecondary(x, y); + } + } + } + return true; + } + + return false; +} + +void +CXWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) +{ + m_receiver->onOneShotTimerExpired(id); +} + +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CXWindowsPrimaryScreen::onPreMainLoop() +{ + assert(m_window != None); +} + +void +CXWindowsPrimaryScreen::onPreOpen() +{ + assert(m_window == None); +} + +void +CXWindowsPrimaryScreen::onPostOpen() +{ + assert(m_window != None); + + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // get the input method + CDisplayLock display(m_screen); + m_im = XOpenIM(display, NULL, NULL, NULL); + if (m_im == NULL) { + return; + } + + // find the appropriate style. synergy supports XIMPreeditNothing + // only at the moment. + XIMStyles* styles; + if (XGetIMValues(m_im, XNQueryInputStyle, &styles, NULL) != NULL || + styles == NULL) { + LOG((CLOG_WARN "cannot get IM styles")); + return; + } + XIMStyle style = 0; + for (unsigned short i = 0; i < styles->count_styles; ++i) { + style = styles->supported_styles[i]; + if ((style & XIMPreeditNothing) != 0) { + if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) { + break; + } + } + } + XFree(styles); + if (style == 0) { + LOG((CLOG_WARN "no supported IM styles")); + return; + } + + // create an input context for the style and tell it about our window + m_ic = XCreateIC(m_im, XNInputStyle, style, XNClientWindow, m_window, NULL); + if (m_ic == NULL) { + LOG((CLOG_WARN "cannot create IC")); + return; + } + + // find out the events we must select for and do so + unsigned long mask; + if (XGetICValues(m_ic, XNFilterEvents, &mask, NULL) != NULL) { + LOG((CLOG_WARN "cannot get IC filter events")); + return; + } + XWindowAttributes attr; + XGetWindowAttributes(display, m_window, &attr); + XSelectInput(display, m_window, attr.your_event_mask | mask); + + // no previous keycode + m_lastKeycode = 0; +} + +void +CXWindowsPrimaryScreen::onPreClose() +{ + CDisplayLock display(m_screen); + if (m_ic != NULL) { + XDestroyIC(m_ic); + m_ic = NULL; + } + if (m_im != NULL) { + XCloseIM(m_im); + m_im = NULL; + } + m_lastKeycode = 0; +} + +void +CXWindowsPrimaryScreen::onPreEnter() +{ + assert(m_window != None); + + if (m_ic != NULL) { + XUnsetICFocus(m_ic); + } +} + +void +CXWindowsPrimaryScreen::onPreLeave() +{ + assert(m_window != None); + + if (m_ic != NULL) { + XmbResetIC(m_ic); + XSetICFocus(m_ic); + } +} + +void +CXWindowsPrimaryScreen::onEnterScreenSaver() +{ + CDisplayLock display(m_screen); + + // set keyboard focus to root window. the screensaver should then + // pick up key events for when the user enters a password to unlock. + XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); +} + +void +CXWindowsPrimaryScreen::createWindow() +{ + assert(m_window == None); + + // get size of screen + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + + // grab window attributes. this window is used to capture user + // input when the user is focused on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask | PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = m_screen->getBlankCursor(); + + { + // create the grab window + CDisplayLock display(m_screen); + m_window = XCreateWindow(display, m_screen->getRoot(), + x, y, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); + + // start watching for events on other windows + selectEvents(display, m_screen->getRoot()); + } + + // tell generic screen about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsPrimaryScreen::destroyWindow() +{ + // display can be NULL if the server unexpectedly disconnected + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } +} + +bool +CXWindowsPrimaryScreen::showWindow() +{ + assert(m_window != None); + + CDisplayLock display(m_screen); + + // raise and show the input window + XMapRaised(display, m_window); + + // grab the mouse and keyboard. keep trying until we get them. + // if we can't grab one after grabbing the other then ungrab + // and wait before retrying. give up after s_timeout seconds. + static const double s_timeout = 1.0; + int result; + CStopwatch timer; + do { + // keyboard first + do { + result = XGrabKeyboard(display, m_window, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + LOG((CLOG_DEBUG2 "waiting to grab keyboard")); + ARCH->sleep(0.05); + if (timer.getTime() >= s_timeout) { + LOG((CLOG_DEBUG2 "grab keyboard timed out")); + XUnmapWindow(display, m_window); + return false; + } + } + } while (result != GrabSuccess); + LOG((CLOG_DEBUG2 "grabbed keyboard")); + + // now the mouse + result = XGrabPointer(display, m_window, True, 0, + GrabModeAsync, GrabModeAsync, + m_window, None, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + // back off to avoid grab deadlock + XUngrabKeyboard(display, CurrentTime); + LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); + ARCH->sleep(0.05); + if (timer.getTime() >= s_timeout) { + LOG((CLOG_DEBUG2 "grab pointer timed out")); + XUnmapWindow(display, m_window); + return false; + } + } + } while (result != GrabSuccess); + LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); + + return true; +} + +void +CXWindowsPrimaryScreen::hideWindow() +{ + CDisplayLock display(m_screen); + + // unmap the grab window. this also ungrabs the mouse and keyboard. + XUnmapWindow(display, m_window); +} + +void +CXWindowsPrimaryScreen::warpCursorToCenter() +{ + warpCursor(m_xCenter, m_yCenter); +} + +void +CXWindowsPrimaryScreen::warpCursorNoFlush( + Display* display, SInt32 x, SInt32 y) +{ + assert(display != NULL); + assert(m_window != None); + + // send an event that we can recognize before the mouse warp + XEvent eventBefore; + eventBefore.type = MotionNotify; + eventBefore.xmotion.display = display; + eventBefore.xmotion.window = m_window; + eventBefore.xmotion.root = m_screen->getRoot(); + eventBefore.xmotion.subwindow = m_window; + eventBefore.xmotion.time = CurrentTime; + eventBefore.xmotion.x = x; + eventBefore.xmotion.y = y; + eventBefore.xmotion.x_root = x; + eventBefore.xmotion.y_root = y; + eventBefore.xmotion.state = 0; + eventBefore.xmotion.is_hint = NotifyNormal; + eventBefore.xmotion.same_screen = True; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &eventBefore); + + // warp mouse + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); + + // send an event that we can recognize after the mouse warp + XSendEvent(display, m_window, False, 0, &eventAfter); + XSync(display, False); + + LOG((CLOG_DEBUG2 "warped to %d,%d", x, y)); +} + +void +CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const +{ + // ignore errors while we adjust event masks. windows could be + // destroyed at any time after the XQueryTree() in doSelectEvents() + // so we must ignore BadWindow errors. + CXWindowsUtil::CErrorLock lock(display); + + // adjust event masks + doSelectEvents(display, w); +} + +void +CXWindowsPrimaryScreen::doSelectEvents(Display* display, Window w) const +{ + // we want to track the mouse everywhere on the display. to achieve + // that we select PointerMotionMask on every window. we also select + // SubstructureNotifyMask in order to get CreateNotify events so we + // select events on new windows too. + // + // note that this can break certain clients due a design flaw of X. + // X will deliver a PointerMotion event to the deepest window in the + // hierarchy that contains the pointer and has PointerMotionMask + // selected by *any* client. if another client doesn't select + // motion events in a subwindow so the parent window will get them + // then by selecting for motion events on the subwindow we break + // that client because the parent will no longer get the events. + + // FIXME -- should provide some workaround for event selection + // design flaw. perhaps only select for motion events on windows + // that already do or are top-level windows or don't propagate + // pointer events. or maybe an option to simply poll the mouse. + + // we don't want to adjust our grab window + if (w == m_window) { + return; + } + + // select events of interest. do this before querying the tree so + // we'll get notifications of children created after the XQueryTree() + // so we won't miss them. + XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); + + // recurse on child windows + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + doSelectEvents(display, cw[i]); + } + XFree(cw); + } +} + +KeyModifierMask +CXWindowsPrimaryScreen::mapModifier(unsigned int state) const +{ + KeyModifierMask mask = 0; + if (state & ShiftMask) + mask |= KeyModifierShift; + if (state & LockMask) + mask |= KeyModifierCapsLock; + if (state & ControlMask) + mask |= KeyModifierControl; + if (state & m_altMask) + mask |= KeyModifierAlt; + if (state & m_metaMask) + mask |= KeyModifierMeta; + if (state & m_superMask) + mask |= KeyModifierSuper; + if (state & m_modeSwitchMask) + mask |= KeyModifierModeSwitch; + if (state & m_numLockMask) + mask |= KeyModifierNumLock; + if (state & m_scrollLockMask) + mask |= KeyModifierScrollLock; + return mask; +} + +// map "Internet" keys to KeyIDs +static const KeySym g_map1008FF[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, + /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, + /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, + /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, 0, 0, 0, 0, + /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; + +KeyID +CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const +{ + CDisplayLock display(m_screen); + + // convert to a keysym + KeySym keysym; + if (event->type == KeyPress && m_ic != NULL) { + // do multibyte lookup. can only call XmbLookupString with a + // key press event and a valid XIC so we checked those above. + char scratch[32]; + int n = sizeof(scratch) / sizeof(scratch[0]); + char* buffer = scratch; + int status; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + if (status == XBufferOverflow) { + // not enough space. grow buffer and try again. + buffer = new char[n]; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + delete[] buffer; + } + + // see what we got. since we don't care about the string + // we'll just look for a keysym. + switch (status) { + default: + case XLookupNone: + case XLookupChars: + keysym = 0; + break; + + case XLookupKeySym: + case XLookupBoth: + break; + } + } + else { + // plain old lookup + char dummy[1]; + XLookupString(event, dummy, 0, &keysym, NULL); + } + + // convert key + switch (keysym & 0xffffff00) { + case 0x0000: + // Latin-1 + return static_cast(keysym); + + case 0xfe00: + // ISO 9995 Function and Modifier Keys + if (keysym == XK_ISO_Left_Tab) { + return kKeyLeftTab; + } + return kKeyNone; + + case 0xff00: + // MISCELLANY + return static_cast(keysym - 0xff00 + 0xef00); + + case 0x1008ff00: + // "Internet" keys + return g_map1008FF[keysym & 0xff]; + + default: { + // lookup character in table + UInt32 key = CXWindowsUtil::mapKeySymToUCS4(keysym); + if (key != 0x0000ffff) { + return static_cast(key); + } + + // unknown character + return kKeyNone; + } + } +} + +ButtonID +CXWindowsPrimaryScreen::mapButton(unsigned int button) const +{ + // first three buttons map to 1, 2, 3 (kButtonLeft, Middle, Right) + if (button >= 1 && button <= 3) { + return static_cast(button); + } + + // buttons 4 and 5 are ignored here. they're used for the wheel. + // buttons 6, 7, etc and up map to 4, 5, etc. + else if (button >= 6) { + return static_cast(button - 2); + } + + // unknown button + else { + return kButtonNone; + } +} + +void +CXWindowsPrimaryScreen::updateKeys() +{ + CDisplayLock display(m_screen); + + // get modifier map from server + XModifierKeymap* keymap = XGetModifierMapping(display); + + // initialize + m_altMask = 0; + m_metaMask = 0; + m_superMask = 0; + m_modeSwitchMask = 0; + m_numLockMask = 0; + m_capsLockMask = 0; + m_scrollLockMask = 0; + + // work around for my system, which reports this state bit when + // mode switch is down, instead of the appropriate modifier bit. + // should have no effect on other systems. -crs 9/02. + m_modeSwitchMask |= (1 << 13); + + // set keycodes and masks + for (unsigned int i = 0; i < 8; ++i) { + const unsigned int bit = (1 << i); + for (int j = 0; j < keymap->max_keypermod; ++j) { + KeyCode keycode = keymap->modifiermap[i * + keymap->max_keypermod + j]; + + // note mask for particular modifiers + const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); + switch (keysym) { + case XK_Alt_L: + case XK_Alt_R: + m_altMask |= bit; + break; + + case XK_Meta_L: + case XK_Meta_R: + m_metaMask |= bit; + break; + + case XK_Super_L: + case XK_Super_R: + m_superMask |= bit; + break; + + case XK_Mode_switch: + m_modeSwitchMask |= bit; + break; + + case XK_Num_Lock: + m_numLockMask |= bit; + break; + + case XK_Caps_Lock: + m_capsLockMask |= bit; + break; + + case XK_Scroll_Lock: + m_scrollLockMask |= bit; + break; + } + } + } + + XFreeModifiermap(keymap); +} + +Bool +CXWindowsPrimaryScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) +{ + CKeyEventInfo* filter = reinterpret_cast(arg); + return (xevent->type == filter->m_event && + xevent->xkey.window == filter->m_window && + xevent->xkey.time == filter->m_time && + xevent->xkey.keycode == filter->m_keycode) ? True : False; +} diff --git a/lib/platform/CXWindowsPrimaryScreen.h b/lib/platform/CXWindowsPrimaryScreen.h new file mode 100644 index 0000000..2343ca0 --- /dev/null +++ b/lib/platform/CXWindowsPrimaryScreen.h @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSPRIMARYSCREEN_H +#define CXWINDOWSPRIMARYSCREEN_H + +#include "CPrimaryScreen.h" +#include "IScreenEventHandler.h" +#include "MouseTypes.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class CXWindowsScreen; +class IScreenReceiver; +class IPrimaryScreenReceiver; + +//! X11 primary screen implementation +class CXWindowsPrimaryScreen : + public CPrimaryScreen, public IScreenEventHandler { +public: + CXWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); + virtual ~CXWindowsPrimaryScreen(); + + // CPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual UInt32 addOneShotTimer(double timeout); + virtual KeyModifierMask getToggleMask() const; + virtual bool isLockedToScreen() const; + virtual IScreen* getScreen() const; + + // IScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + +protected: + // CPrimaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void onEnterScreenSaver(); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); + +private: + void warpCursorNoFlush(Display*, + SInt32 xAbsolute, SInt32 yAbsolute); + + void selectEvents(Display*, Window) const; + void doSelectEvents(Display*, Window) const; + + KeyModifierMask mapModifier(unsigned int state) const; + KeyID mapKey(XKeyEvent*) const; + ButtonID mapButton(unsigned int button) const; + + class CKeyEventInfo { + public: + int m_event; + Window m_window; + Time m_time; + KeyCode m_keycode; + }; + static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); + +private: + CXWindowsScreen* m_screen; + IPrimaryScreenReceiver* m_receiver; + + // our window + Window m_window; + + // note toggle keys that toggle on up/down (false) or on + // transition (true) + bool m_numLockHalfDuplex; + bool m_capsLockHalfDuplex; + + // modifier masks + unsigned int m_altMask; + unsigned int m_metaMask; + unsigned int m_superMask; + unsigned int m_modeSwitchMask; + unsigned int m_numLockMask; + unsigned int m_capsLockMask; + unsigned int m_scrollLockMask; + + // last mouse position + SInt32 m_x, m_y; + + // position of center pixel of screen + SInt32 m_xCenter, m_yCenter; + + // input method stuff + XIM m_im; + XIC m_ic; + KeyCode m_lastKeycode; +}; + +#endif diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp new file mode 100644 index 0000000..870da6d --- /dev/null +++ b/lib/platform/CXWindowsScreen.cpp @@ -0,0 +1,922 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsScreen.h" +#include "CXWindowsClipboard.h" +#include "CXWindowsScreenSaver.h" +#include "CXWindowsUtil.h" +#include "CClipboard.h" +#include "IScreenEventHandler.h" +#include "IScreenReceiver.h" +#include "XScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" +#include "IJob.h" +#include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# if HAVE_X11_EXTENSIONS_XINERAMA_H + // Xinerama.h may lack extern "C" for inclusion by C++ + extern "C" { +# include + } +# endif +#endif +#if UNIX_LIKE +# if HAVE_POLL +# include +# else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +# endif +#endif + +// +// CXWindowsScreen::CTimer +// + +CXWindowsScreen::CTimer::CTimer(IJob* job, double startTime, double resetTime) : + m_job(job), + m_timeout(resetTime), + m_time(resetTime), + m_startTime(startTime) +{ + assert(m_timeout > 0.0); +} + +CXWindowsScreen::CTimer::~CTimer() +{ + // do nothing +} + +void +CXWindowsScreen::CTimer::run() +{ + if (m_job != NULL) { + m_job->run(); + } +} + +void +CXWindowsScreen::CTimer::reset() +{ + m_time = m_timeout; + m_startTime = 0.0; +} + +CXWindowsScreen::CTimer::CTimer& +CXWindowsScreen::CTimer::operator-=(double dt) +{ + m_time -= dt - m_startTime; + m_startTime = 0.0; + return *this; +} + +CXWindowsScreen::CTimer::operator double() const +{ + return m_time; +} + +bool +CXWindowsScreen::CTimer::operator<(const CTimer& t) const +{ + return m_time < t.m_time; +} + + +// +// CXWindowsScreen +// + +CXWindowsScreen* CXWindowsScreen::s_screen = NULL; + +CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, + IScreenEventHandler* eventHandler) : + m_display(NULL), + m_root(None), + m_stop(false), + m_receiver(receiver), + m_eventHandler(eventHandler), + m_window(None), + m_x(0), m_y(0), + m_w(0), m_h(0), + m_xCenter(0), m_yCenter(0), + m_screensaver(NULL), + m_screensaverNotify(false), + m_atomScreensaver(None), + m_oneShotTimer(NULL) +{ + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + + s_screen = this; + + // no clipboards to start with + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = NULL; + } +} + +CXWindowsScreen::~CXWindowsScreen() +{ + assert(s_screen != NULL); + assert(m_display == NULL); + + delete m_oneShotTimer; + s_screen = NULL; +} + +void +CXWindowsScreen::addTimer(IJob* job, double timeout) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); + m_timers.push(CTimer(job, m_time.getTime(), timeout)); +} + +void +CXWindowsScreen::removeTimer(IJob* job) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); +} + +void +CXWindowsScreen::removeTimerNoLock(IJob* job) +{ + // do it the hard way. first collect all jobs that are not + // the removed job. + CTimerPriorityQueue::container_type tmp; + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + if (index->getJob() != job) { + tmp.push_back(*index); + } + } + + // now swap in the new list + m_timers.swap(tmp); +} + +UInt32 +CXWindowsScreen::addOneShotTimer(double timeout) +{ + CLock lock(&m_timersMutex); + // FIXME -- support multiple one-shot timers + m_oneShotTimer = new CTimer(NULL, m_time.getTime(), timeout); + return 0; +} + +void +CXWindowsScreen::setWindow(Window window) +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + // destroy the clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // save the new window + m_window = window; + + // initialize the clipboards + if (m_window != None) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); + } + } +} + +Window +CXWindowsScreen::getRoot() const +{ + assert(m_display != NULL); + return m_root; +} + +Cursor +CXWindowsScreen::getBlankCursor() const +{ + return m_cursor; +} + +void +CXWindowsScreen::open() +{ + assert(m_display == NULL); + + // set the X I/O error handler so we catch the display disconnecting + XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + + // get the DISPLAY + const char* display = getenv("DISPLAY"); + if (display == NULL) { + display = ":0.0"; + } + + // open the display + LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); + m_display = XOpenDisplay(display); + if (m_display == NULL) { + throw XScreenUnavailable(60.0); + } + + // get root window + m_root = DefaultRootWindow(m_display); + + // create the transparent cursor + createBlankCursor(); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_atomScreensaver = XInternAtom(m_display, "SYNERGY_SCREENSAVER", False); + m_screensaver = new CXWindowsScreenSaver(this, m_display); +} + +void +CXWindowsScreen::mainLoop() +{ + // wait for an event in a cancellable way and don't lock the + // display while we're waiting. + CEvent event; + m_mutex.lock(); + +#if UNIX_LIKE + + // use poll() to wait for a message from the X server or for timeout. + // this is a good deal more efficient than polling and sleeping. +#if HAVE_POLL + struct pollfd pfds[1]; + pfds[0].fd = ConnectionNumber(m_display); + pfds[0].events = POLLIN; +#endif + while (!m_stop) { + // compute timeout to next timer + double dtimeout; + { + CLock timersLock(&m_timersMutex); + dtimeout = (m_timers.empty() ? -1.0 : m_timers.top()); + if (m_oneShotTimer != NULL && + (dtimeout == -1.0 || *m_oneShotTimer < dtimeout)) { + dtimeout = *m_oneShotTimer; + } + } +#if HAVE_POLL + int timeout = static_cast(1000.0 * dtimeout); +#else + struct timeval timeout; + struct timeval* timeoutPtr; + if (dtimeout < 0.0) { + timeoutPtr = NULL; + } + else { + timeout.tv_sec = static_cast(dtimeout); + timeout.tv_usec = static_cast(1.0e+6 * + (dtimeout - timeout.tv_sec)); + timeoutPtr = &timeout; + } + + // initialize file descriptor sets + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(ConnectionNumber(m_display), &rfds); +#endif + + // wait for message from X server or for timeout. also check + // if the thread has been cancelled. poll() should return -1 + // with EINTR when the thread is cancelled. + m_mutex.unlock(); + CThread::testCancel(); +#if HAVE_POLL + poll(pfds, 1, timeout); +#else + select(ConnectionNumber(m_display) + 1, + SELECT_TYPE_ARG234 &rfds, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 timeoutPtr); +#endif + CThread::testCancel(); + m_mutex.lock(); + + // process timers + processTimers(); + + // handle pending events + while (!m_stop && XPending(m_display) > 0) { + // get the event + XNextEvent(m_display, &event.m_event); + + // process the event. if unhandled then let the subclass + // have a go at it. + m_mutex.unlock(); + if (!onPreDispatch(&event)) { + m_eventHandler->onEvent(&event); + } + m_mutex.lock(); + } + } + +#else // !UNIX_LIKE + + // poll and sleep + while (!m_stop) { + // poll for pending events and process timers + while (!m_stop && XPending(m_display) == 0) { + // check timers + if (processTimers()) { + continue; + } + + // wait + m_mutex.unlock(); + CThread::sleep(0.01); + m_mutex.lock(); + } + + // process events + while (!m_stop && XPending(m_display) > 0) { + // get the event + XNextEvent(m_display, &event.m_event); + + // process the event. if unhandled then let the subclass + // have a go at it. + m_mutex.unlock(); + if (!onPreDispatch(&event)) { + m_eventHandler->onEvent(&event); + } + m_mutex.lock(); + } + } + +#endif // !UNIX_LIKE + + m_mutex.unlock(); +} + +void +CXWindowsScreen::exitMainLoop() +{ + // m_stop should be a condition variable that we signal here + // but we can't wait on both the condition variable and the + // X connection so there's no point. however, we do need + // to wake up the X connection so send ourself some event. + CLock lock(&m_mutex); + m_stop = true; + + if (m_display != NULL && m_window != None) { + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_window; + event.xclient.message_type = XInternAtom(m_display, "ATOM", False); + event.xclient.format = 32; + event.xclient.data.l[0] = 0; + event.xclient.data.l[1] = 0; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_window, False, 0, &event); + } +} + +void +CXWindowsScreen::close() +{ + CLock lock(&m_mutex); + + // done with screen saver + delete m_screensaver; + m_screensaver = NULL; + + // destroy clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // close the display + if (m_display != NULL) { + XCloseDisplay(m_display); + m_display = NULL; + LOG((CLOG_DEBUG "closed display")); + } + XSetIOErrorHandler(NULL); +} + +bool +CXWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) +{ + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + if (clipboard != NULL) { + // save clipboard data + return CClipboard::copy(m_clipboard[id], clipboard, timestamp); + } + else { + // assert clipboard ownership + if (!m_clipboard[id]->open(timestamp)) { + return false; + } + m_clipboard[id]->empty(); + m_clipboard[id]->close(); + return true; + } +} + +void +CXWindowsScreen::checkClipboards() +{ + // do nothing, we're always up to date +} + +void +CXWindowsScreen::openScreensaver(bool notify) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + m_screensaver->setNotify(m_window); + } + else { + m_screensaver->disable(); + } +} + +void +CXWindowsScreen::closeScreensaver() +{ + CLock lock(&m_mutex); + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + m_screensaver->setNotify(None); + } + else { + m_screensaver->enable(); + } + } +} + +void +CXWindowsScreen::screensaver(bool activate) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CXWindowsScreen::syncDesktop() +{ + // do nothing; X doesn't suffer from this bogosity +} + +bool +CXWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const +{ + assert(clipboard != NULL); + + // block others from using the display while we get the clipboard + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + // copy the clipboard + return CClipboard::copy(clipboard, m_clipboard[id], timestamp); +} + +void +CXWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + Window root, window; + int mx, my, xWindow, yWindow; + unsigned int mask; + if (XQueryPointer(m_display, getRoot(), &root, &window, + &mx, &my, &xWindow, &yWindow, &mask)) { + x = mx; + y = my; + } + else { + x = m_xCenter; + y = m_yCenter; + } +} + +void +CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + + x = m_xCenter; + y = m_yCenter; +} + +void +CXWindowsScreen::updateScreenShape() +{ + // get shape of default screen + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); + m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // get center of default screen + m_xCenter = m_x + (m_w >> 1); + m_yCenter = m_y + (m_h >> 1); + +#if HAVE_X11_EXTENSIONS_XINERAMA_H + // get center of first Xinerama screen. Xinerama appears to have + // a bug when XWarpPointer() is used in combination with + // XGrabPointer(). in that case, the warp is successful but the + // next pointer motion warps the pointer again, apparently to + // constrain it to some unknown region, possibly the region from + // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over + // all physical screens. this warp only seems to happen if the + // pointer wasn't in that region before the XWarpPointer(). the + // second (unexpected) warp causes synergy to think the pointer + // has been moved when it hasn't. to work around the problem, + // we warp the pointer to the center of the first physical + // screen instead of the logical screen. + int eventBase, errorBase; + if (XineramaQueryExtension(m_display, &eventBase, &errorBase)) { + if (XineramaIsActive(m_display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(m_display, &numScreens); + if (screens != NULL) { + if (numScreens > 1) { + m_xCenter = screens[0].x_org + (screens[0].width >> 1); + m_yCenter = screens[0].y_org + (screens[0].height >> 1); + } + XFree(screens); + } + } + } +#endif +} + +bool +CXWindowsScreen::onPreDispatch(CEvent* event) +{ + assert(event != NULL); + XEvent* xevent = &event->m_event; + + switch (xevent->type) { + case MappingNotify: + { + CLock lock(&m_mutex); + if (XPending(m_display) > 0) { + XEvent tmpEvent; + XPeekEvent(m_display, &tmpEvent); + if (tmpEvent.type == MappingNotify) { + // discard this MappingNotify since another follows. + // we tend to get a bunch of these in a row. + return true; + } + } + } + + // keyboard mapping changed + XRefreshKeyboardMapping(&xevent->xmapping); + + // pass event on + break; + + case SelectionClear: + { + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the receiver. + ClipboardID id = getClipboardID(xevent->xselectionclear.selection); + if (id != kClipboardEnd) { + LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); + m_clipboard[id]->lost(xevent->xselectionclear.time); + m_receiver->onGrabClipboard(id); + return true; + } + } + break; + + case SelectionNotify: + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent->xselection.property != None) { + CLock lock(&m_mutex); + XDeleteProperty(m_display, + xevent->xselection.requestor, + xevent->xselection.property); + } + return true; + + case SelectionRequest: + { + // somebody is asking for clipboard data + ClipboardID id = getClipboardID( + xevent->xselectionrequest.selection); + if (id != kClipboardEnd) { + CLock lock(&m_mutex); + m_clipboard[id]->addRequest( + xevent->xselectionrequest.owner, + xevent->xselectionrequest.requestor, + xevent->xselectionrequest.target, + xevent->xselectionrequest.time, + xevent->xselectionrequest.property); + return true; + } + } + break; + + case PropertyNotify: + // property delete may be part of a selection conversion + if (xevent->xproperty.state == PropertyDelete) { + processClipboardRequest(xevent->xproperty.window, + xevent->xproperty.time, + xevent->xproperty.atom); + return true; + } + break; + + case ClientMessage: + if (xevent->xclient.message_type == m_atomScreensaver && + xevent->xclient.format == 32) { + // screen saver activation/deactivation event + m_eventHandler->onScreensaver(xevent->xclient.data.l[0] != 0); + return true; + } + break; + + case DestroyNotify: + // looks like one of the windows that requested a clipboard + // transfer has gone bye-bye. + destroyClipboardRequest(xevent->xdestroywindow.window); + + // we don't know if the event was handled or not so continue + break; + } + + // let screen saver have a go + { + CLock lock(&m_mutex); + m_screensaver->onPreDispatch(xevent); + } + + return m_eventHandler->onPreDispatch(event); +} + +void +CXWindowsScreen::createBlankCursor() +{ + // this seems just a bit more complicated than really necessary + + // get the closet cursor size to 1x1 + unsigned int w, h; + XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + + // make bitmap data for cursor of closet size. since the cursor + // is blank we can use the same bitmap for shape and mask: all + // zeros. + const int size = ((w + 7) >> 3) * h; + char* data = new char[size]; + memset(data, 0, size); + + // make bitmap + Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); + + // need an arbitrary color for the cursor + XColor color; + color.pixel = 0; + color.red = color.green = color.blue = 0; + color.flags = DoRed | DoGreen | DoBlue; + + // make cursor from bitmap + m_cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, + &color, &color, 0, 0); + + // don't need bitmap or the data anymore + delete[] data; + XFreePixmap(m_display, bitmap); +} + +bool +CXWindowsScreen::processTimers() +{ + bool oneShot = false; + std::vector jobs; + { + CLock lock(&m_timersMutex); + + // get current time + const double time = m_time.getTime(); + + // done if no timers have expired + if ((m_oneShotTimer == NULL || *m_oneShotTimer > time) && + (m_timers.empty() || m_timers.top() > time)) { + return false; + } + + // handle one shot timers + if (m_oneShotTimer != NULL) { + *m_oneShotTimer -= time; + if (*m_oneShotTimer <= 0.0) { + delete m_oneShotTimer; + m_oneShotTimer = NULL; + oneShot = true; + } + } + + // subtract current time from all timers. note that this won't + // change the order of elements in the priority queue (except + // for floating point round off which we'll ignore). + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + (*index) -= time; + } + + // process all timers at or below zero, saving the jobs + if (!m_timers.empty()) { + while (m_timers.top() <= 0.0) { + CTimer timer = m_timers.top(); + jobs.push_back(timer.getJob()); + timer.reset(); + m_timers.pop(); + m_timers.push(timer); + } + } + + // reset the clock + m_time.reset(); + } + + // now notify of the one shot timers + if (oneShot) { + m_mutex.unlock(); + m_eventHandler->onOneShotTimerExpired(0); + m_mutex.lock(); + } + + // now run the jobs. note that if one of these jobs removes + // a timer later in the jobs list and deletes that job pointer + // then this will crash when it tries to run that job. + for (std::vector::iterator index = jobs.begin(); + index != jobs.end(); ++index) { + (*index)->run(); + } +} + +ClipboardID +CXWindowsScreen::getClipboardID(Atom selection) const +{ + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->getSelection() == selection) { + return id; + } + } + return kClipboardEnd; +} + +void +CXWindowsScreen::processClipboardRequest(Window requestor, + Time time, Atom property) +{ + CLock lock(&m_mutex); + + // check every clipboard until one returns success + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->processRequest(requestor, time, property)) { + break; + } + } +} + +void +CXWindowsScreen::destroyClipboardRequest(Window requestor) +{ + CLock lock(&m_mutex); + + // check every clipboard until one returns success + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->destroyRequest(requestor)) { + break; + } + } +} + +int +CXWindowsScreen::ioErrorHandler(Display*) +{ + // the display has disconnected, probably because X is shutting + // down. X forces us to exit at this point. that's arguably + // a flaw in X but, realistically, it's difficult to gracefully + // handle not having a Display* anymore. we'll simply log the + // error, notify the subclass (which must not use the display + // so we set it to NULL), and exit. + LOG((CLOG_WARN "X display has unexpectedly disconnected")); + s_screen->m_display = NULL; + s_screen->m_receiver->onError(); + LOG((CLOG_CRIT "quiting due to X display disconnection")); + exit(17); +} + + +// +// CDisplayLock +// + +CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : + m_mutex(&screen->m_mutex), + m_display(screen->m_display) +{ + // note -- it's permitted for m_display to be NULL. that might + // happen if we couldn't connect to the display or if the + // display unexpectedly disconnected. the caller is expected + // to check for NULL as necessary. + + m_mutex->lock(); +} + +CDisplayLock::~CDisplayLock() +{ + m_mutex->unlock(); +} + +CDisplayLock::operator Display*() const +{ + return m_display; +} diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h new file mode 100644 index 0000000..ce37a00 --- /dev/null +++ b/lib/platform/CXWindowsScreen.h @@ -0,0 +1,311 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSCREEN_H +#define CXWINDOWSSCREEN_H + +#include "IScreen.h" +#include "CMutex.h" +#include "CStopwatch.h" +#include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif +#include +#include + +class IJob; +class IScreenEventHandler; +class IScreenReceiver; +class CXWindowsClipboard; +class CXWindowsScreenSaver; + +/*! +\class CEvent +\brief User event data +An architecture dependent type holding user event data. +*/ +// X11 event +class CEvent { +public: + XEvent m_event; + SInt32 m_result; +}; + +//! Implementation of IScreen for X11 +class CXWindowsScreen : public IScreen { +public: + CXWindowsScreen(IScreenReceiver*, IScreenEventHandler*); + virtual ~CXWindowsScreen(); + + //! @name manipulators + //@{ + + //! Add timer + /*! + Add a job to invoke every timeout seconds. The job is + called with the display locked. If a job timeout expires twice + or more before the job can be called then the job is called + just once. The caller retains ownership of the job. + */ + void addTimer(IJob*, double timeout); + + //! Remove timer + /*! + Remove a job. The caller retains ownership of the job. + */ + void removeTimer(IJob*); + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to the receiver's + \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + + //! Set window + /*! + Set the window (created by the subclass). This performs some + initialization and saves the window in case it's needed later. + */ + void setWindow(Window); + + //@} + //! @name accessors + //@{ + + //! Get window + /*! + Returns the root window of the screen. + */ + Window getRoot() const; + + //! Get transparent cursor + /*! + Returns a cursor that is transparent everywhere. + */ + Cursor getBlankCursor() const; + + //@} + + // IScreen overrides + void open(); + void mainLoop(); + void exitMainLoop(); + void close(); + bool setClipboard(ClipboardID, const IClipboard*); + void checkClipboards(); + void openScreensaver(bool notify); + void closeScreensaver(); + void screensaver(bool activate); + void syncDesktop(); + bool getClipboard(ClipboardID, IClipboard*) const; + void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + void getCursorPos(SInt32&, SInt32&) const; + void getCursorCenter(SInt32&, SInt32&) const; + +private: + // update screen size cache + void updateScreenShape(); + + // process events before dispatching to receiver + bool onPreDispatch(CEvent* event); + + // create the transparent cursor + void createBlankCursor(); + + // remove a timer without locking + void removeTimerNoLock(IJob*); + + // process timers + bool processTimers(); + + // determine the clipboard from the X selection. returns + // kClipboardEnd if no such clipboard. + ClipboardID getClipboardID(Atom selection) const; + + // continue processing a selection request + void processClipboardRequest(Window window, + Time time, Atom property); + + // terminate a selection request + void destroyClipboardRequest(Window window); + + // X I/O error handler + static int ioErrorHandler(Display*); + +private: + // a priority queue will direct access to the elements + template , + class Compare = std::greater > + class CPriorityQueue { + public: + typedef typename Container::value_type value_type; + typedef typename Container::size_type size_type; + typedef typename Container::iterator iterator; + typedef Container container_type; + + CPriorityQueue() { } + CPriorityQueue(Container& swappedIn); + ~CPriorityQueue() { } + + // manipulators + + void push(const value_type& v) + { + c.push_back(v); + std::push_heap(c.begin(), c.end(), comp); + } + + void pop() + { + std::pop_heap(c.begin(), c.end(), comp); + c.pop_back(); + } + + iterator begin() + { + return c.begin(); + } + + iterator end() + { + return c.end(); + } + + void swap(CPriorityQueue& q) + { + c.swap(q.c); + } + + void swap(Container& c2) + { + c.swap(c2); + std::make_heap(c.begin(), c.end(), comp); + } + + // accessors + + bool empty() const + { + return c.empty(); + } + + size_type size() const + { + return c.size(); + } + + const value_type& + top() const + { + return c.front(); + } + + private: + Container c; + Compare comp; + }; + + // a timer priority queue element + class CTimer { + public: + CTimer(IJob* job, double startTime, double resetTime); + ~CTimer(); + + // manipulators + + void run(); + + void reset(); + + CTimer& operator-=(double); + + // accessors + + IJob* getJob() const + { + return m_job; + } + + operator double() const; + + bool operator<(const CTimer&) const; + + private: + IJob* m_job; + double m_timeout; + double m_time; + double m_startTime; + }; + +private: + friend class CDisplayLock; + + typedef CPriorityQueue CTimerPriorityQueue; + + // X is not thread safe + CMutex m_mutex; + + Display* m_display; + Window m_root; + bool m_stop; + + IScreenReceiver* m_receiver; + IScreenEventHandler* m_eventHandler; + Window m_window; + + SInt32 m_x, m_y; + SInt32 m_w, m_h; + SInt32 m_xCenter, m_yCenter; + + // clipboards + CXWindowsClipboard* m_clipboard[kClipboardEnd]; + + // the transparent cursor + Cursor m_cursor; + + // screen saver stuff + CXWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + Atom m_atomScreensaver; + + // timers, the stopwatch used to time, and a mutex for the timers + CTimerPriorityQueue m_timers; + CStopwatch m_time; + CMutex m_timersMutex; + CTimer* m_oneShotTimer; + + // pointer to (singleton) screen. this is only needed by + // ioErrorHandler(). + static CXWindowsScreen* s_screen; +}; + +//! Convenience object to lock/unlock a CXWindowsScreen +class CDisplayLock { +public: + CDisplayLock(const CXWindowsScreen*); + ~CDisplayLock(); + + operator Display*() const; + +private: + const CMutex* m_mutex; + Display* m_display; +}; + +#endif diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp new file mode 100644 index 0000000..c652c3d --- /dev/null +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -0,0 +1,482 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsScreenSaver.h" +#include "CXWindowsScreen.h" +#include "CXWindowsUtil.h" +#include "CLog.h" +#include "TMethodJob.h" +#include +#if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +#else +# error The XTest extension is required to build synergy +#endif + +// +// CXWindowsScreenSaver +// + +CXWindowsScreenSaver::CXWindowsScreenSaver( + CXWindowsScreen* screen, Display* display) : + m_screen(screen), + m_display(display), + m_notify(None), + m_xscreensaver(None), + m_xscreensaverActive(false), + m_disabled(false), + m_suppressDisable(false), + m_disableJobInstalled(false) +{ + // screen saver disable callback + m_disableJob = new TMethodJob(this, + &CXWindowsScreenSaver::disableCallback); + + // get atoms + m_atomScreenSaver = XInternAtom(m_display, + "SCREENSAVER", False); + m_atomScreenSaverVersion = XInternAtom(m_display, + "_SCREENSAVER_VERSION", False); + m_atomScreenSaverActivate = XInternAtom(m_display, + "ACTIVATE", False); + m_atomScreenSaverDeactivate = XInternAtom(m_display, + "DEACTIVATE", False); + m_atomSynergyScreenSaver = XInternAtom(m_display, + "SYNERGY_SCREENSAVER", False); + + // create dummy window to receive xscreensaver responses. this + // shouldn't be necessary (we should be able to send responses + // to None) but it doesn't hurt. + XSetWindowAttributes attr; + attr.event_mask = 0;//PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + m_xscreensaverSink = XCreateWindow(m_display, + DefaultRootWindow(m_display), + 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect, + &attr); + LOG((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink)); + + // watch top-level windows for changes + { + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + Window root = DefaultRootWindow(m_display); + XWindowAttributes attr; + XGetWindowAttributes(m_display, root, &attr); + m_rootEventMask = attr.your_event_mask; + XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask); + if (error) { + LOG((CLOG_DEBUG "didn't set root event mask")); + m_rootEventMask = 0; + } + } + + // get the xscreensaver window, if any + if (!findXScreenSaver()) { + setXScreenSaver(None); + } + + // get the built-in settings + XGetScreenSaver(m_display, &m_timeout, &m_interval, + &m_preferBlanking, &m_allowExposures); +} + +CXWindowsScreenSaver::~CXWindowsScreenSaver() +{ + // clear watch list + clearWatchForXScreenSaver(); + + // stop watching root for events + CXWindowsUtil::CErrorLock lock(m_display); + Window root = DefaultRootWindow(m_display); + XSelectInput(m_display, root, m_rootEventMask); + + // destroy dummy sink window + XDestroyWindow(m_display, m_xscreensaverSink); + + // done with disable job + m_screen->removeTimer(m_disableJob); + delete m_disableJob; +} + +bool +CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent) +{ + switch (xevent->type) { + case CreateNotify: + if (m_xscreensaver == None) { + if (isXScreenSaver(xevent->xcreatewindow.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + else { + // another window to watch. to detect the xscreensaver + // window we look for a property but that property may + // not yet exist by the time we get this event so we + // have to watch the window for property changes. + // this would be so much easier if xscreensaver did the + // smart thing and stored its window in a property on + // the root window. + addWatchXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + + case DestroyNotify: + if (xevent->xdestroywindow.window == m_xscreensaver) { + // xscreensaver is gone + LOG((CLOG_DEBUG "xscreensaver died")); + setXScreenSaver(None); + return true; + } + break; + + case PropertyNotify: + if (xevent->xproperty.state == PropertyNewValue) { + if (isXScreenSaver(xevent->xproperty.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + + case MapNotify: + if (xevent->xmap.window == m_xscreensaver) { + // xscreensaver has activated + setXScreenSaverActive(true); + return true; + } + break; + + case UnmapNotify: + if (xevent->xunmap.window == m_xscreensaver) { + // xscreensaver has deactivated + setXScreenSaverActive(false); + return true; + } + break; + } + + return false; +} + +void +CXWindowsScreenSaver::setNotify(Window notify) +{ + m_notify = notify; +} + +void +CXWindowsScreenSaver::enable() +{ + // for xscreensaver + m_disabled = false; + updateDisableJob(); + + // for built-in X screen saver + XSetScreenSaver(m_display, m_timeout, m_interval, + m_preferBlanking, m_allowExposures); +} + +void +CXWindowsScreenSaver::disable() +{ + // for xscreensaver + m_disabled = true; + updateDisableJob(); + + // use built-in X screen saver + XGetScreenSaver(m_display, &m_timeout, &m_interval, + &m_preferBlanking, &m_allowExposures); + XSetScreenSaver(m_display, 0, m_interval, + m_preferBlanking, m_allowExposures); + // FIXME -- now deactivate? +} + +void +CXWindowsScreenSaver::activate() +{ + // remove disable job timer + m_suppressDisable = true; + updateDisableJob(); + + // try xscreensaver + findXScreenSaver(); + if (m_xscreensaver != None) { + sendXScreenSaverCommand(m_atomScreenSaverActivate); + return; + } + + // use built-in X screen saver + XForceScreenSaver(m_display, ScreenSaverActive); +} + +void +CXWindowsScreenSaver::deactivate() +{ + // reinstall disable job timer + m_suppressDisable = false; + updateDisableJob(); + + // try xscreensaver + findXScreenSaver(); + if (m_xscreensaver != None) { + sendXScreenSaverCommand(m_atomScreenSaverDeactivate); + return; + } + + // use built-in X screen saver + XForceScreenSaver(m_display, ScreenSaverReset); +} + +bool +CXWindowsScreenSaver::isActive() const +{ + // check xscreensaver + if (m_xscreensaver != None) { + return m_xscreensaverActive; + } + + // can't check built-in X screen saver activity + return false; +} + +void +CXWindowsScreenSaver::sendNotify(bool activated) +{ + if (m_notify != None) { + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_notify; + event.xclient.message_type = m_atomSynergyScreenSaver; + event.xclient.format = 32; + event.xclient.data.l[0] = activated ? 1 : 0; + event.xclient.data.l[1] = 0; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_notify, False, 0, &event); + } +} + +bool +CXWindowsScreenSaver::findXScreenSaver() +{ + // do nothing if we've already got the xscreensaver window + if (m_xscreensaver == None) { + // find top-level window xscreensaver window + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + if (isXScreenSaver(cw[i])) { + setXScreenSaver(cw[i]); + break; + } + } + XFree(cw); + } + } + + return (m_xscreensaver != None); +} + +void +CXWindowsScreenSaver::setXScreenSaver(Window window) +{ + LOG((CLOG_DEBUG "xscreensaver window: 0x%08x", window)); + + // save window + m_xscreensaver = window; + + if (m_xscreensaver != None) { + // clear old watch list + clearWatchForXScreenSaver(); + + // see if xscreensaver is active + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_xscreensaver, &attr); + setXScreenSaverActive(!error && attr.map_state != IsUnmapped); + } + else { + // screen saver can't be active if it doesn't exist + setXScreenSaverActive(false); + + // start watching for xscreensaver + watchForXScreenSaver(); + } +} + +bool +CXWindowsScreenSaver::isXScreenSaver(Window w) const +{ + // check for m_atomScreenSaverVersion string property + Atom type; + return (CXWindowsUtil::getWindowProperty(m_display, w, + m_atomScreenSaverVersion, + NULL, &type, NULL, False) && + type == XA_STRING); +} + +void +CXWindowsScreenSaver::setXScreenSaverActive(bool activated) +{ + if (m_xscreensaverActive != activated) { + LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x", activated ? "activated" : "deactivated", m_xscreensaver)); + m_xscreensaverActive = activated; + + // if screen saver was activated forcefully (i.e. against + // our will) then just accept it. don't try to keep it + // from activating since that'll just pop up the password + // dialog if locking is enabled. + m_suppressDisable = activated; + updateDisableJob(); + + sendNotify(activated); + } +} + +void +CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) +{ + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_xscreensaverSink; + event.xclient.message_type = m_atomScreenSaver; + event.xclient.format = 32; + event.xclient.data.l[0] = static_cast(cmd); + event.xclient.data.l[1] = arg1; + event.xclient.data.l[2] = arg2; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + LOG((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2)); + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + if (error) { + findXScreenSaver(); + } +} + +void +CXWindowsScreenSaver::watchForXScreenSaver() +{ + // clear old watch list + clearWatchForXScreenSaver(); + + // add every child of the root to the list of windows to watch + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + addWatchXScreenSaver(cw[i]); + } + XFree(cw); + } + + // now check for xscreensaver window in case it set the property + // before we could request property change events. + if (findXScreenSaver()) { + // found it so clear out our watch list + clearWatchForXScreenSaver(); + } +} + +void +CXWindowsScreenSaver::clearWatchForXScreenSaver() +{ + // stop watching all windows + CXWindowsUtil::CErrorLock lock(m_display); + for (CWatchList::iterator index = m_watchWindows.begin(); + index != m_watchWindows.end(); ++index) { + XSelectInput(m_display, index->first, index->second); + } + m_watchWindows.clear(); +} + +void +CXWindowsScreenSaver::addWatchXScreenSaver(Window window) +{ + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + + // get window attributes + XWindowAttributes attr; + XGetWindowAttributes(m_display, window, &attr); + + // if successful and window uses override_redirect (like xscreensaver + // does) then watch it for property changes. + if (!error && attr.override_redirect == True) { + XSelectInput(m_display, window, + attr.your_event_mask | PropertyChangeMask); + if (!error) { + // if successful then add the window to our list + m_watchWindows.insert(std::make_pair(window, attr.your_event_mask)); + } + } +} + +void +CXWindowsScreenSaver::updateDisableJob() +{ + assert(m_disableJob != NULL); + + if (m_disabled && !m_suppressDisable && !m_disableJobInstalled) { + // 5 seconds should be plenty often to suppress the screen saver + m_disableJobInstalled = true; + m_screen->addTimer(m_disableJob, 5.0); + } + else if ((!m_disabled || m_suppressDisable) && m_disableJobInstalled) { + m_disableJobInstalled = false; + m_screen->removeTimer(m_disableJob); + } +} + +void +CXWindowsScreenSaver::disableCallback(void*) +{ + // send fake mouse motion directly to xscreensaver + if (m_xscreensaver != None) { + XEvent event; + event.xmotion.type = MotionNotify; + event.xmotion.display = m_display; + event.xmotion.window = m_xscreensaver; + event.xmotion.root = DefaultRootWindow(m_display); + event.xmotion.subwindow = None; + event.xmotion.time = CurrentTime; + event.xmotion.x = 0; + event.xmotion.y = 0; + event.xmotion.x_root = 0; + event.xmotion.y_root = 0; + event.xmotion.state = 0; + event.xmotion.is_hint = NotifyNormal; + event.xmotion.same_screen = True; + + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + } +} diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h new file mode 100644 index 0000000..3285a1b --- /dev/null +++ b/lib/platform/CXWindowsScreenSaver.h @@ -0,0 +1,161 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSCREENSAVER_H +#define CXWINDOWSSCREENSAVER_H + +#include "IScreenSaver.h" +#include "stdmap.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class IJob; +class CXWindowsScreen; + +//! X11 screen saver implementation +class CXWindowsScreenSaver : public IScreenSaver { +public: + // note -- the caller must ensure that Display* passed to c'tor isn't + // being used in another call to Xlib when calling any method on this + // object (including during the c'tor and d'tor). + CXWindowsScreenSaver(CXWindowsScreen*, Display*); + virtual ~CXWindowsScreenSaver(); + + //! @name manipulators + //@{ + + //! Event filtering + /*! + Called for each event before event translation and dispatch. Return + true to skip translation and dispatch. Subclasses should call the + superclass's version first and return true if it returns true. + */ + bool onPreDispatch(const XEvent*); + + //! Set notify target + /*! + Tells this object to send a ClientMessage to the given window + when the screen saver activates or deactivates. Only one window + can be notified at a time. The message type is the "SCREENSAVER" + atom, the format is 32, and the data.l[0] member is non-zero + if activated, zero if deactivated. Pass None to disable + notification. + */ + void setNotify(Window); + + //@} + + // IScreenSaver overrides + virtual void enable(); + virtual void disable(); + virtual void activate(); + virtual void deactivate(); + virtual bool isActive() const; + +private: + // send a notification + void sendNotify(bool activated); + + // find and set the running xscreensaver's window. returns true iff + // found. + bool findXScreenSaver(); + + // set the xscreensaver's window, updating the activation state flag + void setXScreenSaver(Window); + + // returns true if the window appears to be the xscreensaver window + bool isXScreenSaver(Window) const; + + // set xscreensaver's activation state flag. sends notification + // if the state has changed. + void setXScreenSaverActive(bool activated); + + // send a command to xscreensaver + void sendXScreenSaverCommand(Atom, long = 0, long = 0); + + // watch all windows that could potentially be the xscreensaver for + // the events that will confirm it. + void watchForXScreenSaver(); + + // stop watching all watched windows + void clearWatchForXScreenSaver(); + + // add window to the watch list + void addWatchXScreenSaver(Window window); + + // install/uninstall the job used to suppress the screensaver + void updateDisableJob(); + + // called periodically to prevent the screen saver from starting + void disableCallback(void*); + +private: + typedef std::map CWatchList; + + // the event loop object + CXWindowsScreen* m_screen; + + // the X display + Display* m_display; + + // old event mask on root window + long m_rootEventMask; + + // window to notify on screen saver activation/deactivation + Window m_notify; + + // xscreensaver's window + Window m_xscreensaver; + + // xscreensaver activation state + bool m_xscreensaverActive; + + // dummy window to receive xscreensaver repsonses + Window m_xscreensaverSink; + + // potential xscreensaver windows being watched + CWatchList m_watchWindows; + + // atoms used to communicate with xscreensaver's window + Atom m_atomScreenSaver; + Atom m_atomScreenSaverVersion; + Atom m_atomScreenSaverActivate; + Atom m_atomScreenSaverDeactivate; + Atom m_atomSynergyScreenSaver; + + // built-in screen saver settings + int m_timeout; + int m_interval; + int m_preferBlanking; + int m_allowExposures; + + // true iff the client wants the screen saver suppressed + bool m_disabled; + + // true iff we're ignoring m_disabled. this is true, for example, + // when the client has called activate() and so presumably wants + // to activate the screen saver even if disabled. + bool m_suppressDisable; + + // true iff the disabled job timer is installed + bool m_disableJobInstalled; + + // the job used to invoke disableCallback + IJob* m_disableJob; +}; + +#endif diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp new file mode 100644 index 0000000..b69369e --- /dev/null +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -0,0 +1,2070 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsSecondaryScreen.h" +#include "CXWindowsClipboard.h" +#include "CXWindowsScreen.h" +#include "CXWindowsScreenSaver.h" +#include "CXWindowsUtil.h" +#include "IScreenReceiver.h" +#include "XScreen.h" +#include "CThread.h" +#include "CLog.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# define XK_MISCELLANY +# define XK_XKB_KEYS +# define XK_LATIN1 +# define XK_LATIN2 +# define XK_LATIN3 +# define XK_LATIN4 +# define XK_LATIN8 +# define XK_LATIN9 +# include +# if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +# else +# error The XTest extension is required to build synergy +# endif +# if HAVE_X11_EXTENSIONS_XINERAMA_H + // Xinerama.h may lack extern "C" for inclusion by C++ + extern "C" { +# include + } +# endif +# if defined(HAVE_X11_XF86KEYSYM_H) +# include +# endif +# if !defined(XF86XK_Launch0) +# define XF86XK_Launch0 0x1008FF40 +# endif +# if !defined(XF86XK_Launch1) +# define XF86XK_Launch1 0x1008FF41 +# endif +#endif + +// +// utility functions +// + +inline +static +unsigned int getBits(unsigned int src, unsigned int mask) +{ + return src & mask; +} + +inline +static +unsigned int setBits(unsigned int src, unsigned int mask) +{ + return src | mask; +} + +inline +static +unsigned int clearBits(unsigned int src, unsigned int mask) +{ + return src & ~mask; +} + +inline +static +unsigned int flipBits(unsigned int src, unsigned int mask) +{ + return src ^ mask; +} + +inline +static +unsigned int assignBits(unsigned int src, + unsigned int mask, unsigned int value) +{ + return setBits(clearBits(src, mask), clearBits(value, ~mask)); +} + + +// +// CXWindowsSecondaryScreen +// + +CXWindowsSecondaryScreen::KeySymsMap + CXWindowsSecondaryScreen::s_decomposedKeySyms; + +CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : + CSecondaryScreen(), + m_window(None), + m_xtestIsXineramaUnaware(true) +{ + m_screen = new CXWindowsScreen(receiver, this); + + // make sure decomposed keysym table is prepared + getDecomposedKeySymTable(); +} + +CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() +{ + assert(m_window == None); + delete m_screen; +} + +void +CXWindowsSecondaryScreen::keyDown(KeyID key, + KeyModifierMask mask, KeyButton button) +{ + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "ctrl+alt+del emulation")); + // just pass the key through + } + + // get the sequence of keys to simulate key press and the final + // modifier state. + Keystrokes keys; + KeyCode keycode; + m_mask = mapKey(keys, keycode, key, mask, kPress); + if (keys.empty()) { + // do nothing if there are no associated keys (i.e. lookup failed) + return; + } + + // generate key events + doKeystrokes(keys, 1); + + // do not record button down if button is 0 (invalid) + if (button != 0) { + // note that key is now down + m_serverKeyMap[button] = keycode; + m_keys[keycode] = true; + m_fakeKeys[keycode] = true; + } +} + +void +CXWindowsSecondaryScreen::keyRepeat(KeyID key, + KeyModifierMask mask, SInt32 count, KeyButton button) +{ + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + Keystrokes keys; + KeyCode keycode; + m_mask = mapKey(keys, keycode, key, mask, kRepeat); + if (keys.empty()) { + return; + } + + // if this keycode shouldn't auto-repeat then ignore + if ((m_keyControl.auto_repeats[keycode >> 3] & (1 << (keycode & 7))) == 0) { + return; + } + + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (keycode != index->second) { + // replace key up with previous keycode but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. the key up is the first keystroke + // with the keycode returned by mapKey(). + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_keycode == keycode) { + index2->m_keycode = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] = false; + m_fakeKeys[index->second] = false; + + // map server key to new key + index->second = keycode; + + // note that new key is now down + m_keys[index->second] = true; + m_fakeKeys[index->second] = true; + } + + // generate key events + doKeystrokes(keys, count); +} + +void +CXWindowsSecondaryScreen::keyUp(KeyID key, + KeyModifierMask mask, KeyButton button) +{ + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + KeyCode keycode = index->second; + + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "ctrl+alt+del emulation")); + // just pass the key through + } + + // get the sequence of keys to simulate key release and the final + // modifier state. + Keystrokes keys; + if (!((key == kKeyCapsLock && m_capsLockHalfDuplex) || + (key == kKeyNumLock && m_numLockHalfDuplex))) { + m_mask = mapKeyRelease(keys, keycode); + } + + // generate key events + doKeystrokes(keys, 1); + + // note that key is now up + m_serverKeyMap.erase(index); + m_keys[keycode] = false; + m_fakeKeys[keycode] = false; +} + +void +CXWindowsSecondaryScreen::flush(Display* display) const +{ + XFlush(display); +} + +void +CXWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + const unsigned int xButton = mapButton(button); + if (xButton != 0) { + CDisplayLock display(m_screen); + XTestFakeButtonEvent(display, xButton, True, CurrentTime); + flush(display); + } +} + +void +CXWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + const unsigned int xButton = mapButton(button); + if (xButton != 0) { + CDisplayLock display(m_screen); + XTestFakeButtonEvent(display, xButton, False, CurrentTime); + flush(display); + } +} + +void +CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + warpCursor(x, y); +} + +void +CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) +{ + // choose button depending on rotation direction + const unsigned int xButton = mapButton(static_cast( + (delta >= 0) ? -1 : -2)); + if (xButton == 0) { + return; + } + + // now use absolute value of delta + if (delta < 0) { + delta = -delta; + } + + // send as many clicks as necessary + CDisplayLock display(m_screen); + for (; delta >= 120; delta -= 120) { + XTestFakeButtonEvent(display, xButton, True, CurrentTime); + XTestFakeButtonEvent(display, xButton, False, CurrentTime); + } + flush(display); +} + +void +CXWindowsSecondaryScreen::resetOptions() +{ + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; + m_xtestIsXineramaUnaware = true; + CSecondaryScreen::resetOptions(); +} + +void +CXWindowsSecondaryScreen::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionXTestXineramaUnaware) { + m_xtestIsXineramaUnaware = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false")); + } + } + CSecondaryScreen::setOptions(options); +} + +IScreen* +CXWindowsSecondaryScreen::getScreen() const +{ + return m_screen; +} + +void +CXWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + +bool +CXWindowsSecondaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CXWindowsSecondaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + XEvent& xevent = event->m_event; + + // handle event + switch (xevent.type) { + case MappingNotify: { + // keyboard mapping changed + CDisplayLock display(m_screen); + doUpdateKeys(display); + return true; + } + + case LeaveNotify: + // mouse moved out of hider window somehow. hide the window. + hideWindow(); + return true; + } +} + +void +CXWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) +{ + // ignore +} + +SInt32 +CXWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void +CXWindowsSecondaryScreen::onPreMainLoop() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPreOpen() +{ + assert(m_window == None); +} + +void +CXWindowsSecondaryScreen::onPostOpen() +{ + assert(m_window != None); + + // get the keyboard control state + CDisplayLock display(m_screen); + XGetKeyboardControl(display, &m_keyControl); + + // check if xinerama is enabled and there is more than one screen + m_xinerama = false; +#if HAVE_X11_EXTENSIONS_XINERAMA_H + int eventBase, errorBase; + if (XineramaQueryExtension(display, &eventBase, &errorBase)) { + if (XineramaIsActive(display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(display, &numScreens); + if (screens != NULL) { + m_xinerama = (numScreens > 1); + XFree(screens); + } + } + } +#endif +} + +void +CXWindowsSecondaryScreen::onPreClose() +{ + if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { + CDisplayLock display(m_screen); + XAutoRepeatOn(display); + } +} + +void +CXWindowsSecondaryScreen::onPreEnter() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPostEnter() +{ + assert(m_window != None); + + // get the keyboard control state + CDisplayLock display(m_screen); + XGetKeyboardControl(display, &m_keyControl); + + // turn off auto-repeat. we do this so fake key press events don't + // cause the local server to generate their own auto-repeats of + // those keys. + XAutoRepeatOff(display); +} + +void +CXWindowsSecondaryScreen::onPreLeave() +{ + assert(m_window != None); + + // restore the previous keyboard auto-repeat state. if the user + // changed the auto-repeat configuration while on the client then + // that state is lost. that's because we can't get notified by + // the X server when the auto-repeat configuration is changed so + // we can't track the desired configuration. + if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { + CDisplayLock display(m_screen); + XAutoRepeatOn(display); + } +} + +void +CXWindowsSecondaryScreen::createWindow() +{ + { + CDisplayLock display(m_screen); + + // verify the availability of the XTest extension + int majorOpcode, firstEvent, firstError; + if (!XQueryExtension(display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) { + LOG((CLOG_ERR "XTEST extension not available")); + throw XScreenOpenFailure(); + } + + // cursor hider window attributes. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + XSetWindowAttributes attr; + attr.event_mask = LeaveWindowMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = m_screen->getBlankCursor(); + + // create the cursor hider window + m_window = XCreateWindow(display, m_screen->getRoot(), + 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); + + // become impervious to server grabs + XTestGrabControl(display, True); + } + + // tell generic screen about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsSecondaryScreen::destroyWindow() +{ + { + CDisplayLock display(m_screen); + if (display != NULL) { + // release keys that are still pressed + doReleaseKeys(display); + + // no longer impervious to server grabs + XTestGrabControl(display, False); + + // update + flush(display); + } + } + + // destroy window + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } +} + +void +CXWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) +{ + { + CDisplayLock display(m_screen); + + // move hider window under the given position + XMoveWindow(display, m_window, x, y); + + // raise and show the hider window. take activation. + // FIXME -- take focus? + XMapRaised(display, m_window); + } + + // now warp the mouse. we warp after showing the window so we're + // guaranteed to get the mouse leave event and to prevent the + // keyboard focus from changing under point-to-focus policies. + warpCursor(x, y); +} + +void +CXWindowsSecondaryScreen::hideWindow() +{ + assert(m_window != None); + + CDisplayLock display(m_screen); + XUnmapWindow(display, m_window); +} + +void +CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + CDisplayLock display(m_screen); + Display* pDisplay = display; + + if (m_xinerama && m_xtestIsXineramaUnaware) { + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); + } + else { + XTestFakeMotionEvent(display, DefaultScreen(pDisplay), + x, y, CurrentTime); + } + flush(display); +} + +void +CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) +{ + CDisplayLock display(m_screen); + + // toggle modifiers that don't match the desired state + ModifierMask xMask = maskToX(mask); + if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { + toggleKey(display, m_capsLockKeysym, m_capsLockMask); + } + if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { + toggleKey(display, m_numLockKeysym, m_numLockMask); + } + if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { + toggleKey(display, m_scrollLockKeysym, m_scrollLockMask); + } +} + +KeyModifierMask +CXWindowsSecondaryScreen::getToggleState() const +{ + KeyModifierMask mask = 0; + if ((m_mask & m_capsLockMask) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_mask & m_numLockMask) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_mask & m_scrollLockMask) != 0) { + mask |= KeyModifierScrollLock; + } + return mask; +} + +unsigned int +CXWindowsSecondaryScreen::mapButton(ButtonID id) const +{ + // map button -1 to button 4 (+wheel) + if (id == static_cast(-1)) { + id = 4; + } + + // map button -2 to button 5 (-wheel) + else if (id == static_cast(-2)) { + id = 5; + } + + // map buttons 4, 5, etc. to 6, 7, etc. to make room for buttons + // 4 and 5 used to simulate the mouse wheel. + else if (id >= 4) { + id += 2; + } + + // check button is in legal range + if (id < 1 || id > m_buttons.size()) { + // out of range + return 0; + } + + // map button + return static_cast(m_buttons[id - 1]); +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, + KeyID id, KeyModifierMask mask, EKeyAction action) const +{ + // note -- must have display locked on entry + + // the system translates key events into characters depending + // on the modifier key state at the time of the event. to + // generate the right keysym we need to set the modifier key + // states appropriately. + // + // the mask passed by the caller is the desired mask. however, + // there may not be a keycode mapping to generate the desired + // keysym with that mask. we override the bits in the mask + // that cannot be accomodated. + + // ignore releases and repeats for half-duplex keys + const bool isHalfDuplex = ((id == kKeyCapsLock && m_capsLockHalfDuplex) || + (id == kKeyNumLock && m_numLockHalfDuplex)); + if (isHalfDuplex && action != kPress) { + return m_mask; + } + + // requested notes the modifiers requested by the server. + ModifierMask requested = maskToX(mask); + + // convert KeyID to a KeySym + KeySym keysym = keyIDToKeySym(id, requested); + if (keysym == NoSymbol) { + // unknown key + LOG((CLOG_DEBUG2 "no keysym for id 0x%08x", id)); + return m_mask; + } + + // get the mapping for this keysym + KeySymIndex keyIndex = m_keysymMap.find(keysym); + + // if the mapping isn't found and keysym is caps lock sensitive + // then convert the case of the keysym and try again. + if (keyIndex == m_keysymMap.end()) { + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + if (lKey != uKey) { + if (lKey == keysym) { + keyIndex = m_keysymMap.find(uKey); + } + else { + keyIndex = m_keysymMap.find(lKey); + } + } + } + + if (keyIndex != m_keysymMap.end()) { + // the keysym is mapped to some keycode. if it's a modifier + // and that modifier is already in the desired state then + // ignore the request since there's nothing to do. never + // ignore a toggle modifier on press or release, though. + const KeyMapping& keyMapping = keyIndex->second; + const ModifierMask modifierBit = keyMapping.m_modifierMask; + if (modifierBit != 0) { + if (action == kRepeat) { + LOG((CLOG_DEBUG2 "ignore repeating modifier")); + return m_mask; + } + if (getBits(m_toggleModifierMask, modifierBit) == 0) { + if ((action == kPress && (m_mask & modifierBit) != 0) || + (action == kRelease && (m_mask & modifierBit) == 0)) { + LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); + return m_mask; + } + } + } + + // create the keystrokes for this keysym + ModifierMask mask; + if (!mapToKeystrokes(keys, keycode, mask, keyIndex, m_mask, action)) { + // failed to generate keystrokes + keys.clear(); + return m_mask; + } + else { + // success + LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); + return mask; + } + } + + // we can't find the keysym mapped to any keycode. this doesn't + // necessarily mean we can't generate the keysym, though. if the + // keysym can be created by combining keysyms then we may still + // be okay. + KeySyms decomposition; + if (decomposeKeySym(keysym, decomposition)) { + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); + + // map each decomposed keysym to keystrokes. we want the mask + // and the keycode from the last keysym (which should be the + // only non-dead key). the dead keys are not sensitive to + // anything but shift and mode switch. + ModifierMask mask; + for (KeySyms::const_iterator i = decomposition.begin(); + i != decomposition.end();) { + // increment the iterator + KeySyms::const_iterator next = i; + ++next; + + // lookup the key + keysym = *i; + keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end()) { + // missing a required keysym + LOG((CLOG_DEBUG2 "no keycode for decomposed keysym 0x%08x", keysym)); + keys.clear(); + return m_mask; + } + + // the keysym is mapped to some keycode + if (!mapToKeystrokes(keys, keycode, mask, + keyIndex, m_mask, action)) { + // failed to generate keystrokes + keys.clear(); + return m_mask; + } + + // on to the next keysym + i = next; + } + LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); + return mask; + } + + LOG((CLOG_DEBUG2 "no keycode for keysym")); + return m_mask; +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, KeyCode keycode) const +{ + // add key release + Keystroke keystroke; + keystroke.m_keycode = keycode; + keystroke.m_press = False; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // if this is a modifier keycode then update the current modifier mask + KeyCodeToModifierMap::const_iterator i = m_keycodeToModifier.find(keycode); + if (i != m_keycodeToModifier.end()) { + ModifierMask bit = (1 << i->second); + if (getBits(m_toggleModifierMask, bit) != 0) { + // toggle keys modify the state on release + return flipBits(m_mask, bit); + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any (except keycode) are pressed. + KeyCodes::const_iterator j; + const KeyCodes& keycodes = m_modifierKeycodes[i->second]; + for (j = keycodes.begin(); j != keycodes.end(); ++j) { + KeyCode modKeycode = *j; + if (modKeycode != keycode && m_keys[modKeycode]) { + break; + } + } + if (j == keycodes.end()) { + return clearBits(m_mask, bit); + } + } + } + + return m_mask; +} + +unsigned int +CXWindowsSecondaryScreen::findBestKeyIndex(KeySymIndex keyIndex, + ModifierMask /*currentMask*/) const +{ + // there are up to 4 keycodes per keysym to choose from. the + // best choice is the one that requires the fewest adjustments + // to the modifier state. for example, the letter A normally + // requires shift + a. if shift isn't already down we'd have + // to synthesize a shift press before the a press. however, + // if A could also be created with some other keycode without + // shift then we'd prefer that when shift wasn't down. + // + // if the action is kRepeat or kRelease then we don't call this + // method since we just need to synthesize a key repeat/release + // on the same keycode that we pressed. + // XXX -- do this right + for (unsigned int i = 0; i < 4; ++i) { + if (keyIndex->second.m_keycode[i] != 0) { + return i; + } + } + + assert(0 && "no keycode found for keysym"); + return 0; +} + +bool +CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, + ModifierMask currentMask) const +{ + // each keycode has up to 4 keysym associated with it, one each for: + // no modifiers, shift, mode switch, and shift and mode switch. if + // a keysym is modified by num lock and num lock is active then you + // get the shifted keysym when shift is not down and the unshifted + // keysym when it is. that is, num lock inverts the sense of the + // shift modifier when active. similarly for caps lock. this + // method returns true iff the sense of shift should be inverted + // for this key given a modifier state. + if (keyIndex->second.m_numLockSensitive) { + if (getBits(currentMask, m_numLockMask) != 0) { + return true; + } + } + + // if a keysym is num lock sensitive it is never caps lock + // sensitive, thus the else here. + else if (keyIndex->second.m_capsLockSensitive) { + if (getBits(currentMask, m_capsLockMask) != 0) { + return true; + } + } + + return false; +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::getModifierMask(KeySym keysym) const +{ + // find the keysym mapping. if it exists and there's a keycode + // for index 0 (the index we use for modifiers) then return the + // modifierMask, which might be 0. otherwise return 0. + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex != m_keysymMap.end() && keyIndex->second.m_keycode[0] != 0) { + return keyIndex->second.m_modifierMask; + } + else { + return 0; + } +} + +bool +CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, + KeyCode& keycode, + ModifierMask& finalMask, + KeySymIndex keyIndex, + ModifierMask currentMask, + EKeyAction action) const +{ + // keyIndex must be valid + assert(keyIndex != m_keysymMap.end()); + + // get the keysym we're trying to generate and possible keycodes + const KeySym keysym = keyIndex->first; + const KeyMapping& mapping = keyIndex->second; + LOG((CLOG_DEBUG2 "keysym = 0x%08x", keysym)); + + // get the best keycode index for the keysym and modifiers. note + // that (bestIndex & 1) == 0 if the keycode is a shift modifier + // and (bestIndex & 2) == 0 if the keycode is a mode switch + // modifier. this is important later because we don't want + // adjustModifiers() to adjust a modifier if that's the key we're + // mapping. + unsigned int bestIndex = findBestKeyIndex(keyIndex, currentMask); + + // get the keycode + keycode = mapping.m_keycode[bestIndex]; + + // flip low bit of bestIndex if shift is inverted. if there's a + // keycode for this new index then use it. otherwise use the old + // keycode. you'd think we should fail if there isn't a keycode + // for the new index but some keymaps only include the upper case + // keysyms (notably those on Sun Solaris) so to handle the missing + // lower case keysyms we just use the old keycode. note that + // isShiftInverted() will always return false for a shift modifier. + if (isShiftInverted(keyIndex, currentMask)) { + LOG((CLOG_DEBUG2 "shift is inverted")); + bestIndex ^= 1; + if (mapping.m_keycode[bestIndex] != 0) { + keycode = mapping.m_keycode[bestIndex]; + } + } + LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); + + // compute desired mask. the desired mask is the one that matches + // bestIndex, except if the key being synthesized is a shift key + // where we desire what we already have or if it's the mode switch + // key where we only desire to adjust shift. also, if the keycode + // is not sensitive to shift then don't adjust it, otherwise + // something like shift+home would become just home. similiarly + // for mode switch. + ModifierMask desiredMask = currentMask; + if (keyIndex->second.m_modifierMask != m_shiftMask) { + if (keyIndex->second.m_shiftSensitive[bestIndex]) { + if ((bestIndex & 1) != 0) { + desiredMask = setBits(desiredMask, m_shiftMask); + } + else { + desiredMask = clearBits(desiredMask, m_shiftMask); + } + } + if (keyIndex->second.m_modifierMask != m_modeSwitchMask) { + if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { + if ((bestIndex & 2) != 0) { + desiredMask = setBits(desiredMask, m_modeSwitchMask); + } + else { + desiredMask = clearBits(desiredMask, m_modeSwitchMask); + } + } + } + } + + // adjust the modifiers to match the desired modifiers + Keystrokes undo; + ModifierMask tmpMask = currentMask; + if (!adjustModifiers(keys, undo, tmpMask, desiredMask)) { + LOG((CLOG_DEBUG2 "failed to adjust modifiers")); + return false; + } + + // note if the press of a half-duplex key should be treated as a release + const bool isHalfDuplex = + ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || + (keysym == m_numLockKeysym && m_numLockHalfDuplex)); + if (isHalfDuplex && getBits(currentMask, mapping.m_modifierMask) != 0) { + action = kRelease; + } + + // add the key event + Keystroke keystroke; + keystroke.m_keycode = keycode; + switch (action) { + case kPress: + keystroke.m_press = True; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRelease: + keystroke.m_press = False; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRepeat: + keystroke.m_press = False; + keystroke.m_repeat = true; + keys.push_back(keystroke); + keystroke.m_press = True; + keys.push_back(keystroke); + break; + } + + // put undo keystrokes at end of keystrokes in reverse order + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + // if the key is a modifier key then compute the modifier map after + // this key is pressed or released. + finalMask = currentMask; + if (mapping.m_modifierMask != 0) { + // can't be repeating if we've gotten here + assert(action != kRepeat); + + // toggle keys modify the state on release. other keys set the + // bit on press and clear the bit on release. if half-duplex + // then toggle each time we get here. + if (getBits(m_toggleModifierMask, mapping.m_modifierMask) != 0) { + if (isHalfDuplex) { + finalMask = flipBits(finalMask, mapping.m_modifierMask); + } + } + else if (action == kPress) { + finalMask = setBits(finalMask, mapping.m_modifierMask); + } + } + + return true; +} + +bool +CXWindowsSecondaryScreen::adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + ModifierMask& inOutMask, + ModifierMask desiredMask) const +{ + // get mode switch set correctly. do this before shift because + // mode switch may be sensitive to the shift modifier and will + // set/reset it as necessary. + const bool wantModeSwitch = ((desiredMask & m_modeSwitchMask) != 0); + const bool haveModeSwitch = ((inOutMask & m_modeSwitchMask) != 0); + if (wantModeSwitch != haveModeSwitch) { + LOG((CLOG_DEBUG2 "fix mode switch")); + + // adjust shift if necessary + KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); + assert(modeSwitchIndex != m_keysymMap.end()); + if (modeSwitchIndex->second.m_shiftSensitive[0]) { + const bool wantShift = false; + const bool haveShift = ((inOutMask & m_shiftMask) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift for mode switch")); + if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { + return false; + } + inOutMask ^= m_shiftMask; + } + } + + // add mode switch keystrokes + if (!adjustModifier(keys, undo, m_modeSwitchKeysym, wantModeSwitch)) { + return false; + } + inOutMask ^= m_modeSwitchMask; + } + + // get shift set correctly + const bool wantShift = ((desiredMask & m_shiftMask) != 0); + const bool haveShift = ((inOutMask & m_shiftMask) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift")); + if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { + return false; + } + inOutMask ^= m_shiftMask; + } + + return true; +} + +bool +CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, + Keystrokes& undo, KeySym keysym, bool desireActive) const +{ + // this method generates keystrokes to change a modifier into the + // desired state. under X11, we only expect to adjust the shift + // and mode switch states. other modifiers don't affect keysym + // generation, except num lock and caps lock and we don't change + // those but instead just invert the handling of the shift key. + // we don't check here if the modifier is already in the desired + // state; the caller should do that. + + // get the key mapping for keysym + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end() || keyIndex->second.m_keycode[0] == 0) { + // no keycode for keysym or keycode is not a modifier + LOG((CLOG_DEBUG2 "no modifier for 0x%08x", keysym)); + return false; + } + + // this had better be a modifier + assert(keyIndex->second.m_modifierMask != 0); + + // we do not handle toggle modifiers here. they never need to be + // adjusted + assert((keyIndex->second.m_modifierMask & m_toggleModifierMask) == 0); + + // initialize keystroke + Keystroke keystroke; + keystroke.m_repeat = false; + + // releasing a modifier is quite different from pressing one. + // when we release a modifier we have to release every keycode that + // is assigned to the modifier since the modifier is active if any + // one of them is down. when we press a modifier we just have to + // press one of those keycodes. + if (desireActive) { + // press + keystroke.m_keycode = keyIndex->second.m_keycode[0]; + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + undo.push_back(keystroke); + } + else { + // release + KeyCodeToModifierMap::const_iterator index = + m_keycodeToModifier.find(keyIndex->second.m_keycode[0]); + if (index != m_keycodeToModifier.end()) { + const KeyCodes& keycodes = m_modifierKeycodes[index->second]; + for (KeyCodes::const_iterator j = keycodes.begin(); + j != keycodes.end(); ++j) { + if (m_keys[*j]) { + keystroke.m_keycode = *j; + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + } + } + } + + return true; +} + +void +CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) { + return; + } + + // lock display + CDisplayLock display(m_screen); + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + XTestFakeKeyEvent(display, + k->m_keycode, k->m_press, CurrentTime); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + LOG((CLOG_DEBUG2 "keystrokes:")); + LOG((CLOG_DEBUG2 " %d %s", k->m_keycode, k->m_press ? "down" : "up")); + XTestFakeKeyEvent(display, k->m_keycode, k->m_press, CurrentTime); + + // next key + ++k; + } + } + + // update + flush(display); +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const +{ + ModifierMask outMask = 0; + if (inMask & KeyModifierShift) { + outMask |= m_shiftMask; + } + if (inMask & KeyModifierControl) { + outMask |= m_ctrlMask; + } + if (inMask & KeyModifierAlt) { + outMask |= m_altMask; + } + if (inMask & KeyModifierMeta) { + outMask |= m_metaMask; + } + if (inMask & KeyModifierSuper) { + outMask |= m_superMask; + } + if (inMask & KeyModifierModeSwitch) { + outMask |= m_modeSwitchMask; + } + if (inMask & KeyModifierCapsLock) { + outMask |= m_capsLockMask; + } + if (inMask & KeyModifierNumLock) { + outMask |= m_numLockMask; + } + if (inMask & KeyModifierScrollLock) { + outMask |= m_scrollLockMask; + } + return outMask; +} + +void +CXWindowsSecondaryScreen::doReleaseKeys(Display* display) +{ + assert(display != NULL); + + // key release for each key that we faked a press for + for (UInt32 i = 0; i < 256; ++i) { + if (m_fakeKeys[i]) { + XTestFakeKeyEvent(display, i, False, CurrentTime); + m_fakeKeys[i] = false; + m_keys[i] = false; + } + } +} + +void +CXWindowsSecondaryScreen::doUpdateKeys(Display* display) +{ + // query the button mapping + UInt32 numButtons = XGetPointerMapping(display, NULL, 0); + unsigned char* tmpButtons = new unsigned char[numButtons]; + XGetPointerMapping(display, tmpButtons, numButtons); + + // find the largest logical button id + unsigned char maxButton = 0; + for (UInt32 i = 0; i < numButtons; ++i) { + if (tmpButtons[i] > maxButton) { + maxButton = tmpButtons[i]; + } + } + + // allocate button array + m_buttons.resize(maxButton); + + // fill in button array values. m_buttons[i] is the physical + // button number for logical button i+1. + for (UInt32 i = 0; i < numButtons; ++i) { + m_buttons[i] = 0; + } + for (UInt32 i = 0; i < numButtons; ++i) { + m_buttons[tmpButtons[i] - 1] = i + 1; + } + + // clean up + delete[] tmpButtons; + + // update mappings and current modifiers + updateKeysymMap(display); + updateModifiers(display); +} + +void +CXWindowsSecondaryScreen::updateKeys() +{ + CDisplayLock display(m_screen); + + // ask server which keys are pressed + char keys[32]; + XQueryKeymap(display, keys); + + // transfer to our state + for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { + m_keys[j + 0] = ((keys[i] & 0x01) != 0); + m_keys[j + 1] = ((keys[i] & 0x02) != 0); + m_keys[j + 2] = ((keys[i] & 0x04) != 0); + m_keys[j + 3] = ((keys[i] & 0x08) != 0); + m_keys[j + 4] = ((keys[i] & 0x10) != 0); + m_keys[j + 5] = ((keys[i] & 0x20) != 0); + m_keys[j + 6] = ((keys[i] & 0x40) != 0); + m_keys[j + 7] = ((keys[i] & 0x80) != 0); + } + + // we've fake pressed no keys + m_fakeKeys.reset(); + + // update mappings and current modifiers and mouse buttons + doUpdateKeys(display); +} + +void +CXWindowsSecondaryScreen::releaseKeys() +{ + CDisplayLock display(m_screen); + if (display != NULL) { + doReleaseKeys(display); + } +} + +void +CXWindowsSecondaryScreen::updateKeysymMap(Display* display) +{ + // there are up to 4 keysyms per keycode + static const unsigned int maxKeysyms = 4; + + // get the number of keycodes + int minKeycode, maxKeycode; + XDisplayKeycodes(display, &minKeycode, &maxKeycode); + const int numKeycodes = maxKeycode - minKeycode + 1; + + // get the keyboard mapping for all keys + int keysymsPerKeycode; + KeySym* keysyms = XGetKeyboardMapping(display, + minKeycode, numKeycodes, + &keysymsPerKeycode); + + // we only understand up to maxKeysyms keysyms per keycodes + unsigned int numKeysyms = keysymsPerKeycode; + if (numKeysyms > maxKeysyms) { + numKeysyms = maxKeysyms; + } + + // get modifier map from server + XModifierKeymap* modifiers = XGetModifierMapping(display); + + // determine shift and mode switch sensitivity. a keysym is shift + // or mode switch sensitive if its keycode is. a keycode is mode + // mode switch sensitive if it has keysyms for indices 2 or 3. + // it's shift sensitive if the keysym for index 1 (if any) is + // different from the keysym for index 0 and, if the keysym for + // for index 3 (if any) is different from the keysym for index 2. + // that is, if shift changes the generated keysym for the keycode. + std::vector usesShift(numKeycodes); + std::vector usesModeSwitch(numKeycodes); + for (int i = 0; i < numKeycodes; ++i) { + // check mode switch first + if (numKeysyms > 2 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol || + keysyms[i * keysymsPerKeycode + 3] != NoSymbol) { + usesModeSwitch[i] = true; + } + + // check index 0 with index 1 keysyms + if (keysyms[i * keysymsPerKeycode + 0] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != + keysyms[i * keysymsPerKeycode + 0]) { + usesShift[i] = true; + } + + else if (numKeysyms >= 4 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != + keysyms[i * keysymsPerKeycode + 2]) { + usesShift[i] = true; + } + } + + // initialize + m_keysymMap.clear(); + int keysPerModifier = modifiers->max_keypermod; + + // for each modifier keycode, get the index 0 keycode and add it to + // the keysym map. also collect all keycodes for each modifier. + m_keycodeToModifier.clear(); + for (ModifierIndex i = 0; i < 8; ++i) { + // start with no keycodes for this modifier + m_modifierKeycodes[i].clear(); + + // add each keycode for modifier + for (unsigned int j = 0; j < keysPerModifier; ++j) { + // get keycode and ignore unset keycodes + KeyCode keycode = modifiers->modifiermap[i * keysPerModifier + j]; + if (keycode == 0) { + continue; + } + + // save keycode for modifier and modifier for keycode + m_modifierKeycodes[i].push_back(keycode); + m_keycodeToModifier[keycode] = i; + + // get keysym and get/create key mapping + const int keycodeIndex = keycode - minKeycode; + const KeySym keysym = keysyms[keycodeIndex * + keysymsPerKeycode + 0]; + KeyMapping& mapping = m_keysymMap[keysym]; + + // skip if we already have a keycode for this index + if (mapping.m_keycode[0] != 0) { + continue; + } + + // fill in keysym info + mapping.m_keycode[0] = keycode; + mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; + mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; + mapping.m_modifierMask = (1 << i); + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + } + + // create a convenient NoSymbol entry (if it doesn't exist yet). + // sometimes it's useful to handle NoSymbol like a normal keysym. + // remove any entry for NoSymbol. that keysym doesn't count. + { + KeyMapping& mapping = m_keysymMap[NoSymbol]; + for (unsigned int i = 0; i < numKeysyms; ++i) { + mapping.m_keycode[i] = 0; + mapping.m_shiftSensitive[i] = false; + mapping.m_modeSwitchSensitive[i] = false; + } + mapping.m_modifierMask = 0; + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + + // add each keysym to the map, unless we've already inserted a key + // for that keysym index. + for (int i = 0; i < numKeycodes; ++i) { + for (unsigned int j = 0; j < numKeysyms; ++j) { + // lookup keysym + const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + if (keysym == NoSymbol) { + continue; + } + KeyMapping& mapping = m_keysymMap[keysym]; + + // skip if we already have a keycode for this index + if (mapping.m_keycode[j] != 0) { + continue; + } + + // fill in keysym info + if (mapping.m_keycode[0] == 0) { + mapping.m_modifierMask = 0; + } + mapping.m_keycode[j] = static_cast( + minKeycode + i); + mapping.m_shiftSensitive[j] = usesShift[i]; + mapping.m_modeSwitchSensitive[j] = usesModeSwitch[i]; + mapping.m_numLockSensitive = adjustForNumLock(keysym); + mapping.m_capsLockSensitive = adjustForCapsLock(keysym); + } + } + + // choose the keysym to use for each modifier. if the modifier + // isn't mapped then use NoSymbol. if a modifier has both left + // and right versions then (arbitrarily) prefer the left. also + // collect the available modifier bits. + struct CModifierBitInfo { + public: + KeySym CXWindowsSecondaryScreen::*m_keysym; + KeySym m_left; + KeySym m_right; + }; + static const CModifierBitInfo s_modifierBitTable[] = { + { &CXWindowsSecondaryScreen::m_shiftKeysym, XK_Shift_L, XK_Shift_R }, + { &CXWindowsSecondaryScreen::m_ctrlKeysym, XK_Control_L, XK_Control_R }, + { &CXWindowsSecondaryScreen::m_altKeysym, XK_Alt_L, XK_Alt_R }, + { &CXWindowsSecondaryScreen::m_metaKeysym, XK_Meta_L, XK_Meta_R }, + { &CXWindowsSecondaryScreen::m_superKeysym, XK_Super_L, XK_Super_R }, + { &CXWindowsSecondaryScreen::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, + { &CXWindowsSecondaryScreen::m_numLockKeysym, XK_Num_Lock, NoSymbol }, + { &CXWindowsSecondaryScreen::m_capsLockKeysym, XK_Caps_Lock, NoSymbol }, + { &CXWindowsSecondaryScreen::m_scrollLockKeysym, XK_Scroll_Lock, NoSymbol } + }; + m_modifierMask = 0; + m_toggleModifierMask = 0; + for (size_t i = 0; i < sizeof(s_modifierBitTable) / + sizeof(s_modifierBitTable[0]); ++i) { + const CModifierBitInfo& info = s_modifierBitTable[i]; + + // find available keysym + KeySymIndex keyIndex = m_keysymMap.find(info.m_left); + if (keyIndex == m_keysymMap.end() && info.m_right != NoSymbol) { + keyIndex = m_keysymMap.find(info.m_right); + } + if (keyIndex != m_keysymMap.end() && + keyIndex->second.m_modifierMask != 0) { + this->*(info.m_keysym) = keyIndex->first; + } + else { + this->*(info.m_keysym) = NoSymbol; + continue; + } + + // add modifier bit + m_modifierMask |= keyIndex->second.m_modifierMask; + if (isToggleKeysym(this->*(info.m_keysym))) { + m_toggleModifierMask |= keyIndex->second.m_modifierMask; + } + } + + // if there's no mode switch key mapped then remove all keycodes + // that depend on it and no keycode can be mode switch sensitive. + if (m_modeSwitchKeysym == NoSymbol) { + LOG((CLOG_DEBUG2 "no mode switch in keymap")); + for (KeySymMap::iterator i = m_keysymMap.begin(); + i != m_keysymMap.end(); ) { + i->second.m_keycode[2] = 0; + i->second.m_keycode[3] = 0; + i->second.m_modeSwitchSensitive[0] = false; + i->second.m_modeSwitchSensitive[1] = false; + i->second.m_modeSwitchSensitive[2] = false; + i->second.m_modeSwitchSensitive[3] = false; + + // if this keysym no has no keycodes then remove it + // except for the NoSymbol keysym mapping. + if (i->second.m_keycode[0] == 0 && i->second.m_keycode[1] == 0) { + m_keysymMap.erase(i++); + } + else { + ++i; + } + } + } + + // cache the bits for the modifier + m_shiftMask = getModifierMask(m_shiftKeysym); + m_ctrlMask = getModifierMask(m_ctrlKeysym); + m_altMask = getModifierMask(m_altKeysym); + m_metaMask = getModifierMask(m_metaKeysym); + m_superMask = getModifierMask(m_superKeysym); + m_capsLockMask = getModifierMask(m_capsLockKeysym); + m_numLockMask = getModifierMask(m_numLockKeysym); + m_modeSwitchMask = getModifierMask(m_modeSwitchKeysym); + m_scrollLockMask = getModifierMask(m_scrollLockKeysym); + + // clean up + XFree(keysyms); + XFreeModifiermap(modifiers); +} + +void +CXWindowsSecondaryScreen::updateModifiers(Display* display) +{ + // query the pointer to get the keyboard state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + state = 0; + } + + // update active modifier mask + m_mask = 0; + for (ModifierIndex i = 0; i < 8; ++i) { + const ModifierMask bit = (1 << i); + if ((bit & m_toggleModifierMask) == 0) { + for (KeyCodes::const_iterator j = m_modifierKeycodes[i].begin(); + j != m_modifierKeycodes[i].end(); ++j) { + if (m_keys[*j]) { + m_mask |= bit; + break; + } + } + } + else if ((bit & state) != 0) { + // toggle is on + m_mask |= bit; + } + } +} + +void +CXWindowsSecondaryScreen::toggleKey(Display* display, + KeySym keysym, ModifierMask mask) +{ + // lookup the key mapping + KeySymIndex index = m_keysymMap.find(keysym); + if (index == m_keysymMap.end()) { + return; + } + KeyCode keycode = index->second.m_keycode[0]; + + // toggle the key + if ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || + (keysym == m_numLockKeysym && m_numLockHalfDuplex)) { + // "half-duplex" toggle + XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime); + } + else { + // normal toggle + XTestFakeKeyEvent(display, keycode, True, CurrentTime); + XTestFakeKeyEvent(display, keycode, False, CurrentTime); + } + + // toggle shadow state + m_mask ^= mask; +} + +bool +CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) +{ + switch (key) { + case XK_Caps_Lock: + case XK_Shift_Lock: + case XK_Num_Lock: + case XK_Scroll_Lock: + return true; + + default: + return false; + } +} + +// map special KeyID keys to KeySyms +#if defined(HAVE_X11_XF86KEYSYM_H) +static const KeySym g_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, + /* 0xa6 */ XF86XK_Back, XF86XK_Forward, + /* 0xa8 */ XF86XK_Refresh, XF86XK_Stop, + /* 0xaa */ XF86XK_Search, XF86XK_Favorites, + /* 0xac */ XF86XK_HomePage, XF86XK_AudioMute, + /* 0xae */ XF86XK_AudioLowerVolume, XF86XK_AudioRaiseVolume, + /* 0xb0 */ XF86XK_AudioNext, XF86XK_AudioPrev, + /* 0xb2 */ XF86XK_AudioStop, XF86XK_AudioPlay, + /* 0xb4 */ XF86XK_Mail, XF86XK_AudioMedia, + /* 0xb6 */ XF86XK_Launch0, XF86XK_Launch1, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + +KeySym +CXWindowsSecondaryScreen::keyIDToKeySym(KeyID id, ModifierMask mask) const +{ + // convert id to keysym + KeySym keysym = NoSymbol; + if ((id & 0xfffff000) == 0xe000) { + // special character + switch (id & 0x0000ff00) { +#if defined(HAVE_X11_XF86KEYSYM_H) + case 0xe000: + return g_mapE000[id & 0xff]; +#endif + + case 0xee00: + // ISO 9995 Function and Modifier Keys + if (id == kKeyLeftTab) { + keysym = XK_ISO_Left_Tab; + } + break; + + case 0xef00: + // MISCELLANY + keysym = static_cast(id - 0xef00 + 0xff00); + break; + } + } + else if ((id >= 0x0020 && id <= 0x007e) || + (id >= 0x00a0 && id <= 0x00ff)) { + // Latin-1 maps directly + return static_cast(id); + } + else { + // lookup keysym in table + return CXWindowsUtil::mapUCS4ToKeySym(id); + } + + // fail if unknown key + if (keysym == NoSymbol) { + return keysym; + } + + // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab + // instead. if that doesn't work, we'll fall back to XK_Tab with + // shift active. this is to handle primary screens that don't map + // XK_ISO_Left_Tab sending events to secondary screens that do. + if (keysym == XK_Tab && (mask & ShiftMask) != 0) { + keysym = XK_ISO_Left_Tab; + } + + // some keysyms have emergency backups (particularly the numpad + // keys since most laptops don't have a separate numpad and the + // numpad overlaying the main keyboard may not have movement + // key bindings). figure out the emergency backup. + KeySym backupKeysym; + switch (keysym) { + case XK_KP_Home: + backupKeysym = XK_Home; + break; + + case XK_KP_Left: + backupKeysym = XK_Left; + break; + + case XK_KP_Up: + backupKeysym = XK_Up; + break; + + case XK_KP_Right: + backupKeysym = XK_Right; + break; + + case XK_KP_Down: + backupKeysym = XK_Down; + break; + + case XK_KP_Prior: + backupKeysym = XK_Prior; + break; + + case XK_KP_Next: + backupKeysym = XK_Next; + break; + + case XK_KP_End: + backupKeysym = XK_End; + break; + + case XK_KP_Insert: + backupKeysym = XK_Insert; + break; + + case XK_KP_Delete: + backupKeysym = XK_Delete; + break; + + case XK_ISO_Left_Tab: + backupKeysym = XK_Tab; + break; + + default: + backupKeysym = keysym; + break; + } + + // see if the keysym is assigned to any keycode. if not and the + // backup keysym is then use the backup keysym. + if (backupKeysym != keysym && + m_keysymMap.find(keysym) == m_keysymMap.end() && + m_keysymMap.find(backupKeysym) != m_keysymMap.end()) { + keysym = backupKeysym; + } + + return keysym; +} + +bool +CXWindowsSecondaryScreen::decomposeKeySym(KeySym keysym, + KeySyms& decomposed) const +{ + // unfortunately, X11 doesn't appear to have any way of + // decomposing a keysym into its component keysyms. we'll + // use a lookup table for certain character sets. + const KeySymsMap& table = getDecomposedKeySymTable(); + KeySymsMap::const_iterator i = table.find(keysym); + if (i == table.end()) { + return false; + } + decomposed = i->second; + return true; +} + +bool +CXWindowsSecondaryScreen::adjustForNumLock(KeySym keysym) const +{ + return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); +} + +bool +CXWindowsSecondaryScreen::adjustForCapsLock(KeySym keysym) const +{ + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + return (lKey != uKey); +} + +const CXWindowsSecondaryScreen::KeySymsMap& +CXWindowsSecondaryScreen::getDecomposedKeySymTable() +{ + static const KeySym s_rawTable[] = { + // non-dead version of dead keys + XK_grave, XK_dead_grave, XK_space, 0, + XK_acute, XK_dead_acute, XK_space, 0, + XK_asciicircum, XK_dead_circumflex, XK_space, 0, + XK_asciitilde, XK_dead_tilde, XK_space, 0, + XK_cedilla, XK_dead_cedilla, XK_space, 0, + XK_ogonek, XK_dead_ogonek, XK_space, 0, + XK_caron, XK_dead_caron, XK_space, 0, + XK_abovedot, XK_dead_abovedot, XK_space, 0, + XK_doubleacute, XK_dead_doubleacute, XK_space, 0, + XK_breve, XK_dead_breve, XK_space, 0, + XK_macron, XK_dead_macron, XK_space, 0, + + // Latin-1 (ISO 8859-1) + XK_Agrave, XK_dead_grave, XK_A, 0, + XK_Aacute, XK_dead_acute, XK_A, 0, + XK_Acircumflex, XK_dead_circumflex, XK_A, 0, + XK_Atilde, XK_dead_tilde, XK_A, 0, + XK_Adiaeresis, XK_dead_diaeresis, XK_A, 0, + XK_Aring, XK_dead_abovering, XK_A, 0, + XK_Ccedilla, XK_dead_cedilla, XK_C, 0, + XK_Egrave, XK_dead_grave, XK_E, 0, + XK_Eacute, XK_dead_acute, XK_E, 0, + XK_Ecircumflex, XK_dead_circumflex, XK_E, 0, + XK_Ediaeresis, XK_dead_diaeresis, XK_E, 0, + XK_Igrave, XK_dead_grave, XK_I, 0, + XK_Iacute, XK_dead_acute, XK_I, 0, + XK_Icircumflex, XK_dead_circumflex, XK_I, 0, + XK_Idiaeresis, XK_dead_diaeresis, XK_I, 0, + XK_Ntilde, XK_dead_tilde, XK_N, 0, + XK_Ograve, XK_dead_grave, XK_O, 0, + XK_Oacute, XK_dead_acute, XK_O, 0, + XK_Ocircumflex, XK_dead_circumflex, XK_O, 0, + XK_Otilde, XK_dead_tilde, XK_O, 0, + XK_Odiaeresis, XK_dead_diaeresis, XK_O, 0, + XK_Ugrave, XK_dead_grave, XK_U, 0, + XK_Uacute, XK_dead_acute, XK_U, 0, + XK_Ucircumflex, XK_dead_circumflex, XK_U, 0, + XK_Udiaeresis, XK_dead_diaeresis, XK_U, 0, + XK_Yacute, XK_dead_acute, XK_Y, 0, + XK_agrave, XK_dead_grave, XK_a, 0, + XK_aacute, XK_dead_acute, XK_a, 0, + XK_acircumflex, XK_dead_circumflex, XK_a, 0, + XK_atilde, XK_dead_tilde, XK_a, 0, + XK_adiaeresis, XK_dead_diaeresis, XK_a, 0, + XK_aring, XK_dead_abovering, XK_a, 0, + XK_ccedilla, XK_dead_cedilla, XK_c, 0, + XK_egrave, XK_dead_grave, XK_e, 0, + XK_eacute, XK_dead_acute, XK_e, 0, + XK_ecircumflex, XK_dead_circumflex, XK_e, 0, + XK_ediaeresis, XK_dead_diaeresis, XK_e, 0, + XK_igrave, XK_dead_grave, XK_i, 0, + XK_iacute, XK_dead_acute, XK_i, 0, + XK_icircumflex, XK_dead_circumflex, XK_i, 0, + XK_idiaeresis, XK_dead_diaeresis, XK_i, 0, + XK_ntilde, XK_dead_tilde, XK_n, 0, + XK_ograve, XK_dead_grave, XK_o, 0, + XK_oacute, XK_dead_acute, XK_o, 0, + XK_ocircumflex, XK_dead_circumflex, XK_o, 0, + XK_otilde, XK_dead_tilde, XK_o, 0, + XK_odiaeresis, XK_dead_diaeresis, XK_o, 0, + XK_ugrave, XK_dead_grave, XK_u, 0, + XK_uacute, XK_dead_acute, XK_u, 0, + XK_ucircumflex, XK_dead_circumflex, XK_u, 0, + XK_udiaeresis, XK_dead_diaeresis, XK_u, 0, + XK_yacute, XK_dead_acute, XK_y, 0, + XK_ydiaeresis, XK_dead_diaeresis, XK_y, 0, + + // Latin-2 (ISO 8859-2) + XK_Aogonek, XK_dead_ogonek, XK_A, 0, + XK_Lcaron, XK_dead_caron, XK_L, 0, + XK_Sacute, XK_dead_acute, XK_S, 0, + XK_Scaron, XK_dead_caron, XK_S, 0, + XK_Scedilla, XK_dead_cedilla, XK_S, 0, + XK_Tcaron, XK_dead_caron, XK_T, 0, + XK_Zacute, XK_dead_acute, XK_Z, 0, + XK_Zcaron, XK_dead_caron, XK_Z, 0, + XK_Zabovedot, XK_dead_abovedot, XK_Z, 0, + XK_aogonek, XK_dead_ogonek, XK_a, 0, + XK_lcaron, XK_dead_caron, XK_l, 0, + XK_sacute, XK_dead_acute, XK_s, 0, + XK_scaron, XK_dead_caron, XK_s, 0, + XK_scedilla, XK_dead_cedilla, XK_s, 0, + XK_tcaron, XK_dead_caron, XK_t, 0, + XK_zacute, XK_dead_acute, XK_z, 0, + XK_zcaron, XK_dead_caron, XK_z, 0, + XK_zabovedot, XK_dead_abovedot, XK_z, 0, + XK_Racute, XK_dead_acute, XK_R, 0, + XK_Abreve, XK_dead_breve, XK_A, 0, + XK_Lacute, XK_dead_acute, XK_L, 0, + XK_Cacute, XK_dead_acute, XK_C, 0, + XK_Ccaron, XK_dead_caron, XK_C, 0, + XK_Eogonek, XK_dead_ogonek, XK_E, 0, + XK_Ecaron, XK_dead_caron, XK_E, 0, + XK_Dcaron, XK_dead_caron, XK_D, 0, + XK_Nacute, XK_dead_acute, XK_N, 0, + XK_Ncaron, XK_dead_caron, XK_N, 0, + XK_Odoubleacute, XK_dead_doubleacute, XK_O, 0, + XK_Rcaron, XK_dead_caron, XK_R, 0, + XK_Uring, XK_dead_abovering, XK_U, 0, + XK_Udoubleacute, XK_dead_doubleacute, XK_U, 0, + XK_Tcedilla, XK_dead_cedilla, XK_T, 0, + XK_racute, XK_dead_acute, XK_r, 0, + XK_abreve, XK_dead_breve, XK_a, 0, + XK_lacute, XK_dead_acute, XK_l, 0, + XK_cacute, XK_dead_acute, XK_c, 0, + XK_ccaron, XK_dead_caron, XK_c, 0, + XK_eogonek, XK_dead_ogonek, XK_e, 0, + XK_ecaron, XK_dead_caron, XK_e, 0, + XK_dcaron, XK_dead_caron, XK_d, 0, + XK_nacute, XK_dead_acute, XK_n, 0, + XK_ncaron, XK_dead_caron, XK_n, 0, + XK_odoubleacute, XK_dead_doubleacute, XK_o, 0, + XK_rcaron, XK_dead_caron, XK_r, 0, + XK_uring, XK_dead_abovering, XK_u, 0, + XK_udoubleacute, XK_dead_doubleacute, XK_u, 0, + XK_tcedilla, XK_dead_cedilla, XK_t, 0, + + // Latin-3 (ISO 8859-3) + XK_Hcircumflex, XK_dead_circumflex, XK_H, 0, + XK_Iabovedot, XK_dead_abovedot, XK_I, 0, + XK_Gbreve, XK_dead_breve, XK_G, 0, + XK_Jcircumflex, XK_dead_circumflex, XK_J, 0, + XK_hcircumflex, XK_dead_circumflex, XK_h, 0, + XK_gbreve, XK_dead_breve, XK_g, 0, + XK_jcircumflex, XK_dead_circumflex, XK_j, 0, + XK_Cabovedot, XK_dead_abovedot, XK_C, 0, + XK_Ccircumflex, XK_dead_circumflex, XK_C, 0, + XK_Gabovedot, XK_dead_abovedot, XK_G, 0, + XK_Gcircumflex, XK_dead_circumflex, XK_G, 0, + XK_Ubreve, XK_dead_breve, XK_U, 0, + XK_Scircumflex, XK_dead_circumflex, XK_S, 0, + XK_cabovedot, XK_dead_abovedot, XK_c, 0, + XK_ccircumflex, XK_dead_circumflex, XK_c, 0, + XK_gabovedot, XK_dead_abovedot, XK_g, 0, + XK_gcircumflex, XK_dead_circumflex, XK_g, 0, + XK_ubreve, XK_dead_breve, XK_u, 0, + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + + // Latin-4 (ISO 8859-4) + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + XK_Rcedilla, XK_dead_cedilla, XK_R, 0, + XK_Itilde, XK_dead_tilde, XK_I, 0, + XK_Lcedilla, XK_dead_cedilla, XK_L, 0, + XK_Emacron, XK_dead_macron, XK_E, 0, + XK_Gcedilla, XK_dead_cedilla, XK_G, 0, + XK_rcedilla, XK_dead_cedilla, XK_r, 0, + XK_itilde, XK_dead_tilde, XK_i, 0, + XK_lcedilla, XK_dead_cedilla, XK_l, 0, + XK_emacron, XK_dead_macron, XK_e, 0, + XK_gcedilla, XK_dead_cedilla, XK_g, 0, + XK_Amacron, XK_dead_macron, XK_A, 0, + XK_Iogonek, XK_dead_ogonek, XK_I, 0, + XK_Eabovedot, XK_dead_abovedot, XK_E, 0, + XK_Imacron, XK_dead_macron, XK_I, 0, + XK_Ncedilla, XK_dead_cedilla, XK_N, 0, + XK_Omacron, XK_dead_macron, XK_O, 0, + XK_Kcedilla, XK_dead_cedilla, XK_K, 0, + XK_Uogonek, XK_dead_ogonek, XK_U, 0, + XK_Utilde, XK_dead_tilde, XK_U, 0, + XK_Umacron, XK_dead_macron, XK_U, 0, + XK_amacron, XK_dead_macron, XK_a, 0, + XK_iogonek, XK_dead_ogonek, XK_i, 0, + XK_eabovedot, XK_dead_abovedot, XK_e, 0, + XK_imacron, XK_dead_macron, XK_i, 0, + XK_ncedilla, XK_dead_cedilla, XK_n, 0, + XK_omacron, XK_dead_macron, XK_o, 0, + XK_kcedilla, XK_dead_cedilla, XK_k, 0, + XK_uogonek, XK_dead_ogonek, XK_u, 0, + XK_utilde, XK_dead_tilde, XK_u, 0, + XK_umacron, XK_dead_macron, XK_u, 0, + + // Latin-8 (ISO 8859-14) + XK_Babovedot, XK_dead_abovedot, XK_B, 0, + XK_babovedot, XK_dead_abovedot, XK_b, 0, + XK_Dabovedot, XK_dead_abovedot, XK_D, 0, + XK_Wgrave, XK_dead_grave, XK_W, 0, + XK_Wacute, XK_dead_acute, XK_W, 0, + XK_dabovedot, XK_dead_abovedot, XK_d, 0, + XK_Ygrave, XK_dead_grave, XK_Y, 0, + XK_Fabovedot, XK_dead_abovedot, XK_F, 0, + XK_fabovedot, XK_dead_abovedot, XK_f, 0, + XK_Mabovedot, XK_dead_abovedot, XK_M, 0, + XK_mabovedot, XK_dead_abovedot, XK_m, 0, + XK_Pabovedot, XK_dead_abovedot, XK_P, 0, + XK_wgrave, XK_dead_grave, XK_w, 0, + XK_pabovedot, XK_dead_abovedot, XK_p, 0, + XK_wacute, XK_dead_acute, XK_w, 0, + XK_Sabovedot, XK_dead_abovedot, XK_S, 0, + XK_ygrave, XK_dead_grave, XK_y, 0, + XK_Wdiaeresis, XK_dead_diaeresis, XK_W, 0, + XK_wdiaeresis, XK_dead_diaeresis, XK_w, 0, + XK_sabovedot, XK_dead_abovedot, XK_s, 0, + XK_Wcircumflex, XK_dead_circumflex, XK_W, 0, + XK_Tabovedot, XK_dead_abovedot, XK_T, 0, + XK_Ycircumflex, XK_dead_circumflex, XK_Y, 0, + XK_wcircumflex, XK_dead_circumflex, XK_w, 0, + XK_tabovedot, XK_dead_abovedot, XK_t, 0, + XK_ycircumflex, XK_dead_circumflex, XK_y, 0, + + // Latin-9 (ISO 8859-15) + XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, + + // end of table + 0 + }; + + // fill table if not yet initialized + if (s_decomposedKeySyms.empty()) { + const KeySym* scan = s_rawTable; + while (*scan != 0) { + // add an entry for this keysym + KeySyms& entry = s_decomposedKeySyms[*scan]; + + // add the decomposed keysyms for the keysym + while (*++scan != 0) { + entry.push_back(*scan); + } + + // skip end of entry marker + ++scan; + } + } + + return s_decomposedKeySyms; +} + + +// +// CXWindowsSecondaryScreen::KeyMapping +// + +CXWindowsSecondaryScreen::KeyMapping::KeyMapping() +{ + m_keycode[0] = 0; + m_keycode[1] = 0; + m_keycode[2] = 0; + m_keycode[3] = 0; +} diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h new file mode 100644 index 0000000..c2de53e --- /dev/null +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -0,0 +1,240 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSECONDARYSCREEN_H +#define CXWINDOWSSECONDARYSCREEN_H + +#include "CSecondaryScreen.h" +#include "IScreenEventHandler.h" +#include "stdbitset.h" +#include "stdmap.h" +#include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class CXWindowsScreen; +class IScreenReceiver; + +//! X11 secondary screen implementation +class CXWindowsSecondaryScreen : + public CSecondaryScreen, public IScreenEventHandler { +public: + CXWindowsSecondaryScreen(IScreenReceiver*); + virtual ~CXWindowsSecondaryScreen(); + + // CSecondaryScreen overrides + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 x, SInt32 y); + virtual void mouseWheel(SInt32 delta); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual IScreen* getScreen() const; + + // IScreenEventHandler overrides + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); + virtual SInt32 getJumpZoneSize() const; + +protected: + // CSecondaryScreen overrides + virtual void onPreMainLoop(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void createWindow(); + virtual void destroyWindow(); + virtual void showWindow(SInt32 x, SInt32 y); + virtual void hideWindow(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void updateKeys(); + virtual void releaseKeys(); + virtual void setToggleState(KeyModifierMask); + virtual KeyModifierMask getToggleState() const; + +private: + enum EKeyAction { kPress, kRelease, kRepeat }; + typedef unsigned int ModifierIndex; + typedef unsigned int ModifierMask; + class Keystroke { + public: + KeyCode m_keycode; + Bool m_press; + bool m_repeat; + }; + class KeyMapping { + public: + KeyMapping(); + + public: + // KeyCode to generate keysym and whether keycode[i] is + // sensitive to shift and mode switch. + KeyCode m_keycode[4]; + bool m_shiftSensitive[4]; + bool m_modeSwitchSensitive[4]; + + // the modifier mask of keysym or 0 if not a modifier + ModifierMask m_modifierMask; + + // whether keysym is sensitive to caps and num lock + bool m_numLockSensitive; + bool m_capsLockSensitive; + }; + + typedef std::vector KeyCodes; + typedef std::map KeyCodeToModifierMap; + typedef std::map KeySymMap; + typedef KeySymMap::const_iterator KeySymIndex; + typedef std::vector Keystrokes; + typedef std::vector KeySyms; + typedef std::map KeySymsMap; + typedef std::map ServerKeyMap; + + void flush(Display*) const; + + unsigned int mapButton(ButtonID button) const; + + ModifierMask mapKey(Keystrokes&, KeyCode&, KeyID, + KeyModifierMask, EKeyAction) const; + ModifierMask mapKeyRelease(Keystrokes&, KeyCode) const; + bool mapToKeystrokes(Keystrokes& keys, + KeyCode& keycode, + ModifierMask& finalMask, + KeySymIndex keyIndex, + ModifierMask currentMask, + EKeyAction action) const; + bool adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + ModifierMask& inOutMask, + ModifierMask desiredMask) const; + bool adjustModifier(Keystrokes& keys, + Keystrokes& undo, + KeySym keysym, + bool desireActive) const; + void doKeystrokes(const Keystrokes&, SInt32 count); + ModifierMask maskToX(KeyModifierMask) const; + + unsigned int findBestKeyIndex(KeySymIndex keyIndex, + ModifierMask currentMask) const; + bool isShiftInverted(KeySymIndex keyIndex, + ModifierMask currentMask) const; + ModifierMask getModifierMask(KeySym) const; + + void doUpdateKeys(Display*); + void doReleaseKeys(Display*); + void updateKeysymMap(Display* display); + void updateModifiers(Display* display); + ModifierIndex keySymToModifierIndex(KeySym) const; + void toggleKey(Display*, KeySym, ModifierMask mask); + static bool isToggleKeysym(KeySym); + + KeySym keyIDToKeySym(KeyID id, ModifierMask mask) const; + bool adjustForNumLock(KeySym) const; + bool adjustForCapsLock(KeySym) const; + + bool decomposeKeySym(KeySym keysym, + KeySyms& decomposed) const; + static const KeySymsMap& getDecomposedKeySymTable(); + +private: + CXWindowsScreen* m_screen; + Window m_window; + + // note toggle keys that toggles on up/down (false) or on + // transition (true) + bool m_numLockHalfDuplex; + bool m_capsLockHalfDuplex; + + // set entries indicate keys that are pressed (by us or by the user). + // indexed by keycode. + std::bitset<256> m_keys; + + // set entries indicate keys that are synthetically pressed by us. + // this is normally the same as m_keys. + std::bitset<256> m_fakeKeys; + + // logical to physical button mapping. m_buttons[i] gives the + // physical button for logical button i+1. + std::vector m_buttons; + + // current active modifiers (X key masks) + ModifierMask m_mask; + + // the modifiers that have keys bound to them + ModifierMask m_modifierMask; + + // set bits indicate modifiers that toggle (e.g. caps-lock) + ModifierMask m_toggleModifierMask; + + // keysym to keycode mapping + KeySymMap m_keysymMap; + + // modifier index to keycodes + KeyCodes m_modifierKeycodes[8]; + + // keycode to modifier index + KeyCodeToModifierMap m_keycodeToModifier; + + // modifier keysyms + KeySym m_shiftKeysym; + KeySym m_ctrlKeysym; + KeySym m_altKeysym; + KeySym m_metaKeysym; + KeySym m_superKeysym; + KeySym m_modeSwitchKeysym; + KeySym m_numLockKeysym; + KeySym m_capsLockKeysym; + KeySym m_scrollLockKeysym; + + // modifier masks + ModifierMask m_shiftMask; + ModifierMask m_ctrlMask; + ModifierMask m_altMask; + ModifierMask m_metaMask; + ModifierMask m_superMask; + ModifierMask m_modeSwitchMask; + ModifierMask m_numLockMask; + ModifierMask m_capsLockMask; + ModifierMask m_scrollLockMask; + + // map server key buttons to local keycodes + ServerKeyMap m_serverKeyMap; + + // the keyboard control state the last time this screen was entered + XKeyboardState m_keyControl; + + // stuff to workaround xtest being xinerama unaware. attempting + // to fake a mouse motion under xinerama may behave strangely, + // especially if screen 0 is not at 0,0 or if faking a motion on + // a screen other than screen 0. + bool m_xtestIsXineramaUnaware; + bool m_xinerama; + + // a table of keysym decompositions + static KeySymsMap s_decomposedKeySyms; +}; + +#endif diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp new file mode 100644 index 0000000..05415dd --- /dev/null +++ b/lib/platform/CXWindowsUtil.cpp @@ -0,0 +1,1111 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsUtil.h" +#include "CThread.h" +#include "CLog.h" +#include + +/* + * This table maps keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. + * + * Author: Markus G. Kuhn , + * University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + */ + +struct codepair { + KeySym keysym; + UInt32 ucs4; +} s_keymap[] = { +{ 0x01a1, 0x0104 }, /* Aogonek LATIN CAPITAL LETTER A WITH OGONEK */ +{ 0x01a2, 0x02d8 }, /* breve BREVE */ +{ 0x01a3, 0x0141 }, /* Lstroke LATIN CAPITAL LETTER L WITH STROKE */ +{ 0x01a5, 0x013d }, /* Lcaron LATIN CAPITAL LETTER L WITH CARON */ +{ 0x01a6, 0x015a }, /* Sacute LATIN CAPITAL LETTER S WITH ACUTE */ +{ 0x01a9, 0x0160 }, /* Scaron LATIN CAPITAL LETTER S WITH CARON */ +{ 0x01aa, 0x015e }, /* Scedilla LATIN CAPITAL LETTER S WITH CEDILLA */ +{ 0x01ab, 0x0164 }, /* Tcaron LATIN CAPITAL LETTER T WITH CARON */ +{ 0x01ac, 0x0179 }, /* Zacute LATIN CAPITAL LETTER Z WITH ACUTE */ +{ 0x01ae, 0x017d }, /* Zcaron LATIN CAPITAL LETTER Z WITH CARON */ +{ 0x01af, 0x017b }, /* Zabovedot LATIN CAPITAL LETTER Z WITH DOT ABOVE */ +{ 0x01b1, 0x0105 }, /* aogonek LATIN SMALL LETTER A WITH OGONEK */ +{ 0x01b2, 0x02db }, /* ogonek OGONEK */ +{ 0x01b3, 0x0142 }, /* lstroke LATIN SMALL LETTER L WITH STROKE */ +{ 0x01b5, 0x013e }, /* lcaron LATIN SMALL LETTER L WITH CARON */ +{ 0x01b6, 0x015b }, /* sacute LATIN SMALL LETTER S WITH ACUTE */ +{ 0x01b7, 0x02c7 }, /* caron CARON */ +{ 0x01b9, 0x0161 }, /* scaron LATIN SMALL LETTER S WITH CARON */ +{ 0x01ba, 0x015f }, /* scedilla LATIN SMALL LETTER S WITH CEDILLA */ +{ 0x01bb, 0x0165 }, /* tcaron LATIN SMALL LETTER T WITH CARON */ +{ 0x01bc, 0x017a }, /* zacute LATIN SMALL LETTER Z WITH ACUTE */ +{ 0x01bd, 0x02dd }, /* doubleacute DOUBLE ACUTE ACCENT */ +{ 0x01be, 0x017e }, /* zcaron LATIN SMALL LETTER Z WITH CARON */ +{ 0x01bf, 0x017c }, /* zabovedot LATIN SMALL LETTER Z WITH DOT ABOVE */ +{ 0x01c0, 0x0154 }, /* Racute LATIN CAPITAL LETTER R WITH ACUTE */ +{ 0x01c3, 0x0102 }, /* Abreve LATIN CAPITAL LETTER A WITH BREVE */ +{ 0x01c5, 0x0139 }, /* Lacute LATIN CAPITAL LETTER L WITH ACUTE */ +{ 0x01c6, 0x0106 }, /* Cacute LATIN CAPITAL LETTER C WITH ACUTE */ +{ 0x01c8, 0x010c }, /* Ccaron LATIN CAPITAL LETTER C WITH CARON */ +{ 0x01ca, 0x0118 }, /* Eogonek LATIN CAPITAL LETTER E WITH OGONEK */ +{ 0x01cc, 0x011a }, /* Ecaron LATIN CAPITAL LETTER E WITH CARON */ +{ 0x01cf, 0x010e }, /* Dcaron LATIN CAPITAL LETTER D WITH CARON */ +{ 0x01d0, 0x0110 }, /* Dstroke LATIN CAPITAL LETTER D WITH STROKE */ +{ 0x01d1, 0x0143 }, /* Nacute LATIN CAPITAL LETTER N WITH ACUTE */ +{ 0x01d2, 0x0147 }, /* Ncaron LATIN CAPITAL LETTER N WITH CARON */ +{ 0x01d5, 0x0150 }, /* Odoubleacute LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ +{ 0x01d8, 0x0158 }, /* Rcaron LATIN CAPITAL LETTER R WITH CARON */ +{ 0x01d9, 0x016e }, /* Uring LATIN CAPITAL LETTER U WITH RING ABOVE */ +{ 0x01db, 0x0170 }, /* Udoubleacute LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ +{ 0x01de, 0x0162 }, /* Tcedilla LATIN CAPITAL LETTER T WITH CEDILLA */ +{ 0x01e0, 0x0155 }, /* racute LATIN SMALL LETTER R WITH ACUTE */ +{ 0x01e3, 0x0103 }, /* abreve LATIN SMALL LETTER A WITH BREVE */ +{ 0x01e5, 0x013a }, /* lacute LATIN SMALL LETTER L WITH ACUTE */ +{ 0x01e6, 0x0107 }, /* cacute LATIN SMALL LETTER C WITH ACUTE */ +{ 0x01e8, 0x010d }, /* ccaron LATIN SMALL LETTER C WITH CARON */ +{ 0x01ea, 0x0119 }, /* eogonek LATIN SMALL LETTER E WITH OGONEK */ +{ 0x01ec, 0x011b }, /* ecaron LATIN SMALL LETTER E WITH CARON */ +{ 0x01ef, 0x010f }, /* dcaron LATIN SMALL LETTER D WITH CARON */ +{ 0x01f0, 0x0111 }, /* dstroke LATIN SMALL LETTER D WITH STROKE */ +{ 0x01f1, 0x0144 }, /* nacute LATIN SMALL LETTER N WITH ACUTE */ +{ 0x01f2, 0x0148 }, /* ncaron LATIN SMALL LETTER N WITH CARON */ +{ 0x01f5, 0x0151 }, /* odoubleacute LATIN SMALL LETTER O WITH DOUBLE ACUTE */ +{ 0x01f8, 0x0159 }, /* rcaron LATIN SMALL LETTER R WITH CARON */ +{ 0x01f9, 0x016f }, /* uring LATIN SMALL LETTER U WITH RING ABOVE */ +{ 0x01fb, 0x0171 }, /* udoubleacute LATIN SMALL LETTER U WITH DOUBLE ACUTE */ +{ 0x01fe, 0x0163 }, /* tcedilla LATIN SMALL LETTER T WITH CEDILLA */ +{ 0x01ff, 0x02d9 }, /* abovedot DOT ABOVE */ +{ 0x02a1, 0x0126 }, /* Hstroke LATIN CAPITAL LETTER H WITH STROKE */ +{ 0x02a6, 0x0124 }, /* Hcircumflex LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ +{ 0x02a9, 0x0130 }, /* Iabovedot LATIN CAPITAL LETTER I WITH DOT ABOVE */ +{ 0x02ab, 0x011e }, /* Gbreve LATIN CAPITAL LETTER G WITH BREVE */ +{ 0x02ac, 0x0134 }, /* Jcircumflex LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ +{ 0x02b1, 0x0127 }, /* hstroke LATIN SMALL LETTER H WITH STROKE */ +{ 0x02b6, 0x0125 }, /* hcircumflex LATIN SMALL LETTER H WITH CIRCUMFLEX */ +{ 0x02b9, 0x0131 }, /* idotless LATIN SMALL LETTER DOTLESS I */ +{ 0x02bb, 0x011f }, /* gbreve LATIN SMALL LETTER G WITH BREVE */ +{ 0x02bc, 0x0135 }, /* jcircumflex LATIN SMALL LETTER J WITH CIRCUMFLEX */ +{ 0x02c5, 0x010a }, /* Cabovedot LATIN CAPITAL LETTER C WITH DOT ABOVE */ +{ 0x02c6, 0x0108 }, /* Ccircumflex LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ +{ 0x02d5, 0x0120 }, /* Gabovedot LATIN CAPITAL LETTER G WITH DOT ABOVE */ +{ 0x02d8, 0x011c }, /* Gcircumflex LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ +{ 0x02dd, 0x016c }, /* Ubreve LATIN CAPITAL LETTER U WITH BREVE */ +{ 0x02de, 0x015c }, /* Scircumflex LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ +{ 0x02e5, 0x010b }, /* cabovedot LATIN SMALL LETTER C WITH DOT ABOVE */ +{ 0x02e6, 0x0109 }, /* ccircumflex LATIN SMALL LETTER C WITH CIRCUMFLEX */ +{ 0x02f5, 0x0121 }, /* gabovedot LATIN SMALL LETTER G WITH DOT ABOVE */ +{ 0x02f8, 0x011d }, /* gcircumflex LATIN SMALL LETTER G WITH CIRCUMFLEX */ +{ 0x02fd, 0x016d }, /* ubreve LATIN SMALL LETTER U WITH BREVE */ +{ 0x02fe, 0x015d }, /* scircumflex LATIN SMALL LETTER S WITH CIRCUMFLEX */ +{ 0x03a2, 0x0138 }, /* kra LATIN SMALL LETTER KRA */ +{ 0x03a3, 0x0156 }, /* Rcedilla LATIN CAPITAL LETTER R WITH CEDILLA */ +{ 0x03a5, 0x0128 }, /* Itilde LATIN CAPITAL LETTER I WITH TILDE */ +{ 0x03a6, 0x013b }, /* Lcedilla LATIN CAPITAL LETTER L WITH CEDILLA */ +{ 0x03aa, 0x0112 }, /* Emacron LATIN CAPITAL LETTER E WITH MACRON */ +{ 0x03ab, 0x0122 }, /* Gcedilla LATIN CAPITAL LETTER G WITH CEDILLA */ +{ 0x03ac, 0x0166 }, /* Tslash LATIN CAPITAL LETTER T WITH STROKE */ +{ 0x03b3, 0x0157 }, /* rcedilla LATIN SMALL LETTER R WITH CEDILLA */ +{ 0x03b5, 0x0129 }, /* itilde LATIN SMALL LETTER I WITH TILDE */ +{ 0x03b6, 0x013c }, /* lcedilla LATIN SMALL LETTER L WITH CEDILLA */ +{ 0x03ba, 0x0113 }, /* emacron LATIN SMALL LETTER E WITH MACRON */ +{ 0x03bb, 0x0123 }, /* gcedilla LATIN SMALL LETTER G WITH CEDILLA */ +{ 0x03bc, 0x0167 }, /* tslash LATIN SMALL LETTER T WITH STROKE */ +{ 0x03bd, 0x014a }, /* ENG LATIN CAPITAL LETTER ENG */ +{ 0x03bf, 0x014b }, /* eng LATIN SMALL LETTER ENG */ +{ 0x03c0, 0x0100 }, /* Amacron LATIN CAPITAL LETTER A WITH MACRON */ +{ 0x03c7, 0x012e }, /* Iogonek LATIN CAPITAL LETTER I WITH OGONEK */ +{ 0x03cc, 0x0116 }, /* Eabovedot LATIN CAPITAL LETTER E WITH DOT ABOVE */ +{ 0x03cf, 0x012a }, /* Imacron LATIN CAPITAL LETTER I WITH MACRON */ +{ 0x03d1, 0x0145 }, /* Ncedilla LATIN CAPITAL LETTER N WITH CEDILLA */ +{ 0x03d2, 0x014c }, /* Omacron LATIN CAPITAL LETTER O WITH MACRON */ +{ 0x03d3, 0x0136 }, /* Kcedilla LATIN CAPITAL LETTER K WITH CEDILLA */ +{ 0x03d9, 0x0172 }, /* Uogonek LATIN CAPITAL LETTER U WITH OGONEK */ +{ 0x03dd, 0x0168 }, /* Utilde LATIN CAPITAL LETTER U WITH TILDE */ +{ 0x03de, 0x016a }, /* Umacron LATIN CAPITAL LETTER U WITH MACRON */ +{ 0x03e0, 0x0101 }, /* amacron LATIN SMALL LETTER A WITH MACRON */ +{ 0x03e7, 0x012f }, /* iogonek LATIN SMALL LETTER I WITH OGONEK */ +{ 0x03ec, 0x0117 }, /* eabovedot LATIN SMALL LETTER E WITH DOT ABOVE */ +{ 0x03ef, 0x012b }, /* imacron LATIN SMALL LETTER I WITH MACRON */ +{ 0x03f1, 0x0146 }, /* ncedilla LATIN SMALL LETTER N WITH CEDILLA */ +{ 0x03f2, 0x014d }, /* omacron LATIN SMALL LETTER O WITH MACRON */ +{ 0x03f3, 0x0137 }, /* kcedilla LATIN SMALL LETTER K WITH CEDILLA */ +{ 0x03f9, 0x0173 }, /* uogonek LATIN SMALL LETTER U WITH OGONEK */ +{ 0x03fd, 0x0169 }, /* utilde LATIN SMALL LETTER U WITH TILDE */ +{ 0x03fe, 0x016b }, /* umacron LATIN SMALL LETTER U WITH MACRON */ +{ 0x047e, 0x203e }, /* overline OVERLINE */ +{ 0x04a1, 0x3002 }, /* kana_fullstop IDEOGRAPHIC FULL STOP */ +{ 0x04a2, 0x300c }, /* kana_openingbracket LEFT CORNER BRACKET */ +{ 0x04a3, 0x300d }, /* kana_closingbracket RIGHT CORNER BRACKET */ +{ 0x04a4, 0x3001 }, /* kana_comma IDEOGRAPHIC COMMA */ +{ 0x04a5, 0x30fb }, /* kana_conjunctive KATAKANA MIDDLE DOT */ +{ 0x04a6, 0x30f2 }, /* kana_WO KATAKANA LETTER WO */ +{ 0x04a7, 0x30a1 }, /* kana_a KATAKANA LETTER SMALL A */ +{ 0x04a8, 0x30a3 }, /* kana_i KATAKANA LETTER SMALL I */ +{ 0x04a9, 0x30a5 }, /* kana_u KATAKANA LETTER SMALL U */ +{ 0x04aa, 0x30a7 }, /* kana_e KATAKANA LETTER SMALL E */ +{ 0x04ab, 0x30a9 }, /* kana_o KATAKANA LETTER SMALL O */ +{ 0x04ac, 0x30e3 }, /* kana_ya KATAKANA LETTER SMALL YA */ +{ 0x04ad, 0x30e5 }, /* kana_yu KATAKANA LETTER SMALL YU */ +{ 0x04ae, 0x30e7 }, /* kana_yo KATAKANA LETTER SMALL YO */ +{ 0x04af, 0x30c3 }, /* kana_tsu KATAKANA LETTER SMALL TU */ +{ 0x04b0, 0x30fc }, /* prolongedsound KATAKANA-HIRAGANA PROLONGED SOUND MARK */ +{ 0x04b1, 0x30a2 }, /* kana_A KATAKANA LETTER A */ +{ 0x04b2, 0x30a4 }, /* kana_I KATAKANA LETTER I */ +{ 0x04b3, 0x30a6 }, /* kana_U KATAKANA LETTER U */ +{ 0x04b4, 0x30a8 }, /* kana_E KATAKANA LETTER E */ +{ 0x04b5, 0x30aa }, /* kana_O KATAKANA LETTER O */ +{ 0x04b6, 0x30ab }, /* kana_KA KATAKANA LETTER KA */ +{ 0x04b7, 0x30ad }, /* kana_KI KATAKANA LETTER KI */ +{ 0x04b8, 0x30af }, /* kana_KU KATAKANA LETTER KU */ +{ 0x04b9, 0x30b1 }, /* kana_KE KATAKANA LETTER KE */ +{ 0x04ba, 0x30b3 }, /* kana_KO KATAKANA LETTER KO */ +{ 0x04bb, 0x30b5 }, /* kana_SA KATAKANA LETTER SA */ +{ 0x04bc, 0x30b7 }, /* kana_SHI KATAKANA LETTER SI */ +{ 0x04bd, 0x30b9 }, /* kana_SU KATAKANA LETTER SU */ +{ 0x04be, 0x30bb }, /* kana_SE KATAKANA LETTER SE */ +{ 0x04bf, 0x30bd }, /* kana_SO KATAKANA LETTER SO */ +{ 0x04c0, 0x30bf }, /* kana_TA KATAKANA LETTER TA */ +{ 0x04c1, 0x30c1 }, /* kana_CHI KATAKANA LETTER TI */ +{ 0x04c2, 0x30c4 }, /* kana_TSU KATAKANA LETTER TU */ +{ 0x04c3, 0x30c6 }, /* kana_TE KATAKANA LETTER TE */ +{ 0x04c4, 0x30c8 }, /* kana_TO KATAKANA LETTER TO */ +{ 0x04c5, 0x30ca }, /* kana_NA KATAKANA LETTER NA */ +{ 0x04c6, 0x30cb }, /* kana_NI KATAKANA LETTER NI */ +{ 0x04c7, 0x30cc }, /* kana_NU KATAKANA LETTER NU */ +{ 0x04c8, 0x30cd }, /* kana_NE KATAKANA LETTER NE */ +{ 0x04c9, 0x30ce }, /* kana_NO KATAKANA LETTER NO */ +{ 0x04ca, 0x30cf }, /* kana_HA KATAKANA LETTER HA */ +{ 0x04cb, 0x30d2 }, /* kana_HI KATAKANA LETTER HI */ +{ 0x04cc, 0x30d5 }, /* kana_FU KATAKANA LETTER HU */ +{ 0x04cd, 0x30d8 }, /* kana_HE KATAKANA LETTER HE */ +{ 0x04ce, 0x30db }, /* kana_HO KATAKANA LETTER HO */ +{ 0x04cf, 0x30de }, /* kana_MA KATAKANA LETTER MA */ +{ 0x04d0, 0x30df }, /* kana_MI KATAKANA LETTER MI */ +{ 0x04d1, 0x30e0 }, /* kana_MU KATAKANA LETTER MU */ +{ 0x04d2, 0x30e1 }, /* kana_ME KATAKANA LETTER ME */ +{ 0x04d3, 0x30e2 }, /* kana_MO KATAKANA LETTER MO */ +{ 0x04d4, 0x30e4 }, /* kana_YA KATAKANA LETTER YA */ +{ 0x04d5, 0x30e6 }, /* kana_YU KATAKANA LETTER YU */ +{ 0x04d6, 0x30e8 }, /* kana_YO KATAKANA LETTER YO */ +{ 0x04d7, 0x30e9 }, /* kana_RA KATAKANA LETTER RA */ +{ 0x04d8, 0x30ea }, /* kana_RI KATAKANA LETTER RI */ +{ 0x04d9, 0x30eb }, /* kana_RU KATAKANA LETTER RU */ +{ 0x04da, 0x30ec }, /* kana_RE KATAKANA LETTER RE */ +{ 0x04db, 0x30ed }, /* kana_RO KATAKANA LETTER RO */ +{ 0x04dc, 0x30ef }, /* kana_WA KATAKANA LETTER WA */ +{ 0x04dd, 0x30f3 }, /* kana_N KATAKANA LETTER N */ +{ 0x04de, 0x309b }, /* voicedsound KATAKANA-HIRAGANA VOICED SOUND MARK */ +{ 0x04df, 0x309c }, /* semivoicedsound KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +{ 0x05ac, 0x060c }, /* Arabic_comma ARABIC COMMA */ +{ 0x05bb, 0x061b }, /* Arabic_semicolon ARABIC SEMICOLON */ +{ 0x05bf, 0x061f }, /* Arabic_question_mark ARABIC QUESTION MARK */ +{ 0x05c1, 0x0621 }, /* Arabic_hamza ARABIC LETTER HAMZA */ +{ 0x05c2, 0x0622 }, /* Arabic_maddaonalef ARABIC LETTER ALEF WITH MADDA ABOVE */ +{ 0x05c3, 0x0623 }, /* Arabic_hamzaonalef ARABIC LETTER ALEF WITH HAMZA ABOVE */ +{ 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ARABIC LETTER WAW WITH HAMZA ABOVE */ +{ 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef ARABIC LETTER ALEF WITH HAMZA BELOW */ +{ 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ARABIC LETTER YEH WITH HAMZA ABOVE */ +{ 0x05c7, 0x0627 }, /* Arabic_alef ARABIC LETTER ALEF */ +{ 0x05c8, 0x0628 }, /* Arabic_beh ARABIC LETTER BEH */ +{ 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ARABIC LETTER TEH MARBUTA */ +{ 0x05ca, 0x062a }, /* Arabic_teh ARABIC LETTER TEH */ +{ 0x05cb, 0x062b }, /* Arabic_theh ARABIC LETTER THEH */ +{ 0x05cc, 0x062c }, /* Arabic_jeem ARABIC LETTER JEEM */ +{ 0x05cd, 0x062d }, /* Arabic_hah ARABIC LETTER HAH */ +{ 0x05ce, 0x062e }, /* Arabic_khah ARABIC LETTER KHAH */ +{ 0x05cf, 0x062f }, /* Arabic_dal ARABIC LETTER DAL */ +{ 0x05d0, 0x0630 }, /* Arabic_thal ARABIC LETTER THAL */ +{ 0x05d1, 0x0631 }, /* Arabic_ra ARABIC LETTER REH */ +{ 0x05d2, 0x0632 }, /* Arabic_zain ARABIC LETTER ZAIN */ +{ 0x05d3, 0x0633 }, /* Arabic_seen ARABIC LETTER SEEN */ +{ 0x05d4, 0x0634 }, /* Arabic_sheen ARABIC LETTER SHEEN */ +{ 0x05d5, 0x0635 }, /* Arabic_sad ARABIC LETTER SAD */ +{ 0x05d6, 0x0636 }, /* Arabic_dad ARABIC LETTER DAD */ +{ 0x05d7, 0x0637 }, /* Arabic_tah ARABIC LETTER TAH */ +{ 0x05d8, 0x0638 }, /* Arabic_zah ARABIC LETTER ZAH */ +{ 0x05d9, 0x0639 }, /* Arabic_ain ARABIC LETTER AIN */ +{ 0x05da, 0x063a }, /* Arabic_ghain ARABIC LETTER GHAIN */ +{ 0x05e0, 0x0640 }, /* Arabic_tatweel ARABIC TATWEEL */ +{ 0x05e1, 0x0641 }, /* Arabic_feh ARABIC LETTER FEH */ +{ 0x05e2, 0x0642 }, /* Arabic_qaf ARABIC LETTER QAF */ +{ 0x05e3, 0x0643 }, /* Arabic_kaf ARABIC LETTER KAF */ +{ 0x05e4, 0x0644 }, /* Arabic_lam ARABIC LETTER LAM */ +{ 0x05e5, 0x0645 }, /* Arabic_meem ARABIC LETTER MEEM */ +{ 0x05e6, 0x0646 }, /* Arabic_noon ARABIC LETTER NOON */ +{ 0x05e7, 0x0647 }, /* Arabic_ha ARABIC LETTER HEH */ +{ 0x05e8, 0x0648 }, /* Arabic_waw ARABIC LETTER WAW */ +{ 0x05e9, 0x0649 }, /* Arabic_alefmaksura ARABIC LETTER ALEF MAKSURA */ +{ 0x05ea, 0x064a }, /* Arabic_yeh ARABIC LETTER YEH */ +{ 0x05eb, 0x064b }, /* Arabic_fathatan ARABIC FATHATAN */ +{ 0x05ec, 0x064c }, /* Arabic_dammatan ARABIC DAMMATAN */ +{ 0x05ed, 0x064d }, /* Arabic_kasratan ARABIC KASRATAN */ +{ 0x05ee, 0x064e }, /* Arabic_fatha ARABIC FATHA */ +{ 0x05ef, 0x064f }, /* Arabic_damma ARABIC DAMMA */ +{ 0x05f0, 0x0650 }, /* Arabic_kasra ARABIC KASRA */ +{ 0x05f1, 0x0651 }, /* Arabic_shadda ARABIC SHADDA */ +{ 0x05f2, 0x0652 }, /* Arabic_sukun ARABIC SUKUN */ +{ 0x06a1, 0x0452 }, /* Serbian_dje CYRILLIC SMALL LETTER DJE */ +{ 0x06a2, 0x0453 }, /* Macedonia_gje CYRILLIC SMALL LETTER GJE */ +{ 0x06a3, 0x0451 }, /* Cyrillic_io CYRILLIC SMALL LETTER IO */ +{ 0x06a4, 0x0454 }, /* Ukrainian_ie CYRILLIC SMALL LETTER UKRAINIAN IE */ +{ 0x06a5, 0x0455 }, /* Macedonia_dse CYRILLIC SMALL LETTER DZE */ +{ 0x06a6, 0x0456 }, /* Ukrainian_i CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ 0x06a7, 0x0457 }, /* Ukrainian_yi CYRILLIC SMALL LETTER YI */ +{ 0x06a8, 0x0458 }, /* Cyrillic_je CYRILLIC SMALL LETTER JE */ +{ 0x06a9, 0x0459 }, /* Cyrillic_lje CYRILLIC SMALL LETTER LJE */ +{ 0x06aa, 0x045a }, /* Cyrillic_nje CYRILLIC SMALL LETTER NJE */ +{ 0x06ab, 0x045b }, /* Serbian_tshe CYRILLIC SMALL LETTER TSHE */ +{ 0x06ac, 0x045c }, /* Macedonia_kje CYRILLIC SMALL LETTER KJE */ +{ 0x06ae, 0x045e }, /* Byelorussian_shortu CYRILLIC SMALL LETTER SHORT U */ +{ 0x06af, 0x045f }, /* Cyrillic_dzhe CYRILLIC SMALL LETTER DZHE */ +{ 0x06b0, 0x2116 }, /* numerosign NUMERO SIGN */ +{ 0x06b1, 0x0402 }, /* Serbian_DJE CYRILLIC CAPITAL LETTER DJE */ +{ 0x06b2, 0x0403 }, /* Macedonia_GJE CYRILLIC CAPITAL LETTER GJE */ +{ 0x06b3, 0x0401 }, /* Cyrillic_IO CYRILLIC CAPITAL LETTER IO */ +{ 0x06b4, 0x0404 }, /* Ukrainian_IE CYRILLIC CAPITAL LETTER UKRAINIAN IE */ +{ 0x06b5, 0x0405 }, /* Macedonia_DSE CYRILLIC CAPITAL LETTER DZE */ +{ 0x06b6, 0x0406 }, /* Ukrainian_I CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ 0x06b7, 0x0407 }, /* Ukrainian_YI CYRILLIC CAPITAL LETTER YI */ +{ 0x06b8, 0x0408 }, /* Cyrillic_JE CYRILLIC CAPITAL LETTER JE */ +{ 0x06b9, 0x0409 }, /* Cyrillic_LJE CYRILLIC CAPITAL LETTER LJE */ +{ 0x06ba, 0x040a }, /* Cyrillic_NJE CYRILLIC CAPITAL LETTER NJE */ +{ 0x06bb, 0x040b }, /* Serbian_TSHE CYRILLIC CAPITAL LETTER TSHE */ +{ 0x06bc, 0x040c }, /* Macedonia_KJE CYRILLIC CAPITAL LETTER KJE */ +{ 0x06be, 0x040e }, /* Byelorussian_SHORTU CYRILLIC CAPITAL LETTER SHORT U */ +{ 0x06bf, 0x040f }, /* Cyrillic_DZHE CYRILLIC CAPITAL LETTER DZHE */ +{ 0x06c0, 0x044e }, /* Cyrillic_yu CYRILLIC SMALL LETTER YU */ +{ 0x06c1, 0x0430 }, /* Cyrillic_a CYRILLIC SMALL LETTER A */ +{ 0x06c2, 0x0431 }, /* Cyrillic_be CYRILLIC SMALL LETTER BE */ +{ 0x06c3, 0x0446 }, /* Cyrillic_tse CYRILLIC SMALL LETTER TSE */ +{ 0x06c4, 0x0434 }, /* Cyrillic_de CYRILLIC SMALL LETTER DE */ +{ 0x06c5, 0x0435 }, /* Cyrillic_ie CYRILLIC SMALL LETTER IE */ +{ 0x06c6, 0x0444 }, /* Cyrillic_ef CYRILLIC SMALL LETTER EF */ +{ 0x06c7, 0x0433 }, /* Cyrillic_ghe CYRILLIC SMALL LETTER GHE */ +{ 0x06c8, 0x0445 }, /* Cyrillic_ha CYRILLIC SMALL LETTER HA */ +{ 0x06c9, 0x0438 }, /* Cyrillic_i CYRILLIC SMALL LETTER I */ +{ 0x06ca, 0x0439 }, /* Cyrillic_shorti CYRILLIC SMALL LETTER SHORT I */ +{ 0x06cb, 0x043a }, /* Cyrillic_ka CYRILLIC SMALL LETTER KA */ +{ 0x06cc, 0x043b }, /* Cyrillic_el CYRILLIC SMALL LETTER EL */ +{ 0x06cd, 0x043c }, /* Cyrillic_em CYRILLIC SMALL LETTER EM */ +{ 0x06ce, 0x043d }, /* Cyrillic_en CYRILLIC SMALL LETTER EN */ +{ 0x06cf, 0x043e }, /* Cyrillic_o CYRILLIC SMALL LETTER O */ +{ 0x06d0, 0x043f }, /* Cyrillic_pe CYRILLIC SMALL LETTER PE */ +{ 0x06d1, 0x044f }, /* Cyrillic_ya CYRILLIC SMALL LETTER YA */ +{ 0x06d2, 0x0440 }, /* Cyrillic_er CYRILLIC SMALL LETTER ER */ +{ 0x06d3, 0x0441 }, /* Cyrillic_es CYRILLIC SMALL LETTER ES */ +{ 0x06d4, 0x0442 }, /* Cyrillic_te CYRILLIC SMALL LETTER TE */ +{ 0x06d5, 0x0443 }, /* Cyrillic_u CYRILLIC SMALL LETTER U */ +{ 0x06d6, 0x0436 }, /* Cyrillic_zhe CYRILLIC SMALL LETTER ZHE */ +{ 0x06d7, 0x0432 }, /* Cyrillic_ve CYRILLIC SMALL LETTER VE */ +{ 0x06d8, 0x044c }, /* Cyrillic_softsign CYRILLIC SMALL LETTER SOFT SIGN */ +{ 0x06d9, 0x044b }, /* Cyrillic_yeru CYRILLIC SMALL LETTER YERU */ +{ 0x06da, 0x0437 }, /* Cyrillic_ze CYRILLIC SMALL LETTER ZE */ +{ 0x06db, 0x0448 }, /* Cyrillic_sha CYRILLIC SMALL LETTER SHA */ +{ 0x06dc, 0x044d }, /* Cyrillic_e CYRILLIC SMALL LETTER E */ +{ 0x06dd, 0x0449 }, /* Cyrillic_shcha CYRILLIC SMALL LETTER SHCHA */ +{ 0x06de, 0x0447 }, /* Cyrillic_che CYRILLIC SMALL LETTER CHE */ +{ 0x06df, 0x044a }, /* Cyrillic_hardsign CYRILLIC SMALL LETTER HARD SIGN */ +{ 0x06e0, 0x042e }, /* Cyrillic_YU CYRILLIC CAPITAL LETTER YU */ +{ 0x06e1, 0x0410 }, /* Cyrillic_A CYRILLIC CAPITAL LETTER A */ +{ 0x06e2, 0x0411 }, /* Cyrillic_BE CYRILLIC CAPITAL LETTER BE */ +{ 0x06e3, 0x0426 }, /* Cyrillic_TSE CYRILLIC CAPITAL LETTER TSE */ +{ 0x06e4, 0x0414 }, /* Cyrillic_DE CYRILLIC CAPITAL LETTER DE */ +{ 0x06e5, 0x0415 }, /* Cyrillic_IE CYRILLIC CAPITAL LETTER IE */ +{ 0x06e6, 0x0424 }, /* Cyrillic_EF CYRILLIC CAPITAL LETTER EF */ +{ 0x06e7, 0x0413 }, /* Cyrillic_GHE CYRILLIC CAPITAL LETTER GHE */ +{ 0x06e8, 0x0425 }, /* Cyrillic_HA CYRILLIC CAPITAL LETTER HA */ +{ 0x06e9, 0x0418 }, /* Cyrillic_I CYRILLIC CAPITAL LETTER I */ +{ 0x06ea, 0x0419 }, /* Cyrillic_SHORTI CYRILLIC CAPITAL LETTER SHORT I */ +{ 0x06eb, 0x041a }, /* Cyrillic_KA CYRILLIC CAPITAL LETTER KA */ +{ 0x06ec, 0x041b }, /* Cyrillic_EL CYRILLIC CAPITAL LETTER EL */ +{ 0x06ed, 0x041c }, /* Cyrillic_EM CYRILLIC CAPITAL LETTER EM */ +{ 0x06ee, 0x041d }, /* Cyrillic_EN CYRILLIC CAPITAL LETTER EN */ +{ 0x06ef, 0x041e }, /* Cyrillic_O CYRILLIC CAPITAL LETTER O */ +{ 0x06f0, 0x041f }, /* Cyrillic_PE CYRILLIC CAPITAL LETTER PE */ +{ 0x06f1, 0x042f }, /* Cyrillic_YA CYRILLIC CAPITAL LETTER YA */ +{ 0x06f2, 0x0420 }, /* Cyrillic_ER CYRILLIC CAPITAL LETTER ER */ +{ 0x06f3, 0x0421 }, /* Cyrillic_ES CYRILLIC CAPITAL LETTER ES */ +{ 0x06f4, 0x0422 }, /* Cyrillic_TE CYRILLIC CAPITAL LETTER TE */ +{ 0x06f5, 0x0423 }, /* Cyrillic_U CYRILLIC CAPITAL LETTER U */ +{ 0x06f6, 0x0416 }, /* Cyrillic_ZHE CYRILLIC CAPITAL LETTER ZHE */ +{ 0x06f7, 0x0412 }, /* Cyrillic_VE CYRILLIC CAPITAL LETTER VE */ +{ 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN CYRILLIC CAPITAL LETTER SOFT SIGN */ +{ 0x06f9, 0x042b }, /* Cyrillic_YERU CYRILLIC CAPITAL LETTER YERU */ +{ 0x06fa, 0x0417 }, /* Cyrillic_ZE CYRILLIC CAPITAL LETTER ZE */ +{ 0x06fb, 0x0428 }, /* Cyrillic_SHA CYRILLIC CAPITAL LETTER SHA */ +{ 0x06fc, 0x042d }, /* Cyrillic_E CYRILLIC CAPITAL LETTER E */ +{ 0x06fd, 0x0429 }, /* Cyrillic_SHCHA CYRILLIC CAPITAL LETTER SHCHA */ +{ 0x06fe, 0x0427 }, /* Cyrillic_CHE CYRILLIC CAPITAL LETTER CHE */ +{ 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN CYRILLIC CAPITAL LETTER HARD SIGN */ +{ 0x07a1, 0x0386 }, /* Greek_ALPHAaccent GREEK CAPITAL LETTER ALPHA WITH TONOS */ +{ 0x07a2, 0x0388 }, /* Greek_EPSILONaccent GREEK CAPITAL LETTER EPSILON WITH TONOS */ +{ 0x07a3, 0x0389 }, /* Greek_ETAaccent GREEK CAPITAL LETTER ETA WITH TONOS */ +{ 0x07a4, 0x038a }, /* Greek_IOTAaccent GREEK CAPITAL LETTER IOTA WITH TONOS */ +{ 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ +{ 0x07a7, 0x038c }, /* Greek_OMICRONaccent GREEK CAPITAL LETTER OMICRON WITH TONOS */ +{ 0x07a8, 0x038e }, /* Greek_UPSILONaccent GREEK CAPITAL LETTER UPSILON WITH TONOS */ +{ 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ +{ 0x07ab, 0x038f }, /* Greek_OMEGAaccent GREEK CAPITAL LETTER OMEGA WITH TONOS */ +{ 0x07ae, 0x0385 }, /* Greek_accentdieresis GREEK DIALYTIKA TONOS */ +{ 0x07af, 0x2015 }, /* Greek_horizbar HORIZONTAL BAR */ +{ 0x07b1, 0x03ac }, /* Greek_alphaaccent GREEK SMALL LETTER ALPHA WITH TONOS */ +{ 0x07b2, 0x03ad }, /* Greek_epsilonaccent GREEK SMALL LETTER EPSILON WITH TONOS */ +{ 0x07b3, 0x03ae }, /* Greek_etaaccent GREEK SMALL LETTER ETA WITH TONOS */ +{ 0x07b4, 0x03af }, /* Greek_iotaaccent GREEK SMALL LETTER IOTA WITH TONOS */ +{ 0x07b5, 0x03ca }, /* Greek_iotadieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA */ +{ 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ +{ 0x07b7, 0x03cc }, /* Greek_omicronaccent GREEK SMALL LETTER OMICRON WITH TONOS */ +{ 0x07b8, 0x03cd }, /* Greek_upsilonaccent GREEK SMALL LETTER UPSILON WITH TONOS */ +{ 0x07b9, 0x03cb }, /* Greek_upsilondieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ +{ 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ +{ 0x07bb, 0x03ce }, /* Greek_omegaaccent GREEK SMALL LETTER OMEGA WITH TONOS */ +{ 0x07c1, 0x0391 }, /* Greek_ALPHA GREEK CAPITAL LETTER ALPHA */ +{ 0x07c2, 0x0392 }, /* Greek_BETA GREEK CAPITAL LETTER BETA */ +{ 0x07c3, 0x0393 }, /* Greek_GAMMA GREEK CAPITAL LETTER GAMMA */ +{ 0x07c4, 0x0394 }, /* Greek_DELTA GREEK CAPITAL LETTER DELTA */ +{ 0x07c5, 0x0395 }, /* Greek_EPSILON GREEK CAPITAL LETTER EPSILON */ +{ 0x07c6, 0x0396 }, /* Greek_ZETA GREEK CAPITAL LETTER ZETA */ +{ 0x07c7, 0x0397 }, /* Greek_ETA GREEK CAPITAL LETTER ETA */ +{ 0x07c8, 0x0398 }, /* Greek_THETA GREEK CAPITAL LETTER THETA */ +{ 0x07c9, 0x0399 }, /* Greek_IOTA GREEK CAPITAL LETTER IOTA */ +{ 0x07ca, 0x039a }, /* Greek_KAPPA GREEK CAPITAL LETTER KAPPA */ +{ 0x07cb, 0x039b }, /* Greek_LAMBDA GREEK CAPITAL LETTER LAMDA */ +{ 0x07cc, 0x039c }, /* Greek_MU GREEK CAPITAL LETTER MU */ +{ 0x07cd, 0x039d }, /* Greek_NU GREEK CAPITAL LETTER NU */ +{ 0x07ce, 0x039e }, /* Greek_XI GREEK CAPITAL LETTER XI */ +{ 0x07cf, 0x039f }, /* Greek_OMICRON GREEK CAPITAL LETTER OMICRON */ +{ 0x07d0, 0x03a0 }, /* Greek_PI GREEK CAPITAL LETTER PI */ +{ 0x07d1, 0x03a1 }, /* Greek_RHO GREEK CAPITAL LETTER RHO */ +{ 0x07d2, 0x03a3 }, /* Greek_SIGMA GREEK CAPITAL LETTER SIGMA */ +{ 0x07d4, 0x03a4 }, /* Greek_TAU GREEK CAPITAL LETTER TAU */ +{ 0x07d5, 0x03a5 }, /* Greek_UPSILON GREEK CAPITAL LETTER UPSILON */ +{ 0x07d6, 0x03a6 }, /* Greek_PHI GREEK CAPITAL LETTER PHI */ +{ 0x07d7, 0x03a7 }, /* Greek_CHI GREEK CAPITAL LETTER CHI */ +{ 0x07d8, 0x03a8 }, /* Greek_PSI GREEK CAPITAL LETTER PSI */ +{ 0x07d9, 0x03a9 }, /* Greek_OMEGA GREEK CAPITAL LETTER OMEGA */ +{ 0x07e1, 0x03b1 }, /* Greek_alpha GREEK SMALL LETTER ALPHA */ +{ 0x07e2, 0x03b2 }, /* Greek_beta GREEK SMALL LETTER BETA */ +{ 0x07e3, 0x03b3 }, /* Greek_gamma GREEK SMALL LETTER GAMMA */ +{ 0x07e4, 0x03b4 }, /* Greek_delta GREEK SMALL LETTER DELTA */ +{ 0x07e5, 0x03b5 }, /* Greek_epsilon GREEK SMALL LETTER EPSILON */ +{ 0x07e6, 0x03b6 }, /* Greek_zeta GREEK SMALL LETTER ZETA */ +{ 0x07e7, 0x03b7 }, /* Greek_eta GREEK SMALL LETTER ETA */ +{ 0x07e8, 0x03b8 }, /* Greek_theta GREEK SMALL LETTER THETA */ +{ 0x07e9, 0x03b9 }, /* Greek_iota GREEK SMALL LETTER IOTA */ +{ 0x07ea, 0x03ba }, /* Greek_kappa GREEK SMALL LETTER KAPPA */ +{ 0x07eb, 0x03bb }, /* Greek_lambda GREEK SMALL LETTER LAMDA */ +{ 0x07ec, 0x03bc }, /* Greek_mu GREEK SMALL LETTER MU */ +{ 0x07ed, 0x03bd }, /* Greek_nu GREEK SMALL LETTER NU */ +{ 0x07ee, 0x03be }, /* Greek_xi GREEK SMALL LETTER XI */ +{ 0x07ef, 0x03bf }, /* Greek_omicron GREEK SMALL LETTER OMICRON */ +{ 0x07f0, 0x03c0 }, /* Greek_pi GREEK SMALL LETTER PI */ +{ 0x07f1, 0x03c1 }, /* Greek_rho GREEK SMALL LETTER RHO */ +{ 0x07f2, 0x03c3 }, /* Greek_sigma GREEK SMALL LETTER SIGMA */ +{ 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma GREEK SMALL LETTER FINAL SIGMA */ +{ 0x07f4, 0x03c4 }, /* Greek_tau GREEK SMALL LETTER TAU */ +{ 0x07f5, 0x03c5 }, /* Greek_upsilon GREEK SMALL LETTER UPSILON */ +{ 0x07f6, 0x03c6 }, /* Greek_phi GREEK SMALL LETTER PHI */ +{ 0x07f7, 0x03c7 }, /* Greek_chi GREEK SMALL LETTER CHI */ +{ 0x07f8, 0x03c8 }, /* Greek_psi GREEK SMALL LETTER PSI */ +{ 0x07f9, 0x03c9 }, /* Greek_omega GREEK SMALL LETTER OMEGA */ +{ 0x08a1, 0x23b7 }, /* leftradical ??? */ +{ 0x08a2, 0x250c }, /* topleftradical BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ 0x08a3, 0x2500 }, /* horizconnector BOX DRAWINGS LIGHT HORIZONTAL */ +{ 0x08a4, 0x2320 }, /* topintegral TOP HALF INTEGRAL */ +{ 0x08a5, 0x2321 }, /* botintegral BOTTOM HALF INTEGRAL */ +{ 0x08a6, 0x2502 }, /* vertconnector BOX DRAWINGS LIGHT VERTICAL */ +{ 0x08a7, 0x23a1 }, /* topleftsqbracket ??? */ +{ 0x08a8, 0x23a3 }, /* botleftsqbracket ??? */ +{ 0x08a9, 0x23a4 }, /* toprightsqbracket ??? */ +{ 0x08aa, 0x23a6 }, /* botrightsqbracket ??? */ +{ 0x08ab, 0x239b }, /* topleftparens ??? */ +{ 0x08ac, 0x239d }, /* botleftparens ??? */ +{ 0x08ad, 0x239e }, /* toprightparens ??? */ +{ 0x08ae, 0x23a0 }, /* botrightparens ??? */ +{ 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ??? */ +{ 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ??? */ +{ 0x08bc, 0x2264 }, /* lessthanequal LESS-THAN OR EQUAL TO */ +{ 0x08bd, 0x2260 }, /* notequal NOT EQUAL TO */ +{ 0x08be, 0x2265 }, /* greaterthanequal GREATER-THAN OR EQUAL TO */ +{ 0x08bf, 0x222b }, /* integral INTEGRAL */ +{ 0x08c0, 0x2234 }, /* therefore THEREFORE */ +{ 0x08c1, 0x221d }, /* variation PROPORTIONAL TO */ +{ 0x08c2, 0x221e }, /* infinity INFINITY */ +{ 0x08c5, 0x2207 }, /* nabla NABLA */ +{ 0x08c8, 0x223c }, /* approximate TILDE OPERATOR */ +{ 0x08c9, 0x2243 }, /* similarequal ASYMPTOTICALLY EQUAL TO */ +{ 0x08cd, 0x21d4 }, /* ifonlyif LEFT RIGHT DOUBLE ARROW */ +{ 0x08ce, 0x21d2 }, /* implies RIGHTWARDS DOUBLE ARROW */ +{ 0x08cf, 0x2261 }, /* identical IDENTICAL TO */ +{ 0x08d6, 0x221a }, /* radical SQUARE ROOT */ +{ 0x08da, 0x2282 }, /* includedin SUBSET OF */ +{ 0x08db, 0x2283 }, /* includes SUPERSET OF */ +{ 0x08dc, 0x2229 }, /* intersection INTERSECTION */ +{ 0x08dd, 0x222a }, /* union UNION */ +{ 0x08de, 0x2227 }, /* logicaland LOGICAL AND */ +{ 0x08df, 0x2228 }, /* logicalor LOGICAL OR */ +{ 0x08ef, 0x2202 }, /* partialderivative PARTIAL DIFFERENTIAL */ +{ 0x08f6, 0x0192 }, /* function LATIN SMALL LETTER F WITH HOOK */ +{ 0x08fb, 0x2190 }, /* leftarrow LEFTWARDS ARROW */ +{ 0x08fc, 0x2191 }, /* uparrow UPWARDS ARROW */ +{ 0x08fd, 0x2192 }, /* rightarrow RIGHTWARDS ARROW */ +{ 0x08fe, 0x2193 }, /* downarrow DOWNWARDS ARROW */ +/* 0x09df blank ??? */ +{ 0x09e0, 0x25c6 }, /* soliddiamond BLACK DIAMOND */ +{ 0x09e1, 0x2592 }, /* checkerboard MEDIUM SHADE */ +{ 0x09e2, 0x2409 }, /* ht SYMBOL FOR HORIZONTAL TABULATION */ +{ 0x09e3, 0x240c }, /* ff SYMBOL FOR FORM FEED */ +{ 0x09e4, 0x240d }, /* cr SYMBOL FOR CARRIAGE RETURN */ +{ 0x09e5, 0x240a }, /* lf SYMBOL FOR LINE FEED */ +{ 0x09e8, 0x2424 }, /* nl SYMBOL FOR NEWLINE */ +{ 0x09e9, 0x240b }, /* vt SYMBOL FOR VERTICAL TABULATION */ +{ 0x09ea, 0x2518 }, /* lowrightcorner BOX DRAWINGS LIGHT UP AND LEFT */ +{ 0x09eb, 0x2510 }, /* uprightcorner BOX DRAWINGS LIGHT DOWN AND LEFT */ +{ 0x09ec, 0x250c }, /* upleftcorner BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ 0x09ed, 0x2514 }, /* lowleftcorner BOX DRAWINGS LIGHT UP AND RIGHT */ +{ 0x09ee, 0x253c }, /* crossinglines BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ +{ 0x09ef, 0x23ba }, /* horizlinescan1 HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ +{ 0x09f0, 0x23bb }, /* horizlinescan3 HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ +{ 0x09f1, 0x2500 }, /* horizlinescan5 BOX DRAWINGS LIGHT HORIZONTAL */ +{ 0x09f2, 0x23bc }, /* horizlinescan7 HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ +{ 0x09f3, 0x23bd }, /* horizlinescan9 HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ +{ 0x09f4, 0x251c }, /* leftt BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +{ 0x09f5, 0x2524 }, /* rightt BOX DRAWINGS LIGHT VERTICAL AND LEFT */ +{ 0x09f6, 0x2534 }, /* bott BOX DRAWINGS LIGHT UP AND HORIZONTAL */ +{ 0x09f7, 0x252c }, /* topt BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ +{ 0x09f8, 0x2502 }, /* vertbar BOX DRAWINGS LIGHT VERTICAL */ +{ 0x0aa1, 0x2003 }, /* emspace EM SPACE */ +{ 0x0aa2, 0x2002 }, /* enspace EN SPACE */ +{ 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ +{ 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ +{ 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ +{ 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ +{ 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ +{ 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ +{ 0x0aa9, 0x2014 }, /* emdash EM DASH */ +{ 0x0aaa, 0x2013 }, /* endash EN DASH */ +/* 0x0aac signifblank ??? */ +{ 0x0aae, 0x2026 }, /* ellipsis HORIZONTAL ELLIPSIS */ +{ 0x0aaf, 0x2025 }, /* doubbaselinedot TWO DOT LEADER */ +{ 0x0ab0, 0x2153 }, /* onethird VULGAR FRACTION ONE THIRD */ +{ 0x0ab1, 0x2154 }, /* twothirds VULGAR FRACTION TWO THIRDS */ +{ 0x0ab2, 0x2155 }, /* onefifth VULGAR FRACTION ONE FIFTH */ +{ 0x0ab3, 0x2156 }, /* twofifths VULGAR FRACTION TWO FIFTHS */ +{ 0x0ab4, 0x2157 }, /* threefifths VULGAR FRACTION THREE FIFTHS */ +{ 0x0ab5, 0x2158 }, /* fourfifths VULGAR FRACTION FOUR FIFTHS */ +{ 0x0ab6, 0x2159 }, /* onesixth VULGAR FRACTION ONE SIXTH */ +{ 0x0ab7, 0x215a }, /* fivesixths VULGAR FRACTION FIVE SIXTHS */ +{ 0x0ab8, 0x2105 }, /* careof CARE OF */ +{ 0x0abb, 0x2012 }, /* figdash FIGURE DASH */ +{ 0x0abc, 0x2329 }, /* leftanglebracket LEFT-POINTING ANGLE BRACKET */ +/* 0x0abd decimalpoint ??? */ +{ 0x0abe, 0x232a }, /* rightanglebracket RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ??? */ +{ 0x0ac3, 0x215b }, /* oneeighth VULGAR FRACTION ONE EIGHTH */ +{ 0x0ac4, 0x215c }, /* threeeighths VULGAR FRACTION THREE EIGHTHS */ +{ 0x0ac5, 0x215d }, /* fiveeighths VULGAR FRACTION FIVE EIGHTHS */ +{ 0x0ac6, 0x215e }, /* seveneighths VULGAR FRACTION SEVEN EIGHTHS */ +{ 0x0ac9, 0x2122 }, /* trademark TRADE MARK SIGN */ +{ 0x0aca, 0x2613 }, /* signaturemark SALTIRE */ +/* 0x0acb trademarkincircle ??? */ +{ 0x0acc, 0x25c1 }, /* leftopentriangle WHITE LEFT-POINTING TRIANGLE */ +{ 0x0acd, 0x25b7 }, /* rightopentriangle WHITE RIGHT-POINTING TRIANGLE */ +{ 0x0ace, 0x25cb }, /* emopencircle WHITE CIRCLE */ +{ 0x0acf, 0x25af }, /* emopenrectangle WHITE VERTICAL RECTANGLE */ +{ 0x0ad0, 0x2018 }, /* leftsinglequotemark LEFT SINGLE QUOTATION MARK */ +{ 0x0ad1, 0x2019 }, /* rightsinglequotemark RIGHT SINGLE QUOTATION MARK */ +{ 0x0ad2, 0x201c }, /* leftdoublequotemark LEFT DOUBLE QUOTATION MARK */ +{ 0x0ad3, 0x201d }, /* rightdoublequotemark RIGHT DOUBLE QUOTATION MARK */ +{ 0x0ad4, 0x211e }, /* prescription PRESCRIPTION TAKE */ +{ 0x0ad6, 0x2032 }, /* minutes PRIME */ +{ 0x0ad7, 0x2033 }, /* seconds DOUBLE PRIME */ +{ 0x0ad9, 0x271d }, /* latincross LATIN CROSS */ +/* 0x0ada hexagram ??? */ +{ 0x0adb, 0x25ac }, /* filledrectbullet BLACK RECTANGLE */ +{ 0x0adc, 0x25c0 }, /* filledlefttribullet BLACK LEFT-POINTING TRIANGLE */ +{ 0x0add, 0x25b6 }, /* filledrighttribullet BLACK RIGHT-POINTING TRIANGLE */ +{ 0x0ade, 0x25cf }, /* emfilledcircle BLACK CIRCLE */ +{ 0x0adf, 0x25ae }, /* emfilledrect BLACK VERTICAL RECTANGLE */ +{ 0x0ae0, 0x25e6 }, /* enopencircbullet WHITE BULLET */ +{ 0x0ae1, 0x25ab }, /* enopensquarebullet WHITE SMALL SQUARE */ +{ 0x0ae2, 0x25ad }, /* openrectbullet WHITE RECTANGLE */ +{ 0x0ae3, 0x25b3 }, /* opentribulletup WHITE UP-POINTING TRIANGLE */ +{ 0x0ae4, 0x25bd }, /* opentribulletdown WHITE DOWN-POINTING TRIANGLE */ +{ 0x0ae5, 0x2606 }, /* openstar WHITE STAR */ +{ 0x0ae6, 0x2022 }, /* enfilledcircbullet BULLET */ +{ 0x0ae7, 0x25aa }, /* enfilledsqbullet BLACK SMALL SQUARE */ +{ 0x0ae8, 0x25b2 }, /* filledtribulletup BLACK UP-POINTING TRIANGLE */ +{ 0x0ae9, 0x25bc }, /* filledtribulletdown BLACK DOWN-POINTING TRIANGLE */ +{ 0x0aea, 0x261c }, /* leftpointer WHITE LEFT POINTING INDEX */ +{ 0x0aeb, 0x261e }, /* rightpointer WHITE RIGHT POINTING INDEX */ +{ 0x0aec, 0x2663 }, /* club BLACK CLUB SUIT */ +{ 0x0aed, 0x2666 }, /* diamond BLACK DIAMOND SUIT */ +{ 0x0aee, 0x2665 }, /* heart BLACK HEART SUIT */ +{ 0x0af0, 0x2720 }, /* maltesecross MALTESE CROSS */ +{ 0x0af1, 0x2020 }, /* dagger DAGGER */ +{ 0x0af2, 0x2021 }, /* doubledagger DOUBLE DAGGER */ +{ 0x0af3, 0x2713 }, /* checkmark CHECK MARK */ +{ 0x0af4, 0x2717 }, /* ballotcross BALLOT X */ +{ 0x0af5, 0x266f }, /* musicalsharp MUSIC SHARP SIGN */ +{ 0x0af6, 0x266d }, /* musicalflat MUSIC FLAT SIGN */ +{ 0x0af7, 0x2642 }, /* malesymbol MALE SIGN */ +{ 0x0af8, 0x2640 }, /* femalesymbol FEMALE SIGN */ +{ 0x0af9, 0x260e }, /* telephone BLACK TELEPHONE */ +{ 0x0afa, 0x2315 }, /* telephonerecorder TELEPHONE RECORDER */ +{ 0x0afb, 0x2117 }, /* phonographcopyright SOUND RECORDING COPYRIGHT */ +{ 0x0afc, 0x2038 }, /* caret CARET */ +{ 0x0afd, 0x201a }, /* singlelowquotemark SINGLE LOW-9 QUOTATION MARK */ +{ 0x0afe, 0x201e }, /* doublelowquotemark DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ??? */ +{ 0x0ba3, 0x003c }, /* leftcaret LESS-THAN SIGN */ +{ 0x0ba6, 0x003e }, /* rightcaret GREATER-THAN SIGN */ +{ 0x0ba8, 0x2228 }, /* downcaret LOGICAL OR */ +{ 0x0ba9, 0x2227 }, /* upcaret LOGICAL AND */ +{ 0x0bc0, 0x00af }, /* overbar MACRON */ +{ 0x0bc2, 0x22a5 }, /* downtack UP TACK */ +{ 0x0bc3, 0x2229 }, /* upshoe INTERSECTION */ +{ 0x0bc4, 0x230a }, /* downstile LEFT FLOOR */ +{ 0x0bc6, 0x005f }, /* underbar LOW LINE */ +{ 0x0bca, 0x2218 }, /* jot RING OPERATOR */ +{ 0x0bcc, 0x2395 }, /* quad APL FUNCTIONAL SYMBOL QUAD */ +{ 0x0bce, 0x22a4 }, /* uptack DOWN TACK */ +{ 0x0bcf, 0x25cb }, /* circle WHITE CIRCLE */ +{ 0x0bd3, 0x2308 }, /* upstile LEFT CEILING */ +{ 0x0bd6, 0x222a }, /* downshoe UNION */ +{ 0x0bd8, 0x2283 }, /* rightshoe SUPERSET OF */ +{ 0x0bda, 0x2282 }, /* leftshoe SUBSET OF */ +{ 0x0bdc, 0x22a2 }, /* lefttack RIGHT TACK */ +{ 0x0bfc, 0x22a3 }, /* righttack LEFT TACK */ +{ 0x0cdf, 0x2017 }, /* hebrew_doublelowline DOUBLE LOW LINE */ +{ 0x0ce0, 0x05d0 }, /* hebrew_aleph HEBREW LETTER ALEF */ +{ 0x0ce1, 0x05d1 }, /* hebrew_bet HEBREW LETTER BET */ +{ 0x0ce2, 0x05d2 }, /* hebrew_gimel HEBREW LETTER GIMEL */ +{ 0x0ce3, 0x05d3 }, /* hebrew_dalet HEBREW LETTER DALET */ +{ 0x0ce4, 0x05d4 }, /* hebrew_he HEBREW LETTER HE */ +{ 0x0ce5, 0x05d5 }, /* hebrew_waw HEBREW LETTER VAV */ +{ 0x0ce6, 0x05d6 }, /* hebrew_zain HEBREW LETTER ZAYIN */ +{ 0x0ce7, 0x05d7 }, /* hebrew_chet HEBREW LETTER HET */ +{ 0x0ce8, 0x05d8 }, /* hebrew_tet HEBREW LETTER TET */ +{ 0x0ce9, 0x05d9 }, /* hebrew_yod HEBREW LETTER YOD */ +{ 0x0cea, 0x05da }, /* hebrew_finalkaph HEBREW LETTER FINAL KAF */ +{ 0x0ceb, 0x05db }, /* hebrew_kaph HEBREW LETTER KAF */ +{ 0x0cec, 0x05dc }, /* hebrew_lamed HEBREW LETTER LAMED */ +{ 0x0ced, 0x05dd }, /* hebrew_finalmem HEBREW LETTER FINAL MEM */ +{ 0x0cee, 0x05de }, /* hebrew_mem HEBREW LETTER MEM */ +{ 0x0cef, 0x05df }, /* hebrew_finalnun HEBREW LETTER FINAL NUN */ +{ 0x0cf0, 0x05e0 }, /* hebrew_nun HEBREW LETTER NUN */ +{ 0x0cf1, 0x05e1 }, /* hebrew_samech HEBREW LETTER SAMEKH */ +{ 0x0cf2, 0x05e2 }, /* hebrew_ayin HEBREW LETTER AYIN */ +{ 0x0cf3, 0x05e3 }, /* hebrew_finalpe HEBREW LETTER FINAL PE */ +{ 0x0cf4, 0x05e4 }, /* hebrew_pe HEBREW LETTER PE */ +{ 0x0cf5, 0x05e5 }, /* hebrew_finalzade HEBREW LETTER FINAL TSADI */ +{ 0x0cf6, 0x05e6 }, /* hebrew_zade HEBREW LETTER TSADI */ +{ 0x0cf7, 0x05e7 }, /* hebrew_qoph HEBREW LETTER QOF */ +{ 0x0cf8, 0x05e8 }, /* hebrew_resh HEBREW LETTER RESH */ +{ 0x0cf9, 0x05e9 }, /* hebrew_shin HEBREW LETTER SHIN */ +{ 0x0cfa, 0x05ea }, /* hebrew_taw HEBREW LETTER TAV */ +{ 0x0da1, 0x0e01 }, /* Thai_kokai THAI CHARACTER KO KAI */ +{ 0x0da2, 0x0e02 }, /* Thai_khokhai THAI CHARACTER KHO KHAI */ +{ 0x0da3, 0x0e03 }, /* Thai_khokhuat THAI CHARACTER KHO KHUAT */ +{ 0x0da4, 0x0e04 }, /* Thai_khokhwai THAI CHARACTER KHO KHWAI */ +{ 0x0da5, 0x0e05 }, /* Thai_khokhon THAI CHARACTER KHO KHON */ +{ 0x0da6, 0x0e06 }, /* Thai_khorakhang THAI CHARACTER KHO RAKHANG */ +{ 0x0da7, 0x0e07 }, /* Thai_ngongu THAI CHARACTER NGO NGU */ +{ 0x0da8, 0x0e08 }, /* Thai_chochan THAI CHARACTER CHO CHAN */ +{ 0x0da9, 0x0e09 }, /* Thai_choching THAI CHARACTER CHO CHING */ +{ 0x0daa, 0x0e0a }, /* Thai_chochang THAI CHARACTER CHO CHANG */ +{ 0x0dab, 0x0e0b }, /* Thai_soso THAI CHARACTER SO SO */ +{ 0x0dac, 0x0e0c }, /* Thai_chochoe THAI CHARACTER CHO CHOE */ +{ 0x0dad, 0x0e0d }, /* Thai_yoying THAI CHARACTER YO YING */ +{ 0x0dae, 0x0e0e }, /* Thai_dochada THAI CHARACTER DO CHADA */ +{ 0x0daf, 0x0e0f }, /* Thai_topatak THAI CHARACTER TO PATAK */ +{ 0x0db0, 0x0e10 }, /* Thai_thothan THAI CHARACTER THO THAN */ +{ 0x0db1, 0x0e11 }, /* Thai_thonangmontho THAI CHARACTER THO NANGMONTHO */ +{ 0x0db2, 0x0e12 }, /* Thai_thophuthao THAI CHARACTER THO PHUTHAO */ +{ 0x0db3, 0x0e13 }, /* Thai_nonen THAI CHARACTER NO NEN */ +{ 0x0db4, 0x0e14 }, /* Thai_dodek THAI CHARACTER DO DEK */ +{ 0x0db5, 0x0e15 }, /* Thai_totao THAI CHARACTER TO TAO */ +{ 0x0db6, 0x0e16 }, /* Thai_thothung THAI CHARACTER THO THUNG */ +{ 0x0db7, 0x0e17 }, /* Thai_thothahan THAI CHARACTER THO THAHAN */ +{ 0x0db8, 0x0e18 }, /* Thai_thothong THAI CHARACTER THO THONG */ +{ 0x0db9, 0x0e19 }, /* Thai_nonu THAI CHARACTER NO NU */ +{ 0x0dba, 0x0e1a }, /* Thai_bobaimai THAI CHARACTER BO BAIMAI */ +{ 0x0dbb, 0x0e1b }, /* Thai_popla THAI CHARACTER PO PLA */ +{ 0x0dbc, 0x0e1c }, /* Thai_phophung THAI CHARACTER PHO PHUNG */ +{ 0x0dbd, 0x0e1d }, /* Thai_fofa THAI CHARACTER FO FA */ +{ 0x0dbe, 0x0e1e }, /* Thai_phophan THAI CHARACTER PHO PHAN */ +{ 0x0dbf, 0x0e1f }, /* Thai_fofan THAI CHARACTER FO FAN */ +{ 0x0dc0, 0x0e20 }, /* Thai_phosamphao THAI CHARACTER PHO SAMPHAO */ +{ 0x0dc1, 0x0e21 }, /* Thai_moma THAI CHARACTER MO MA */ +{ 0x0dc2, 0x0e22 }, /* Thai_yoyak THAI CHARACTER YO YAK */ +{ 0x0dc3, 0x0e23 }, /* Thai_rorua THAI CHARACTER RO RUA */ +{ 0x0dc4, 0x0e24 }, /* Thai_ru THAI CHARACTER RU */ +{ 0x0dc5, 0x0e25 }, /* Thai_loling THAI CHARACTER LO LING */ +{ 0x0dc6, 0x0e26 }, /* Thai_lu THAI CHARACTER LU */ +{ 0x0dc7, 0x0e27 }, /* Thai_wowaen THAI CHARACTER WO WAEN */ +{ 0x0dc8, 0x0e28 }, /* Thai_sosala THAI CHARACTER SO SALA */ +{ 0x0dc9, 0x0e29 }, /* Thai_sorusi THAI CHARACTER SO RUSI */ +{ 0x0dca, 0x0e2a }, /* Thai_sosua THAI CHARACTER SO SUA */ +{ 0x0dcb, 0x0e2b }, /* Thai_hohip THAI CHARACTER HO HIP */ +{ 0x0dcc, 0x0e2c }, /* Thai_lochula THAI CHARACTER LO CHULA */ +{ 0x0dcd, 0x0e2d }, /* Thai_oang THAI CHARACTER O ANG */ +{ 0x0dce, 0x0e2e }, /* Thai_honokhuk THAI CHARACTER HO NOKHUK */ +{ 0x0dcf, 0x0e2f }, /* Thai_paiyannoi THAI CHARACTER PAIYANNOI */ +{ 0x0dd0, 0x0e30 }, /* Thai_saraa THAI CHARACTER SARA A */ +{ 0x0dd1, 0x0e31 }, /* Thai_maihanakat THAI CHARACTER MAI HAN-AKAT */ +{ 0x0dd2, 0x0e32 }, /* Thai_saraaa THAI CHARACTER SARA AA */ +{ 0x0dd3, 0x0e33 }, /* Thai_saraam THAI CHARACTER SARA AM */ +{ 0x0dd4, 0x0e34 }, /* Thai_sarai THAI CHARACTER SARA I */ +{ 0x0dd5, 0x0e35 }, /* Thai_saraii THAI CHARACTER SARA II */ +{ 0x0dd6, 0x0e36 }, /* Thai_saraue THAI CHARACTER SARA UE */ +{ 0x0dd7, 0x0e37 }, /* Thai_sarauee THAI CHARACTER SARA UEE */ +{ 0x0dd8, 0x0e38 }, /* Thai_sarau THAI CHARACTER SARA U */ +{ 0x0dd9, 0x0e39 }, /* Thai_sarauu THAI CHARACTER SARA UU */ +{ 0x0dda, 0x0e3a }, /* Thai_phinthu THAI CHARACTER PHINTHU */ +/* 0x0dde Thai_maihanakat_maitho ??? */ +{ 0x0ddf, 0x0e3f }, /* Thai_baht THAI CURRENCY SYMBOL BAHT */ +{ 0x0de0, 0x0e40 }, /* Thai_sarae THAI CHARACTER SARA E */ +{ 0x0de1, 0x0e41 }, /* Thai_saraae THAI CHARACTER SARA AE */ +{ 0x0de2, 0x0e42 }, /* Thai_sarao THAI CHARACTER SARA O */ +{ 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan THAI CHARACTER SARA AI MAIMUAN */ +{ 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai THAI CHARACTER SARA AI MAIMALAI */ +{ 0x0de5, 0x0e45 }, /* Thai_lakkhangyao THAI CHARACTER LAKKHANGYAO */ +{ 0x0de6, 0x0e46 }, /* Thai_maiyamok THAI CHARACTER MAIYAMOK */ +{ 0x0de7, 0x0e47 }, /* Thai_maitaikhu THAI CHARACTER MAITAIKHU */ +{ 0x0de8, 0x0e48 }, /* Thai_maiek THAI CHARACTER MAI EK */ +{ 0x0de9, 0x0e49 }, /* Thai_maitho THAI CHARACTER MAI THO */ +{ 0x0dea, 0x0e4a }, /* Thai_maitri THAI CHARACTER MAI TRI */ +{ 0x0deb, 0x0e4b }, /* Thai_maichattawa THAI CHARACTER MAI CHATTAWA */ +{ 0x0dec, 0x0e4c }, /* Thai_thanthakhat THAI CHARACTER THANTHAKHAT */ +{ 0x0ded, 0x0e4d }, /* Thai_nikhahit THAI CHARACTER NIKHAHIT */ +{ 0x0df0, 0x0e50 }, /* Thai_leksun THAI DIGIT ZERO */ +{ 0x0df1, 0x0e51 }, /* Thai_leknung THAI DIGIT ONE */ +{ 0x0df2, 0x0e52 }, /* Thai_leksong THAI DIGIT TWO */ +{ 0x0df3, 0x0e53 }, /* Thai_leksam THAI DIGIT THREE */ +{ 0x0df4, 0x0e54 }, /* Thai_leksi THAI DIGIT FOUR */ +{ 0x0df5, 0x0e55 }, /* Thai_lekha THAI DIGIT FIVE */ +{ 0x0df6, 0x0e56 }, /* Thai_lekhok THAI DIGIT SIX */ +{ 0x0df7, 0x0e57 }, /* Thai_lekchet THAI DIGIT SEVEN */ +{ 0x0df8, 0x0e58 }, /* Thai_lekpaet THAI DIGIT EIGHT */ +{ 0x0df9, 0x0e59 }, /* Thai_lekkao THAI DIGIT NINE */ +{ 0x0ea1, 0x3131 }, /* Hangul_Kiyeog HANGUL LETTER KIYEOK */ +{ 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog HANGUL LETTER SSANGKIYEOK */ +{ 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios HANGUL LETTER KIYEOK-SIOS */ +{ 0x0ea4, 0x3134 }, /* Hangul_Nieun HANGUL LETTER NIEUN */ +{ 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj HANGUL LETTER NIEUN-CIEUC */ +{ 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh HANGUL LETTER NIEUN-HIEUH */ +{ 0x0ea7, 0x3137 }, /* Hangul_Dikeud HANGUL LETTER TIKEUT */ +{ 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud HANGUL LETTER SSANGTIKEUT */ +{ 0x0ea9, 0x3139 }, /* Hangul_Rieul HANGUL LETTER RIEUL */ +{ 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog HANGUL LETTER RIEUL-KIYEOK */ +{ 0x0eab, 0x313b }, /* Hangul_RieulMieum HANGUL LETTER RIEUL-MIEUM */ +{ 0x0eac, 0x313c }, /* Hangul_RieulPieub HANGUL LETTER RIEUL-PIEUP */ +{ 0x0ead, 0x313d }, /* Hangul_RieulSios HANGUL LETTER RIEUL-SIOS */ +{ 0x0eae, 0x313e }, /* Hangul_RieulTieut HANGUL LETTER RIEUL-THIEUTH */ +{ 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf HANGUL LETTER RIEUL-PHIEUPH */ +{ 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh HANGUL LETTER RIEUL-HIEUH */ +{ 0x0eb1, 0x3141 }, /* Hangul_Mieum HANGUL LETTER MIEUM */ +{ 0x0eb2, 0x3142 }, /* Hangul_Pieub HANGUL LETTER PIEUP */ +{ 0x0eb3, 0x3143 }, /* Hangul_SsangPieub HANGUL LETTER SSANGPIEUP */ +{ 0x0eb4, 0x3144 }, /* Hangul_PieubSios HANGUL LETTER PIEUP-SIOS */ +{ 0x0eb5, 0x3145 }, /* Hangul_Sios HANGUL LETTER SIOS */ +{ 0x0eb6, 0x3146 }, /* Hangul_SsangSios HANGUL LETTER SSANGSIOS */ +{ 0x0eb7, 0x3147 }, /* Hangul_Ieung HANGUL LETTER IEUNG */ +{ 0x0eb8, 0x3148 }, /* Hangul_Jieuj HANGUL LETTER CIEUC */ +{ 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj HANGUL LETTER SSANGCIEUC */ +{ 0x0eba, 0x314a }, /* Hangul_Cieuc HANGUL LETTER CHIEUCH */ +{ 0x0ebb, 0x314b }, /* Hangul_Khieuq HANGUL LETTER KHIEUKH */ +{ 0x0ebc, 0x314c }, /* Hangul_Tieut HANGUL LETTER THIEUTH */ +{ 0x0ebd, 0x314d }, /* Hangul_Phieuf HANGUL LETTER PHIEUPH */ +{ 0x0ebe, 0x314e }, /* Hangul_Hieuh HANGUL LETTER HIEUH */ +{ 0x0ebf, 0x314f }, /* Hangul_A HANGUL LETTER A */ +{ 0x0ec0, 0x3150 }, /* Hangul_AE HANGUL LETTER AE */ +{ 0x0ec1, 0x3151 }, /* Hangul_YA HANGUL LETTER YA */ +{ 0x0ec2, 0x3152 }, /* Hangul_YAE HANGUL LETTER YAE */ +{ 0x0ec3, 0x3153 }, /* Hangul_EO HANGUL LETTER EO */ +{ 0x0ec4, 0x3154 }, /* Hangul_E HANGUL LETTER E */ +{ 0x0ec5, 0x3155 }, /* Hangul_YEO HANGUL LETTER YEO */ +{ 0x0ec6, 0x3156 }, /* Hangul_YE HANGUL LETTER YE */ +{ 0x0ec7, 0x3157 }, /* Hangul_O HANGUL LETTER O */ +{ 0x0ec8, 0x3158 }, /* Hangul_WA HANGUL LETTER WA */ +{ 0x0ec9, 0x3159 }, /* Hangul_WAE HANGUL LETTER WAE */ +{ 0x0eca, 0x315a }, /* Hangul_OE HANGUL LETTER OE */ +{ 0x0ecb, 0x315b }, /* Hangul_YO HANGUL LETTER YO */ +{ 0x0ecc, 0x315c }, /* Hangul_U HANGUL LETTER U */ +{ 0x0ecd, 0x315d }, /* Hangul_WEO HANGUL LETTER WEO */ +{ 0x0ece, 0x315e }, /* Hangul_WE HANGUL LETTER WE */ +{ 0x0ecf, 0x315f }, /* Hangul_WI HANGUL LETTER WI */ +{ 0x0ed0, 0x3160 }, /* Hangul_YU HANGUL LETTER YU */ +{ 0x0ed1, 0x3161 }, /* Hangul_EU HANGUL LETTER EU */ +{ 0x0ed2, 0x3162 }, /* Hangul_YI HANGUL LETTER YI */ +{ 0x0ed3, 0x3163 }, /* Hangul_I HANGUL LETTER I */ +{ 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog HANGUL JONGSEONG KIYEOK */ +{ 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog HANGUL JONGSEONG SSANGKIYEOK */ +{ 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios HANGUL JONGSEONG KIYEOK-SIOS */ +{ 0x0ed7, 0x11ab }, /* Hangul_J_Nieun HANGUL JONGSEONG NIEUN */ +{ 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj HANGUL JONGSEONG NIEUN-CIEUC */ +{ 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh HANGUL JONGSEONG NIEUN-HIEUH */ +{ 0x0eda, 0x11ae }, /* Hangul_J_Dikeud HANGUL JONGSEONG TIKEUT */ +{ 0x0edb, 0x11af }, /* Hangul_J_Rieul HANGUL JONGSEONG RIEUL */ +{ 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog HANGUL JONGSEONG RIEUL-KIYEOK */ +{ 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum HANGUL JONGSEONG RIEUL-MIEUM */ +{ 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub HANGUL JONGSEONG RIEUL-PIEUP */ +{ 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios HANGUL JONGSEONG RIEUL-SIOS */ +{ 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut HANGUL JONGSEONG RIEUL-THIEUTH */ +{ 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf HANGUL JONGSEONG RIEUL-PHIEUPH */ +{ 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh HANGUL JONGSEONG RIEUL-HIEUH */ +{ 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum HANGUL JONGSEONG MIEUM */ +{ 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub HANGUL JONGSEONG PIEUP */ +{ 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios HANGUL JONGSEONG PIEUP-SIOS */ +{ 0x0ee6, 0x11ba }, /* Hangul_J_Sios HANGUL JONGSEONG SIOS */ +{ 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios HANGUL JONGSEONG SSANGSIOS */ +{ 0x0ee8, 0x11bc }, /* Hangul_J_Ieung HANGUL JONGSEONG IEUNG */ +{ 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj HANGUL JONGSEONG CIEUC */ +{ 0x0eea, 0x11be }, /* Hangul_J_Cieuc HANGUL JONGSEONG CHIEUCH */ +{ 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq HANGUL JONGSEONG KHIEUKH */ +{ 0x0eec, 0x11c0 }, /* Hangul_J_Tieut HANGUL JONGSEONG THIEUTH */ +{ 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf HANGUL JONGSEONG PHIEUPH */ +{ 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh HANGUL JONGSEONG HIEUH */ +{ 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh HANGUL LETTER RIEUL-YEORINHIEUH */ +{ 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum HANGUL LETTER KAPYEOUNMIEUM */ +{ 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub HANGUL LETTER KAPYEOUNPIEUP */ +{ 0x0ef2, 0x317f }, /* Hangul_PanSios HANGUL LETTER PANSIOS */ +{ 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung HANGUL LETTER YESIEUNG */ +{ 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf HANGUL LETTER KAPYEOUNPHIEUPH */ +{ 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh HANGUL LETTER YEORINHIEUH */ +{ 0x0ef6, 0x318d }, /* Hangul_AraeA HANGUL LETTER ARAEA */ +{ 0x0ef7, 0x318e }, /* Hangul_AraeAE HANGUL LETTER ARAEAE */ +{ 0x0ef8, 0x11eb }, /* Hangul_J_PanSios HANGUL JONGSEONG PANSIOS */ +{ 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung HANGUL JONGSEONG YESIEUNG */ +{ 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh HANGUL JONGSEONG YEORINHIEUH */ +{ 0x0eff, 0x20a9 }, /* Korean_Won WON SIGN */ +{ 0x13a4, 0x20ac }, /* Euro EURO SIGN */ +{ 0x13bc, 0x0152 }, /* OE LATIN CAPITAL LIGATURE OE */ +{ 0x13bd, 0x0153 }, /* oe LATIN SMALL LIGATURE OE */ +{ 0x13be, 0x0178 }, /* Ydiaeresis LATIN CAPITAL LETTER Y WITH DIAERESIS */ +{ 0x20ac, 0x20ac } /* EuroSign EURO SIGN */ +}; + + +// +// CXWindowsUtil +// + +CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; +CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; + +bool +CXWindowsUtil::getWindowProperty(Display* display, Window window, + Atom property, CString* data, Atom* type, + SInt32* format, bool deleteProperty) +{ + assert(display != NULL); + + Atom actualType; + int actualDatumSize; + + // ignore errors. XGetWindowProperty() will report failure. + CXWindowsUtil::CErrorLock lock(display); + + // read the property + bool okay = true; + const long length = XMaxRequestSize(display); + long offset = 0; + unsigned long bytesLeft = 1; + while (bytesLeft != 0) { + // get more data + unsigned long numItems; + unsigned char* rawData; + if (XGetWindowProperty(display, window, property, + offset, length, False, AnyPropertyType, + &actualType, &actualDatumSize, + &numItems, &bytesLeft, &rawData) != Success || + actualType == None || actualDatumSize == 0) { + // failed + okay = false; + break; + } + + // compute bytes read and advance offset + unsigned long numBytes; + switch (actualDatumSize) { + case 8: + default: + numBytes = numItems; + offset += numItems / 4; + break; + + case 16: + numBytes = 2 * numItems; + offset += numItems / 2; + break; + + case 32: + numBytes = 4 * numItems; + offset += numItems; + break; + } + + // append data + if (data != NULL) { + data->append((char*)rawData, numBytes); + } + else { + // data is not required so don't try to get any more + bytesLeft = 0; + } + + // done with returned data + XFree(rawData); + } + + // delete the property if requested + if (deleteProperty) { + XDeleteProperty(display, window, property); + } + + // save property info + if (type != NULL) { + *type = actualType; + } + if (format != NULL) { + *format = static_cast(actualDatumSize); + } + + if (okay) { + LOG((CLOG_DEBUG2 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); + return true; + } + else { + LOG((CLOG_DEBUG2 "can't read property %d on window 0x%08x", property, window)); + return false; + } +} + +bool +CXWindowsUtil::setWindowProperty(Display* display, Window window, + Atom property, const void* vdata, UInt32 size, + Atom type, SInt32 format) +{ + const UInt32 length = 4 * XMaxRequestSize(display); + const unsigned char* data = reinterpret_cast(vdata); + const UInt32 datumSize = static_cast(format / 8); + + // save errors + bool error = false; + CXWindowsUtil::CErrorLock lock(display, &error); + + // how much data to send in first chunk? + UInt32 chunkSize = size; + if (chunkSize > length) { + chunkSize = length; + } + + // send first chunk + XChangeProperty(display, window, property, + type, format, PropModeReplace, + data, chunkSize / datumSize); + + // append remaining chunks + data += chunkSize; + size -= chunkSize; + while (!error && size > 0) { + chunkSize = size; + if (chunkSize > length) { + chunkSize = length; + } + XChangeProperty(display, window, property, + type, format, PropModeAppend, + data, chunkSize / datumSize); + data += chunkSize; + size -= chunkSize; + } + + return !error; +} + +Time +CXWindowsUtil::getCurrentTime(Display* display, Window window) +{ + // select property events on window + XWindowAttributes attr; + XGetWindowAttributes(display, window, &attr); + XSelectInput(display, window, attr.your_event_mask | PropertyChangeMask); + + // make a property name to receive dummy change + Atom atom = XInternAtom(display, "TIMESTAMP", False); + + // do a zero-length append to get the current time + unsigned char dummy; + XChangeProperty(display, window, atom, + XA_INTEGER, 8, + PropModeAppend, + &dummy, 0); + + // look for property notify events with the following + CPropertyNotifyPredicateInfo filter; + filter.m_window = window; + filter.m_property = atom; + + // wait for reply + XEvent xevent; + XIfEvent(display, &xevent, &CXWindowsUtil::propertyNotifyPredicate, + (XPointer)&filter); + assert(xevent.type == PropertyNotify); + assert(xevent.xproperty.window == window); + assert(xevent.xproperty.atom == atom); + + // restore event mask + XSelectInput(display, window, attr.your_event_mask); + + return xevent.xproperty.time; +} + +UInt32 +CXWindowsUtil::mapKeySymToUCS4(KeySym k) +{ + initKeyMaps(); + + CKeySymMap::const_iterator index = s_keySymToUCS4.find(k); + if (index != s_keySymToUCS4.end()) { + return index->second; + } + else { + return 0x0000ffff; + } +} + +KeySym +CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) +{ + initKeyMaps(); + + CUCS4Map::const_iterator index = s_UCS4ToKeySym.find(c); + if (index != s_UCS4ToKeySym.end()) { + return index->second; + } + else { + return NoSymbol; + } +} + +Bool +CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) +{ + CPropertyNotifyPredicateInfo* filter = + reinterpret_cast(arg); + return (xevent->type == PropertyNotify && + xevent->xproperty.window == filter->m_window && + xevent->xproperty.atom == filter->m_property && + xevent->xproperty.state == PropertyNewValue) ? True : False; +} + +void +CXWindowsUtil::initKeyMaps() +{ + // note that keysyms 0x13a4 and 0x20ac both map to 0x20ac, which + // means ambiguity when converting unicode 0x20ac to a keysym. + // as written, the m_UCS4ToKeySym will map to XK_EuroSign. + if (s_keySymToUCS4.empty()) { + for (size_t i =0; i < sizeof(s_keymap) / sizeof(s_keymap[0]); ++i) { + s_keySymToUCS4[s_keymap[i].keysym] = s_keymap[i].ucs4; + s_UCS4ToKeySym[s_keymap[i].ucs4] = s_keymap[i].keysym; + } + } +} + + +// +// CXWindowsUtil::CErrorLock +// + +CXWindowsUtil::CErrorLock* CXWindowsUtil::CErrorLock::s_top = NULL; + +CXWindowsUtil::CErrorLock::CErrorLock(Display* display) : + m_display(display) +{ + install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL); +} + +CXWindowsUtil::CErrorLock::CErrorLock(Display* display, bool* flag) : + m_display(display) +{ + install(&CXWindowsUtil::CErrorLock::saveHandler, flag); +} + +CXWindowsUtil::CErrorLock::CErrorLock(Display* display, + ErrorHandler handler, void* data) : + m_display(display) +{ + install(handler, data); +} + +CXWindowsUtil::CErrorLock::~CErrorLock() +{ + // make sure everything finishes before uninstalling handler + if (m_display != NULL) { + XSync(m_display, False); + } + + // restore old handler + XSetErrorHandler(m_oldXHandler); + s_top = m_next; +} + +void +CXWindowsUtil::CErrorLock::install(ErrorHandler handler, void* data) +{ + // make sure everything finishes before installing handler + if (m_display != NULL) { + XSync(m_display, False); + } + + // install handler + m_handler = handler; + m_userData = data; + m_oldXHandler = XSetErrorHandler( + &CXWindowsUtil::CErrorLock::internalHandler); + m_next = s_top; + s_top = this; +} + +int +CXWindowsUtil::CErrorLock::internalHandler(Display* display, XErrorEvent* event) +{ + if (s_top != NULL && s_top->m_handler != NULL) { + s_top->m_handler(display, event, s_top->m_userData); + } + return 0; +} + +void +CXWindowsUtil::CErrorLock::ignoreHandler(Display*, XErrorEvent* e, void*) +{ + LOG((CLOG_DEBUG1 "ignoring X error: %d", e->error_code)); +} + +void +CXWindowsUtil::CErrorLock::saveHandler(Display*, XErrorEvent* e, void* flag) +{ + LOG((CLOG_DEBUG1 "flagging X error: %d", e->error_code)); + *reinterpret_cast(flag) = true; +} diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h new file mode 100644 index 0000000..7136b73 --- /dev/null +++ b/lib/platform/CXWindowsUtil.h @@ -0,0 +1,141 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSUTIL_H +#define CXWINDOWSUTIL_H + +#include "CString.h" +#include "BasicTypes.h" +#include "stdmap.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +//! X11 utility functions +class CXWindowsUtil { +public: + //! Get property + /*! + Gets property \c property on \c window. \b Appends the data to + \c *data if \c data is not NULL, saves the property type in \c *type + if \c type is not NULL, and saves the property format in \c *format + if \c format is not NULL. If \c deleteProperty is true then the + property is deleted after being read. + */ + static bool getWindowProperty(Display*, + Window window, Atom property, + CString* data, Atom* type, + SInt32* format, bool deleteProperty); + + //! Set property + /*! + Sets property \c property on \c window to \c size bytes of data from + \c data. + */ + static bool setWindowProperty(Display*, + Window window, Atom property, + const void* data, UInt32 size, + Atom type, SInt32 format); + + //! Get X server time + /*! + Returns the current X server time. + */ + static Time getCurrentTime(Display*, Window); + + //! Convert KeySym to UCS-4 + /*! + Converts a KeySym to the equivalent UCS-4 character. Returns + 0x0000ffff if the KeySym cannot be mapped. + */ + static UInt32 mapKeySymToUCS4(KeySym); + + //! Convert UCS-4 to KeySym + /*! + Converts a UCS-4 character to the equivalent KeySym. Returns + NoSymbol (0) if the character cannot be mapped. + */ + static KeySym mapUCS4ToKeySym(UInt32); + + //! X11 error handler + /*! + This class sets an X error handler in the c'tor and restores the + previous error handler in the d'tor. A lock should only be + installed while the display is locked by the thread. + + CErrorLock() ignores errors + CErrorLock(bool* flag) sets *flag to true if any error occurs + */ + class CErrorLock { + public: + //! Error handler type + typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData); + + /*! + Ignore X11 errors. + */ + CErrorLock(Display*); + + /*! + Set \c *errorFlag if any error occurs. + */ + CErrorLock(Display*, bool* errorFlag); + + /*! + Call \c handler on each error. + */ + CErrorLock(Display*, ErrorHandler handler, void* userData); + + ~CErrorLock(); + + private: + void install(ErrorHandler, void*); + static int internalHandler(Display*, XErrorEvent*); + static void ignoreHandler(Display*, XErrorEvent*, void*); + static void saveHandler(Display*, XErrorEvent*, void*); + + private: + typedef int (*XErrorHandler)(Display*, XErrorEvent*); + + Display* m_display; + ErrorHandler m_handler; + void* m_userData; + XErrorHandler m_oldXHandler; + CErrorLock* m_next; + static CErrorLock* s_top; + }; + +private: + class CPropertyNotifyPredicateInfo { + public: + Window m_window; + Atom m_property; + }; + + static Bool propertyNotifyPredicate(Display*, + XEvent* xevent, XPointer arg); + + static void initKeyMaps(); + +private: + typedef std::map CKeySymMap; + typedef std::map CUCS4Map; + + static CKeySymMap s_keySymToUCS4; + static CUCS4Map s_UCS4ToKeySym; +}; + +#endif diff --git a/lib/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h new file mode 100644 index 0000000..ee24ea8 --- /dev/null +++ b/lib/platform/IMSWindowsScreenEventHandler.h @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IMSWINDOWSSCREENEVENTHANDLER_H +#define IMSWINDOWSSCREENEVENTHANDLER_H + +#include "IScreenEventHandler.h" +#define WIN32_LEAN_AND_MEAN +#include + +//! MS Windows screen event handler interface +class IMSWindowsScreenEventHandler : public IScreenEventHandler { +public: + //! @name manipulators + //@{ + + //! Notify of window creation + /*! + This is called after the window is created. + */ + virtual void postCreateWindow(HWND) = 0; + + //! Notify of window destruction + /*! + This is called before the window is destroyed. + */ + virtual void preDestroyWindow(HWND) = 0; + + //! Notify of newly accessible desktop + /*! + This is called when the user switched from an inaccessible desktop + to an accessible desktop. + */ + virtual void onAccessibleDesktop() = 0; + + //@} + + // IScreenEventHandler overrides + virtual void onScreensaver(bool activated) = 0; + virtual bool onPreDispatch(const CEvent* event) = 0; + virtual bool onEvent(CEvent* event) = 0; + virtual SInt32 getJumpZoneSize() const = 0; +}; + +#endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am new file mode 100644 index 0000000..ea082e1 --- /dev/null +++ b/lib/platform/Makefile.am @@ -0,0 +1,75 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + makehook.dsp \ + platform.dsp \ + synrgyhk.dsp \ + CMSWindowsClipboard.cpp \ + CMSWindowsClipboardAnyTextConverter.cpp \ + CMSWindowsClipboardTextConverter.cpp \ + CMSWindowsClipboardUTF16Converter.cpp \ + CMSWindowsPrimaryScreen.cpp \ + CMSWindowsScreen.cpp \ + CMSWindowsScreenSaver.cpp \ + CMSWindowsSecondaryScreen.cpp \ + CSynergyHook.cpp \ + CMSWindowsClipboard.h \ + CMSWindowsClipboardAnyTextConverter.h \ + CMSWindowsClipboardTextConverter.h \ + CMSWindowsClipboardUTF16Converter.h \ + CMSWindowsPrimaryScreen.h \ + CMSWindowsScreen.h \ + CMSWindowsScreenSaver.h \ + CMSWindowsSecondaryScreen.h \ + CSynergyHook.h \ + IMSWindowsScreenEventHandler.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libplatform.a +libplatform_a_SOURCES = \ + CXWindowsClipboard.cpp \ + CXWindowsClipboardTextConverter.cpp \ + CXWindowsClipboardUCS2Converter.cpp \ + CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsPrimaryScreen.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsSecondaryScreen.cpp \ + CXWindowsUtil.cpp \ + CXWindowsClipboard.h \ + CXWindowsClipboardTextConverter.h \ + CXWindowsClipboardUCS2Converter.h \ + CXWindowsClipboardUTF8Converter.h \ + CXWindowsPrimaryScreen.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsSecondaryScreen.h \ + CXWindowsUtil.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/synergy \ + $(NULL) diff --git a/lib/platform/Makefile.in b/lib/platform/Makefile.in new file mode 100644 index 0000000..f2f4d45 --- /dev/null +++ b/lib/platform/Makefile.in @@ -0,0 +1,396 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + makehook.dsp \ + platform.dsp \ + synrgyhk.dsp \ + CMSWindowsClipboard.cpp \ + CMSWindowsClipboardAnyTextConverter.cpp \ + CMSWindowsClipboardTextConverter.cpp \ + CMSWindowsClipboardUTF16Converter.cpp \ + CMSWindowsPrimaryScreen.cpp \ + CMSWindowsScreen.cpp \ + CMSWindowsScreenSaver.cpp \ + CMSWindowsSecondaryScreen.cpp \ + CSynergyHook.cpp \ + CMSWindowsClipboard.h \ + CMSWindowsClipboardAnyTextConverter.h \ + CMSWindowsClipboardTextConverter.h \ + CMSWindowsClipboardUTF16Converter.h \ + CMSWindowsPrimaryScreen.h \ + CMSWindowsScreen.h \ + CMSWindowsScreenSaver.h \ + CMSWindowsSecondaryScreen.h \ + CSynergyHook.h \ + IMSWindowsScreenEventHandler.h \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libplatform.a +libplatform_a_SOURCES = \ + CXWindowsClipboard.cpp \ + CXWindowsClipboardTextConverter.cpp \ + CXWindowsClipboardUCS2Converter.cpp \ + CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsPrimaryScreen.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsSecondaryScreen.cpp \ + CXWindowsUtil.cpp \ + CXWindowsClipboard.h \ + CXWindowsClipboardTextConverter.h \ + CXWindowsClipboardUCS2Converter.h \ + CXWindowsClipboardUTF8Converter.h \ + CXWindowsPrimaryScreen.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsSecondaryScreen.h \ + CXWindowsUtil.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/synergy \ + $(NULL) + +subdir = lib/platform +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libplatform_a_AR = $(AR) cru +libplatform_a_LIBADD = +am_libplatform_a_OBJECTS = CXWindowsClipboard.$(OBJEXT) \ + CXWindowsClipboardTextConverter.$(OBJEXT) \ + CXWindowsClipboardUCS2Converter.$(OBJEXT) \ + CXWindowsClipboardUTF8Converter.$(OBJEXT) \ + CXWindowsPrimaryScreen.$(OBJEXT) CXWindowsScreen.$(OBJEXT) \ + CXWindowsScreenSaver.$(OBJEXT) \ + CXWindowsSecondaryScreen.$(OBJEXT) CXWindowsUtil.$(OBJEXT) +libplatform_a_OBJECTS = $(am_libplatform_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CXWindowsClipboard.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClipboardTextConverter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClipboardUCS2Converter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsClipboardUTF8Converter.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsPrimaryScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsScreenSaver.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsSecondaryScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CXWindowsUtil.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libplatform_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libplatform_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/platform/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libplatform.a: $(libplatform_a_OBJECTS) $(libplatform_a_DEPENDENCIES) + -rm -f libplatform.a + $(libplatform_a_AR) libplatform.a $(libplatform_a_OBJECTS) $(libplatform_a_LIBADD) + $(RANLIB) libplatform.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboardTextConverter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboardUCS2Converter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsClipboardUTF8Converter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsPrimaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsScreenSaver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsSecondaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CXWindowsUtil.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/platform/makehook.dsp b/lib/platform/makehook.dsp new file mode 100644 index 0000000..2f66320 --- /dev/null +++ b/lib/platform/makehook.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=makehook - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "makehook.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makehook.mak" CFG="makehook - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "makehook - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "makehook - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "makehook - Win32 Release" +# Name "makehook - Win32 Debug" +# End Target +# End Project diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp new file mode 100644 index 0000000..ed478c6 --- /dev/null +++ b/lib/platform/platform.dsp @@ -0,0 +1,162 @@ +# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=platform - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "platform.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "platform.mak" CFG="platform - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "platform - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "platform - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "platform - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "platform - Win32 Release" +# Name "platform - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IMSWindowsScreenEventHandler.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp new file mode 100644 index 0000000..2e2210f --- /dev/null +++ b/lib/platform/synrgyhk.dsp @@ -0,0 +1,116 @@ +# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=synrgyhk - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "synrgyhk.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synrgyhk - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "ReleaseHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib +# SUBTRACT LINK32 /verbose + +!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "DebugHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "synrgyhk - Win32 Release" +# Name "synrgyhk - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CSynergyHook.cpp +# ADD CPP /D _WIN32_WINNT=0x0400 +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CSynergyHook.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp new file mode 100644 index 0000000..55e940f --- /dev/null +++ b/lib/server/CClientProxy.cpp @@ -0,0 +1,67 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy.h" +#include "IInputStream.h" +#include "IOutputStream.h" + +// +// CClientProxy +// + +CClientProxy::CClientProxy(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + m_server(server), + m_name(name), + m_input(input), + m_output(output) +{ + // do nothing +} + +CClientProxy::~CClientProxy() +{ + delete m_output; + delete m_input; +} + +IServer* +CClientProxy::getServer() const +{ + return m_server; +} + +IInputStream* +CClientProxy::getInputStream() const +{ + return m_input; +} + +IOutputStream* +CClientProxy::getOutputStream() const +{ + return m_output; +} + +CString +CClientProxy::getName() const +{ + return m_name; +} + +const CMutex* +CClientProxy::getMutex() const +{ + return &m_mutex; +} diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h new file mode 100644 index 0000000..2ab41c3 --- /dev/null +++ b/lib/server/CClientProxy.h @@ -0,0 +1,105 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY_H +#define CCLIENTPROXY_H + +#include "IClient.h" +#include "CMutex.h" +#include "CString.h" + +class IInputStream; +class IOutputStream; +class IServer; + +//! Generic proxy for client +class CClientProxy : public IClient { +public: + /*! + \c name is the name of the client. + */ + CClientProxy(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy(); + + //! @name accessors + //@{ + + //! Get server + /*! + Returns the server passed to the c'tor. + */ + IServer* getServer() const; + + //! Get input stream + /*! + Returns the input stream passed to the c'tor. + */ + IInputStream* getInputStream() const; + + //! Get output stream + /*! + Returns the output stream passed to the c'tor. + */ + IOutputStream* getOutputStream() const; + + //@} + + // IClient overrides + virtual void open() = 0; + virtual void mainLoop() = 0; + virtual void close() = 0; + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver) = 0; + virtual bool leave() = 0; + virtual void setClipboard(ClipboardID, const CString&) = 0; + virtual void grabClipboard(ClipboardID) = 0; + virtual void setClipboardDirty(ClipboardID, bool) = 0; + virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton) = 0; + virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 0; + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseWheel(SInt32 delta) = 0; + virtual void screensaver(bool activate) = 0; + virtual void resetOptions() = 0; + virtual void setOptions(const COptionsList& options) = 0; + virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + +protected: + //! Get mutex + /*! + Returns the mutex for this object. Subclasses should use this + mutex to protect their data. + */ + const CMutex* getMutex() const; + +private: + CMutex m_mutex; + IServer* m_server; + CString m_name; + IInputStream* m_input; + IOutputStream* m_output; +}; + +#endif diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp new file mode 100644 index 0000000..7a9e734 --- /dev/null +++ b/lib/server/CClientProxy1_0.cpp @@ -0,0 +1,398 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy1_0.h" +#include "CServer.h" +#include "CClipboard.h" +#include "CProtocolUtil.h" +#include "XSynergy.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#include + +// +// CClientProxy1_0 +// + +CClientProxy1_0::CClientProxy1_0(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + CClientProxy(server, name, input, output), + m_heartRate(kHeartRate), + m_heartDeath(kHeartRate * kHeartBeatsUntilDeath) +{ + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardDirty[i] = true; + } +} + +CClientProxy1_0::~CClientProxy1_0() +{ + // do nothing +} + +void +CClientProxy1_0::open() +{ + // send request + LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgQInfo); + getOutputStream()->flush(); + + // wait for and verify reply + UInt8 code[4]; + for (;;) { + UInt32 n = getInputStream()->read(code, 4, -1.0); + if (n == 4) { + if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard heartbeats + continue; + } + if (memcmp(code, kMsgDInfo, 4) == 0) { + break; + } + } + throw XBadClient(); + } + + // handle reply + recvInfo(false); +} + +void +CClientProxy1_0::mainLoop() +{ + // handle messages until the client hangs up or stops sending heartbeats + CStopwatch heartTimer; + for (;;) { + CThread::testCancel(); + + // wait for a message + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4, m_heartRate); + CThread::testCancel(); + + // check if client hungup + if (n == 0) { + LOG((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); + return; + } + + // check if client has stopped sending heartbeats + if (n == (UInt32)-1) { + if (m_heartDeath >= 0.0 && heartTimer.getTime() > m_heartDeath) { + LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); + return; + } + continue; + } + + // got a message so reset heartbeat monitor + heartTimer.reset(); + + // verify we got an entire code + if (n != 4) { + LOG((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n)); + + // client sent an incomplete message + throw XBadClient(); + } + + // parse message + LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDInfo, 4) == 0) { + recvInfo(true); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard no-ops + LOG((CLOG_DEBUG2 "no-op from", getName().c_str())); + continue; + } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + recvGrabClipboard(); + } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + recvClipboard(); + } + // note -- more message handlers go here + else { + LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); + + // unknown message + throw XBadClient(); + } + } +} + +void +CClientProxy1_0::close() +{ + LOG((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClose); + + // force the close to be sent before we return + getOutputStream()->flush(); +} + +void +CClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, bool) +{ + LOG((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, + xAbs, yAbs, seqNum, mask); +} + +bool +CClientProxy1_0::leave() +{ + LOG((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCLeave); + + // we can never prevent the user from leaving + return true; +} + +void +CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) +{ + // ignore if this clipboard is already clean + CLock lock(getMutex()); + if (m_clipboardDirty[id]) { + // this clipboard is now clean + m_clipboardDirty[id] = false; + + LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); + } +} + +void +CClientProxy1_0::grabClipboard(ClipboardID id) +{ + LOG((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); + + // this clipboard is now dirty + CLock lock(getMutex()); + m_clipboardDirty[id] = true; +} + +void +CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) +{ + CLock lock(getMutex()); + m_clipboardDirty[id] = dirty; +} + +void +CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton) +{ + LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown1_0, key, mask); +} + +void +CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton) +{ + LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat1_0, key, mask, count); +} + +void +CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask, KeyButton) +{ + LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp1_0, key, mask); +} + +void +CClientProxy1_0::mouseDown(ButtonID button) +{ + LOG((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); +} + +void +CClientProxy1_0::mouseUp(ButtonID button) +{ + LOG((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); +} + +void +CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs) +{ + LOG((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); +} + +void +CClientProxy1_0::mouseWheel(SInt32 delta) +{ + LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); +} + +void +CClientProxy1_0::screensaver(bool on) +{ + LOG((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); + CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); +} + +void +CClientProxy1_0::resetOptions() +{ + LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCResetOptions); + + // reset heart rate and death + CLock lock(getMutex()); + m_heartRate = kHeartRate; + m_heartDeath = kHeartRate * kHeartBeatsUntilDeath; +} + +void +CClientProxy1_0::setOptions(const COptionsList& options) +{ + LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDSetOptions, &options); + + // check options + CLock lock(getMutex()); + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHeartbeat) { + m_heartRate = 1.0e-3 * static_cast(options[i + 1]); + if (m_heartRate <= 0.0) { + m_heartRate = -1.0; + } + m_heartDeath = m_heartRate * kHeartBeatsUntilDeath; + } + } +} + +SInt32 +CClientProxy1_0::getJumpZoneSize() const +{ + CLock lock(getMutex()); + return m_info.m_zoneSize; +} + +void +CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(getMutex()); + x = m_info.m_x; + y = m_info.m_y; + w = m_info.m_w; + h = m_info.m_h; +} + +void +CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const +{ + CLock lock(getMutex()); + x = m_info.m_mx; + y = m_info.m_my; +} + +void +CClientProxy1_0::recvInfo(bool notify) +{ + { + CLock lock(getMutex()); + + // parse the message + SInt16 x, y, w, h, zoneSize, mx, my; + CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, + &x, &y, &w, &h, &zoneSize, &mx, &my); + LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); + + // validate + if (w <= 0 || h <= 0 || zoneSize < 0) { + throw XBadClient(); + } + if (mx < x || my < y || mx >= x + w || my >= y + h) { + throw XBadClient(); + } + + // save + m_info.m_x = x; + m_info.m_y = y; + m_info.m_w = w; + m_info.m_h = h; + m_info.m_zoneSize = zoneSize; + m_info.m_mx = mx; + m_info.m_my = my; + } + + // tell server of change + if (notify) { + getServer()->onInfoChanged(getName(), m_info); + } + + // acknowledge receipt + LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); +} + +void +CClientProxy1_0::recvClipboard() +{ + // parse message + ClipboardID id; + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); + LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size())); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update. this calls us back to reset our clipboard dirty flag + // so don't hold a lock during the call. + getServer()->onClipboardChanged(id, seqNum, data); +} + +void +CClientProxy1_0::recvGrabClipboard() +{ + // parse message + ClipboardID id; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + LOG((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum)); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update. this calls us back to reset our clipboard dirty flag + // so don't hold a lock during the call. + getServer()->onGrabClipboard(getName(), id, seqNum); +} diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h new file mode 100644 index 0000000..0a86e1a --- /dev/null +++ b/lib/server/CClientProxy1_0.h @@ -0,0 +1,69 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY1_0_H +#define CCLIENTPROXY1_0_H + +#include "CClientProxy.h" +#include "ProtocolTypes.h" + +//! Proxy for client implementing protocol version 1.0 +class CClientProxy1_0 : public CClientProxy { +public: + CClientProxy1_0(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy1_0(); + + // IClient overrides + virtual void open(); + virtual void mainLoop(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual SInt32 getJumpZoneSize() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; + +private: + void recvInfo(bool notify); + void recvClipboard(); + void recvGrabClipboard(); + +private: + CClientInfo m_info; + bool m_clipboardDirty[kClipboardEnd]; + double m_heartRate; + double m_heartDeath; +}; + +#endif diff --git a/lib/server/CClientProxy1_1.cpp b/lib/server/CClientProxy1_1.cpp new file mode 100644 index 0000000..a09b27b --- /dev/null +++ b/lib/server/CClientProxy1_1.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy1_1.h" +#include "CProtocolUtil.h" +#include "CLog.h" +#include + +// +// CClientProxy1_1 +// + +CClientProxy1_1::CClientProxy1_1(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + CClientProxy1_0(server, name, input, output) +{ + // do nothing +} + +CClientProxy1_1::~CClientProxy1_1() +{ + // do nothing +} + +void +CClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask, button); +} + +void +CClientProxy1_1::keyRepeat(KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d, button=0x%04x", getName().c_str(), key, mask, count, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count, button); +} + +void +CClientProxy1_1::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask, button); +} diff --git a/lib/server/CClientProxy1_1.h b/lib/server/CClientProxy1_1.h new file mode 100644 index 0000000..dbae783 --- /dev/null +++ b/lib/server/CClientProxy1_1.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY1_1_H +#define CCLIENTPROXY1_1_H + +#include "CClientProxy1_0.h" + +//! Proxy for client implementing protocol version 1.1 +class CClientProxy1_1 : public CClientProxy1_0 { +public: + CClientProxy1_1(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy1_1(); + + // IClient overrides + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); +}; + +#endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp new file mode 100644 index 0000000..89417b2 --- /dev/null +++ b/lib/server/CConfig.cpp @@ -0,0 +1,1174 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "KeyTypes.h" +#include "XSocket.h" +#include "stdistream.h" +#include "stdostream.h" +#include + +// +// CConfig +// + +CConfig::CConfig() +{ + // do nothing +} + +CConfig::~CConfig() +{ + // do nothing +} + +bool +CConfig::addScreen(const CString& name) +{ + // alias name must not exist + if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) { + return false; + } + + // add cell + m_map.insert(std::make_pair(name, CCell())); + + // add name + m_nameToCanonicalName.insert(std::make_pair(name, name)); + + return true; +} + +bool +CConfig::renameScreen(const CString& oldName, + const CString& newName) +{ + // get canonical name and find cell + CString oldCanonical = getCanonicalName(oldName); + CCellMap::iterator index = m_map.find(oldCanonical); + if (index == m_map.end()) { + return false; + } + + // accept if names are equal but replace with new name to maintain + // case. otherwise, the new name must not exist. + if (!CStringUtil::CaselessCmp::equal(oldName, newName) && + m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) { + return false; + } + + // update cell + CCell tmpCell = index->second; + m_map.erase(index); + m_map.insert(std::make_pair(newName, tmpCell)); + + // update name + m_nameToCanonicalName.erase(oldCanonical); + m_nameToCanonicalName.insert(std::make_pair(newName, newName)); + + // update connections + for (index = m_map.begin(); index != m_map.end(); ++index) { + for (UInt32 i = 0; i < kNumDirections; ++i) { + if (CStringUtil::CaselessCmp::equal(getCanonicalName( + index->second.m_neighbor[i]), oldCanonical)) { + index->second.m_neighbor[i] = newName; + } + } + } + + // update alias targets + if (CStringUtil::CaselessCmp::equal(oldName, oldCanonical)) { + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ++index) { + if (CStringUtil::CaselessCmp::equal( + index->second, oldCanonical)) { + index->second = newName; + } + } + } + + return true; +} + +void +CConfig::removeScreen(const CString& name) +{ + // get canonical name and find cell + CString canonical = getCanonicalName(name); + CCellMap::iterator index = m_map.find(canonical); + if (index == m_map.end()) { + return; + } + + // remove from map + m_map.erase(index); + + // disconnect + for (index = m_map.begin(); index != m_map.end(); ++index) { + CCell& cell = index->second; + for (UInt32 i = 0; i < kNumDirections; ++i) { + if (getCanonicalName(cell.m_neighbor[i]) == canonical) { + cell.m_neighbor[i].erase(); + } + } + } + + // remove aliases (and canonical name) + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ) { + if (index->second == canonical) { + m_nameToCanonicalName.erase(index++); + } + else { + ++index; + } + } +} + +void +CConfig::removeAllScreens() +{ + m_map.clear(); + m_nameToCanonicalName.clear(); +} + +bool +CConfig::addAlias(const CString& canonical, const CString& alias) +{ + // alias name must not exist + if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) { + return false; + } + + // canonical name must be known + if (m_map.find(canonical) == m_map.end()) { + return false; + } + + // insert alias + m_nameToCanonicalName.insert(std::make_pair(alias, canonical)); + + return true; +} + +bool +CConfig::removeAlias(const CString& alias) +{ + // must not be a canonical name + if (m_map.find(alias) != m_map.end()) { + return false; + } + + // find alias + CNameMap::iterator index = m_nameToCanonicalName.find(alias); + if (index == m_nameToCanonicalName.end()) { + return false; + } + + // remove alias + m_nameToCanonicalName.erase(index); + + return true; +} + +void +CConfig::removeAllAliases() +{ + // remove all names + m_nameToCanonicalName.clear(); + + // put the canonical names back in + for (CCellMap::iterator index = m_map.begin(); + index != m_map.end(); ++index) { + m_nameToCanonicalName.insert( + std::make_pair(index->first, index->first)); + } +} + +bool +CConfig::connect(const CString& srcName, + EDirection srcSide, const CString& dstName) +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::iterator index = m_map.find(getCanonicalName(srcName)); + if (index == m_map.end()) { + return false; + } + + // connect side (overriding any previous connection). we + // canonicalize in getNeighbor() instead of here because the + // destination name doesn't have to exist yet. + index->second.m_neighbor[srcSide - kFirstDirection] = dstName; + + return true; +} + +bool +CConfig::disconnect(const CString& srcName, EDirection srcSide) +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::iterator index = m_map.find(srcName); + if (index == m_map.end()) { + return false; + } + + // disconnect side + index->second.m_neighbor[srcSide - kFirstDirection].erase(); + + return true; +} + +void +CConfig::setSynergyAddress(const CNetworkAddress& addr) +{ + m_synergyAddress = addr; +} + +void +CConfig::setHTTPAddress(const CNetworkAddress& addr) +{ + m_httpAddress = addr; +} + +bool +CConfig::addOption(const CString& name, OptionID option, OptionValue value) +{ + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { + return false; + } + + // add option + options->insert(std::make_pair(option, value)); + return true; +} + +bool +CConfig::removeOption(const CString& name, OptionID option) +{ + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { + return false; + } + + // remove option + options->erase(option); + return true; +} + +bool +CConfig::removeOptions(const CString& name) +{ + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { + return false; + } + + // remove options + options->clear(); + return true; +} + +bool +CConfig::isValidScreenName(const CString& name) const +{ + // name is valid if matches validname + // name ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9] + // domain ::= . name + // validname ::= name domain* + + // check each dot separated part + CString::size_type b = 0; + for (;;) { + // find end of part + CString::size_type e = name.find('.', b); + if (e == CString::npos) { + e = name.size(); + } + + // part may not be empty + if (e - b < 1) { + return false; + } + + // check first and last characters + if (!(isalnum(name[b]) || name[b] == '_') || + !(isalnum(name[e - 1]) || name[e - 1] == '_')) { + return false; + } + + // check interior characters + for (CString::size_type i = b; i < e; ++i) { + if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') { + return false; + } + } + + // next part + if (e == name.size()) { + // no more parts + break; + } + b = e + 1; + } + + return true; +} + +CConfig::const_iterator +CConfig::begin() const +{ + return const_iterator(m_map.begin()); +} + +CConfig::const_iterator +CConfig::end() const +{ + return const_iterator(m_map.end()); +} + +CConfig::all_const_iterator +CConfig::beginAll() const +{ + return m_nameToCanonicalName.begin(); +} + +CConfig::all_const_iterator +CConfig::endAll() const +{ + return m_nameToCanonicalName.end(); +} + +bool +CConfig::isScreen(const CString& name) const +{ + return (m_nameToCanonicalName.count(name) > 0); +} + +bool +CConfig::isCanonicalName(const CString& name) const +{ + return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); +} + +CString +CConfig::getCanonicalName(const CString& name) const +{ + CNameMap::const_iterator index = m_nameToCanonicalName.find(name); + if (index == m_nameToCanonicalName.end()) { + return CString(); + } + else { + return index->second; + } +} + +CString +CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + if (index == m_map.end()) { + return CString(); + } + + // return connection + return getCanonicalName(index->second.m_neighbor[ + srcSide - kFirstDirection]); +} + +const CNetworkAddress& +CConfig::getSynergyAddress() const +{ + return m_synergyAddress; +} + +const CNetworkAddress& +CConfig::getHTTPAddress() const +{ + return m_httpAddress; +} + +const CConfig::CScreenOptions* +CConfig::getOptions(const CString& name) const +{ + // find options + const CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::const_iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + + // return options + return options; +} + +bool +CConfig::operator==(const CConfig& x) const +{ +/* FIXME -- no compare available for CNetworkAddress + if (m_synergyAddress != x.m_synergyAddress) { + return false; + } + if (m_httpAddress != x.m_httpAddress) { + return false; + } +*/ + if (m_map.size() != x.m_map.size()) { + return false; + } + if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) { + return false; + } + + // compare global options + if (m_globalOptions != x.m_globalOptions) { + return false; + } + + for (CCellMap::const_iterator index1 = m_map.begin(), + index2 = x.m_map.begin(); + index1 != m_map.end(); ++index1, ++index2) { + // compare names + if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first)) { + return false; + } + + // compare options + if (index1->second.m_options != index2->second.m_options) { + return false; + } + + // compare neighbors + for (UInt32 i = 0; i < kNumDirections; ++i) { + if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i], + index2->second.m_neighbor[i])) { + return false; + } + } + } + + for (CNameMap::const_iterator index1 = m_nameToCanonicalName.begin(), + index2 = x.m_nameToCanonicalName.begin(); + index1 != m_nameToCanonicalName.end(); + ++index1, ++index2) { + if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first) || + !CStringUtil::CaselessCmp::equal(index1->second, index2->second)) { + return false; + } + } + + return true; +} + +bool +CConfig::operator!=(const CConfig& x) const +{ + return !operator==(x); +} + +const char* +CConfig::dirName(EDirection dir) +{ + static const char* s_name[] = { "left", "right", "top", "bottom" }; + + assert(dir >= kFirstDirection && dir <= kLastDirection); + + return s_name[dir - kFirstDirection]; +} + +bool +CConfig::readLine(std::istream& s, CString& line) +{ + s >> std::ws; + while (std::getline(s, line)) { + // strip comments and then trailing whitespace + CString::size_type i = line.find('#'); + if (i != CString::npos) { + line.erase(i); + } + i = line.find_last_not_of(" \r\t"); + if (i != CString::npos) { + line.erase(i + 1); + } + + // return non empty line + if (!line.empty()) { + return true; + } + s >> std::ws; + } + return false; +} + +OptionValue +CConfig::parseBoolean(const CString& arg) +{ + if (CStringUtil::CaselessCmp::equal(arg, "true")) { + return static_cast(true); + } + if (CStringUtil::CaselessCmp::equal(arg, "false")) { + return static_cast(false); + } + throw XConfigRead("invalid argument"); +} + +OptionValue +CConfig::parseInt(const CString& arg) +{ + const char* s = arg.c_str(); + char* end; + long tmp = strtol(s, &end, 10); + if (*end != '\0') { + // invalid characters + throw XConfigRead("invalid argument"); + } + OptionValue value = static_cast(tmp); + if (value != tmp) { + // out of range + throw XConfigRead("argument out of range"); + } + return value; +} + +OptionValue +CConfig::parseModifierKey(const CString& arg) +{ + if (CStringUtil::CaselessCmp::equal(arg, "shift")) { + return static_cast(kKeyModifierIDShift); + } + if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) { + return static_cast(kKeyModifierIDControl); + } + if (CStringUtil::CaselessCmp::equal(arg, "alt")) { + return static_cast(kKeyModifierIDAlt); + } + if (CStringUtil::CaselessCmp::equal(arg, "meta")) { + return static_cast(kKeyModifierIDMeta); + } + if (CStringUtil::CaselessCmp::equal(arg, "super")) { + return static_cast(kKeyModifierIDSuper); + } + if (CStringUtil::CaselessCmp::equal(arg, "none")) { + return static_cast(kKeyModifierIDNull); + } + throw XConfigRead("invalid argument"); +} + +const char* +CConfig::getOptionName(OptionID id) +{ + if (id == kOptionHalfDuplexCapsLock) { + return "halfDuplexCapsLock"; + } + if (id == kOptionHalfDuplexNumLock) { + return "halfDuplexNumLock"; + } + if (id == kOptionModifierMapForShift) { + return "shift"; + } + if (id == kOptionModifierMapForControl) { + return "ctrl"; + } + if (id == kOptionModifierMapForAlt) { + return "alt"; + } + if (id == kOptionModifierMapForMeta) { + return "meta"; + } + if (id == kOptionModifierMapForSuper) { + return "super"; + } + if (id == kOptionHeartbeat) { + return "heartbeat"; + } + if (id == kOptionScreenSwitchDelay) { + return "switchDelay"; + } + if (id == kOptionScreenSwitchTwoTap) { + return "switchDoubleTap"; + } + if (id == kOptionScreenSaverSync) { + return "screenSaverSync"; + } + if (id == kOptionXTestXineramaUnaware) { + return "xtestIsXineramaUnaware"; + } + return NULL; +} + +CString +CConfig::getOptionValue(OptionID id, OptionValue value) +{ + if (id == kOptionHalfDuplexCapsLock || + id == kOptionHalfDuplexNumLock || + id == kOptionScreenSaverSync || + id == kOptionXTestXineramaUnaware) { + return (value != 0) ? "true" : "false"; + } + if (id == kOptionModifierMapForShift || + id == kOptionModifierMapForControl || + id == kOptionModifierMapForAlt || + id == kOptionModifierMapForMeta || + id == kOptionModifierMapForSuper) { + switch (value) { + case kKeyModifierIDShift: + return "shift"; + + case kKeyModifierIDControl: + return "ctrl"; + + case kKeyModifierIDAlt: + return "alt"; + + case kKeyModifierIDMeta: + return "meta"; + + case kKeyModifierIDSuper: + return "super"; + + default: + return "none"; + } + } + if (id == kOptionHeartbeat || + id == kOptionScreenSwitchDelay || + id == kOptionScreenSwitchTwoTap) { + return CStringUtil::print("%d", value); + } + + return ""; +} + +void +CConfig::readSection(std::istream& s) +{ + static const char s_section[] = "section:"; + static const char s_options[] = "options"; + static const char s_screens[] = "screens"; + static const char s_links[] = "links"; + static const char s_aliases[] = "aliases"; + + CString line; + if (!readLine(s, line)) { + // no more sections + return; + } + + // should be a section header + if (line.find(s_section) != 0) { + throw XConfigRead("found data outside section"); + } + + // get section name + CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1); + if (i == CString::npos) { + throw XConfigRead("section name is missing"); + } + CString name = line.substr(i); + i = name.find_first_of(" \t"); + if (i != CString::npos) { + throw XConfigRead("unexpected data after section name"); + } + + // read section + if (name == s_options) { + readSectionOptions(s); + } + else if (name == s_screens) { + readSectionScreens(s); + } + else if (name == s_links) { + readSectionLinks(s); + } + else if (name == s_aliases) { + readSectionAliases(s); + } + else { + throw XConfigRead("unknown section name"); + } +} + +void +CConfig::readSectionOptions(std::istream& s) +{ + CString line; + CString name; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + if (value.empty()) { + throw XConfigRead("missing value after ="); + } + + if (name == "address") { + try { + m_synergyAddress = CNetworkAddress(value, kDefaultPort); + } + catch (XSocketAddress&) { + throw XConfigRead("invalid address argument"); + } + } + else if (name == "http") { + try { + m_httpAddress = CNetworkAddress(value, kDefaultPort + 1); + } + catch (XSocketAddress&) { + throw XConfigRead("invalid http argument"); + } + } + else if (name == "heartbeat") { + addOption("", kOptionHeartbeat, parseInt(value)); + } + else if (name == "switchDelay") { + addOption("", kOptionScreenSwitchDelay, parseInt(value)); + } + else if (name == "switchDoubleTap") { + addOption("", kOptionScreenSwitchTwoTap, parseInt(value)); + } + else if (name == "screenSaverSync") { + addOption("", kOptionScreenSaverSync, parseBoolean(value)); + } + else { + throw XConfigRead("unknown argument"); + } + } + throw XConfigRead("unexpected end of screens section"); +} + +void +CConfig::readSectionScreens(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify validity of screen name + if (!isValidScreenName(screen)) { + throw XConfigRead("invalid screen name"); + } + + // add the screen to the configuration + if (!addScreen(screen)) { + throw XConfigRead("duplicate screen name"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + + // handle argument + if (name == "halfDuplexCapsLock") { + addOption(screen, kOptionHalfDuplexCapsLock, + parseBoolean(value)); + } + else if (name == "halfDuplexNumLock") { + addOption(screen, kOptionHalfDuplexNumLock, + parseBoolean(value)); + } + else if (name == "shift") { + addOption(screen, kOptionModifierMapForShift, + parseModifierKey(value)); + } + else if (name == "ctrl") { + addOption(screen, kOptionModifierMapForControl, + parseModifierKey(value)); + } + else if (name == "alt") { + addOption(screen, kOptionModifierMapForAlt, + parseModifierKey(value)); + } + else if (name == "meta") { + addOption(screen, kOptionModifierMapForMeta, + parseModifierKey(value)); + } + else if (name == "super") { + addOption(screen, kOptionModifierMapForSuper, + parseModifierKey(value)); + } + else if (name == "xtestIsXineramaUnaware") { + addOption(screen, kOptionXTestXineramaUnaware, + parseBoolean(value)); + } + else { + // unknown argument + throw XConfigRead("unknown argument"); + } + } + } + throw XConfigRead("unexpected end of screens section"); +} + +void +CConfig::readSectionLinks(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead("unknown screen name"); + } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + + // handle argument + if (name == "left") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kLeft, value); + } + else if (name == "right") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kRight, value); + } + else if (name == "up") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kTop, value); + } + else if (name == "down") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kBottom, value); + } + else { + // unknown argument + throw XConfigRead("unknown argument"); + } + } + } + throw XConfigRead("unexpected end of links section"); +} + +void +CConfig::readSectionAliases(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead("unknown screen name"); + } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // verify validity of screen name + if (!isValidScreenName(line)) { + throw XConfigRead("invalid screen alias"); + } + + // add alias + if (!addAlias(screen, line)) { + throw XConfigRead("alias is duplicate screen name"); + } + } + } + throw XConfigRead("unexpected end of aliases section"); +} + + +// +// CConfig I/O +// + +std::istream& +operator>>(std::istream& s, CConfig& config) +{ + // FIXME -- should track line and column to improve error reporting + + CConfig tmp; + while (s) { + tmp.readSection(s); + } + config = tmp; + return s; +} + +std::ostream& +operator<<(std::ostream& s, const CConfig& config) +{ + // options section + s << "section: options" << std::endl; + const CConfig::CScreenOptions* options = config.getOptions(""); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator + option = options->begin(); + option != options->end(); ++option) { + const char* name = CConfig::getOptionName(option->first); + CString value = CConfig::getOptionValue(option->first, + option->second); + if (name != NULL && !value.empty()) { + s << "\t" << name << " = " << value << std::endl; + } + } + } + if (config.m_synergyAddress.isValid()) { + s << "\taddress = " << + config.m_synergyAddress.getHostname().c_str() << std::endl; + } + if (config.m_httpAddress.isValid()) { + s << "\thttp = " << + config.m_httpAddress.getHostname().c_str() << std::endl; + } + s << "end" << std::endl; + + // screens section + s << "section: screens" << std::endl; + for (CConfig::const_iterator screen = config.begin(); + screen != config.end(); ++screen) { + s << "\t" << screen->c_str() << ":" << std::endl; + const CConfig::CScreenOptions* options = config.getOptions(*screen); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator + option = options->begin(); + option != options->end(); ++option) { + const char* name = CConfig::getOptionName(option->first); + CString value = CConfig::getOptionValue(option->first, + option->second); + if (name != NULL && !value.empty()) { + s << "\t\t" << name << " = " << value << std::endl; + } + } + } + } + s << "end" << std::endl; + + // links section + CString neighbor; + s << "section: links" << std::endl; + for (CConfig::const_iterator screen = config.begin(); + screen != config.end(); ++screen) { + s << "\t" << screen->c_str() << ":" << std::endl; + + neighbor = config.getNeighbor(*screen, kLeft); + if (!neighbor.empty()) { + s << "\t\tleft=" << neighbor.c_str() << std::endl; + } + + neighbor = config.getNeighbor(*screen, kRight); + if (!neighbor.empty()) { + s << "\t\tright=" << neighbor.c_str() << std::endl; + } + + neighbor = config.getNeighbor(*screen, kTop); + if (!neighbor.empty()) { + s << "\t\tup=" << neighbor.c_str() << std::endl; + } + + neighbor = config.getNeighbor(*screen, kBottom); + if (!neighbor.empty()) { + s << "\t\tdown=" << neighbor.c_str() << std::endl; + } + } + s << "end" << std::endl; + + // aliases section (if there are any) + if (config.m_map.size() != config.m_nameToCanonicalName.size()) { + // map canonical to alias + typedef std::multimap CMNameMap; + CMNameMap aliases; + for (CConfig::CNameMap::const_iterator + index = config.m_nameToCanonicalName.begin(); + index != config.m_nameToCanonicalName.end(); + ++index) { + if (index->first != index->second) { + aliases.insert(std::make_pair(index->second, index->first)); + } + } + + // dump it + CString screen; + s << "section: aliases" << std::endl; + for (CMNameMap::const_iterator index = aliases.begin(); + index != aliases.end(); ++index) { + if (index->first != screen) { + screen = index->first; + s << "\t" << screen.c_str() << ":" << std::endl; + } + s << "\t\t" << index->second.c_str() << std::endl; + } + s << "end" << std::endl; + } + + return s; +} + + +// +// CConfig I/O exceptions +// + +XConfigRead::XConfigRead(const CString& error) : + m_error(error) +{ + // do nothing +} + +XConfigRead::~XConfigRead() +{ + // do nothing +} + +CString +XConfigRead::getWhat() const throw() +{ + return format("XConfigRead", "read error: %{1}", m_error.c_str()); +} diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h new file mode 100644 index 0000000..28e2c5b --- /dev/null +++ b/lib/server/CConfig.h @@ -0,0 +1,332 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCONFIG_H +#define CCONFIG_H + +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "CNetworkAddress.h" +#include "CStringUtil.h" +#include "XBase.h" +#include "stdmap.h" +#include "stdset.h" +#include + +class CConfig; + +namespace std { +template <> +struct iterator_traits { + typedef CString value_type; + typedef ptrdiff_t difference_type; + typedef bidirectional_iterator_tag iterator_category; + typedef CString* pointer; + typedef CString& reference; +}; +}; + +//! Server configuration +/*! +This class holds server configuration information. That includes +the names of screens and their aliases, the links between them, +and network addresses. + +Note that case is preserved in screen names but is ignored when +comparing names. Screen names and their aliases share a +namespace and must be unique. +*/ +class CConfig { +public: + typedef std::map CScreenOptions; + +private: + class CCell { + public: + CString m_neighbor[kLastDirection - kFirstDirection + 1]; + CScreenOptions m_options; + }; + typedef std::map CCellMap; + typedef std::map CNameMap; + +public: + typedef CCellMap::const_iterator internal_const_iterator; + typedef CNameMap::const_iterator all_const_iterator; + class const_iterator : std::iterator_traits { + public: + explicit const_iterator() : m_i() { } + explicit const_iterator(const internal_const_iterator& i) : m_i(i) { } + + const_iterator& operator=(const const_iterator& i) { + m_i = i.m_i; + return *this; + } + CString operator*() { return m_i->first; } + const CString* operator->() { return &(m_i->first); } + const_iterator& operator++() { ++m_i; return *this; } + const_iterator operator++(int) { return const_iterator(m_i++); } + const_iterator& operator--() { --m_i; return *this; } + const_iterator operator--(int) { return const_iterator(m_i--); } + bool operator==(const const_iterator& i) const { + return (m_i == i.m_i); + } + bool operator!=(const const_iterator& i) const { + return (m_i != i.m_i); + } + + private: + internal_const_iterator m_i; + }; + + CConfig(); + virtual ~CConfig(); + + //! @name manipulators + //@{ + + //! Add screen + /*! + Adds a screen, returning true iff successful. If a screen or + alias with the given name exists then it fails. + */ + bool addScreen(const CString& name); + + //! Rename screen + /*! + Renames a screen. All references to the name are updated. + Returns true iff successful. + */ + bool renameScreen(const CString& oldName, + const CString& newName); + + //! Remove screen + /*! + Removes a screen. This also removes aliases for the screen and + disconnects any connections to the screen. \c name may be an + alias. + */ + void removeScreen(const CString& name); + + //! Remove all screens + /*! + Removes all screens, aliases, and connections. + */ + void removeAllScreens(); + + //! Add alias + /*! + Adds an alias for a screen name. An alias can be used + any place the canonical screen name can (except addScreen()). + Returns false if the alias name already exists or the canonical + name is unknown, otherwise returns true. + */ + bool addAlias(const CString& canonical, + const CString& alias); + + //! Remove alias + /*! + Removes an alias for a screen name. It returns false if the + alias is unknown or a canonical name, otherwise returns true. + */ + bool removeAlias(const CString& alias); + + //! Remove all aliases + /*! + This removes all aliases but not the screens. + */ + void removeAllAliases(); + + //! Connect screens + /*! + Establishes a one-way connection between opposite edges of two + screens. The user will be able to jump from the \c srcSide of + screen \c srcName to the opposite side of screen \c dstName + when both screens are connected to the server and the user + isn't locked to a screen. Returns false if \c srcName is + unknown. + */ + bool connect(const CString& srcName, + EDirection srcSide, + const CString& dstName); + + //! Disconnect screens + /*! + Removes a connection created by connect(). Returns false if + \c srcName is unknown. + */ + bool disconnect(const CString& srcName, + EDirection srcSide); + + //! Set server address + /*! + Set the synergy listen addresses. There is no default address so + this must be called to run a server using this configuration. + */ + void setSynergyAddress(const CNetworkAddress&); + + //! Set HTTP server address + /*! + Set the HTTP listen addresses. There is no default address so + this must be called to run an HTTP server using this configuration. + */ + void setHTTPAddress(const CNetworkAddress&); + + //! Add a screen option + /*! + Adds an option and its value to the named screen. Replaces the + existing option's value if there is one. Returns true iff \c name + is a known screen. + */ + bool addOption(const CString& name, + OptionID option, OptionValue value); + + //! Remove a screen option + /*! + Removes an option and its value from the named screen. Does + nothing if the option doesn't exist on the screen. Returns true + iff \c name is a known screen. + */ + bool removeOption(const CString& name, OptionID option); + + //! Remove a screen options + /*! + Removes all options and values from the named screen. Returns true + iff \c name is a known screen. + */ + bool removeOptions(const CString& name); + + //@} + //! @name accessors + //@{ + + //! Test screen name validity + /*! + Returns true iff \c name is a valid screen name. + */ + bool isValidScreenName(const CString& name) const; + + //! Get beginning (canonical) screen name iterator + const_iterator begin() const; + //! Get ending (canonical) screen name iterator + const_iterator end() const; + + //! Get beginning screen name iterator + all_const_iterator beginAll() const; + //! Get ending screen name iterator + all_const_iterator endAll() const; + + //! Test for screen name + /*! + Returns true iff \c name names a screen. + */ + bool isScreen(const CString& name) const; + + //! Test for canonical screen name + /*! + Returns true iff \c name is the canonical name of a screen. + */ + bool isCanonicalName(const CString& name) const; + + //! Get canonical name + /*! + Returns the canonical name of a screen or the empty string if + the name is unknown. Returns the canonical name if one is given. + */ + CString getCanonicalName(const CString& name) const; + + //! Get neighbor + /*! + Returns the canonical screen name of the neighbor in the given + direction (set through connect()). Returns the empty string + if there is no neighbor in that direction. + */ + CString getNeighbor(const CString&, EDirection) const; + + //! Get the server address + const CNetworkAddress& getSynergyAddress() const; + //! Get the HTTP server address + const CNetworkAddress& getHTTPAddress() const; + + //! Get the screen options + /*! + Returns all the added options for the named screen. Returns NULL + if the screen is unknown and an empty collection if there are no + options. + */ + const CScreenOptions* getOptions(const CString& name) const; + + //! Compare configurations + bool operator==(const CConfig&) const; + //! Compare configurations + bool operator!=(const CConfig&) const; + + //! Read configuration + /*! + Reads a configuration from a stream. Throws XConfigRead on error. + */ + friend std::istream& operator>>(std::istream&, CConfig&); + + //! Write configuration + /*! + Writes a configuration to a stream. + */ + friend std::ostream& operator<<(std::ostream&, const CConfig&); + + //! Get direction name + /*! + Returns the name of a direction (for debugging). + */ + static const char* dirName(EDirection); + + //@} + +private: + static bool readLine(std::istream&, CString&); + static OptionValue parseBoolean(const CString&); + static OptionValue parseInt(const CString&); + static OptionValue parseModifierKey(const CString&); + static const char* getOptionName(OptionID); + static CString getOptionValue(OptionID, OptionValue); + void readSection(std::istream&); + void readSectionOptions(std::istream&); + void readSectionScreens(std::istream&); + void readSectionLinks(std::istream&); + void readSectionAliases(std::istream&); + +private: + CCellMap m_map; + CNameMap m_nameToCanonicalName; + CNetworkAddress m_synergyAddress; + CNetworkAddress m_httpAddress; + CScreenOptions m_globalOptions; +}; + +//! Configuration stream read exception +/*! +Thrown when a configuration stream cannot be parsed. +*/ +class XConfigRead : public XBase { +public: + XConfigRead(const CString&); + ~XConfigRead(); + +protected: + // XBase overrides + virtual CString getWhat() const throw(); + +private: + CString m_error; +}; + +#endif diff --git a/lib/server/CHTTPServer.cpp b/lib/server/CHTTPServer.cpp new file mode 100644 index 0000000..d3582ac --- /dev/null +++ b/lib/server/CHTTPServer.cpp @@ -0,0 +1,799 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CHTTPServer.h" +#include "CConfig.h" +#include "CHTTPProtocol.h" +#include "CServer.h" +#include "XHTTP.h" +#include "IDataSocket.h" +#include "XThread.h" +#include "CLog.h" +#include "stdset.h" +#include "stdsstream.h" + +// +// CHTTPServer +// + +// maximum size of an HTTP request. this should be large enough to +// handle any reasonable request but small enough to prevent a +// malicious client from causing us to use too much memory. +const UInt32 CHTTPServer::s_maxRequestSize = 32768; + +CHTTPServer::CHTTPServer( + CServer* server) : + m_server(server) +{ + // do nothing +} + +CHTTPServer::~CHTTPServer() +{ + // do nothing +} + +void +CHTTPServer::processRequest(IDataSocket* socket) +{ + assert(socket != NULL); + + CHTTPRequest* request = NULL; + try { + // parse request + request = CHTTPProtocol::readRequest( + socket->getInputStream(), s_maxRequestSize); + if (request == NULL) { + throw XHTTP(400); + } + + // if absolute uri then strip off scheme and host + if (request->m_uri[0] != '/') { + CString::size_type n = request->m_uri.find('/'); + if (n == CString::npos) { + throw XHTTP(404); + } + request->m_uri = request->m_uri.substr(n); + } + + // prepare reply + CHTTPReply reply; + reply.m_majorVersion = request->m_majorVersion; + reply.m_minorVersion = request->m_minorVersion; + reply.m_status = 200; + reply.m_reason = "OK"; + reply.m_method = request->m_method; + + // process + doProcessRequest(*request, reply); + + // send reply + CHTTPProtocol::reply(socket->getOutputStream(), reply); + LOG((CLOG_INFO "HTTP reply %d for %s %s", reply.m_status, request->m_method.c_str(), request->m_uri.c_str())); + + // clean up + delete request; + } + catch (XHTTP& e) { + LOG((CLOG_WARN "returning HTTP error %d %s for %s", e.getStatus(), e.getReason().c_str(), (request != NULL) ? request->m_uri.c_str() : "")); + + // clean up + delete request; + + // return error + CHTTPReply reply; + reply.m_majorVersion = 1; + reply.m_minorVersion = 0; + reply.m_status = e.getStatus(); + reply.m_reason = e.getReason(); + reply.m_method = "GET"; +// FIXME -- use a nicer error page + reply.m_headers.push_back(std::make_pair(CString("Content-Type"), + CString("text/plain"))); + reply.m_body = e.getReason(); + e.addHeaders(reply); + CHTTPProtocol::reply(socket->getOutputStream(), reply); + } + catch (...) { + // ignore other exceptions + RETHROW_XTHREAD + } +} + +void +CHTTPServer::doProcessRequest(CHTTPRequest& request, CHTTPReply& reply) +{ + // switch based on uri + if (request.m_uri == "/editmap") { + if (request.m_method == "GET" || request.m_method == "HEAD") { + doProcessGetEditMap(request, reply); + reply.m_headers.push_back(std::make_pair( + CString("Content-Type"), + CString("text/html"))); + } + else if (request.m_method == "POST") { + doProcessPostEditMap(request, reply); + reply.m_headers.push_back(std::make_pair( + CString("Content-Type"), + CString("text/html"))); + } + else { + throw XHTTPAllow("GET, HEAD, POST"); + } + } + else { + // unknown page + throw XHTTP(404); + } +} + +void +CHTTPServer::doProcessGetEditMap(CHTTPRequest& /*request*/, CHTTPReply& reply) +{ + static const char* s_editMapProlog1 = + "\r\n" + "\r\n" + " Synergy -- Edit Screens\r\n" + "\r\n" + "\r\n" + "
\r\n" + " \r\n"; + static const char* s_editMapEpilog = + " \r\n" + " \r\n" + "
\r\n" + "
\r\n" + "\r\n" + "\r\n"; + static const char* s_editMapTableProlog = + "
" + " \r\n"; + static const char* s_editMapTableEpilog = + "
" + "
\r\n"; + static const char* s_editMapRowProlog = + "\r\n"; + static const char* s_editMapRowEpilog = + "\r\n"; + static const char* s_editMapScreenDummy = + ""; + static const char* s_editMapScreenPrimary = + ""; + static const char* s_editMapScreenEnd = + "\r\n"; + + std::ostringstream s; + + // convert screen map into a temporary screen map + CScreenArray screens; + { + CConfig config; + m_server->getConfig(&config); + screens.convertFrom(config); + // FIXME -- note to user if config couldn't be exactly represented + } + + // insert blank columns and rows around array (to allow the user + // to insert new screens) + screens.insertColumn(0); + screens.insertColumn(screens.getWidth()); + screens.insertRow(0); + screens.insertRow(screens.getHeight()); + + // get array size + const SInt32 w = screens.getWidth(); + const SInt32 h = screens.getHeight(); + + // construct reply + reply.m_body += s_editMapProlog1; + s << w << "x" << h; + reply.m_body += s.str(); + reply.m_body += s_editMapProlog2; + + // add screen map for editing + const CString primaryName = m_server->getPrimaryScreenName(); + reply.m_body += s_editMapTableProlog; + for (SInt32 y = 0; y < h; ++y) { + reply.m_body += s_editMapRowProlog; + for (SInt32 x = 0; x < w; ++x) { + s.str(""); + if (!screens.isAllowed(x, y) && screens.get(x, y) != primaryName) { + s << s_editMapScreenDummy; + } + else { + if (!screens.isSet(x, y)) { + s << s_editMapScreenDead; + } + else if (screens.get(x, y) == primaryName) { + s << s_editMapScreenPrimary; + } + else { + s << s_editMapScreenLive; + } + s << screens.get(x, y) << + s_editMapScreenLiveDead1 << + "n" << x << "x" << y << + s_editMapScreenLiveDead2; + } + s << s_editMapScreenEnd; + reply.m_body += s.str(); + } + reply.m_body += s_editMapRowEpilog; + } + reply.m_body += s_editMapTableEpilog; + + reply.m_body += s_editMapEpilog; +} + +void +CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) +{ + typedef std::vector ScreenArray; + typedef std::set ScreenSet; + + // parse the result + CHTTPProtocol::CFormParts parts; + if (!CHTTPProtocol::parseFormData(request, parts)) { + LOG((CLOG_WARN "editmap: cannot parse form data")); + throw XHTTP(400); + } + + try { + std::ostringstream s; + + // convert post data into a temporary screen map. also check + // that no screen name is invalid or used more than once. + SInt32 w, h; + CHTTPProtocol::CFormParts::iterator index = parts.find("size"); + if (index == parts.end() || + !parseXY(index->second, w, h) || + w <= 0 || h <= 0) { + LOG((CLOG_WARN "editmap: cannot parse size or size is invalid")); + throw XHTTP(400); + } + ScreenSet screenNames; + CScreenArray screens; + screens.resize(w, h); + for (SInt32 y = 0; y < h; ++y) { + for (SInt32 x = 0; x < w; ++x) { + // find part + s.str(""); + s << "n" << x << "x" << y; + index = parts.find(s.str()); + if (index == parts.end()) { + // FIXME -- screen is missing. error? + continue; + } + + // skip blank names + const CString& name = index->second; + if (name.empty()) { + continue; + } + + // check name. name must be legal and must not have + // already been seen. + if (screenNames.count(name)) { + // FIXME -- better error message + LOG((CLOG_WARN "editmap: duplicate name %s", name.c_str())); + throw XHTTP(400); + } + // FIXME -- check that name is legal + + // save name. if we've already seen the name then + // report an error. + screens.set(x, y, name); + screenNames.insert(name); + } + } + + // if new map is invalid then return error. map is invalid if: + // there are no screens, or + // the screens are not 4-connected. + if (screenNames.empty()) { + // no screens + // FIXME -- need better no screens + LOG((CLOG_WARN "editmap: no screens")); + throw XHTTP(400); + } + if (!screens.isValid()) { + // FIXME -- need better unconnected screens error + LOG((CLOG_WARN "editmap: unconnected screens")); + throw XHTTP(400); + } + + // convert temporary screen map into a regular map + CConfig config; + m_server->getConfig(&config); + screens.convertTo(config); + + // set new screen map on server + m_server->setConfig(config); + + // now reply with current map + doProcessGetEditMap(request, reply); + } + catch (XHTTP&) { + // FIXME -- construct a more meaningful error? + throw; + } +} + +bool +CHTTPServer::parseXY(const CString& xy, SInt32& x, SInt32& y) +{ + std::istringstream s(xy); + char delimiter; + s >> x; + s.get(delimiter); + s >> y; + return (!!s && delimiter == 'x'); +} + + +// +// CHTTPServer::CScreenArray +// + +CHTTPServer::CScreenArray::CScreenArray() : + m_w(0), + m_h(0) +{ + // do nothing +} + +CHTTPServer::CScreenArray::~CScreenArray() +{ + // do nothing +} + +void +CHTTPServer::CScreenArray::resize(SInt32 w, SInt32 h) +{ + m_screens.clear(); + m_screens.resize(w * h); + m_w = w; + m_h = h; +} + +void +CHTTPServer::CScreenArray::insertRow(SInt32 i) +{ + assert(i >= 0 && i <= m_h); + + CNames newScreens; + newScreens.resize(m_w * (m_h + 1)); + + for (SInt32 y = 0; y < i; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * m_w] = m_screens[x + y * m_w]; + } + } + for (SInt32 y = i; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + (y + 1) * m_w] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + ++m_h; +} + +void +CHTTPServer::CScreenArray::insertColumn(SInt32 i) +{ + assert(i >= 0 && i <= m_w); + + CNames newScreens; + newScreens.resize((m_w + 1) * m_h); + + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < i; ++x) { + newScreens[x + y * (m_w + 1)] = m_screens[x + y * m_w]; + } + for (SInt32 x = i; x < m_w; ++x) { + newScreens[(x + 1) + y * (m_w + 1)] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + ++m_w; +} + +void +CHTTPServer::CScreenArray::eraseRow(SInt32 i) +{ + assert(i >= 0 && i < m_h); + + CNames newScreens; + newScreens.resize(m_w * (m_h - 1)); + + for (SInt32 y = 0; y < i; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * m_w] = m_screens[x + y * m_w]; + } + } + for (SInt32 y = i + 1; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + --m_h; +} + +void +CHTTPServer::CScreenArray::eraseColumn(SInt32 i) +{ + assert(i >= 0 && i < m_w); + + CNames newScreens; + newScreens.resize((m_w - 1) * m_h); + + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * (m_w - 1)] = m_screens[x + y * m_w]; + } + for (SInt32 x = i + 1; x < m_w; ++x) { + newScreens[(x - 1) + y * (m_w - 1)] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + --m_w; +} + +void +CHTTPServer::CScreenArray::rotateRows(SInt32 i) +{ + // nothing to do if no rows + if (m_h == 0) { + return; + } + + // convert to canonical form + if (i < 0) { + i = m_h - ((-i) % m_h); + } + else { + i %= m_h; + } + if (i == 0 || i == m_h) { + return; + } + + while (i > 0) { + // rotate one row + for (SInt32 x = 0; x < m_w; ++x) { + CString tmp = m_screens[x]; + for (SInt32 y = 1; y < m_h; ++y) { + m_screens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; + } + m_screens[x + (m_h - 1) * m_w] = tmp; + } + } +} + +void +CHTTPServer::CScreenArray::rotateColumns(SInt32 i) +{ + // nothing to do if no columns + if (m_h == 0) { + return; + } + + // convert to canonical form + if (i < 0) { + i = m_w - ((-i) % m_w); + } + else { + i %= m_w; + } + if (i == 0 || i == m_w) { + return; + } + + while (i > 0) { + // rotate one column + for (SInt32 y = 0; y < m_h; ++y) { + CString tmp = m_screens[0 + y * m_w]; + for (SInt32 x = 1; x < m_w; ++x) { + m_screens[x - 1 + y * m_w] = m_screens[x + y * m_w]; + } + m_screens[m_w - 1 + y * m_w] = tmp; + } + } +} + +void +CHTTPServer::CScreenArray::remove(SInt32 x, SInt32 y) +{ + set(x, y, CString()); +} + +void +CHTTPServer::CScreenArray::set(SInt32 x, SInt32 y, const CString& name) +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + m_screens[x + y * m_w] = name; +} + +bool +CHTTPServer::CScreenArray::isAllowed(SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + if (x > 0 && !m_screens[(x - 1) + y * m_w].empty()) { + return true; + } + if (x < m_w - 1 && !m_screens[(x + 1) + y * m_w].empty()) { + return true; + } + if (y > 0 && !m_screens[x + (y - 1) * m_w].empty()) { + return true; + } + if (y < m_h - 1 && !m_screens[x + (y + 1) * m_w].empty()) { + return true; + } + return false; +} + +bool +CHTTPServer::CScreenArray::isSet(SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + return !m_screens[x + y * m_w].empty(); +} + +CString +CHTTPServer::CScreenArray::get(SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + return m_screens[x + y * m_w]; +} + +bool +CHTTPServer::CScreenArray::find(const CString& name, + SInt32& xOut, SInt32& yOut) const +{ + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (m_screens[x + y * m_w] == name) { + xOut = x; + yOut = y; + return true; + } + } + } + return false; +} + +bool +CHTTPServer::CScreenArray::isValid() const +{ + SInt32 count = 0, isolated = 0; + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (isSet(x, y)) { + ++count; + if (!isAllowed(x, y)) { + ++isolated; + } + } + } + } + return (count <= 1 || isolated == 0); +} + +bool +CHTTPServer::CScreenArray::convertFrom(const CConfig& config) +{ + typedef std::set ScreenSet; + + // insert the first screen + CConfig::const_iterator index = config.begin(); + if (index == config.end()) { + // no screens + resize(0, 0); + return true; + } + CString name = *index; + resize(1, 1); + set(0, 0, name); + + // flood fill state + CNames screenStack; + ScreenSet doneSet; + + // put all but the first screen on the stack + // note -- if all screens are 4-connected then we can skip this + while (++index != config.end()) { + screenStack.push_back(*index); + } + + // put the first screen on the stack last so we process it first + screenStack.push_back(name); + + // perform a flood fill using the stack as the seeds + while (!screenStack.empty()) { + // get next screen from stack + CString name = screenStack.back(); + screenStack.pop_back(); + + // skip screen if we've seen it before + if (doneSet.count(name) > 0) { + continue; + } + + // add this screen to doneSet so we don't process it again + doneSet.insert(name); + + // find the screen. if it's not found then not all of the + // screens are 4-connected. discard disconnected screens. + SInt32 x, y; + if (!find(name, x, y)) { + continue; + } + + // insert the screen's neighbors + // FIXME -- handle edge wrapping + CString neighbor; + neighbor = config.getNeighbor(name, kLeft); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert left neighbor, adding a column if necessary + if (x == 0 || get(x - 1, y) != neighbor) { + ++x; + insertColumn(x - 1); + set(x - 1, y, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = config.getNeighbor(name, kRight); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert right neighbor, adding a column if necessary + if (x == m_w - 1 || get(x + 1, y) != neighbor) { + insertColumn(x + 1); + set(x + 1, y, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = config.getNeighbor(name, kTop); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert top neighbor, adding a row if necessary + if (y == 0 || get(x, y - 1) != neighbor) { + ++y; + insertRow(y - 1); + set(x, y - 1, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = config.getNeighbor(name, kBottom); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert bottom neighbor, adding a row if necessary + if (y == m_h - 1 || get(x, y + 1) != neighbor) { + insertRow(y + 1); + set(x, y + 1, neighbor); + } + screenStack.push_back(neighbor); + } + } + + // check symmetry + // FIXME -- handle edge wrapping + for (index = config.begin(); index != config.end(); ++index) { + const CString& name = *index; + SInt32 x, y; + if (!find(name, x, y)) { + return false; + } + + CString neighbor; + neighbor = config.getNeighbor(name, kLeft); + if ((x == 0 && !neighbor.empty()) || + (x > 0 && get(x - 1, y) != neighbor)) { + return false; + } + + neighbor = config.getNeighbor(name, kRight); + if ((x == m_w - 1 && !neighbor.empty()) || + (x < m_w - 1 && get(x + 1, y) != neighbor)) { + return false; + } + + neighbor = config.getNeighbor(name, kTop); + if ((y == 0 && !neighbor.empty()) || + (y > 0 && get(x, y - 1) != neighbor)) { + return false; + } + + neighbor = config.getNeighbor(name, kBottom); + if ((y == m_h - 1 && !neighbor.empty()) || + (y < m_h - 1 && get(x, y + 1) != neighbor)) { + return false; + } + } + + return true; +} + +void +CHTTPServer::CScreenArray::convertTo(CConfig& config) const +{ + config.removeAllScreens(); + + // add screens and find smallest box containing all screens + SInt32 x0 = m_w, x1 = 0, y0 = m_h, y1 = 0; + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (isSet(x, y)) { + config.addScreen(get(x, y)); + if (x < x0) { + x0 = x; + } + if (x > x1) { + x1 = x; + } + if (y < y0) { + y0 = y; + } + if (y > y1) { + y1 = y; + } + } + + } + } + + // make connections between screens + // FIXME -- add support for wrapping + // FIXME -- mark topmost and leftmost screens + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (!isSet(x, y)) { + continue; + } + if (x > x0 && isSet(x - 1, y)) { + config.connect(get(x, y), kLeft, get(x - 1, y)); + } + if (x < x1 && isSet(x + 1, y)) { + config.connect(get(x, y), kRight, get(x + 1, y)); + } + if (y > y0 && isSet(x, y - 1)) { + config.connect(get(x, y), kTop, get(x, y - 1)); + } + if (y < y1 && isSet(x, y + 1)) { + config.connect(get(x, y), kBottom, get(x, y + 1)); + } + } + } +} diff --git a/lib/server/CHTTPServer.h b/lib/server/CHTTPServer.h new file mode 100644 index 0000000..18f8044 --- /dev/null +++ b/lib/server/CHTTPServer.h @@ -0,0 +1,142 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CHTTPSERVER_H +#define CHTTPSERVER_H + +#include "CString.h" +#include "BasicTypes.h" +#include "stdvector.h" + +class CServer; +class CConfig; +class CHTTPRequest; +class CHTTPReply; +class IDataSocket; + +//! Simple HTTP server +/*! +This class implements a simple HTTP server for interacting with the +synergy server. +*/ +class CHTTPServer { +public: + CHTTPServer(CServer*); + virtual ~CHTTPServer(); + + //! @name manipulators + //@{ + + //! Process HTTP request + /*! + Synchronously processes an HTTP request on the given socket. + */ + void processRequest(IDataSocket*); + + //@} + +protected: + //! Process HTTP request + /*! + Processes a successfully read HTTP request. The reply is partially + filled in (version, method, status (200) and reason (OK)). This + method checks the URI and handles the request, filling in the rest + of the reply. If the request cannot be satisfied it throws an + appropriate XHTTP exception. + */ + virtual void doProcessRequest(CHTTPRequest&, CHTTPReply&); + + //! Process request for map + virtual void doProcessGetEditMap(CHTTPRequest&, CHTTPReply&); + + //! Process request for changing map + virtual void doProcessPostEditMap(CHTTPRequest&, CHTTPReply&); + + //! Parse coordinate string + static bool parseXY(const CString&, SInt32& x, SInt32& y); + + //! Screen map helper + /*! + This class represents the screen map as a resizable array. It's + used to handle map requests. + */ + class CScreenArray { + public: + CScreenArray(); + ~CScreenArray(); + + // resize the array. this also clears all the elements. + void resize(SInt32 w, SInt32 h); + + // insert/remove a row/column. all elements in a new row/column + // are unset. + void insertRow(SInt32 insertedBeforeRow); + void insertColumn(SInt32 insertedBeforeColumn); + void eraseRow(SInt32 row); + void eraseColumn(SInt32 column); + + // rotate rows or columns + void rotateRows(SInt32 rowsDown); + void rotateColumns(SInt32 columnsDown); + + // remove/set a screen name. setting an empty name is the + // same as removing a name. names are not checked for + // validity. + void remove(SInt32 x, SInt32 y); + void set(SInt32 x, SInt32 y, const CString&); + + // convert a CConfig to a CScreenArray. returns true iff + // all connections are symmetric and therefore exactly + // representable by a CScreenArray. + bool convertFrom(const CConfig&); + + // accessors + + // get the array size + SInt32 getWidth() const { return m_w; } + SInt32 getHeight() const { return m_h; } + + // returns true iff the cell has a 4-connected neighbor + bool isAllowed(SInt32 x, SInt32 y) const; + + // returns true iff the cell has a (non-empty) name + bool isSet(SInt32 x, SInt32 y) const; + + // get a screen name + CString get(SInt32 x, SInt32 y) const; + + // find a screen by name. returns true iff found. + bool find(const CString&, SInt32& x, SInt32& y) const; + + // return true iff the overall array is valid. that means + // just zero or one screen or all screens are 4-connected + // to other screens. + bool isValid() const; + + // convert this to a CConfig + void convertTo(CConfig&) const; + + private: + typedef std::vector CNames; + + SInt32 m_w, m_h; + CNames m_screens; + }; + +private: + CServer* m_server; + static const UInt32 s_maxRequestSize; +}; + +#endif diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp new file mode 100644 index 0000000..b425a68 --- /dev/null +++ b/lib/server/CPrimaryClient.cpp @@ -0,0 +1,295 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CPrimaryClient.h" +#include "IPrimaryScreenFactory.h" +#include "IServer.h" +#include "XScreen.h" +#include "XSynergy.h" +#include "CPrimaryScreen.h" +#include "CClipboard.h" +#include "CLog.h" + +// +// CPrimaryClient +// + +CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, + IServer* server, + IPrimaryScreenReceiver* receiver, + const CString& name) : + m_server(server), + m_name(name), + m_seqNum(0) +{ + assert(m_server != NULL); + + // create screen + LOG((CLOG_DEBUG1 "creating primary screen")); + if (screenFactory != NULL) { + m_screen = screenFactory->create(this, receiver); + } + if (m_screen == NULL) { + throw XScreenOpenFailure(); + } +} + +CPrimaryClient::~CPrimaryClient() +{ + LOG((CLOG_DEBUG1 "destroying primary screen")); + delete m_screen; +} + +void +CPrimaryClient::exitMainLoop() +{ + m_screen->exitMainLoop(); +} + +void +CPrimaryClient::reconfigure(UInt32 activeSides) +{ + m_screen->reconfigure(activeSides); +} + +UInt32 +CPrimaryClient::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + +void +CPrimaryClient::getClipboard(ClipboardID id, CString& data) const +{ + CClipboard clipboard; + m_screen->getClipboard(id, &clipboard); + data = clipboard.marshall(); +} + +bool +CPrimaryClient::isLockedToScreen() const +{ + return m_screen->isLockedToScreen(); +} + +KeyModifierMask +CPrimaryClient::getToggleMask() const +{ + return m_screen->getToggleMask(); +} + +void +CPrimaryClient::onError() +{ + // forward to server + m_server->onError(); +} + +void +CPrimaryClient::onInfoChanged(const CClientInfo& info) +{ + m_info = info; + try { + m_server->onInfoChanged(getName(), m_info); + } + catch (XBadClient&) { + // ignore + } +} + +bool +CPrimaryClient::onGrabClipboard(ClipboardID id) +{ + try { + return m_server->onGrabClipboard(getName(), id, m_seqNum); + } + catch (XBadClient&) { + return false; + } +} + +void +CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) +{ + m_server->onClipboardChanged(id, m_seqNum, data); +} + +void +CPrimaryClient::open() +{ + // all clipboards are clean + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardDirty[i] = false; + } + + // now open the screen + m_screen->open(); +} + +void +CPrimaryClient::mainLoop() +{ + m_screen->mainLoop(); +} + +void +CPrimaryClient::close() +{ + m_screen->close(); +} + +void +CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask, bool screensaver) +{ + // note -- we must not call any server methods except onError(). + m_seqNum = seqNum; + m_screen->enter(xAbs, yAbs, screensaver); +} + +bool +CPrimaryClient::leave() +{ + // note -- we must not call any server methods except onError(). + return m_screen->leave(); +} + +void +CPrimaryClient::setClipboard(ClipboardID id, const CString& data) +{ + // note -- we must not call any server methods except onError(). + + // ignore if this clipboard is already clean + if (m_clipboardDirty[id]) { + // this clipboard is now clean + m_clipboardDirty[id] = false; + + // unmarshall data + CClipboard clipboard; + clipboard.unmarshall(data, 0); + + // set clipboard + m_screen->setClipboard(id, &clipboard); + } +} + +void +CPrimaryClient::grabClipboard(ClipboardID id) +{ + // grab clipboard + m_screen->grabClipboard(id); + + // clipboard is dirty (because someone else owns it now) + m_clipboardDirty[id] = true; +} + +void +CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty) +{ + m_clipboardDirty[id] = dirty; +} + +void +CPrimaryClient::keyDown(KeyID, KeyModifierMask, KeyButton) +{ + // ignore +} + +void +CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton) +{ + // ignore +} + +void +CPrimaryClient::keyUp(KeyID, KeyModifierMask, KeyButton) +{ + // ignore +} + +void +CPrimaryClient::mouseDown(ButtonID) +{ + // ignore +} + +void +CPrimaryClient::mouseUp(ButtonID) +{ + // ignore +} + +void +CPrimaryClient::mouseMove(SInt32 x, SInt32 y) +{ + m_screen->warpCursor(x, y); +} + +void +CPrimaryClient::mouseWheel(SInt32) +{ + // ignore +} + +void +CPrimaryClient::screensaver(bool) +{ + // ignore +} + +void +CPrimaryClient::resetOptions() +{ + m_screen->resetOptions(); +} + +void +CPrimaryClient::setOptions(const COptionsList& options) +{ + m_screen->setOptions(options); +} + +CString +CPrimaryClient::getName() const +{ + return m_name; +} + +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_info.m_zoneSize; +} + +void +CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + x = m_info.m_x; + y = m_info.m_y; + w = m_info.m_w; + h = m_info.m_h; +} + +void +CPrimaryClient::getCursorPos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = m_info.m_mx; + y = m_info.m_my; +} diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h new file mode 100644 index 0000000..d0202cf --- /dev/null +++ b/lib/server/CPrimaryClient.h @@ -0,0 +1,137 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPRIMARYCLIENT_H +#define CPRIMARYCLIENT_H + +#include "IClient.h" +#include "IScreenReceiver.h" +#include "ProtocolTypes.h" + +class IClipboard; +class CPrimaryScreen; +class IPrimaryScreenFactory; +class IPrimaryScreenReceiver; +class IServer; + +//! Primary screen as pseudo-client +/*! +The primary screen does not have a client associated with it. This +class provides a pseudo-client to allow the primary screen to be +treated as if it was on a client. +*/ +class CPrimaryClient : public IScreenReceiver, public IClient { +public: + /*! + \c name is the name of the server. The caller retains ownership of + \c factory. Throws XScreenOpenFailure or whatever the factory can + throw if the screen cannot be created. + */ + CPrimaryClient(IPrimaryScreenFactory* factory, IServer*, + IPrimaryScreenReceiver*, const CString& name); + ~CPrimaryClient(); + + //! @name manipulators + //@{ + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + void exitMainLoop(); + + //! Update configuration + /*! + Handles reconfiguration of jump zones. + */ + void reconfigure(UInt32 activeSides); + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + + //@} + //! @name accessors + //@{ + + //! Get clipboard + /*! + Save the marshalled contents of the clipboard indicated by \c id. + */ + void getClipboard(ClipboardID, CString&) const; + + //! Get toggle key state + /*! + Returns the primary screen's current toggle modifier key state. + */ + KeyModifierMask getToggleMask() const; + + //! Get screen lock state + /*! + Returns true if the user is locked to the screen. + */ + bool isLockedToScreen() const; + + //@} + + // IScreenReceiver overrides + virtual void onError(); + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString&); + + // IClient overrides + virtual void open(); + virtual void mainLoop(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; + +private: + IServer* m_server; + CPrimaryScreen* m_screen; + CString m_name; + UInt32 m_seqNum; + CClientInfo m_info; + bool m_clipboardDirty[kClipboardEnd]; +}; + +#endif diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp new file mode 100644 index 0000000..fa2487d --- /dev/null +++ b/lib/server/CServer.cpp @@ -0,0 +1,2141 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServer.h" +#include "CHTTPServer.h" +#include "CPrimaryClient.h" +#include "IPrimaryScreenFactory.h" +#include "CInputPacketStream.h" +#include "COutputPacketStream.h" +#include "CProtocolUtil.h" +#include "CClientProxy1_0.h" +#include "CClientProxy1_1.h" +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "XScreen.h" +#include "XSynergy.h" +#include "CTCPListenSocket.h" +#include "IDataSocket.h" +#include "ISocketFactory.h" +#include "XSocket.h" +#include "IStreamFilterFactory.h" +#include "CLock.h" +#include "CThread.h" +#include "CTimerThread.h" +#include "XMT.h" +#include "XThread.h" +#include "CFunctionJob.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CServer +// + +const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; + +CServer::CServer(const CString& serverName) : + m_name(serverName), + m_error(false), + m_bindTimeout(5.0 * 60.0), + m_screenFactory(NULL), + m_socketFactory(NULL), + m_streamFilterFactory(NULL), + m_acceptClientThread(NULL), + m_active(NULL), + m_primaryClient(NULL), + m_seqNum(0), + m_activeSaver(NULL), + m_httpServer(NULL), + m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests), + m_switchDir(kNoDirection), + m_switchScreen(NULL), + m_switchWaitDelay(0.0), + m_switchWaitEngaged(false), + m_switchTwoTapDelay(0.0), + m_switchTwoTapEngaged(false), + m_switchTwoTapArmed(false), + m_switchTwoTapZone(3), + m_status(kNotRunning) +{ + // do nothing +} + +CServer::~CServer() +{ + delete m_screenFactory; + delete m_socketFactory; + delete m_streamFilterFactory; +} + +void +CServer::open() +{ + // open the screen + try { + LOG((CLOG_INFO "opening screen")); + openPrimaryScreen(); + setStatus(kNotRunning); + } + catch (XScreen& e) { + // can't open screen + setStatus(kError, e.what()); + LOG((CLOG_INFO "failed to open screen")); + throw; + } + catch (XUnknownClient& e) { + // can't open screen + setStatus(kServerNameUnknown); + LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); + throw; + } +} + +void +CServer::mainLoop() +{ + // check preconditions + { + CLock lock(&m_mutex); + assert(m_primaryClient != NULL); + } + + try { + setStatus(kNotRunning); + LOG((CLOG_NOTE "starting server")); + + // start listening for new clients + m_acceptClientThread = new CThread(startThread( + new TMethodJob(this, + &CServer::acceptClients))); + + // start listening for HTTP requests + if (m_config.getHTTPAddress().isValid()) { + m_httpServer = new CHTTPServer(this); + startThread(new TMethodJob(this, + &CServer::acceptHTTPClients)); + } + + // handle events + m_primaryClient->mainLoop(); + + // clean up + LOG((CLOG_NOTE "stopping server")); + + // use a macro to write the stuff that should go into a finally + // block so we can repeat it easily. stroustrup's view that + // "resource acquistion is initialization" is a better solution + // than a finally block is parochial. they both have their + // place. adding finally to C++ would've been a drop in a big + // bucket. +#define FINALLY do { \ + stopThreads(); \ + delete m_httpServer; \ + m_httpServer = NULL; \ + runStatusJobs(); \ + } while (false) + FINALLY; + } + catch (XMT& e) { + LOG((CLOG_ERR "server error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + throw; + } + catch (XBase& e) { + LOG((CLOG_ERR "server error: %s", e.what())); + setStatus(kError, e.what()); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + } + catch (XThread&) { + setStatus(kNotRunning); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + throw; + } + catch (...) { + LOG((CLOG_DEBUG "unknown server error")); + setStatus(kError); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + throw; + } +#undef FINALLY + + // throw if there was an error + if (m_error) { + LOG((CLOG_DEBUG "forwarding child thread exception")); + throw XServerRethrow(); + } +} + +void +CServer::exitMainLoop() +{ + m_primaryClient->exitMainLoop(); +} + +void +CServer::exitMainLoopWithError() +{ + { + CLock lock(&m_mutex); + m_error = true; + } + exitMainLoop(); +} + +void +CServer::close() +{ + if (m_primaryClient != NULL) { + closePrimaryScreen(); + } + LOG((CLOG_INFO "closed screen")); +} + +bool +CServer::setConfig(const CConfig& config) +{ + // refuse configuration if it doesn't include the primary screen + { + CLock lock(&m_mutex); + if (m_primaryClient != NULL && + !config.isScreen(m_primaryClient->getName())) { + return false; + } + } + + // close clients that are connected but being dropped from the + // configuration. + closeClients(config); + + // cut over + CLock lock(&m_mutex); + m_config = config; + + // process global options + const CConfig::CScreenOptions* options = m_config.getOptions(""); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + const OptionID id = index->first; + const OptionValue value = index->second; + if (id == kOptionScreenSwitchDelay) { + m_switchWaitDelay = 1.0e-3 * static_cast(value); + if (m_switchWaitDelay < 0.0) { + m_switchWaitDelay = 0.0; + } + m_switchWaitEngaged = false; + } + else if (id == kOptionScreenSwitchTwoTap) { + m_switchTwoTapDelay = 1.0e-3 * static_cast(value); + if (m_switchTwoTapDelay < 0.0) { + m_switchTwoTapDelay = 0.0; + } + m_switchTwoTapEngaged = false; + } + } + } + + // tell primary screen about reconfiguration + if (m_primaryClient != NULL) { + m_primaryClient->reconfigure(getActivePrimarySides()); + } + + // tell all (connected) clients about current options + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + sendOptions(client); + } + + // notify of status + runStatusJobs(); + + return true; +} + +void +CServer::setScreenFactory(IPrimaryScreenFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_screenFactory; + m_screenFactory = adopted; +} + +void +CServer::setSocketFactory(ISocketFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_socketFactory; + m_socketFactory = adopted; +} + +void +CServer::setStreamFilterFactory(IStreamFilterFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_streamFilterFactory; + m_streamFilterFactory = adopted; +} + +void +CServer::addStatusJob(IJob* job) +{ + m_statusJobs.addJob(job); +} + +void +CServer::removeStatusJob(IJob* job) +{ + m_statusJobs.removeJob(job); +} + +CString +CServer::getPrimaryScreenName() const +{ + return m_name; +} + +UInt32 +CServer::getNumClients() const +{ + CLock lock(&m_mutex); + return m_clients.size(); +} + +void +CServer::getClients(std::vector& list) const +{ + CLock lock(&m_mutex); + list.clear(); + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + list.push_back(index->first); + } +} + +CServer::EStatus +CServer::getStatus(CString* msg) const +{ + CLock lock(&m_mutex); + if (msg != NULL) { + *msg = m_statusMessage; + } + return m_status; +} + +void +CServer::getConfig(CConfig* config) const +{ + assert(config != NULL); + + CLock lock(&m_mutex); + *config = m_config; +} + +void +CServer::runStatusJobs() const +{ + m_statusJobs.runJobs(); +} + +void +CServer::setStatus(EStatus status, const char* msg) +{ + { + CLock lock(&m_mutex); + m_status = status; + if (m_status == kError) { + m_statusMessage = (msg == NULL) ? "Error" : msg; + } + else { + m_statusMessage = (msg == NULL) ? "" : msg; + } + } + runStatusJobs(); +} + +UInt32 +CServer::getActivePrimarySides() const +{ + // note -- m_mutex must be locked on entry + UInt32 sides = 0; + if (!m_config.getNeighbor(getPrimaryScreenName(), kLeft).empty()) { + sides |= kLeftMask; + } + if (!m_config.getNeighbor(getPrimaryScreenName(), kRight).empty()) { + sides |= kRightMask; + } + if (!m_config.getNeighbor(getPrimaryScreenName(), kTop).empty()) { + sides |= kTopMask; + } + if (!m_config.getNeighbor(getPrimaryScreenName(), kBottom).empty()) { + sides |= kBottomMask; + } + return sides; +} + +void +CServer::onError() +{ + setStatus(kError); + + // stop all running threads but don't wait too long since some + // threads may be unable to proceed until this thread returns. + stopThreads(3.0); + + // done with the HTTP server + CLock lock(&m_mutex); + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen +} + +void +CServer::onInfoChanged(const CString& name, const CClientInfo& info) +{ + CLock lock(&m_mutex); + + // look up client + CClientList::iterator index = m_clients.find(name); + if (index == m_clients.end()) { + throw XBadClient(); + } + IClient* client = index->second; + assert(client != NULL); + + // update the remote mouse coordinates + if (client == m_active) { + m_x = info.m_mx; + m_y = info.m_my; + } + LOG((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); + + // handle resolution change to primary screen + if (client == m_primaryClient) { + if (client == m_active) { + onMouseMovePrimaryNoLock(m_x, m_y); + } + else { + onMouseMoveSecondaryNoLock(0, 0); + } + } +} + +bool +CServer::onGrabClipboard(const CString& name, ClipboardID id, UInt32 seqNum) +{ + CLock lock(&m_mutex); + + // screen must be connected + CClientList::iterator grabber = m_clients.find(name); + if (grabber == m_clients.end()) { + throw XBadClient(); + } + + // ignore grab if sequence number is old. always allow primary + // screen to grab. + CClipboardInfo& clipboard = m_clipboards[id]; + if (name != m_primaryClient->getName() && + seqNum < clipboard.m_clipboardSeqNum) { + LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id)); + return false; + } + + // mark screen as owning clipboard + LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = name; + clipboard.m_clipboardSeqNum = seqNum; + + // clear the clipboard data (since it's not known at this point) + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + + // tell all other screens to take ownership of clipboard. tell the + // grabber that it's clipboard isn't dirty. + for (CClientList::iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + if (index == grabber) { + client->setClipboardDirty(id, false); + } + else { + client->grabClipboard(id); + } + } + + return true; +} + +void +CServer::onClipboardChanged(ClipboardID id, UInt32 seqNum, const CString& data) +{ + CLock lock(&m_mutex); + onClipboardChangedNoLock(id, seqNum, data); +} + +void +CServer::onClipboardChangedNoLock(ClipboardID id, + UInt32 seqNum, const CString& data) +{ + CClipboardInfo& clipboard = m_clipboards[id]; + + // ignore update if sequence number is old + if (seqNum < clipboard.m_clipboardSeqNum) { + LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); + return; + } + + // ignore if data hasn't changed + if (data == clipboard.m_clipboardData) { + LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); + return; + } + + // unmarshall into our clipboard buffer + LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + clipboard.m_clipboardData = data; + clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); + + // tell all clients except the sender that the clipboard is dirty + CClientList::const_iterator sender = + m_clients.find(clipboard.m_clipboardOwner); + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->setClipboardDirty(id, index != sender); + } + + // send the new clipboard to the active screen + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); +} + +void +CServer::onScreensaver(bool activated) +{ + LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); + CLock lock(&m_mutex); + + if (activated) { + // save current screen and position + m_activeSaver = m_active; + m_xSaver = m_x; + m_ySaver = m_y; + + // jump to primary screen + if (m_active != m_primaryClient) { + switchScreen(m_primaryClient, 0, 0, true); + } + } + else { + // jump back to previous screen and position. we must check + // that the position is still valid since the screen may have + // changed resolutions while the screen saver was running. + if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { + // check position + IClient* screen = m_activeSaver; + SInt32 x, y, w, h; + screen->getShape(x, y, w, h); + SInt32 zoneSize = screen->getJumpZoneSize(); + if (m_xSaver < x + zoneSize) { + m_xSaver = x + zoneSize; + } + else if (m_xSaver >= x + w - zoneSize) { + m_xSaver = x + w - zoneSize - 1; + } + if (m_ySaver < y + zoneSize) { + m_ySaver = y + zoneSize; + } + else if (m_ySaver >= y + h - zoneSize) { + m_ySaver = y + h - zoneSize - 1; + } + + // jump + switchScreen(screen, m_xSaver, m_ySaver, false); + } + + // reset state + m_activeSaver = NULL; + } + + // send message to all clients + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->screensaver(activated); + } +} + +void +CServer::onOneShotTimerExpired(UInt32 id) +{ + CLock lock(&m_mutex); + + // ignore if it's an old timer or if switch wait isn't engaged anymore + if (!m_switchWaitEngaged || id != m_switchWaitTimer) { + return; + } + + // ignore if mouse is locked to screen + if (isLockedToScreenNoLock()) { + LOG((CLOG_DEBUG1 "locked to screen")); + clearSwitchState(); + return; + } + + // switch screen + switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false); +} + +void +CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, true)) { + return; + } + + // relay + m_active->keyDown(id, mask, button); +} + +void +CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + return; + } + + // relay + m_active->keyUp(id, mask, button); +} + +void +CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + onCommandKey(id, mask, true); + return; + } + + // relay + m_active->keyRepeat(id, mask, count, button); +} + +void +CServer::onMouseDown(ButtonID id) +{ + LOG((CLOG_DEBUG1 "onMouseDown id=%d", id)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // relay + m_active->mouseDown(id); +} + +void +CServer::onMouseUp(ButtonID id) +{ + LOG((CLOG_DEBUG1 "onMouseUp id=%d", id)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // relay + m_active->mouseUp(id); +} + +bool +CServer::onMouseMovePrimary(SInt32 x, SInt32 y) +{ + LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); + CLock lock(&m_mutex); + return onMouseMovePrimaryNoLock(x, y); +} + +bool +CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) +{ + // mouse move on primary (server's) screen + assert(m_primaryClient != NULL); + assert(m_active == m_primaryClient); + + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + SInt32 zoneSize = m_active->getJumpZoneSize(); + + // see if we should change screens + EDirection dir; + if (x < ax + zoneSize) { + x -= zoneSize; + dir = kLeft; + } + else if (x >= ax + aw - zoneSize) { + x += zoneSize; + dir = kRight; + } + else if (y < ay + zoneSize) { + y -= zoneSize; + dir = kTop; + } + else if (y >= ay + ah - zoneSize) { + y += zoneSize; + dir = kBottom; + } + else { + // still on local screen. check if we're inside the tap region. + SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? + m_switchTwoTapZone : zoneSize; + bool inTapZone = (x < ax + tapZone || + x >= ax + aw - tapZone || + y < ay + tapZone || + y >= ay + ah - tapZone); + + // failed to switch + onNoSwitch(inTapZone); + return false; + } + + // get jump destination + IClient* newScreen = getNeighbor(m_active, dir, x, y); + + // should we switch or not? + if (isSwitchOkay(newScreen, dir, x, y)) { + // switch screen + switchScreen(newScreen, x, y, false); + return true; + } + else { + return false; + } +} + +void +CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) +{ + LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); + CLock lock(&m_mutex); + onMouseMoveSecondaryNoLock(dx, dy); +} + +void +CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) +{ + // mouse move on secondary (client's) screen + assert(m_active != NULL); + if (m_active == m_primaryClient) { + // we're actually on the primary screen. this can happen + // when the primary screen begins processing a mouse move + // for a secondary screen, then the active (secondary) + // screen disconnects causing us to jump to the primary + // screen, and finally the primary screen finishes + // processing the mouse move, still thinking it's for + // a secondary screen. we just ignore the motion. + return; + } + + // save old position + const SInt32 xOld = m_x; + const SInt32 yOld = m_y; + + // accumulate motion + m_x += dx; + m_y += dy; + + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + + // find direction of neighbor and get the neighbor + bool jump = true; + IClient* newScreen; + do { + EDirection dir; + if (m_x < ax) { + dir = kLeft; + } + else if (m_x > ax + aw - 1) { + dir = kRight; + } + else if (m_y < ay) { + dir = kTop; + } + else if (m_y > ay + ah - 1) { + dir = kBottom; + } + else { + // we haven't left the screen + newScreen = m_active; + jump = false; + + // if waiting and mouse is not on the border we're waiting + // on then stop waiting. also if it's not on the border + // then arm the double tap. + if (m_switchScreen != NULL) { + bool clearWait; + SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); + switch (m_switchDir) { + case kLeft: + clearWait = (m_x >= ax + zoneSize); + break; + + case kRight: + clearWait = (m_x <= ax + aw - 1 - zoneSize); + break; + + case kTop: + clearWait = (m_y >= ay + zoneSize); + break; + + case kBottom: + clearWait = (m_y <= ay + ah - 1 + zoneSize); + break; + + default: + clearWait = false; + break; + } + if (clearWait) { + // still on local screen. check if we're inside the + // tap region. + SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? + m_switchTwoTapZone : zoneSize; + bool inTapZone = (m_x < ax + tapZone || + m_x >= ax + aw - tapZone || + m_y < ay + tapZone || + m_y >= ay + ah - tapZone); + + // failed to switch + onNoSwitch(inTapZone); + } + } + + // skip rest of block + break; + } + + // try to switch screen. get the neighbor. + newScreen = getNeighbor(m_active, dir, m_x, m_y); + + // see if we should switch + if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { + newScreen = m_active; + jump = false; + } + } while (false); + + if (jump) { + // switch screens + switchScreen(newScreen, m_x, m_y, false); + } + else { + // same screen. clamp mouse to edge. + m_x = xOld + dx; + m_y = yOld + dy; + if (m_x < ax) { + m_x = ax; + LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str())); + } + else if (m_x > ax + aw - 1) { + m_x = ax + aw - 1; + LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", m_active->getName().c_str())); + } + if (m_y < ay) { + m_y = ay; + LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", m_active->getName().c_str())); + } + else if (m_y > ay + ah - 1) { + m_y = ay + ah - 1; + LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str())); + } + + // warp cursor if it moved. + if (m_x != xOld || m_y != yOld) { + LOG((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y)); + m_active->mouseMove(m_x, m_y); + } + } +} + +void +CServer::onMouseWheel(SInt32 delta) +{ + LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta)); + CLock lock(&m_mutex); + assert(m_active != NULL); + + // relay + m_active->mouseWheel(delta); +} + +bool +CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) +{ + return false; +} + +bool +CServer::isLockedToScreenNoLock() const +{ + // locked if scroll-lock is toggled on + if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { + LOG((CLOG_DEBUG "locked by ScrollLock")); + return true; + } + + // locked if primary says we're locked + if (m_primaryClient->isLockedToScreen()) { + return true; + } + + // not locked + return false; +} + +void +CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) +{ + // note -- must be locked on entry + + assert(dst != NULL); +#ifndef NDEBUG + { + SInt32 dx, dy, dw, dh; + dst->getShape(dx, dy, dw, dh); + assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh); + } +#endif + assert(m_active != NULL); + + LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); + + // stop waiting to switch + clearSwitchState(); + + // record new position + m_x = x; + m_y = y; + + // wrapping means leaving the active screen and entering it again. + // since that's a waste of time we skip that and just warp the + // mouse. + if (m_active != dst) { + // leave active screen + if (!m_active->leave()) { + // cannot leave screen + LOG((CLOG_WARN "can't leave screen")); + return; + } + + // update the primary client's clipboards if we're leaving the + // primary screen. + if (m_active == m_primaryClient) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + if (clipboard.m_clipboardOwner == m_primaryClient->getName()) { + CString clipboardData; + m_primaryClient->getClipboard(id, clipboardData); + onClipboardChangedNoLock(id, + clipboard.m_clipboardSeqNum, clipboardData); + } + } + } + + // cut over + m_active = dst; + + // increment enter sequence number + ++m_seqNum; + + // enter new screen + m_active->enter(x, y, m_seqNum, + m_primaryClient->getToggleMask(), + forScreensaver); + + // send the clipboard data to new active screen + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); + } + } + else { + m_active->mouseMove(x, y); + } +} + +IClient* +CServer::getNeighbor(IClient* src, EDirection dir) const +{ + // note -- must be locked on entry + + assert(src != NULL); + + // get source screen name + CString srcName = src->getName(); + assert(!srcName.empty()); + LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); + + // get first neighbor. if it's the source then the source jumps + // to itself and we return the source. + CString dstName(m_config.getNeighbor(srcName, dir)); + if (dstName == srcName) { + LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + return src; + } + + // keep checking + for (;;) { + // if nothing in that direction then return NULL. if the + // destination is the source then we can make no more + // progress in this direction. since we haven't found a + // connected neighbor we return NULL. + if (dstName.empty() || dstName == srcName) { + LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); + return NULL; + } + + // look up neighbor cell. if the screen is connected and + // ready then we can stop. + CClientList::const_iterator index = m_clients.find(dstName); + if (index != m_clients.end()) { + LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + return index->second; + } + + // skip over unconnected screen + LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + srcName = dstName; + + // look up name of neighbor of skipped screen + dstName = m_config.getNeighbor(srcName, dir); + } +} + +IClient* +CServer::getNeighbor(IClient* src, + EDirection srcSide, SInt32& x, SInt32& y) const +{ + // note -- must be locked on entry + + assert(src != NULL); + + // get the first neighbor + IClient* dst = getNeighbor(src, srcSide); + if (dst == NULL) { + return NULL; + } + + // get the source screen's size (needed for kRight and kBottom) + SInt32 sx, sy, sw, sh; + SInt32 dx, dy, dw, dh; + IClient* lastGoodScreen = src; + lastGoodScreen->getShape(sx, sy, sw, sh); + lastGoodScreen->getShape(dx, dy, dw, dh); + + // find destination screen, adjusting x or y (but not both). the + // searches are done in a sort of canonical screen space where + // the upper-left corner is 0,0 for each screen. we adjust from + // actual to canonical position on entry to and from canonical to + // actual on exit from the search. + switch (srcSide) { + case kLeft: + x -= dx; + while (dst != NULL) { + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + x += dw; + if (x >= 0) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + x += dx; + break; + + case kRight: + x -= dx; + while (dst != NULL) { + x -= dw; + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + if (x < dw) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + x += dx; + break; + + case kTop: + y -= dy; + while (dst != NULL) { + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + y += dh; + if (y >= 0) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + y += dy; + break; + + case kBottom: + y -= dy; + while (dst != NULL) { + y -= dh; + lastGoodScreen = dst; + lastGoodScreen->getShape(dx, dy, dw, dh); + if (y < sh) { + break; + } + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + assert(lastGoodScreen != NULL); + y += dy; + break; + } + + // save destination screen + assert(lastGoodScreen != NULL); + dst = lastGoodScreen; + + // if entering primary screen then be sure to move in far enough + // to avoid the jump zone. if entering a side that doesn't have + // a neighbor (i.e. an asymmetrical side) then we don't need to + // move inwards because that side can't provoke a jump. + if (dst == m_primaryClient) { + const CString dstName(dst->getName()); + switch (srcSide) { + case kLeft: + if (!m_config.getNeighbor(dstName, kRight).empty() && + x > dx + dw - 1 - dst->getJumpZoneSize()) + x = dx + dw - 1 - dst->getJumpZoneSize(); + break; + + case kRight: + if (!m_config.getNeighbor(dstName, kLeft).empty() && + x < dx + dst->getJumpZoneSize()) + x = dx + dst->getJumpZoneSize(); + break; + + case kTop: + if (!m_config.getNeighbor(dstName, kBottom).empty() && + y > dy + dh - 1 - dst->getJumpZoneSize()) + y = dy + dh - 1 - dst->getJumpZoneSize(); + break; + + case kBottom: + if (!m_config.getNeighbor(dstName, kTop).empty() && + y < dy + dst->getJumpZoneSize()) + y = dy + dst->getJumpZoneSize(); + break; + } + } + + // adjust the coordinate orthogonal to srcSide to account for + // resolution differences. for example, if y is 200 pixels from + // the top on a screen 1000 pixels high (20% from the top) when + // we cross the left edge onto a screen 600 pixels high then y + // should be set 120 pixels from the top (again 20% from the + // top). + switch (srcSide) { + case kLeft: + case kRight: + y -= sy; + if (y < 0) { + y = 0; + } + else if (y >= sh) { + y = dh - 1; + } + else { + y = static_cast(0.5 + y * + static_cast(dh - 1) / (sh - 1)); + } + y += dy; + break; + + case kTop: + case kBottom: + x -= sx; + if (x < 0) { + x = 0; + } + else if (x >= sw) { + x = dw - 1; + } + else { + x = static_cast(0.5 + x * + static_cast(dw - 1) / (sw - 1)); + } + x += dx; + break; + } + + return dst; +} + +bool +CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) +{ + LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); + + // is there a neighbor? + if (newScreen == NULL) { + // there's no neighbor. we don't want to switch and we don't + // want to try to switch later. + LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); + clearSwitchState(); + return false; + } + + // should we switch or not? + bool preventSwitch = false; + bool allowSwitch = false; + + // note if the switch direction has changed. save the new + // direction and screen if so. + bool isNewDirection = (dir != m_switchDir); + if (isNewDirection || m_switchScreen == NULL) { + m_switchDir = dir; + m_switchScreen = newScreen; + } + + // is this a double tap and do we care? + if (!allowSwitch && m_switchTwoTapDelay > 0.0) { + if (isNewDirection || !m_switchTwoTapEngaged) { + // tapping a different or new edge. prepare for second tap. + preventSwitch = true; + m_switchTwoTapEngaged = true; + m_switchTwoTapArmed = false; + m_switchTwoTapTimer.reset(); + LOG((CLOG_DEBUG1 "waiting for second tap")); + } + else { + // second tap if we were armed. if soon enough then switch. + if (m_switchTwoTapArmed && + m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay) { + allowSwitch = true; + } + else { + // not fast enough. reset the clock. + preventSwitch = true; + m_switchTwoTapEngaged = true; + m_switchTwoTapArmed = false; + m_switchTwoTapTimer.reset(); + LOG((CLOG_DEBUG1 "waiting for second tap")); + } + } + } + + // if waiting before a switch then prepare to switch later + if (!allowSwitch && m_switchWaitDelay > 0.0) { + if (isNewDirection || !m_switchWaitEngaged) { + m_switchWaitEngaged = true; + m_switchWaitX = x; + m_switchWaitY = y; + m_switchWaitTimer = m_primaryClient->addOneShotTimer( + m_switchWaitDelay); + LOG((CLOG_DEBUG1 "waiting to switch")); + } + preventSwitch = true; + } + + // ignore if mouse is locked to screen + if (!preventSwitch && isLockedToScreenNoLock()) { + LOG((CLOG_DEBUG1 "locked to screen")); + preventSwitch = true; + + // don't try to switch later. it's possible that we might + // not be locked to the screen when the wait delay expires + // and could switch then but we'll base the decision on + // when the user first attempts the switch. this also + // ensures that all switch tests are using the same + clearSwitchState(); + } + + return !preventSwitch; +} + +void +CServer::onNoSwitch(bool inTapZone) +{ + if (m_switchTwoTapEngaged) { + if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) { + // second tap took too long. disengage. + m_switchTwoTapEngaged = false; + m_switchTwoTapArmed = false; + } + else if (!inTapZone) { + // we've moved away from the edge and there's still + // time to get back for a double tap. + m_switchTwoTapArmed = true; + } + } + + // once the mouse moves away from the edge we no longer want to + // switch after a delay. + m_switchWaitEngaged = false; +} + +void +CServer::clearSwitchState() +{ + if (m_switchScreen != NULL) { + m_switchDir = kNoDirection; + m_switchScreen = NULL; + m_switchWaitEngaged = false; + m_switchTwoTapEngaged = false; + } +} + +void +CServer::closeClients(const CConfig& config) +{ + CThreadList threads; + { + CLock lock(&m_mutex); + + // get the set of clients that are connected but are being + // dropped from the configuration (or who's canonical name + // is changing) and tell them to disconnect. note that + // since m_clientThreads doesn't include a thread for the + // primary client we will not close it. + for (CClientThreadList::iterator + index = m_clientThreads.begin(); + index != m_clientThreads.end(); ) { + const CString& name = index->first; + if (!config.isCanonicalName(name)) { + // lookup IClient with name + CClientList::const_iterator index2 = m_clients.find(name); + assert(index2 != m_clients.end()); + + // save the thread and remove it from m_clientThreads + threads.push_back(index->second); + m_clientThreads.erase(index++); + + // close that client + assert(index2->second != m_primaryClient); + index2->second->close(); + + // don't switch to it if we planned to + if (index2->second == m_switchScreen) { + clearSwitchState(); + } + } + else { + ++index; + } + } + } + + // wait a moment to allow each client to close its connection + // before we close it (to avoid having our socket enter TIME_WAIT). + if (threads.size() > 0) { + ARCH->sleep(1.0); + } + + // cancel the old client threads + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->cancel(); + } + + // wait for old client threads to terminate. we must not hold + // the lock while we do this so those threads can finish any + // calls to this object. + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->wait(); + } + + // clean up thread list + reapThreads(); +} + +CThread +CServer::startThread(IJob* job) +{ + CLock lock(&m_mutex); + + // reap completed threads + doReapThreads(m_threads); + + // add new thread to list + CThread thread(job); + m_threads.push_back(thread); + LOG((CLOG_DEBUG1 "started thread 0x%08x", thread.getID())); + return thread; +} + +void +CServer::stopThreads(double timeout) +{ + LOG((CLOG_DEBUG1 "stopping threads")); + + // cancel the accept client thread to prevent more clients from + // connecting while we're shutting down. + CThread* acceptClientThread; + { + CLock lock(&m_mutex); + acceptClientThread = m_acceptClientThread; + m_acceptClientThread = NULL; + } + if (acceptClientThread != NULL) { + acceptClientThread->cancel(); + acceptClientThread->wait(timeout); + delete acceptClientThread; + } + + // close all clients (except the primary) + { + CConfig emptyConfig; + closeClients(emptyConfig); + } + + // swap thread list so nobody can mess with it + CThreadList threads; + { + CLock lock(&m_mutex); + threads.swap(m_threads); + } + + // cancel every thread + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->cancel(); + } + + // now wait for the threads + CStopwatch timer(true); + while (threads.size() > 0 && (timeout < 0.0 || timer.getTime() < timeout)) { + doReapThreads(threads); + ARCH->sleep(0.01); + } + + // delete remaining threads + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + LOG((CLOG_DEBUG1 "reaped running thread 0x%08x", index->getID())); + } + + LOG((CLOG_DEBUG1 "stopped threads")); +} + +void +CServer::reapThreads() +{ + CLock lock(&m_mutex); + doReapThreads(m_threads); +} + +void +CServer::doReapThreads(CThreadList& threads) +{ + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ) { + if (index->wait(0.0)) { + // thread terminated + LOG((CLOG_DEBUG1 "reaped thread 0x%08x", index->getID())); + index = threads.erase(index); + } + else { + // thread is running + ++index; + } + } +} + +void +CServer::acceptClients(void*) +{ + LOG((CLOG_DEBUG1 "starting to wait for clients")); + + IListenSocket* listen = NULL; + try { + // create socket listener + if (m_socketFactory != NULL) { + listen = m_socketFactory->createListen(); + } + assert(listen != NULL); + + // bind to the desired port. keep retrying if we can't bind + // the address immediately. + CStopwatch timer; + for (;;) { + try { + LOG((CLOG_DEBUG1 "binding listen socket")); + listen->bind(m_config.getSynergyAddress()); + break; + } + catch (XSocketAddressInUse& e) { + setStatus(kError, e.what()); + LOG((CLOG_WARN "bind failed: %s", e.what())); + + // give up if we've waited too long + if (timer.getTime() >= m_bindTimeout) { + LOG((CLOG_ERR "waited too long to bind, giving up")); + throw; + } + + // wait a bit before retrying + ARCH->sleep(5.0); + } + } + + // accept connections and begin processing them + setStatus(kRunning); + LOG((CLOG_DEBUG1 "waiting for client connections")); + for (;;) { + // accept connection + CThread::testCancel(); + IDataSocket* socket = listen->accept(); + LOG((CLOG_NOTE "accepted client connection")); + CThread::testCancel(); + + // start handshake thread + startThread(new TMethodJob( + this, &CServer::runClient, socket)); + } + } + catch (XBase& e) { + setStatus(kError, e.what()); + LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); + delete listen; + exitMainLoopWithError(); + } + catch (...) { + setStatus(kNotRunning); + delete listen; + throw; + } +} + +void +CServer::runClient(void* vsocket) +{ + // get the socket pointer from the argument + assert(vsocket != NULL); + IDataSocket* socket = reinterpret_cast(vsocket); + + // create proxy + CClientProxy* proxy = NULL; + try { + proxy = handshakeClient(socket); + if (proxy == NULL) { + delete socket; + return; + } + } + catch (...) { + delete socket; + throw; + } + + // add the connection + try { + addConnection(proxy); + + // save this client's thread + CLock lock(&m_mutex); + m_clientThreads.insert(std::make_pair(proxy->getName(), + CThread::getCurrentThread())); + + // send configuration options + sendOptions(proxy); + } + catch (XDuplicateClient& e) { + // client has duplicate name + LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); + } + catch (XSocket&) { + // ignore + } + delete proxy; + delete socket; + return; + } + catch (XUnknownClient& e) { + // client has unknown name + LOG((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); + } + catch (XSocket&) { + // ignore + } + delete proxy; + delete socket; + return; + } + catch (...) { + delete proxy; + delete socket; + throw; + } + + // activate screen saver on new client if active on the primary screen + { + CLock lock(&m_mutex); + if (m_activeSaver != NULL) { + proxy->screensaver(true); + } + } + + // handle client messages + try { + LOG((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str())); + proxy->mainLoop(); + } + catch (XBadClient&) { + // client not behaving + LOG((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); + } + catch (XSocket&) { + // ignore. client probably aborted the connection. + } + } + catch (XBase& e) { + // misc error + LOG((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); + } + catch (...) { + // mainLoop() was probably cancelled + removeConnection(proxy->getName()); + delete socket; + throw; + } + + // clean up + removeConnection(proxy->getName()); + delete socket; +} + +CClientProxy* +CServer::handshakeClient(IDataSocket* socket) +{ + LOG((CLOG_DEBUG1 "negotiating with new client")); + + // get the input and output streams + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; + + // attach filters + if (m_streamFilterFactory != NULL) { + input = m_streamFilterFactory->createInput(input, own); + output = m_streamFilterFactory->createOutput(output, own); + own = true; + } + + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + own = true; + + CClientProxy* proxy = NULL; + CString name(""); + try { + // give the client a limited time to complete the handshake + CTimerThread timer(30.0); + + // say hello + LOG((CLOG_DEBUG1 "saying hello")); + CProtocolUtil::writef(output, kMsgHello, + kProtocolMajorVersion, + kProtocolMinorVersion); + output->flush(); + + // wait for the reply + LOG((CLOG_DEBUG1 "waiting for hello reply")); + UInt32 n = input->getSize(); + + // limit the maximum length of the hello + if (n > kMaxHelloLength) { + throw XBadClient(); + } + + // get and parse the reply to hello + SInt16 major, minor; + try { + LOG((CLOG_DEBUG1 "parsing hello reply")); + CProtocolUtil::readf(input, kMsgHelloBack, + &major, &minor, &name); + } + catch (XIO&) { + throw XBadClient(); + } + + // disallow invalid version numbers + if (major <= 0 || minor < 0) { + throw XIncompatibleClient(major, minor); + } + + // convert name to canonical form (if any) + if (m_config.isScreen(name)) { + name = m_config.getCanonicalName(name); + } + + // create client proxy for highest version supported by the client + LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); + if (major == 1) { + switch (minor) { + case 0: + proxy = new CClientProxy1_0(this, name, input, output); + break; + + case 1: + proxy = new CClientProxy1_1(this, name, input, output); + break; + } + } + + // hangup (with error) if version isn't supported + if (proxy == NULL) { + throw XIncompatibleClient(major, minor); + } + + // negotiate + // FIXME + + // ask and wait for the client's info + LOG((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); + proxy->open(); + + return proxy; + } + catch (XIncompatibleClient& e) { + // client is incompatible + LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + try { + CProtocolUtil::writef(output, kMsgEIncompatible, + kProtocolMajorVersion, kProtocolMinorVersion); + } + catch (XSocket&) { + // ignore + } + } + catch (XBadClient&) { + // client not behaving + LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + try { + CProtocolUtil::writef(output, kMsgEBad); + } + catch (XSocket&) { + // ignore. client probably aborted the connection. + } + } + catch (XBase& e) { + // misc error + LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); + } + catch (...) { + // probably timed out + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + throw; + } + + // failed + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + + return NULL; +} + +void +CServer::acceptHTTPClients(void*) +{ + LOG((CLOG_DEBUG1 "starting to wait for HTTP clients")); + + IListenSocket* listen = NULL; + try { + // create socket listener + listen = new CTCPListenSocket; + + // bind to the desired port. keep retrying if we can't bind + // the address immediately. + CStopwatch timer; + for (;;) { + try { + LOG((CLOG_DEBUG1 "binding HTTP listen socket")); + listen->bind(m_config.getHTTPAddress()); + break; + } + catch (XSocketBind& e) { + LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.what())); + + // give up if we've waited too long + if (timer.getTime() >= m_bindTimeout) { + LOG((CLOG_DEBUG1 "waited too long to bind HTTP, giving up")); + throw; + } + + // wait a bit before retrying + ARCH->sleep(5.0); + } + } + + // accept connections and begin processing them + LOG((CLOG_DEBUG1 "waiting for HTTP connections")); + for (;;) { + // limit the number of HTTP requests being handled at once + { + CLock lock(&m_httpAvailable); + while (m_httpAvailable == 0) { + m_httpAvailable.wait(); + } + assert(m_httpAvailable > 0); + m_httpAvailable = m_httpAvailable - 1; + } + + // accept connection + CThread::testCancel(); + IDataSocket* socket = listen->accept(); + LOG((CLOG_NOTE "accepted HTTP connection")); + CThread::testCancel(); + + // handle HTTP request + startThread(new TMethodJob( + this, &CServer::processHTTPRequest, socket)); + } + + // clean up + delete listen; + } + catch (XBase& e) { + LOG((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); + delete listen; + exitMainLoopWithError(); + } + catch (...) { + delete listen; + throw; + } +} + +void +CServer::processHTTPRequest(void* vsocket) +{ + IDataSocket* socket = reinterpret_cast(vsocket); + try { + // process the request and force delivery + m_httpServer->processRequest(socket); + socket->getOutputStream()->flush(); + + // wait a moment to give the client a chance to hangup first + ARCH->sleep(3.0); + + // clean up + socket->close(); + delete socket; + + // increment available HTTP handlers + { + CLock lock(&m_httpAvailable); + m_httpAvailable = m_httpAvailable + 1; + m_httpAvailable.signal(); + } + } + catch (...) { + delete socket; + { + CLock lock(&m_httpAvailable); + m_httpAvailable = m_httpAvailable + 1; + m_httpAvailable.signal(); + } + throw; + } +} + +void +CServer::sendOptions(IClient* client) const +{ + // note -- must be locked on entry + + COptionsList optionsList; + + // look up options for client + const CConfig::CScreenOptions* options = + m_config.getOptions(client->getName()); + if (options != NULL && options->size() > 0) { + // convert options to a more convenient form for sending + optionsList.reserve(2 * options->size()); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + optionsList.push_back(index->first); + optionsList.push_back(static_cast(index->second)); + } + } + + // look up global options + options = m_config.getOptions(""); + if (options != NULL && options->size() > 0) { + // convert options to a more convenient form for sending + optionsList.reserve(optionsList.size() + 2 * options->size()); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + optionsList.push_back(index->first); + optionsList.push_back(static_cast(index->second)); + } + } + + // send the options + client->setOptions(optionsList); +} + +void +CServer::openPrimaryScreen() +{ + assert(m_primaryClient == NULL); + + // reset sequence number + m_seqNum = 0; + + // canonicalize the primary screen name + CString primaryName = m_config.getCanonicalName(getPrimaryScreenName()); + if (primaryName.empty()) { + throw XUnknownClient(getPrimaryScreenName()); + } + + // clear clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + clipboard.m_clipboardOwner = primaryName; + clipboard.m_clipboardSeqNum = m_seqNum; + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + } + + try { + // create the primary client + m_primaryClient = new CPrimaryClient(m_screenFactory, + this, this, primaryName); + + // add connection + addConnection(m_primaryClient); + m_active = m_primaryClient; + + // open the screen + LOG((CLOG_DEBUG1 "opening primary screen")); + m_primaryClient->open(); + + // tell it about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); + + // tell primary client about its options + sendOptions(m_primaryClient); + } + catch (...) { + // if m_active is NULL then we haven't added the connection + // for the primary client so we don't try to remove it. + if (m_active != NULL) { + removeConnection(primaryName); + } + else { + delete m_primaryClient; + } + m_active = NULL; + m_primaryClient = NULL; + throw; + } +} + +void +CServer::closePrimaryScreen() +{ + assert(m_primaryClient != NULL); + + // close the primary screen + try { + LOG((CLOG_DEBUG1 "closing primary screen")); + m_primaryClient->close(); + } + catch (...) { + // ignore + } + + // remove connection + removeConnection(m_primaryClient->getName()); + m_primaryClient = NULL; +} + +void +CServer::addConnection(IClient* client) +{ + assert(client != NULL); + + LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); + + { + CLock lock(&m_mutex); + + // name must be in our configuration + if (!m_config.isScreen(client->getName())) { + throw XUnknownClient(client->getName()); + } + + // can only have one screen with a given name at any given time + if (m_clients.count(client->getName()) != 0) { + throw XDuplicateClient(client->getName()); + } + + // save screen info + m_clients.insert(std::make_pair(client->getName(), client)); + LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); + } + runStatusJobs(); +} + +void +CServer::removeConnection(const CString& name) +{ + LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); + bool updateStatus; + { + CLock lock(&m_mutex); + + // find client + CClientList::iterator index = m_clients.find(name); + assert(index != m_clients.end()); + + // if this is active screen then we have to jump off of it + IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + if (active == index->second && active != m_primaryClient) { + // record new position (center of primary screen) + m_primaryClient->getCursorCenter(m_x, m_y); + + // stop waiting to switch if we were + if (active == m_switchScreen) { + clearSwitchState(); + } + + // don't notify active screen since it probably already + // disconnected. + LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); + + // cut over + m_active = m_primaryClient; + + // enter new screen (unless we already have because of the + // screen saver) + if (m_activeSaver == NULL) { + m_primaryClient->enter(m_x, m_y, m_seqNum, + m_primaryClient->getToggleMask(), false); + } + } + + // if this screen had the cursor when the screen saver activated + // then we can't switch back to it when the screen saver + // deactivates. + if (m_activeSaver == index->second) { + m_activeSaver = NULL; + } + + // done with client + delete index->second; + m_clients.erase(index); + + // remove any thread for this client + m_clientThreads.erase(name); + + updateStatus = (m_clients.size() <= 1); + } + + if (updateStatus) { + runStatusJobs(); + } +} + + +// +// CServer::CClipboardInfo +// + +CString +CServer::XServerRethrow::getWhat() const throw() +{ + return format("XServerRethrow", "child thread failed"); +} + + +// +// CServer::CClipboardInfo +// + +CServer::CClipboardInfo::CClipboardInfo() : + m_clipboard(), + m_clipboardData(), + m_clipboardOwner(), + m_clipboardSeqNum(0) +{ + // do nothing +} diff --git a/lib/server/CServer.h b/lib/server/CServer.h new file mode 100644 index 0000000..89a52bd --- /dev/null +++ b/lib/server/CServer.h @@ -0,0 +1,414 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSERVER_H +#define CSERVER_H + +#include "IServer.h" +#include "IPrimaryScreenReceiver.h" +#include "CConfig.h" +#include "CClipboard.h" +#include "CCondVar.h" +#include "CMutex.h" +#include "CThread.h" +#include "CJobList.h" +#include "CStopwatch.h" +#include "stdlist.h" +#include "stdmap.h" +#include "stdvector.h" + +class CClientProxy; +class CHTTPServer; +class CPrimaryClient; +class IClient; +class IDataSocket; +class IPrimaryScreenFactory; +class IServerProtocol; +class ISocketFactory; +class IStreamFilterFactory; + +//! Synergy server +/*! +This class implements the top-level server algorithms for synergy. +*/ +class CServer : public IServer, public IPrimaryScreenReceiver { +public: + enum EStatus { + kNotRunning, + kRunning, + kServerNameUnknown, + kError, + kMaxStatus + }; + + /*! + The server will look itself up in the configuration using \c serverName + as its name. + */ + CServer(const CString& serverName); + ~CServer(); + + //! @name manipulators + //@{ + + //! Open server + /*! + Open the server. Throws XScreenUnavailable if the server's + screen cannot be opened but might be available after some time. + Otherwise throws some other exception if the server's screen or + the server cannot be opened and retrying won't help. + */ + void open(); + + //! Server main loop + /*! + Run server's event loop and return when exitMainLoop() is called. + This must be called between a successful open() and close(). + + (cancellation point) + */ + void mainLoop(); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + void exitMainLoop(); + + //! Close server + /*! + Close the server. + */ + void close(); + + //! Set configuration + /*! + Change the server's configuration. Returns true iff the new + configuration was accepted (it must include the server's name). + This will disconnect any clients no longer in the configuration. + */ + bool setConfig(const CConfig&); + + //! Set primary screen factory + /*! + Sets the factory for creating primary screens. This must be + set before calling open(). This object takes ownership of the + factory. + */ + void setScreenFactory(IPrimaryScreenFactory*); + + //! Set socket factory + /*! + Sets the factory used to create a socket to connect to the server. + This must be set before calling mainLoop(). This object takes + ownership of the factory. + */ + void setSocketFactory(ISocketFactory*); + + //! Set stream filter factory + /*! + Sets the factory used to filter the socket streams used to + communicate with the server. This object takes ownership + of the factory. + */ + void setStreamFilterFactory(IStreamFilterFactory*); + + //! Add a job to notify of status changes + /*! + The added job is run whenever the server's status changes in + certain externally visible ways. The client keeps ownership + of the job. + */ + void addStatusJob(IJob*); + + //! Remove a job to notify of status changes + /*! + Removes a previously added status notification job. A job can + remove itself when called but must not remove any other jobs. + The client keeps ownership of the job. + */ + void removeStatusJob(IJob*); + + //@} + //! @name accessors + //@{ + + //! Get configuration + /*! + Returns the current configuration. + */ + void getConfig(CConfig*) const; + + //! Get name + /*! + Returns the server's name passed to the c'tor + */ + CString getPrimaryScreenName() const; + + //! Get number of connected clients + /*! + Returns the number of connected clients, including the server itself. + */ + UInt32 getNumClients() const; + + //! Get the list of connected clients + /*! + Set the \c list to the names of the currently connected clients. + */ + void getClients(std::vector& list) const; + + //! Get the status + /*! + Returns the current status and status message. + */ + EStatus getStatus(CString* = NULL) const; + + //@} + + // IServer overrides + virtual void onError(); + virtual void onInfoChanged(const CString&, const CClientInfo&); + virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); + virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); + + // IPrimaryScreenReceiver overrides + virtual void onScreensaver(bool activated); + virtual void onOneShotTimerExpired(UInt32 id); + virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton); + virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton); + virtual void onKeyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + virtual void onMouseWheel(SInt32 delta); + +protected: + //! Handle special keys + /*! + Handles keys with special meaning. + */ + bool onCommandKey(KeyID, KeyModifierMask, bool down); + + //! Exit event loop and note an error condition + /*! + Force mainLoop() to return by throwing an exception. This call + can return before mainLoop() does (i.e. asynchronously). This + may only be called between a successful open() and close(). + */ + void exitMainLoopWithError(); + +private: + typedef std::list CThreadList; + + // notify status jobs of a change + void runStatusJobs() const; + + // set new status + void setStatus(EStatus, const char* msg = NULL); + + // get the sides of the primary screen that have neighbors + UInt32 getActivePrimarySides() const; + + // handle mouse motion + bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); + void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); + + // set the clipboard + void onClipboardChangedNoLock(ClipboardID, + UInt32 seqNum, const CString& data); + + // returns true iff mouse should be locked to the current screen + bool isLockedToScreenNoLock() const; + + // change the active screen + void switchScreen(IClient*, + SInt32 x, SInt32 y, bool forScreenSaver); + + // lookup neighboring screen + IClient* getNeighbor(IClient*, EDirection) const; + + // lookup neighboring screen. given a position relative to the + // source screen, find the screen we should move onto and where. + // if the position is sufficiently far from the source then we + // cross multiple screens. if there is no suitable screen then + // return NULL and x,y are not modified. + IClient* getNeighbor(IClient*, EDirection, + SInt32& x, SInt32& y) const; + + // test if a switch is permitted. this includes testing user + // options like switch delay and tracking any state required to + // implement them. returns true iff a switch is permitted. + bool isSwitchOkay(IClient* dst, EDirection, + SInt32 x, SInt32 y); + + // update switch state due to a mouse move that doesn't try to + // switch screens. + void onNoSwitch(bool inTapZone); + + // reset switch wait state + void clearSwitchState(); + + // send screen options to \c client + void sendOptions(IClient* client) const; + + // open/close the primary screen + void openPrimaryScreen(); + void closePrimaryScreen(); + + // update the clipboard if owned by the primary screen + void updatePrimaryClipboard(ClipboardID); + + // close all clients that are *not* in config, not including the + // primary client. + void closeClients(const CConfig& config); + + // start a thread, adding it to the list of threads + CThread startThread(IJob* adopted); + + // cancel running threads, waiting at most timeout seconds for + // them to finish. + void stopThreads(double timeout = -1.0); + + // reap threads, clearing finished threads from the thread list. + // doReapThreads does the work on the given thread list. + void reapThreads(); + void doReapThreads(CThreadList&); + + // thread method to accept incoming client connections + void acceptClients(void*); + + // thread method to do client interaction + void runClient(void*); + CClientProxy* handshakeClient(IDataSocket*); + + // thread method to accept incoming HTTP connections + void acceptHTTPClients(void*); + + // thread method to process HTTP requests + void processHTTPRequest(void*); + + // connection list maintenance + void addConnection(IClient*); + void removeConnection(const CString& name); + +private: + class XServerRethrow : public XBase { + protected: + // XBase overrides + virtual CString getWhat() const throw(); + }; + + class CClipboardInfo { + public: + CClipboardInfo(); + + public: + CClipboard m_clipboard; + CString m_clipboardData; + CString m_clipboardOwner; + UInt32 m_clipboardSeqNum; + }; + + CMutex m_mutex; + + // the name of the primary screen + CString m_name; + + // true if we should exit the main loop by throwing an exception. + // this is used to propagate an exception from one of our threads + // to the mainLoop() thread. but, since we can't make a copy of + // the original exception, we return an arbitrary, unique + // exception type. the caller of mainLoop() cannot catch this + // exception except through XBase or .... + bool m_error; + + // how long to wait to bind our socket until we give up + double m_bindTimeout; + + // factories + IPrimaryScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; + + // running threads + CThreadList m_threads; + CThread* m_acceptClientThread; + + // the screens + typedef std::map CClientList; + typedef std::map CClientThreadList; + + // all clients indexed by name + CClientList m_clients; + + // run thread of all secondary screen clients. does not include the + // primary screen's run thread. + CClientThreadList m_clientThreads; + + // the primary screen client + CPrimaryClient* m_primaryClient; + + // the client with focus + IClient* m_active; + + // the sequence number of enter messages + UInt32 m_seqNum; + + // current mouse position (in absolute secondary screen coordinates) + SInt32 m_x, m_y; + + // current configuration + CConfig m_config; + + // clipboard cache + CClipboardInfo m_clipboards[kClipboardEnd]; + + // state saved when screen saver activates + IClient* m_activeSaver; + SInt32 m_xSaver, m_ySaver; + + // HTTP request processing stuff + CHTTPServer* m_httpServer; + CCondVar m_httpAvailable; + static const SInt32 s_httpMaxSimultaneousRequests; + + // common state for screen switch tests. all tests are always + // trying to reach the same screen in the same direction. + EDirection m_switchDir; + IClient* m_switchScreen; + + // state for delayed screen switching + double m_switchWaitDelay; + UInt32 m_switchWaitTimer; + bool m_switchWaitEngaged; + SInt32 m_switchWaitX, m_switchWaitY; + + // state for double-tap screen switching + double m_switchTwoTapDelay; + CStopwatch m_switchTwoTapTimer; + bool m_switchTwoTapEngaged; + bool m_switchTwoTapArmed; + SInt32 m_switchTwoTapZone; + + // the status change jobs and status + CJobList m_statusJobs; + EStatus m_status; + CString m_statusMessage; +}; + +#endif diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am new file mode 100644 index 0000000..a414bc9 --- /dev/null +++ b/lib/server/Makefile.am @@ -0,0 +1,53 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + server.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libserver.a +libserver_a_SOURCES = \ + CClientProxy.cpp \ + CClientProxy1_0.cpp \ + CClientProxy1_1.cpp \ + CConfig.cpp \ + CHTTPServer.cpp \ + CPrimaryClient.cpp \ + CServer.cpp \ + CClientProxy.h \ + CClientProxy1_0.h \ + CClientProxy1_1.h \ + CConfig.h \ + CHTTPServer.h \ + CPrimaryClient.h \ + CServer.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) diff --git a/lib/server/Makefile.in b/lib/server/Makefile.in new file mode 100644 index 0000000..e7c12ad --- /dev/null +++ b/lib/server/Makefile.in @@ -0,0 +1,365 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + server.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libserver.a +libserver_a_SOURCES = \ + CClientProxy.cpp \ + CClientProxy1_0.cpp \ + CClientProxy1_1.cpp \ + CConfig.cpp \ + CHTTPServer.cpp \ + CPrimaryClient.cpp \ + CServer.cpp \ + CClientProxy.h \ + CClientProxy1_0.h \ + CClientProxy1_1.h \ + CConfig.h \ + CHTTPServer.h \ + CPrimaryClient.h \ + CServer.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + $(NULL) + +subdir = lib/server +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libserver_a_AR = $(AR) cru +libserver_a_LIBADD = +am_libserver_a_OBJECTS = CClientProxy.$(OBJEXT) \ + CClientProxy1_0.$(OBJEXT) CClientProxy1_1.$(OBJEXT) \ + CConfig.$(OBJEXT) CHTTPServer.$(OBJEXT) \ + CPrimaryClient.$(OBJEXT) CServer.$(OBJEXT) +libserver_a_OBJECTS = $(am_libserver_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClientProxy.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CClientProxy1_0.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CClientProxy1_1.Po $(DEPDIR)/CConfig.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CHTTPServer.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CPrimaryClient.Po $(DEPDIR)/CServer.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libserver_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libserver_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/server/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libserver.a: $(libserver_a_OBJECTS) $(libserver_a_DEPENDENCIES) + -rm -f libserver.a + $(libserver_a_AR) libserver.a $(libserver_a_OBJECTS) $(libserver_a_LIBADD) + $(RANLIB) libserver.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientProxy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientProxy1_0.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClientProxy1_1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CConfig.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CHTTPServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CPrimaryClient.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CServer.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/server/server.dsp b/lib/server/server.dsp new file mode 100644 index 0000000..bc3dbb5 --- /dev/null +++ b/lib/server/server.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=server - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "server.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "server.mak" CFG="server - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "server - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "server - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "server - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "server - Win32 Release" +# Name "server - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClientProxy.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_1.cpp +# End Source File +# Begin Source File + +SOURCE=.\CConfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServer.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClientProxy.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_1.h +# End Source File +# Begin Source File + +SOURCE=.\CConfig.h +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.h +# End Source File +# Begin Source File + +SOURCE=.\CServer.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/synergy/CClipboard.cpp b/lib/synergy/CClipboard.cpp new file mode 100644 index 0000000..d82448a --- /dev/null +++ b/lib/synergy/CClipboard.cpp @@ -0,0 +1,226 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClipboard.h" + +// +// CClipboard +// + +CClipboard::CClipboard() : + m_open(false), + m_owner(false) +{ + open(0); + empty(); + close(); +} + +CClipboard::~CClipboard() +{ + // do nothing +} + +bool +CClipboard::empty() +{ + assert(m_open); + + // clear all data + for (SInt32 index = 0; index < kNumFormats; ++index) { + m_data[index] = ""; + m_added[index] = false; + } + + // save time + m_timeOwned = m_time; + + // we're the owner now + m_owner = true; + + return true; +} + +void +CClipboard::add(EFormat format, const CString& data) +{ + assert(m_open); + assert(m_owner); + + m_data[format] = data; + m_added[format] = true; +} + +bool +CClipboard::open(Time time) const +{ + assert(!m_open); + + m_open = true; + m_time = time; + + return true; +} + +void +CClipboard::close() const +{ + assert(m_open); + + m_open = false; +} + +CClipboard::Time +CClipboard::getTime() const +{ + return m_timeOwned; +} + +bool +CClipboard::has(EFormat format) const +{ + assert(m_open); + return m_added[format]; +} + +CString +CClipboard::get(EFormat format) const +{ + assert(m_open); + return m_data[format]; +} + +bool +CClipboard::copy(IClipboard* dst, const IClipboard* src) +{ + assert(dst != NULL); + assert(src != NULL); + + return copy(dst, src, src->getTime()); +} + +bool +CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) +{ + assert(dst != NULL); + assert(src != NULL); + + bool success = false; + if (src->open(time)) { + if (dst->open(time)) { + if (dst->empty()) { + for (SInt32 format = 0; + format != IClipboard::kNumFormats; ++format) { + IClipboard::EFormat eFormat = (IClipboard::EFormat)format; + if (src->has(eFormat)) { + dst->add(eFormat, src->get(eFormat)); + } + } + success = true; + } + dst->close(); + } + src->close(); + } + + return success; +} + +void +CClipboard::unmarshall(const CString& data, Time time) +{ + const char* index = data.data(); + + // clear existing data + open(time); + empty(); + + // read the number of formats + const UInt32 numFormats = readUInt32(index); + index += 4; + + // read each format + for (UInt32 i = 0; i < numFormats; ++i) { + // get the format id + UInt32 format = readUInt32(index); + index += 4; + + // get the size of the format data + UInt32 size = readUInt32(index); + index += 4; + + // save the data if it's a known format. if either the client + // or server supports more clipboard formats than the other + // then one of them will get a format >= kNumFormats here. + if (format < static_cast(IClipboard::kNumFormats)) { + m_added[format] = true; + m_data[format] = CString(index, size); + } + index += size; + } + + // done + close(); +} + +CString +CClipboard::marshall() const +{ + CString data; + + // compute size of marshalled data + UInt32 size = 4; + UInt32 numFormats = 0; + UInt32 format; + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + ++numFormats; + size += 4 + 4 + m_data[format].size(); + } + } + + // allocate space + data.reserve(size); + + // marshall the data + writeUInt32(&data, numFormats); + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + writeUInt32(&data, format); + writeUInt32(&data, m_data[format].size()); + data += m_data[format]; + } + } + + return data; +} + +UInt32 +CClipboard::readUInt32(const char* buf) const +{ + const unsigned char* ubuf = reinterpret_cast(buf); + return (static_cast(ubuf[0]) << 24) | + (static_cast(ubuf[1]) << 16) | + (static_cast(ubuf[2]) << 8) | + static_cast(ubuf[3]); +} + +void +CClipboard::writeUInt32(CString* buf, UInt32 v) const +{ + *buf += static_cast((v >> 24) & 0xff); + *buf += static_cast((v >> 16) & 0xff); + *buf += static_cast((v >> 8) & 0xff); + *buf += static_cast( v & 0xff); +} diff --git a/lib/synergy/CClipboard.h b/lib/synergy/CClipboard.h new file mode 100644 index 0000000..55d1293 --- /dev/null +++ b/lib/synergy/CClipboard.h @@ -0,0 +1,93 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIPBOARD_H +#define CCLIPBOARD_H + +#include "IClipboard.h" + +//! Memory buffer clipboard +/*! +This class implements a clipboard that stores data in memory. +*/ +class CClipboard : public IClipboard { +public: + CClipboard(); + virtual ~CClipboard(); + + //! @name manipulators + //@{ + + //! Unmarshall clipboard data + /*! + Extract marshalled clipboard data and store it in this clipboard. + Sets the clipboard time to \c time. + */ + void unmarshall(const CString& data, Time time); + + //@} + //! @name accessors + //@{ + + //! Marshall clipboard data + /*! + Merge this clipboard's data into a single buffer that can be later + unmarshalled to restore the clipboard and return the buffer. + */ + CString marshall() const; + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and + they don't have to be the same type). This also sets + the destination clipboard's timestamp to source clipboard's + timestamp. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src); + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and they + don't have to be the same type). This also sets the + timestamp to \c time. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src, Time); + + //@} + + // IClipboard overrides + virtual bool empty(); + virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + +private: + UInt32 readUInt32(const char*) const; + void writeUInt32(CString*, UInt32) const; + +private: + mutable bool m_open; + mutable Time m_time; + bool m_owner; + Time m_timeOwned; + bool m_added[kNumFormats]; + CString m_data[kNumFormats]; +}; + +#endif diff --git a/lib/synergy/CInputPacketStream.cpp b/lib/synergy/CInputPacketStream.cpp new file mode 100644 index 0000000..8b0096f --- /dev/null +++ b/lib/synergy/CInputPacketStream.cpp @@ -0,0 +1,181 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CInputPacketStream.h" +#include "CLock.h" +#include "CStopwatch.h" + +// +// CInputPacketStream +// + +CInputPacketStream::CInputPacketStream(IInputStream* stream, bool adopt) : + CInputStreamFilter(stream, adopt), + m_mutex(), + m_size(0), + m_buffer(&m_mutex, NULL) +{ + // do nothing +} + +CInputPacketStream::~CInputPacketStream() +{ + // do nothing +} + +void +CInputPacketStream::close() +{ + getStream()->close(); +} + +UInt32 +CInputPacketStream::read(void* buffer, UInt32 n, double timeout) +{ + CLock lock(&m_mutex); + + // wait for entire message to be read. return if stream + // hungup or timeout. + switch (waitForFullMessage(timeout)) { + case kData: + break; + + case kHungup: + return 0; + + case kTimedout: + return (UInt32)-1; + } + + // limit number of bytes to read to the number of bytes left in the + // current message. + if (n > m_size) { + n = m_size; + } + + // now read from our buffer + n = m_buffer.readNoLock(buffer, n, -1.0); + assert(n <= m_size); + m_size -= n; + + return n; +} + +UInt32 +CInputPacketStream::getSize() const +{ + CLock lock(&m_mutex); + return getSizeNoLock(); +} + +UInt32 +CInputPacketStream::getSizeNoLock() const +{ + CStopwatch timer(true); + while (!hasFullMessage() && getStream()->getSize() > 0) { + // read more data + if (getMoreMessage(-1.0) != kData) { + // stream hungup + return 0; + } + } + + return m_size; +} + +CInputPacketStream::EResult +CInputPacketStream::waitForFullMessage(double timeout) const +{ + CStopwatch timer(true); + while (!hasFullMessage()) { + // compute remaining timeout + double t = timeout - timer.getTime(); + if (timeout >= 0.0 && t <= 0.0) { + // timeout + return kTimedout; + } + + // read more data + switch (getMoreMessage(t)) { + case kData: + break; + + case kHungup: + // stream hungup + return kHungup; + + case kTimedout: + // stream timed out + return kTimedout; + } + } + + return kData; +} + +CInputPacketStream::EResult +CInputPacketStream::getMoreMessage(double timeout) const +{ + // read more data + char buffer[4096]; + UInt32 n = getStream()->read(buffer, sizeof(buffer), timeout); + + // return if stream timed out + if (n == (UInt32)-1) { + return kTimedout; + } + + // return if stream hungup + if (n == 0) { + m_buffer.hangup(); + return kHungup; + } + + // append to our buffer + m_buffer.write(buffer, n); + + return kData; +} + +bool +CInputPacketStream::hasFullMessage() const +{ + // get payload length if we don't have it yet + if (m_size == 0) { + // check if length field has been read yet + if (m_buffer.getSizeNoLock() < 4) { + // not enough data for payload length + return false; + } + + // save payload length + UInt8 buffer[4]; + UInt32 n = m_buffer.readNoLock(buffer, sizeof(buffer), -1.0); + assert(n == 4); + m_size = ((UInt32)buffer[0] << 24) | + ((UInt32)buffer[1] << 16) | + ((UInt32)buffer[2] << 8) | + (UInt32)buffer[3]; + + // if payload length is zero then discard null message + if (m_size == 0) { + return false; + } + } + assert(m_size > 0); + + // we have the full message when we have at least m_size bytes in + // the buffer + return (m_buffer.getSizeNoLock() >= m_size); +} diff --git a/lib/synergy/CInputPacketStream.h b/lib/synergy/CInputPacketStream.h new file mode 100644 index 0000000..a4122f3 --- /dev/null +++ b/lib/synergy/CInputPacketStream.h @@ -0,0 +1,51 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CINPUTPACKETSTREAM_H +#define CINPUTPACKETSTREAM_H + +#include "CInputStreamFilter.h" +#include "CBufferedInputStream.h" +#include "CMutex.h" + +//! Packetizing input stream filter +/*! +Filters an input stream to extract packet by packet. +*/ +class CInputPacketStream : public CInputStreamFilter { +public: + CInputPacketStream(IInputStream*, bool adoptStream = true); + ~CInputPacketStream(); + + // IInputStream overrides + virtual void close(); + virtual UInt32 read(void*, UInt32 maxCount, double timeout); + virtual UInt32 getSize() const; + +private: + enum EResult { kData, kHungup, kTimedout }; + + UInt32 getSizeNoLock() const; + EResult waitForFullMessage(double timeout) const; + EResult getMoreMessage(double timeout) const; + bool hasFullMessage() const; + +private: + CMutex m_mutex; + mutable UInt32 m_size; + mutable CBufferedInputStream m_buffer; +}; + +#endif + diff --git a/lib/synergy/COutputPacketStream.cpp b/lib/synergy/COutputPacketStream.cpp new file mode 100644 index 0000000..7fbf1ad --- /dev/null +++ b/lib/synergy/COutputPacketStream.cpp @@ -0,0 +1,72 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COutputPacketStream.h" + +// +// COuputPacketStream +// + +COutputPacketStream::COutputPacketStream(IOutputStream* stream, bool adopt) : + COutputStreamFilter(stream, adopt) +{ + // do nothing +} + +COutputPacketStream::~COutputPacketStream() +{ + // do nothing +} + +void +COutputPacketStream::close() +{ + getStream()->close(); +} + +UInt32 +COutputPacketStream::write(const void* buffer, UInt32 count) +{ + // write the length of the payload + UInt8 length[4]; + length[0] = (UInt8)((count >> 24) & 0xff); + length[1] = (UInt8)((count >> 16) & 0xff); + length[2] = (UInt8)((count >> 8) & 0xff); + length[3] = (UInt8)( count & 0xff); + UInt32 count2 = sizeof(length); + const UInt8* cbuffer = length; + while (count2 > 0) { + UInt32 n = getStream()->write(cbuffer, count2); + cbuffer += n; + count2 -= n; + } + + // write the payload + count2 = count; + cbuffer = reinterpret_cast(buffer); + while (count2 > 0) { + UInt32 n = getStream()->write(cbuffer, count2); + cbuffer += n; + count2 -= n; + } + + return count; +} + +void +COutputPacketStream::flush() +{ + getStream()->flush(); +} + diff --git a/lib/synergy/COutputPacketStream.h b/lib/synergy/COutputPacketStream.h new file mode 100644 index 0000000..7e25d47 --- /dev/null +++ b/lib/synergy/COutputPacketStream.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COUTPUTPACKETSTREAM_H +#define COUTPUTPACKETSTREAM_H + +#include "COutputStreamFilter.h" + +//! Packetizing output stream filter +/*! +Filters an output stream to create packets that include message +boundaries. Each write() is considered a single packet. +*/ +class COutputPacketStream : public COutputStreamFilter { +public: + COutputPacketStream(IOutputStream*, bool adoptStream = true); + ~COutputPacketStream(); + + // IOutputStream overrides + virtual void close(); + virtual UInt32 write(const void*, UInt32 count); + virtual void flush(); +}; + +#endif diff --git a/lib/synergy/CPrimaryScreen.cpp b/lib/synergy/CPrimaryScreen.cpp new file mode 100644 index 0000000..320733f --- /dev/null +++ b/lib/synergy/CPrimaryScreen.cpp @@ -0,0 +1,281 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CPrimaryScreen.h" +#include "IScreen.h" +#include "IScreenReceiver.h" +#include "ProtocolTypes.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" + +// +// CPrimaryScreen +// + +CPrimaryScreen::CPrimaryScreen(IScreenReceiver* receiver) : + m_receiver(receiver), + m_active(false) +{ + // do nothing +} + +CPrimaryScreen::~CPrimaryScreen() +{ + // do nothing +} + +void +CPrimaryScreen::mainLoop() +{ + // change our priority + CThread::getCurrentThread().setPriority(-14); + + // run event loop + try { + LOG((CLOG_DEBUG "entering event loop")); + onPreMainLoop(); + getScreen()->mainLoop(); + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CPrimaryScreen::exitMainLoop() +{ + getScreen()->exitMainLoop(); +} + +void +CPrimaryScreen::open() +{ + CClientInfo info; + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window + createWindow(); + + // collect screen info + getScreen()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getScreen()->getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + + // update keyboard state + updateKeys(); + + // get notified of screen saver activation/deactivation + getScreen()->openScreensaver(true); + + // subclass hook + onPostOpen(); + + // reset options + resetOptions(); + } + catch (...) { + close(); + throw; + } + + // enter the screen + { + CLock lock(&m_mutex); + enterNoWarp(); + } + + // send screen info + m_receiver->onInfoChanged(info); +} + +void +CPrimaryScreen::close() +{ + onPreClose(); + getScreen()->closeScreensaver(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) +{ + LOG((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); + CLock lock(&m_mutex); + assert(m_active == true); + + if (!forScreensaver) { + warpCursor(x, y); + } + else { + onEnterScreensaver(); + } + enterNoWarp(); +} + +void +CPrimaryScreen::enterNoWarp() +{ + // note -- must be locked on entry + + // not active anymore + m_active = false; + + // subclass hook + onPreEnter(); + + // restore active window and hide our window + hideWindow(); + + // subclass hook + onPostEnter(); +} + +bool +CPrimaryScreen::leave() +{ + LOG((CLOG_INFO "leaving primary")); + CLock lock(&m_mutex); + assert(m_active == false); + + // subclass hook + onPreLeave(); + + // show our window + if (!showWindow()) { + onPostLeave(false); + return false; + } + + // get keyboard state as we leave + updateKeys(); + + // warp mouse to center + warpCursorToCenter(); + + // subclass hook + onPostLeave(true); + + // local client now active + m_active = true; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); + + return true; +} + +void +CPrimaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CPrimaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +bool +CPrimaryScreen::isActive() const +{ + CLock lock(&m_mutex); + return m_active; +} + +void +CPrimaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +void +CPrimaryScreen::onPreMainLoop() +{ + // do nothing +} + +void +CPrimaryScreen::onPostMainLoop() +{ + // do nothing +} + +void +CPrimaryScreen::onPreOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPostOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPreClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPostClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPreEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onPostEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onEnterScreensaver() +{ + // do nothing +} + +void +CPrimaryScreen::onPreLeave() +{ + // do nothing +} + +void +CPrimaryScreen::onPostLeave(bool) +{ + // do nothing +} diff --git a/lib/synergy/CPrimaryScreen.h b/lib/synergy/CPrimaryScreen.h new file mode 100644 index 0000000..c370d19 --- /dev/null +++ b/lib/synergy/CPrimaryScreen.h @@ -0,0 +1,349 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPRIMARYSCREEN_H +#define CPRIMARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "OptionTypes.h" +#include "CMutex.h" + +class IClipboard; +class IScreen; +class IScreenReceiver; + +//! Generic server-side screen +/*! +This is a platform independent base class for primary screen +implementations. A primary screen is a server-side screen. +Each platform will derive a class from CPrimaryScreen to handle +platform dependent operations. +*/ +class CPrimaryScreen { +public: + CPrimaryScreen(IScreenReceiver*); + virtual ~CPrimaryScreen(); + + //! @name manipulators + //@{ + + //! Open screen + /*! + Opens the screen. This includes initializing the screen, opening + the screen saver, synchronizing keyboard state, and causing events + to be reported to an IPrimaryScreenReceiver (set through another + interface). Calls close() before returning (rethrowing) if it + fails for any reason. + */ + void open(); + + //! Run event loop + /*! + Run the screen's event loop. This returns when it detects + the application should terminate or when exitMainLoop() is called. + mainLoop() may only be called between open() and close(). + */ + void mainLoop(); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). + */ + void exitMainLoop(); + + //! Close screen + /*! + Closes the screen. This close the screen saver and the screen. + */ + void close(); + + //! Enter screen + /*! + Called when the user navigates to the primary screen. Warps + the cursor to the absolute coordinates \c x,y and unhides + it. If \c forScreensaver is true then we're entering because + the screen saver started and the cursor is not warped. + */ + void enter(SInt32 x, SInt32 y, bool forScreensaver); + + //! Leave screen + /*! + Called when the user navigates off the primary screen. Returns + true iff successful. + */ + bool leave(); + + //! Update configuration + /*! + This is called when the configuration has changed. \c activeSides + is a bitmask of EDirectionMask indicating which sides of the + primary screen are linked to clients. Override to handle the + possible change in jump zones. + */ + virtual void reconfigure(UInt32 activeSides) = 0; + + //! Warp cursor + /*! + Warp the cursor to the absolute coordinates \c x,y. Also + discard input events up to and including the warp before + returning. + */ + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + //! Set clipboard + /*! + Sets the system's clipboard contents. This is usually called + soon after an enter(). + */ + void setClipboard(ClipboardID, const IClipboard*); + + //! Grab clipboard + /*! + Grabs (i.e. take ownership of) the system clipboard. + */ + void grabClipboard(ClipboardID); + + //! Notify of options changes + /*! + Reset all options to their default values. + */ + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer. + */ + virtual UInt32 addOneShotTimer(double timeout) = 0; + + //@} + //! @name accessors + //@{ + + //! Test if active + /*! + Returns true iff the screen is active (i.e. the user has left + the screen). Note this is the reverse of a secdonary screen. + */ + bool isActive() const; + + //! Get clipboard + /*! + Saves the contents of the system clipboard indicated by \c id. + */ + void getClipboard(ClipboardID, IClipboard*) const; + + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //! Get toggle key state + /*! + Return the primary screen's current toggle modifier key state. + The returned mask should have the corresponding bit set for + each toggle key that is active. For example, if caps lock is + on then the returned mask should have \c KeyModifierCapsLock set. + */ + virtual KeyModifierMask getToggleMask() const = 0; + + //! Get screen lock state + /*! + Return true if any key or button is being pressed or if there's + any other reason that the user should not be allowed to switch + screens. Active toggle keys (including the scroll lock key) + should not be counted as reasons to lock to the screen. + If this method returns true it should log a message on why at + the CLOG_DEBUG level. + */ + virtual bool isLockedToScreen() const = 0; + + //! Get screen + /*! + Return the platform dependent screen. + */ + virtual IScreen* getScreen() const = 0; + + //@} + +protected: + //! Pre-mainLoop() hook + /*! + Called on entry to mainLoop(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreMainLoop(); + + //! Post-mainLoop() hook + /*! + Called on exit from mainLoop(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostMainLoop(); + + //! Pre-open() hook + /*! + Called on entry to open(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreOpen(); + + //! Post-open() hook + /*! + Called on exit from open() iff the open was successful. Default + does nothing. May throw. + */ + virtual void onPostOpen(); + + //! Pre-close() hook + /*! + Called on entry to close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPreClose(); + + //! Post-close() hook + /*! + Called on exit from close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostClose(); + + //! Pre-enter() hook + /*! + Called from enter() after the cursor has been warped or, if + \c forScreensaver is true, onEnterScreensaver() was called. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreEnter(); + + //! Post-enter() hook + /*! + Called on exit from enter(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostEnter(); + + //! Pre-enter() for screen saver hook + /*! + Called on entry to enter() if the \c forScreensaver passed to it was + true. Override to perform platform specific operations. Default + does nothing. May \b not throw. + */ + virtual void onEnterScreensaver(); + + //! Pre-leave() hook + /*! + Called on entry to leave() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreLeave(); + + //! Post-leave() hook + /*! + Called on exit from leave(). \c success is the value returned by + showWindow(). Override to perform platform specific operations. + Default does nothing. May \b not throw. + */ + virtual void onPostLeave(bool success); + + //! Create window + /*! + Called to create the window. This window is generally used to + receive events, hide the cursor, and to capture keyboard and mouse + input. + */ + virtual void createWindow() = 0; + + //! Destroy window + /*! + Called to destroy the window created by createWindow(). + */ + virtual void destroyWindow() = 0; + + //! Show window + /*! + Called when the user navigates off the primary screen. Hide the + cursor and grab exclusive access to the input devices. Every call + to showWindow() has a matching call to hideWindow() which preceeds + it. Return true iff successful (in particular, iff the input + devices were grabbed). + + After a successful showWindow(), user input events and + screensaver activation/deactivation should be reported to an + IPrimaryScreenReceiver (set through another interface) until + hideWindow() is called. Report mouse motion to its + onMouseMoveSecondary(). User input should not be delivered to + any application except this one. + */ + virtual bool showWindow() = 0; + + //! Hide window + /*! + Called when the user navigates back to the primary screen. Show + the cursor and ungrab the input devices. + + After hideWindow(), user input events should be delivered normally + to other applications. Mouse motion over (at least) the jump zones + must be reported to an IPrimaryScreenReceiver's onMouseMovePrimary(). + */ + virtual void hideWindow() = 0; + + //! Warp cursor for relative motion + /*! + Prepare the cursor to report relative motion. When the user has + navigated to another screen, synergy requires the cursor motion + deltas, not the absolute coordinates. Typically this is done by + warping the cursor to the center of the primary screen and then + every time it moves compute the motion and warp back to the + center (but without reporting that warp as motion). This is + only called after a successful showWindow(). + */ + virtual void warpCursorToCenter() = 0; + + //! Synchronize key state + /*! + Check the current keyboard state. Normally a screen will save + the keyboard state in this method and use this shadow state + when handling user input and in methods like isLockedToScreen(). + */ + virtual void updateKeys() = 0; + +private: + void enterNoWarp(); + +private: + CMutex m_mutex; + + // object to notify of changes + IScreenReceiver* m_receiver; + + // m_active is true if this screen has been left + bool m_active; +}; + +#endif diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp new file mode 100644 index 0000000..7ef9de6 --- /dev/null +++ b/lib/synergy/CProtocolUtil.cpp @@ -0,0 +1,519 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CProtocolUtil.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLog.h" +#include "stdvector.h" +#include +#include + +// +// CProtocolUtil +// + +void +CProtocolUtil::writef(IOutputStream* stream, const char* fmt, ...) +{ + assert(stream != NULL); + assert(fmt != NULL); + LOG((CLOG_DEBUG2 "writef(%s)", fmt)); + + va_list args; + + // determine total size to write + va_start(args, fmt); + UInt32 count = getLength(fmt, args); + va_end(args); + + // done if nothing to write + if (count == 0) { + return; + } + + // fill buffer + UInt8* buffer = new UInt8[count]; + va_start(args, fmt); + writef(buffer, fmt, args); + va_end(args); + + // write buffer + UInt8* scan = buffer; + while (count > 0) { + const UInt32 n = stream->write(scan, count); + LOG((CLOG_DEBUG2 "wrote %d of %d bytes", n, count)); + count -= n; + scan += n; + } + + delete[] buffer; +} + +void +CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) +{ + assert(stream != NULL); + assert(fmt != NULL); + LOG((CLOG_DEBUG2 "readf(%s)", fmt)); + + va_list args; + va_start(args, fmt); + + // begin scanning + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': { + // check for valid length + assert(len == 1 || len == 2 || len == 4); + + // read the data + UInt8 buffer[4]; + read(stream, buffer, len); + + // convert it + void* v = va_arg(args, void*); + switch (len) { + case 1: + // 1 byte integer + *reinterpret_cast(v) = buffer[0]; + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + break; + + case 2: + // 2 byte integer + *reinterpret_cast(v) = + static_cast( + (static_cast(buffer[0]) << 8) | + static_cast(buffer[1])); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + break; + + case 4: + // 4 byte integer + *reinterpret_cast(v) = + (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + break; + } + break; + } + + case 'I': { + // check for valid length + assert(len == 1 || len == 2 || len == 4); + + // read the vector length + UInt8 buffer[4]; + read(stream, buffer, 4); + UInt32 n = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + + // convert it + void* v = va_arg(args, void*); + switch (len) { + case 1: + // 1 byte integer + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 1); + reinterpret_cast*>(v)->push_back( + buffer[0]); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + + case 2: + // 2 byte integer + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 2); + reinterpret_cast*>(v)->push_back( + static_cast( + (static_cast(buffer[0]) << 8) | + static_cast(buffer[1]))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + + case 4: + // 4 byte integer + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 4); + reinterpret_cast*>(v)->push_back( + (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3])); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + } + break; + } + + case 's': { + assert(len == 0); + + // read the string length + UInt8 buffer[128]; + read(stream, buffer, 4); + UInt32 len = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + + // use a fixed size buffer if its big enough + const bool useFixed = (len <= sizeof(buffer)); + + // allocate a buffer to read the data + UInt8* sBuffer; + if (useFixed) { + sBuffer = buffer; + } + else { + sBuffer = new UInt8[len]; + } + + // read the data + try { + read(stream, sBuffer, len); + } + catch (...) { + if (!useFixed) { + delete[] sBuffer; + } + throw; + } + LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); + + // save the data + CString* dst = va_arg(args, CString*); + dst->assign((const char*)sBuffer, len); + + // release the buffer + if (!useFixed) { + delete[] sBuffer; + } + break; + } + + case '%': + assert(len == 0); + break; + + default: + assert(0 && "invalid format specifier"); + } + + // next format character + ++fmt; + } + else { + // read next character + char buffer[1]; + read(stream, buffer, 1); + + // verify match + if (buffer[0] != *fmt) { + LOG((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0])); + throw XIOReadMismatch(); + } + + // next format character + ++fmt; + } + } + + va_end(args); +} + +UInt32 +CProtocolUtil::getLength(const char* fmt, va_list args) +{ + UInt32 n = 0; + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': + assert(len == 1 || len == 2 || len == 4); + (void)va_arg(args, UInt32); + break; + + case 'I': + assert(len == 1 || len == 2 || len == 4); + switch (len) { + case 1: + len = (va_arg(args, std::vector*))->size() + 4; + break; + + case 2: + len = 2 * (va_arg(args, std::vector*))->size() + 4; + break; + + case 4: + len = 4 * (va_arg(args, std::vector*))->size() + 4; + break; + } + break; + + case 's': + assert(len == 0); + len = (va_arg(args, CString*))->size() + 4; + (void)va_arg(args, UInt8*); + break; + + case 'S': + assert(len == 0); + len = va_arg(args, UInt32) + 4; + (void)va_arg(args, UInt8*); + break; + + case '%': + assert(len == 0); + len = 1; + break; + + default: + assert(0 && "invalid format specifier"); + } + + // accumulate size + n += len; + ++fmt; + } + else { + // regular character + ++n; + ++fmt; + } + } + return n; +} + +void +CProtocolUtil::writef(void* buffer, const char* fmt, va_list args) +{ + UInt8* dst = reinterpret_cast(buffer); + + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': { + const UInt32 v = va_arg(args, UInt32); + switch (len) { + case 1: + // 1 byte integer + *dst++ = static_cast(v & 0xff); + break; + + case 2: + // 2 byte integer + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + break; + + case 4: + // 4 byte integer + *dst++ = static_cast((v >> 24) & 0xff); + *dst++ = static_cast((v >> 16) & 0xff); + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + break; + + default: + assert(0 && "invalid integer format length"); + return; + } + break; + } + + case 'I': { + switch (len) { + case 1: { + // 1 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { + *dst++ = (*list)[i]; + } + break; + } + + case 2: { + // 2 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { + const UInt16 v = (*list)[i]; + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + } + break; + } + + case 4: { + // 4 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { + const UInt32 v = (*list)[i]; + *dst++ = static_cast((v >> 24) & 0xff); + *dst++ = static_cast((v >> 16) & 0xff); + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + } + break; + } + + default: + assert(0 && "invalid integer vector format length"); + return; + } + break; + } + + case 's': { + assert(len == 0); + const CString* src = va_arg(args, CString*); + const UInt32 len = (src != NULL) ? src->size() : 0; + *dst++ = static_cast((len >> 24) & 0xff); + *dst++ = static_cast((len >> 16) & 0xff); + *dst++ = static_cast((len >> 8) & 0xff); + *dst++ = static_cast( len & 0xff); + if (len != 0) { + memcpy(dst, src->data(), len); + dst += len; + } + break; + } + + case 'S': { + assert(len == 0); + const UInt32 len = va_arg(args, UInt32); + const UInt8* src = va_arg(args, UInt8*); + *dst++ = static_cast((len >> 24) & 0xff); + *dst++ = static_cast((len >> 16) & 0xff); + *dst++ = static_cast((len >> 8) & 0xff); + *dst++ = static_cast( len & 0xff); + memcpy(dst, src, len); + dst += len; + break; + } + + case '%': + assert(len == 0); + *dst++ = '%'; + break; + + default: + assert(0 && "invalid format specifier"); + } + + // next format character + ++fmt; + } + else { + // copy regular character + *dst++ = *fmt++; + } + } +} + +UInt32 +CProtocolUtil::eatLength(const char** pfmt) +{ + const char* fmt = *pfmt; + UInt32 n = 0; + for (;;) { + UInt32 d; + switch (*fmt) { + case '0': d = 0; break; + case '1': d = 1; break; + case '2': d = 2; break; + case '3': d = 3; break; + case '4': d = 4; break; + case '5': d = 5; break; + case '6': d = 6; break; + case '7': d = 7; break; + case '8': d = 8; break; + case '9': d = 9; break; + default: *pfmt = fmt; return n; + } + n = 10 * n + d; + ++fmt; + } +} + +void +CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) +{ + assert(stream != NULL); + assert(vbuffer != NULL); + + UInt8* buffer = reinterpret_cast(vbuffer); + while (count > 0) { + // read more + UInt32 n = stream->read(buffer, count, -1.0); + + // bail if stream has hungup + if (n == 0) { + LOG((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count)); + throw XIOEndOfStream(); + } + + // prepare for next read + buffer += n; + count -= n; + } +} + + +// +// XIOReadMismatch +// + +CString +XIOReadMismatch::getWhat() const throw() +{ + return format("XIOReadMismatch", "CProtocolUtil::readf() mismatch"); +} diff --git a/lib/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h new file mode 100644 index 0000000..1e0db39 --- /dev/null +++ b/lib/synergy/CProtocolUtil.h @@ -0,0 +1,90 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPROTOCOLUTIL_H +#define CPROTOCOLUTIL_H + +#include "BasicTypes.h" +#include "XIO.h" +#include + +class IInputStream; +class IOutputStream; + +//! Synergy protocol utilities +/*! +This class provides various functions for implementing the synergy +protocol. +*/ +class CProtocolUtil { +public: + //! Write formatted data + /*! + Write formatted binary data to a stream. \c fmt consists of + regular characters and format specifiers. Format specifiers + begin with \%. All characters not part of a format specifier + are regular and are transmitted unchanged. + + Format specifiers are: + - \%\% -- literal `\%' + - \%1i -- converts integer argument to 1 byte integer + - \%2i -- converts integer argument to 2 byte integer in NBO + - \%4i -- converts integer argument to 4 byte integer in NBO + - \%1I -- converts std::vector* to 1 byte integers + - \%2I -- converts std::vector* to 2 byte integers in NBO + - \%4I -- converts std::vector* to 4 byte integers in NBO + - \%s -- converts CString* to stream of bytes + - \%S -- converts integer N and const UInt8* to stream of N bytes + */ + static void writef(IOutputStream*, + const char* fmt, ...); + + //! Read formatted data + /*! + Read formatted binary data from a buffer. This performs the + reverse operation of writef(). + + Format specifiers are: + - \%\% -- read (and discard) a literal `\%' + - \%1i -- reads a 1 byte integer; argument is a SInt32* or UInt32* + - \%2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32* + - \%4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* + - \%1I -- reads 1 byte integers; arg is std::vector* + - \%2I -- reads NBO 2 byte integers; arg is std::vector* + - \%4I -- reads NBO 4 byte integers; arg is std::vector* + - \%s -- reads bytes; argument must be a CString*, \b not a char* + */ + static void readf(IInputStream*, + const char* fmt, ...); + +private: + static UInt32 getLength(const char* fmt, va_list); + static void writef(void*, const char* fmt, va_list); + static UInt32 eatLength(const char** fmt); + static void read(IInputStream*, void*, UInt32); +}; + +//! Mismatched read exception +/*! +Thrown by CProtocolUtil::readf() when the data being read does not +match the format. +*/ +class XIOReadMismatch : public XIO { +public: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +#endif + diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp new file mode 100644 index 0000000..3bf40d4 --- /dev/null +++ b/lib/synergy/CSecondaryScreen.cpp @@ -0,0 +1,381 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CSecondaryScreen.h" +#include "IScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" + +// +// CSecondaryScreen +// + +CSecondaryScreen::CSecondaryScreen() : + m_remoteReady(false), + m_active(false), + m_toggleKeys(0), + m_screenSaverSync(true) +{ + // do nothing +} + +CSecondaryScreen::~CSecondaryScreen() +{ + // do nothing +} + +void +CSecondaryScreen::mainLoop() +{ + // change our priority + CThread::getCurrentThread().setPriority(-14); + + // run event loop + try { + LOG((CLOG_DEBUG "entering event loop")); + onPreMainLoop(); + getScreen()->mainLoop(); + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostMainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CSecondaryScreen::exitMainLoop() +{ + getScreen()->exitMainLoop(); +} + +void +CSecondaryScreen::open() +{ + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window. pretend we're active so + // we don't try to show our window until later. + { + CLock lock(&m_mutex); + assert(m_active == false); + m_active = true; + } + createWindow(); + { + CLock lock(&m_mutex); + m_active = false; + } + + // subclass hook + onPostOpen(); + + // reset options + resetOptions(); + } + catch (...) { + close(); + throw; + } +} + +void +CSecondaryScreen::close() +{ + onPreClose(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CSecondaryScreen::remoteControl() +{ + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); + } + + // update keyboard state + updateKeys(); + + // now remote ready. fake being active for call to leave(). + bool screenSaverSync; + { + CLock lock(&m_mutex); + m_remoteReady = true; + m_active = true; + + // copy screen saver synchronization state + screenSaverSync = m_screenSaverSync; + } + + // disable the screen saver if synchronization is enabled + if (screenSaverSync) { + getScreen()->openScreensaver(false); + } + + // hide the cursor + leave(); +} + +void +CSecondaryScreen::localControl() +{ + getScreen()->closeScreensaver(); + + // not remote ready anymore + CLock lock(&m_mutex); + m_remoteReady = false; +} + +void +CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) +{ + CLock lock(&m_mutex); + assert(m_active == false); + + LOG((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); + + getScreen()->syncDesktop(); + + // now active + m_active = true; + + // subclass hook + onPreEnter(); + + // update our keyboard state to reflect the local state + updateKeys(); + + // remember toggle key state + m_toggleKeys = getToggleState(); + + // toggle modifiers that don't match the desired state + setToggleState(mask); + + // warp to requested location + warpCursor(x, y); + + // show mouse + hideWindow(); + + // subclass hook + onPostEnter(); +} + +void +CSecondaryScreen::leave() +{ + LOG((CLOG_INFO "leaving screen")); + CLock lock(&m_mutex); + assert(m_active == true); + + getScreen()->syncDesktop(); + + // subclass hook + onPreLeave(); + + // restore toggle key state + setToggleState(m_toggleKeys); + + // warp and hide mouse + SInt32 x, y; + getScreen()->getCursorCenter(x, y); + showWindow(x, y); + + // subclass hook + onPostLeave(); + + // not active anymore + m_active = false; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); +} + +void +CSecondaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CSecondaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +void +CSecondaryScreen::screensaver(bool activate) +{ + // get screen saver synchronization flag + bool screenSaverSync; + { + CLock lock(&m_mutex); + screenSaverSync = m_screenSaverSync; + } + + // activate/deactivation screen saver iff synchronization enabled + if (screenSaverSync) { + getScreen()->screensaver(activate); + } +} + +void +CSecondaryScreen::resetOptions() +{ + // set screen saver synchronization flag and see if we need to + // update the screen saver synchronization. + bool screenSaverSyncOn; + { + CLock lock(&m_mutex); + screenSaverSyncOn = (!m_screenSaverSync && m_remoteReady); + m_screenSaverSync = true; + } + + // update screen saver synchronization + if (screenSaverSyncOn) { + getScreen()->openScreensaver(false); + } +} + +void +CSecondaryScreen::setOptions(const COptionsList& options) +{ + // update options + bool updateScreenSaverSync = false; + bool oldScreenSaverSync; + { + CLock lock(&m_mutex); + oldScreenSaverSync = m_screenSaverSync; + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionScreenSaverSync) { + updateScreenSaverSync = true; + m_screenSaverSync = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off")); + } + } + if (!m_remoteReady || oldScreenSaverSync == m_screenSaverSync) { + updateScreenSaverSync = false; + } + } + + // update screen saver synchronization + if (updateScreenSaverSync) { + if (oldScreenSaverSync) { + getScreen()->closeScreensaver(); + } + else { + getScreen()->openScreensaver(false); + } + } +} + +bool +CSecondaryScreen::isActive() const +{ + CLock lock(&m_mutex); + return m_active; +} + +void +CSecondaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +void +CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + getScreen()->syncDesktop(); + getScreen()->getShape(x, y, w, h); +} + +void +CSecondaryScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + getScreen()->syncDesktop(); + getScreen()->getCursorPos(x, y); +} + +void +CSecondaryScreen::onPreMainLoop() +{ + // do nothing +} + +void +CSecondaryScreen::onPostMainLoop() +{ + // do nothing +} + +void +CSecondaryScreen::onPreOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPostOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPreClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPostClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPreEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPostEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPreLeave() +{ + // do nothing +} + +void +CSecondaryScreen::onPostLeave() +{ + // do nothing +} diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h new file mode 100644 index 0000000..f1790a8 --- /dev/null +++ b/lib/synergy/CSecondaryScreen.h @@ -0,0 +1,390 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSECONDARYSCREEN_H +#define CSECONDARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "OptionTypes.h" +#include "CMutex.h" + +class IClipboard; +class IScreen; + +//! Generic client-side screen +/*! +This is a platform independent base class for secondary screen +implementations. A secondary screen is a client-side screen. +Each platform will derive a class from CSecondaryScreen to handle +platform dependent operations. +*/ +class CSecondaryScreen { +public: + CSecondaryScreen(); + virtual ~CSecondaryScreen(); + + //! @name manipulators + //@{ + + //! Open screen + /*! + Opens the screen. It also causes events to the reported to an + IScreenReceiver (which is set through some other interface). + Calls close() before returning (rethrowing) if it fails for any + reason. + */ + void open(); + + //! Run event loop + /*! + Run the screen's event loop. This returns when it detects + the application should terminate or when exitMainLoop() is called. + mainLoop() may only be called between open() and close(). + */ + void mainLoop(); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). + */ + void exitMainLoop(); + + //! Prepare for remote control + /*! + Prepares the screen for remote control by the server. In + particular, it disables the screen saver. + */ + void remoteControl(); + + //! Release from remote control + /*! + Cleans up the screen from remote control by the server. In + particular, it enables the screen saver. It also synthesizes + key up events for any keys that are logically down; without + this the client will leave its keyboard in the wrong logical + state. + */ + void localControl(); + + //! Close screen + /*! + Closes the screen. + */ + void close(); + + //! Enter screen + /*! + Called when the user navigates to this secondary screen. Warps + the cursor to the absolute coordinates \c x,y and unhides + it. Also prepares to synthesize input events. + */ + void enter(SInt32 x, SInt32 y, KeyModifierMask mask); + + //! Leave screen + /*! + Called when the user navigates off the secondary screen. Cleans + up input event synthesis and hides the cursor. + */ + void leave(); + + //! Set clipboard + /*! + Sets the system's clipboard contents. This is usually called + soon after an enter(). + */ + void setClipboard(ClipboardID, const IClipboard*); + + //! Grab clipboard + /*! + Grabs (i.e. take ownership of) the system clipboard. + */ + void grabClipboard(ClipboardID); + + //! Activate/deactivate screen saver + /*! + Forcibly activates the screen saver if \c activate is true otherwise + forcibly deactivates it. + */ + void screensaver(bool activate); + + //! Notify of key press + /*! + Synthesize key events to generate a press of key \c id. If possible + match the given modifier mask. The KeyButton identifies the physical + key on the server that generated this key down. The client must + ensure that a key up or key repeat that uses the same KeyButton will + synthesize an up or repeat for the same client key synthesized by + keyDown(). + */ + virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of key repeat + /*! + Synthesize key events to generate a press and release of key \c id + \c count times. If possible match the given modifier mask. + */ + virtual void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton) = 0; + + //! Notify of key release + /*! + Synthesize key events to generate a release of key \c id. If possible + match the given modifier mask. + */ + virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of mouse press + /*! + Synthesize mouse events to generate a press of mouse button \c id. + */ + virtual void mouseDown(ButtonID id) = 0; + + //! Notify of mouse release + /*! + Synthesize mouse events to generate a release of mouse button \c id. + */ + virtual void mouseUp(ButtonID id) = 0; + + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion to the absolute + screen position \c xAbs,yAbs. + */ + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + + //! Notify of mouse wheel motion + /*! + Synthesize mouse events to generate mouse wheel motion of \c delta. + \c delta is positive for motion away from the user and negative for + motion towards the user. Each wheel click should generate a delta + of +/-120. + */ + virtual void mouseWheel(SInt32 delta) = 0; + + //! Notify of options changes + /*! + Reset all options to their default values. Overrides should call + the superclass's method. + */ + virtual void resetOptions(); + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. Overrides + should call the superclass's method. + */ + virtual void setOptions(const COptionsList& options); + + //@} + //! @name accessors + //@{ + + //! Test if active + /*! + Returns true iff the screen is active (i.e. the user has entered + the screen). Note this is the reverse of a primary screen. + */ + bool isActive() const; + + //! Get clipboard + /*! + Saves the contents of the system clipboard indicated by \c id. + */ + void getClipboard(ClipboardID id, IClipboard*) const; + + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c width and \c height. + */ + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + + //! Get cursor position + /*! + Return the current position of the cursor in \c x,y. + */ + virtual void getCursorPos(SInt32& x, SInt32& y) const; + + //! Get screen + /*! + Return the platform dependent screen. + */ + virtual IScreen* getScreen() const = 0; + + //@} + +protected: + //! Pre-mainLoop() hook + /*! + Called on entry to mainLoop(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreMainLoop(); + + //! Post-mainLoop() hook + /*! + Called on exit from mainLoop(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostMainLoop(); + + //! Pre-open() hook + /*! + Called on entry to open(). Override to perform platform specific + operations. Default does nothing. May throw. + */ + virtual void onPreOpen(); + + //! Post-open() hook + /*! + Called on exit from open() iff the open was successful. Default + does nothing. May throw. + */ + virtual void onPostOpen(); + + //! Pre-close() hook + /*! + Called on entry to close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPreClose(); + + //! Post-close() hook + /*! + Called on exit from close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostClose(); + + //! Pre-enter() hook + /*! + Called on entry to enter() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreEnter(); + + //! Post-enter() hook + /*! + Called on exit from enter(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostEnter(); + + //! Pre-leave() hook + /*! + Called on entry to leave() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ + virtual void onPreLeave(); + + //! Post-leave() hook + /*! + Called on exit from leave(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ + virtual void onPostLeave(); + + //! Create window + /*! + Called to create the window. This window is generally used to + receive events and hide the cursor. + */ + virtual void createWindow() = 0; + + //! Destroy window + /*! + Called to destroy the window created by createWindow(). + */ + virtual void destroyWindow() = 0; + + //! Show window + /*! + Called when the user navigates off this secondary screen. It needn't + actually show the window created by createWindow() but it must move + the cursor to x,y, hide it, and clean up event synthesis. + */ + virtual void showWindow(SInt32 x, SInt32 y) = 0; + + //! Hide window + /*! + Called when the user navigates to this secondary screen. It should + hide the window (if shown), show the cursor, and prepare to synthesize + input events. + */ + virtual void hideWindow() = 0; + + //! Warp cursor + /*! + Warp the cursor to the absolute coordinates \c x,y. + */ + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + //! Synchronize key state + /*! + Check the current keyboard state. Normally a screen will save + the keyboard state in this method and use this shadow state + when synthesizing events. + */ + virtual void updateKeys() = 0; + + //! Release keys + /*! + Synthesizes key release event for any key that our key state + says is down. + */ + virtual void releaseKeys() = 0; + + //! Synchronize toggle key state + /*! + Toggle modifiers that don't match the given state so that they do. + */ + virtual void setToggleState(KeyModifierMask) = 0; + + //! Get the toggle key state + /*! + Returns the current state of the toggle keys. + */ + virtual KeyModifierMask getToggleState() const = 0; + +private: + CMutex m_mutex; + + // true if ready for remote control + bool m_remoteReady; + + // m_active is true if this screen has been entered + bool m_active; + + // the toggle key state when this screen was last entered + KeyModifierMask m_toggleKeys; + + // true if screen saver should be synchronized to server + bool m_screenSaverSync; +}; + +#endif diff --git a/lib/synergy/ClipboardTypes.h b/lib/synergy/ClipboardTypes.h new file mode 100644 index 0000000..a638e82 --- /dev/null +++ b/lib/synergy/ClipboardTypes.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CLIPBOARDTYPES_H +#define CLIPBOARDTYPES_H + +#include "BasicTypes.h" + +//! Clipboard ID +/*! +Type to hold a clipboard identifier. +*/ +typedef UInt8 ClipboardID; + +//! @name Clipboard identifiers +//@{ +// clipboard identifiers. kClipboardClipboard is what is normally +// considered the clipboard (e.g. the cut/copy/paste menu items +// affect it). kClipboardSelection is the selection on those +// platforms that can treat the selection as a clipboard (e.g. X +// windows). clipboard identifiers must be sequential starting +// at zero. +static const ClipboardID kClipboardClipboard = 0; +static const ClipboardID kClipboardSelection = 1; + +// the number of clipboards (i.e. one greater than the last clipboard id) +static const ClipboardID kClipboardEnd = 2; +//@} + +#endif diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h new file mode 100644 index 0000000..01ab441 --- /dev/null +++ b/lib/synergy/IClient.h @@ -0,0 +1,214 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ICLIENT_H +#define ICLIENT_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "OptionTypes.h" +#include "CString.h" + +//! Client interface +/*! +This interface defines the methods necessary for the server to +communicate with a client. +*/ +class IClient : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open client + /*! + Open the client. Throw if the client cannot be opened. If the + screen cannot be opened but retrying later may succeed then throw + XScreenUnavailable. + */ + virtual void open() = 0; + + //! Client main loop + /*! + Run client's event loop. This method is typically called in a + separate thread and is exited by cancelling the thread. This + must be called between a successful open() and close(). + + (cancellation point) + */ + virtual void mainLoop() = 0; + + //! Close client + /*! + Close the client. + */ + virtual void close() = 0; + + //! Enter screen + /*! + Enter the screen. The cursor should be warped to \c xAbs,yAbs. + The client should record seqNum for future reporting of + clipboard changes. \c mask is the expected toggle button state + and the client should update its state to match. \c forScreensaver + is true iff the screen is being entered because the screen saver is + starting. + */ + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver) = 0; + + //! Leave screen + /*! + Leave the screen. Return false iff the user may not leave the + client's screen (because, for example, a button is down). + */ + virtual bool leave() = 0; + + //! Set clipboard + /*! + Update the client's clipboard. This implies that the client's + clipboard is now up to date. If the client's clipboard was + already known to be up to date then this may do nothing. \c data + has marshalled clipboard data. + */ + virtual void setClipboard(ClipboardID, const CString& data) = 0; + + //! Grab clipboard + /*! + Grab (i.e. take ownership of) the client's clipboard. Since this + is called when another client takes ownership of the clipboard it + implies that the client's clipboard is out of date. + */ + virtual void grabClipboard(ClipboardID) = 0; + + //! Mark clipboard dirty + /*! + Mark the client's clipboard as dirty (out of date) or clean (up to + date). + */ + virtual void setClipboardDirty(ClipboardID, bool dirty) = 0; + + //! Notify of key press + /*! + Synthesize key events to generate a press of key \c id. If possible + match the given modifier mask. The KeyButton identifies the physical + key on the server that generated this key down. The client must + ensure that a key up or key repeat that uses the same KeyButton will + synthesize an up or repeat for the same client key synthesized by + keyDown(). + */ + virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of key repeat + /*! + Synthesize key events to generate a press and release of key \c id + \c count times. If possible match the given modifier mask. + */ + virtual void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton) = 0; + + //! Notify of key release + /*! + Synthesize key events to generate a release of key \c id. If possible + match the given modifier mask. + */ + virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0; + + //! Notify of mouse press + /*! + Synthesize mouse events to generate a press of mouse button \c id. + */ + virtual void mouseDown(ButtonID id) = 0; + + //! Notify of mouse release + /*! + Synthesize mouse events to generate a release of mouse button \c id. + */ + virtual void mouseUp(ButtonID id) = 0; + + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion to the absolute + screen position \c xAbs,yAbs. + */ + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + + //! Notify of mouse wheel motion + /*! + Synthesize mouse events to generate mouse wheel motion of \c delta. + \c delta is positive for motion away from the user and negative for + motion towards the user. Each wheel click should generate a delta + of +/-120. + */ + virtual void mouseWheel(SInt32 delta) = 0; + + //! Notify of screen saver change + virtual void screensaver(bool activate) = 0; + + //! Notify of options changes + /*! + Reset all options to their default values. + */ + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + + //@} + //! @name accessors + //@{ + + //! Get client name + /*! + Return the client's name. + */ + virtual CString getName() const = 0; + + //! Get jump zone size + /*! + Called to get the jump zone size. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c width and \c height. + */ + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + //! Get cursor center position + /*! + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. + */ + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h new file mode 100644 index 0000000..25defae --- /dev/null +++ b/lib/synergy/IClipboard.h @@ -0,0 +1,117 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ICLIPBOARD_H +#define ICLIPBOARD_H + +#include "IInterface.h" +#include "CString.h" +#include "BasicTypes.h" + +//! Clipboard interface +/*! +This interface defines the methods common to all clipboards. +*/ +class IClipboard : public IInterface { +public: + //! Timestamp type + /*! + Timestamp type. Timestamps are in milliseconds from some + arbitrary starting time. Timestamps will wrap around to 0 + after about 49 3/4 days. + */ + typedef UInt32 Time; + + //! Clipboard formats + /*! + The list of known clipboard formats. kNumFormats must be last and + formats must be sequential starting from zero. Clipboard data set + via add() and retrieved via get() must be in one of these formats. + Platform dependent clipboard subclasses can and should present any + suitable formats derivable from these formats. + */ + enum EFormat { + kText, //!< Text format, UTF-8, newline is LF + kNumFormats //!< The number of clipboard formats + }; + + //! @name manipulators + //@{ + + //! Empty clipboard + /*! + Take ownership of the clipboard and clear all data from it. + This must be called between a successful open() and close(). + Return false if the clipboard ownership could not be taken; + the clipboard should not be emptied in this case. + */ + virtual bool empty() = 0; + + //! Add data + /*! + Add data in the given format to the clipboard. May only be + called after a successful empty(). + */ + virtual void add(EFormat, const CString& data) = 0; + + //@} + //! @name accessors + //@{ + + //! Open clipboard + /*! + Open the clipboard. Return true iff the clipboard could be + opened. If open() returns true then the client must call + close() at some later time; if it returns false then close() + must not be called. \c time should be the current time or + a time in the past when the open should effectively have taken + place. + */ + virtual bool open(Time time) const = 0; + + //! Close clipboard + /*! + Close the clipboard. close() must match a preceding successful + open(). This signals that the clipboard has been filled with + all the necessary data or all data has been read. It does not + mean the clipboard ownership should be released (if it was + taken). + */ + virtual void close() const = 0; + + //! Get time + /*! + Return the timestamp passed to the last successful open(). + */ + virtual Time getTime() const = 0; + + //! Check for data + /*! + Return true iff the clipboard contains data in the given + format. Must be called between a successful open() and close(). + */ + virtual bool has(EFormat) const = 0; + + //! Get data + /*! + Return the data in the given format. Returns the empty string + if there is no data in that format. Must be called between + a successful open() and close(). + */ + virtual CString get(EFormat) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IPrimaryScreenFactory.h b/lib/synergy/IPrimaryScreenFactory.h new file mode 100644 index 0000000..630449a --- /dev/null +++ b/lib/synergy/IPrimaryScreenFactory.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IPRIMARYSCREENFACTORY_H +#define IPRIMARYSCREENFACTORY_H + +#include "IInterface.h" + +class CPrimaryScreen; +class IPrimaryScreenReceiver; +class IScreenReceiver; + +//! Primary screen factory interface +/*! +This interface provides factory methods to create primary screens. +*/ +class IPrimaryScreenFactory : public IInterface { +public: + //! Create screen + /*! + Create and return a primary screen. The caller must delete the + returned object. + */ + virtual CPrimaryScreen* + create(IScreenReceiver*, IPrimaryScreenReceiver*) = 0; +}; + +#endif diff --git a/lib/synergy/IPrimaryScreenReceiver.h b/lib/synergy/IPrimaryScreenReceiver.h new file mode 100644 index 0000000..44b1bee --- /dev/null +++ b/lib/synergy/IPrimaryScreenReceiver.h @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IPRIMARYSCREENRECEIVER_H +#define IPRIMARYSCREENRECEIVER_H + +#include "IInterface.h" +#include "KeyTypes.h" +#include "MouseTypes.h" + +//! Primary screen event receiver interface +/*! +The interface for receiving notification of events on the primary +screen. The server implements this interface to handle user input. +Platform dependent primary screen implementation will need to take +an IPrimaryScreenReceiver* and notify it of events. +*/ +class IPrimaryScreenReceiver : public IInterface { +public: + //! Notify of screen saver change + /*! + Called when the screensaver is activated or deactivated. + */ + virtual void onScreensaver(bool activated) = 0; + + //! Notify of one-shot timer expiration + /*! + Called when a one-shot timer expires. + */ + virtual void onOneShotTimerExpired(UInt32 id) = 0; + + // call to notify of events. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. + //! Notify of key press + virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton) = 0; + //! Notify of key release + virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton) = 0; + //! Notify of key repeat + virtual void onKeyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton) = 0; + //! Notify of mouse button press + virtual void onMouseDown(ButtonID) = 0; + //! Notify of mouse button release + virtual void onMouseUp(ButtonID) = 0; + //! Notify of mouse motion + /*! + Called when the mouse has moved while on the primary screen. \c x + and \c y are the absolute screen position of the mouse. Return + true iff the mouse enters a jump zone and jumps. + */ + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + //! Notify of mouse motion + /*! + Called when the mouse has moved while on the secondary screen. + \c dx and \c dy are the relative motion from the last position. + */ + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + //! Notify of mouse wheen motion + virtual void onMouseWheel(SInt32 delta) = 0; +}; + +#endif diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h new file mode 100644 index 0000000..1676982 --- /dev/null +++ b/lib/synergy/IScreen.h @@ -0,0 +1,150 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREEN_H +#define ISCREEN_H + +#include "IInterface.h" +#include "ClipboardTypes.h" + +class IClipboard; + +//! Screen interface +/*! +This interface defines the methods common to all platform dependent +screen implementations that are use by both primary and secondary +screens. +*/ +class IScreen : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open screen + /*! + Called to open and initialize the screen. Throw XScreenUnavailable + if the screen cannot be opened but retrying later may succeed. + Otherwise throw some other XScreenOpenFailure exception. + */ + virtual void open() = 0; + + //! Run event loop + /*! + Run the event loop and return when exitMainLoop() is called. + This must be called between a successful open() and close(). + */ + virtual void mainLoop() = 0; + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ + virtual void exitMainLoop() = 0; + + //! Close screen + /*! + Called to close the screen. close() should quietly ignore calls + that don't have a matching successful call to open(). + */ + virtual void close() = 0; + + //! Set clipboard + /*! + Set the contents of the system clipboard indicated by \c id. + */ + virtual bool setClipboard(ClipboardID id, const IClipboard*) = 0; + + //! Check clipboard owner + /*! + Check ownership of all clipboards and notify an IScreenReceiver (set + through some other interface) if any changed. This is used as a + backup in case the system doesn't reliably report clipboard ownership + changes. + */ + virtual void checkClipboards() = 0; + + //! Open screen saver + /*! + Open the screen saver. If \c notify is true then this object must + call an IScreenEventHandler's (set through some other interface) + onScreenSaver() when the screensaver activates or deactivates until + it's closed. If \c notify is false then the screen saver is + disabled on open and restored on close. + */ + virtual void openScreensaver(bool notify) = 0; + + //! Close screen saver + /*! + // Close the screen saver. Stop reporting screen saver activation + and deactivation and, if the screen saver was disabled by + openScreensaver(), enable the screen saver. + */ + virtual void closeScreensaver() = 0; + + //! Activate/deactivate screen saver + /*! + Forcibly activate the screen saver if \c activate is true otherwise + forcibly deactivate it. + */ + virtual void screensaver(bool activate) = 0; + + //! Attach to desktop + /*! + Called to ensure that this thread is attached to the visible desktop. + This is mainly intended for microsoft windows which has an artificial + distinction between desktops where a thread cannot interact with the + visible desktop unless the thread is attached to that desktop. Since + it doesn't report when the visible desktop changes we must poll. + */ + virtual void syncDesktop() = 0; + + //@} + //! @name accessors + //@{ + + //! Get clipboard + /*! + Save the contents of the clipboard indicated by \c id and return + true iff successful. + */ + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c w (width) and \c h (height). + */ + virtual void getShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const = 0; + + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + //! Get cursor center position + /*! + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. + */ + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IScreenEventHandler.h b/lib/synergy/IScreenEventHandler.h new file mode 100644 index 0000000..a68858e --- /dev/null +++ b/lib/synergy/IScreenEventHandler.h @@ -0,0 +1,79 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREENEVENTHANDLER_H +#define ISCREENEVENTHANDLER_H + +#include "IInterface.h" + +// the platform screen should define this +class CEvent; + +class IScreen; + +//! Screen event handler interface +/*! +This is the interface through which IScreen sends notification of events. +Each platform will derive two types from IScreenEventHandler, one +for handling events on the primary screen and one for the +secondary screen. The header file with the IScreen subclass for +each platform should define the CEvent type, which depends on the +type of native events for that platform. +*/ +class IScreenEventHandler : public IInterface { +public: + //! @name manipulators + //@{ + + //! Notify of screen saver change + /*! + Called when the screensaver is activated or deactivated. + */ + virtual void onScreensaver(bool activated) = 0; + + //! Event filtering + /*! + Called for each event before event translation and dispatch. Return + true to skip translation and dispatch. Subclasses should call the + superclass's version first and return true if it returns true. + */ + virtual bool onPreDispatch(const CEvent* event) = 0; + + //! Event handling + /*! + Called to handle an event. Iff the event was handled return true and + store the result, if any, in event->m_result, which defaults to zero. + */ + virtual bool onEvent(CEvent* event) = 0; + + //! Notify of one-shot timer expiration + /*! + Called when a one-shot timer expires. + */ + virtual void onOneShotTimerExpired(UInt32 id) = 0; + + //@} + //! @name accessors + //@{ + + //! Get jump zone size + /*! + Called to get the jump zone size. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IScreenReceiver.h b/lib/synergy/IScreenReceiver.h new file mode 100644 index 0000000..0dd5507 --- /dev/null +++ b/lib/synergy/IScreenReceiver.h @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREENRECEIVER_H +#define ISCREENRECEIVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "ProtocolTypes.h" +#include "CString.h" + +//! Screen event receiver interface +/*! +This interface defines the methods common to most types that receive +events for changes to a screen. Note that the methods in this +interface are similar to the methods in IServer but have different +parameters. This interface is suitable for client-side types. +*/ +class IScreenReceiver : public IInterface { +public: + //! Notify of error + /*! + Called when the screen is unexpectedly closing. This implies that + the screen is no longer usable and that the program should close + the screen and probably terminate. + */ + virtual void onError() = 0; + + //! Notify of client screen change + /*! + Called when the client's info has changed. For example, when the + screen resolution has changed. + */ + virtual void onInfoChanged(const CClientInfo&) = 0; + + //! Notify of clipboad grab + /*! + Called when the clipboard was grabbed by another program and, + therefore, we no longer own it. Returns true if the grab was + honored, false otherwise. + */ + virtual bool onGrabClipboard(ClipboardID) = 0; + + //! Notify of new clipboard data + /*! + Called when the data on the clipboard has changed because some + other program has changed it. \c data will have marshalled + clipboard data. + */ + virtual void onClipboardChanged(ClipboardID, + const CString& data) = 0; +}; + +#endif diff --git a/lib/synergy/IScreenSaver.h b/lib/synergy/IScreenSaver.h new file mode 100644 index 0000000..f2da811 --- /dev/null +++ b/lib/synergy/IScreenSaver.h @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREENSAVER_H +#define ISCREENSAVER_H + +#include "IInterface.h" + +//! Screen saver interface +/*! +This interface defines the methods common to all screen savers. +*/ +class IScreenSaver : public IInterface { +public: + // note -- the c'tor/d'tor must *not* enable/disable the screen saver + + //! @name manipulators + //@{ + + //! Enable screen saver + /*! + Enable the screen saver, restoring the screen saver settings to + what they were when disable() was previously called. If disable() + wasn't previously called then it should keep the current settings + or use reasonable defaults. + */ + virtual void enable() = 0; + + //! Disable screen saver + /*! + Disable the screen saver, saving the old settings for the next + call to enable(). + */ + virtual void disable() = 0; + + //! Activate screen saver + /*! + Activate (i.e. show) the screen saver. + */ + virtual void activate() = 0; + + //! Deactivate screen saver + /*! + Deactivate (i.e. hide) the screen saver, reseting the screen saver + timer. + */ + virtual void deactivate() = 0; + + //@} + //! @name accessors + //@{ + + //! Test if screen saver on + /*! + Returns true iff the screen saver is currently active (showing). + */ + virtual bool isActive() const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/ISecondaryScreenFactory.h b/lib/synergy/ISecondaryScreenFactory.h new file mode 100644 index 0000000..03667ec --- /dev/null +++ b/lib/synergy/ISecondaryScreenFactory.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISECONDARYSCREENFACTORY_H +#define ISECONDARYSCREENFACTORY_H + +#include "IInterface.h" + +class CSecondaryScreen; +class IScreenReceiver; + +//! Secondary screen factory interface +/*! +This interface provides factory methods to create secondary screens. +*/ +class ISecondaryScreenFactory : public IInterface { +public: + //! Create screen + /*! + Create and return a secondary screen. The caller must delete the + returned object. + */ + virtual CSecondaryScreen* + create(IScreenReceiver*) = 0; +}; + +#endif diff --git a/lib/synergy/IServer.h b/lib/synergy/IServer.h new file mode 100644 index 0000000..42b7a67 --- /dev/null +++ b/lib/synergy/IServer.h @@ -0,0 +1,75 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISERVER_H +#define ISERVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "CString.h" + +class CClientInfo; + +//! Server interface +/*! +This interface defines the methods necessary for clients to +communicate with the server. Note that the methods in this +interface are similar to the methods in IScreenReceiver but +include extra parameters. This interface is suitable for +server-side client proxies. Client-side objects should use +the IScreenReceiver interface since the extra parameters are +meaningless on the client-side. +*/ +class IServer : public IInterface { +public: + //! @name manipulators + //@{ + + //! Notify of error + /*! + Called when the screen is unexpectedly closing. This implies that + the screen is no longer usable and that the program should close + the screen and probably terminate. + */ + virtual void onError() = 0; + + //! Notify of client screen change + /*! + Called when the client's info has changed. + */ + virtual void onInfoChanged(const CString& clientName, + const CClientInfo&) = 0; + + //! Notify of clipboad grab + /*! + Called when the clipboard was grabbed by another program and, + therefore, we no longer own it. Returns true if the grab was + honored, false otherwise. + */ + virtual bool onGrabClipboard(const CString& clientName, + ClipboardID, UInt32 seqNum) = 0; + + //! Notify of new clipboard data + /*! + Called when the data on the clipboard has changed because some + other program has changed it. \c data has the marshalled clipboard + data. + */ + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data) = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h new file mode 100644 index 0000000..9af4dd0 --- /dev/null +++ b/lib/synergy/KeyTypes.h @@ -0,0 +1,237 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef KEYTYPES_H +#define KEYTYPES_H + +#include "BasicTypes.h" + +//! Key ID +/*! +Type to hold a key symbol identifier. The encoding is UTF-32, using +U+E000 through U+EFFF for the various control keys (e.g. arrow +keys, function keys, modifier keys, etc). +*/ +typedef UInt32 KeyID; + +//! Key Code +/*! +Type to hold a physical key identifier. That is, it identifies a +physical key on the keyboard. +*/ +typedef UInt16 KeyButton; + +//! Modifier key mask +/*! +Type to hold a bitmask of key modifiers (e.g. shift keys). +*/ +typedef UInt32 KeyModifierMask; + +//! Modifier key ID +/*! +Type to hold the id of a key modifier (e.g. a shift key). +*/ +typedef UInt32 KeyModifierID; + +//! @name Modifier key masks +//@{ +static const KeyModifierMask KeyModifierShift = 0x0001; +static const KeyModifierMask KeyModifierControl = 0x0002; +static const KeyModifierMask KeyModifierAlt = 0x0004; +static const KeyModifierMask KeyModifierMeta = 0x0008; +static const KeyModifierMask KeyModifierSuper = 0x0010; +static const KeyModifierMask KeyModifierModeSwitch = 0x0020; +static const KeyModifierMask KeyModifierCapsLock = 0x1000; +static const KeyModifierMask KeyModifierNumLock = 0x2000; +static const KeyModifierMask KeyModifierScrollLock = 0x4000; +//@} + +//! @name Modifier key identifiers +//@{ +static const KeyModifierID kKeyModifierIDNull = 0; +static const KeyModifierID kKeyModifierIDShift = 1; +static const KeyModifierID kKeyModifierIDControl = 2; +static const KeyModifierID kKeyModifierIDAlt = 3; +static const KeyModifierID kKeyModifierIDMeta = 4; +static const KeyModifierID kKeyModifierIDSuper = 5; +static const KeyModifierID kKeyModifierIDLast = 6; +//@} + +//! @name Key identifiers +//@{ +// all identifiers except kKeyNone and those in 0xE000 to 0xE0FF +// inclusive are equal to the corresponding X11 keysym - 0x1000. + +// no key +static const KeyID kKeyNone = 0x0000; + +// TTY functions +static const KeyID kKeyBackSpace = 0xEF08; /* back space, back char */ +static const KeyID kKeyTab = 0xEF09; +static const KeyID kKeyLinefeed = 0xEF0A; /* Linefeed, LF */ +static const KeyID kKeyClear = 0xEF0B; +static const KeyID kKeyReturn = 0xEF0D; /* Return, enter */ +static const KeyID kKeyPause = 0xEF13; /* Pause, hold */ +static const KeyID kKeyScrollLock = 0xEF14; +static const KeyID kKeySysReq = 0xEF15; +static const KeyID kKeyEscape = 0xEF1B; +static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ + +// multi-key character composition +static const KeyID kKeyMultiKey = 0xEF20; /* Multi-key character compose */ + +// cursor control +static const KeyID kKeyHome = 0xEF50; +static const KeyID kKeyLeft = 0xEF51; /* Move left, left arrow */ +static const KeyID kKeyUp = 0xEF52; /* Move up, up arrow */ +static const KeyID kKeyRight = 0xEF53; /* Move right, right arrow */ +static const KeyID kKeyDown = 0xEF54; /* Move down, down arrow */ +static const KeyID kKeyPageUp = 0xEF55; +static const KeyID kKeyPageDown = 0xEF56; +static const KeyID kKeyEnd = 0xEF57; /* EOL */ +static const KeyID kKeyBegin = 0xEF58; /* BOL */ + +// misc functions +static const KeyID kKeySelect = 0xEF60; /* Select, mark */ +static const KeyID kKeyPrint = 0xEF61; +static const KeyID kKeyExecute = 0xEF62; /* Execute, run, do */ +static const KeyID kKeyInsert = 0xEF63; /* Insert, insert here */ +static const KeyID kKeyUndo = 0xEF65; /* Undo, oops */ +static const KeyID kKeyRedo = 0xEF66; /* redo, again */ +static const KeyID kKeyMenu = 0xEF67; +static const KeyID kKeyFind = 0xEF68; /* Find, search */ +static const KeyID kKeyCancel = 0xEF69; /* Cancel, stop, abort, exit */ +static const KeyID kKeyHelp = 0xEF6A; /* Help */ +static const KeyID kKeyBreak = 0xEF6B; +static const KeyID kKeyModeSwitch = 0xEF7E; /* Character set switch */ +static const KeyID kKeyNumLock = 0xEF7F; + +// keypad +static const KeyID kKeyKP_Space = 0xEF80; /* space */ +static const KeyID kKeyKP_Tab = 0xEF89; +static const KeyID kKeyKP_Enter = 0xEF8D; /* enter */ +static const KeyID kKeyKP_F1 = 0xEF91; /* PF1, KP_A, ... */ +static const KeyID kKeyKP_F2 = 0xEF92; +static const KeyID kKeyKP_F3 = 0xEF93; +static const KeyID kKeyKP_F4 = 0xEF94; +static const KeyID kKeyKP_Home = 0xEF95; +static const KeyID kKeyKP_Left = 0xEF96; +static const KeyID kKeyKP_Up = 0xEF97; +static const KeyID kKeyKP_Right = 0xEF98; +static const KeyID kKeyKP_Down = 0xEF99; +static const KeyID kKeyKP_Prior = 0xEF9A; +static const KeyID kKeyKP_PageUp = 0xEF9A; +static const KeyID kKeyKP_Next = 0xEF9B; +static const KeyID kKeyKP_PageDown = 0xEF9B; +static const KeyID kKeyKP_End = 0xEF9C; +static const KeyID kKeyKP_Begin = 0xEF9D; +static const KeyID kKeyKP_Insert = 0xEF9E; +static const KeyID kKeyKP_Delete = 0xEF9F; +static const KeyID kKeyKP_Equal = 0xEFBD; /* equals */ +static const KeyID kKeyKP_Multiply = 0xEFAA; +static const KeyID kKeyKP_Add = 0xEFAB; +static const KeyID kKeyKP_Separator= 0xEFAC; /* separator, often comma */ +static const KeyID kKeyKP_Subtract = 0xEFAD; +static const KeyID kKeyKP_Decimal = 0xEFAE; +static const KeyID kKeyKP_Divide = 0xEFAF; +static const KeyID kKeyKP_0 = 0xEFB0; +static const KeyID kKeyKP_1 = 0xEFB1; +static const KeyID kKeyKP_2 = 0xEFB2; +static const KeyID kKeyKP_3 = 0xEFB3; +static const KeyID kKeyKP_4 = 0xEFB4; +static const KeyID kKeyKP_5 = 0xEFB5; +static const KeyID kKeyKP_6 = 0xEFB6; +static const KeyID kKeyKP_7 = 0xEFB7; +static const KeyID kKeyKP_8 = 0xEFB8; +static const KeyID kKeyKP_9 = 0xEFB9; + +// function keys +static const KeyID kKeyF1 = 0xEFBE; +static const KeyID kKeyF2 = 0xEFBF; +static const KeyID kKeyF3 = 0xEFC0; +static const KeyID kKeyF4 = 0xEFC1; +static const KeyID kKeyF5 = 0xEFC2; +static const KeyID kKeyF6 = 0xEFC3; +static const KeyID kKeyF7 = 0xEFC4; +static const KeyID kKeyF8 = 0xEFC5; +static const KeyID kKeyF9 = 0xEFC6; +static const KeyID kKeyF10 = 0xEFC7; +static const KeyID kKeyF11 = 0xEFC8; +static const KeyID kKeyF12 = 0xEFC9; +static const KeyID kKeyF13 = 0xEFCA; +static const KeyID kKeyF14 = 0xEFCB; +static const KeyID kKeyF15 = 0xEFCC; +static const KeyID kKeyF16 = 0xEFCD; +static const KeyID kKeyF17 = 0xEFCE; +static const KeyID kKeyF18 = 0xEFCF; +static const KeyID kKeyF19 = 0xEFD0; +static const KeyID kKeyF20 = 0xEFD1; +static const KeyID kKeyF21 = 0xEFD2; +static const KeyID kKeyF22 = 0xEFD3; +static const KeyID kKeyF23 = 0xEFD4; +static const KeyID kKeyF24 = 0xEFD5; +static const KeyID kKeyF25 = 0xEFD6; +static const KeyID kKeyF26 = 0xEFD7; +static const KeyID kKeyF27 = 0xEFD8; +static const KeyID kKeyF28 = 0xEFD9; +static const KeyID kKeyF29 = 0xEFDA; +static const KeyID kKeyF30 = 0xEFDB; +static const KeyID kKeyF31 = 0xEFDC; +static const KeyID kKeyF32 = 0xEFDD; +static const KeyID kKeyF33 = 0xEFDE; +static const KeyID kKeyF34 = 0xEFDF; +static const KeyID kKeyF35 = 0xEFE0; + +// modifiers +static const KeyID kKeyShift_L = 0xEFE1; /* Left shift */ +static const KeyID kKeyShift_R = 0xEFE2; /* Right shift */ +static const KeyID kKeyControl_L = 0xEFE3; /* Left control */ +static const KeyID kKeyControl_R = 0xEFE4; /* Right control */ +static const KeyID kKeyCapsLock = 0xEFE5; /* Caps lock */ +static const KeyID kKeyShiftLock = 0xEFE6; /* Shift lock */ +static const KeyID kKeyMeta_L = 0xEFE7; /* Left meta */ +static const KeyID kKeyMeta_R = 0xEFE8; /* Right meta */ +static const KeyID kKeyAlt_L = 0xEFE9; /* Left alt */ +static const KeyID kKeyAlt_R = 0xEFEA; /* Right alt */ +static const KeyID kKeySuper_L = 0xEFEB; /* Left super */ +static const KeyID kKeySuper_R = 0xEFEC; /* Right super */ +static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */ +static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ + +// more function and modifier keys +static const KeyID kKeyLeftTab = 0xEE20; + +// extended keys +static const KeyID kKeyWWWBack = 0xE0A6; +static const KeyID kKeyWWWForward = 0xE0A7; +static const KeyID kKeyWWWRefresh = 0xE0A8; +static const KeyID kKeyWWWStop = 0xE0A9; +static const KeyID kKeyWWWSearch = 0xE0AA; +static const KeyID kKeyWWWFavorites = 0xE0AB; +static const KeyID kKeyWWWHome = 0xE0AC; +static const KeyID kKeyAudioMute = 0xE0AD; +static const KeyID kKeyAudioDown = 0xE0AE; +static const KeyID kKeyAudioUp = 0xE0AF; +static const KeyID kKeyAudioNext = 0xE0B0; +static const KeyID kKeyAudioPrev = 0xE0B1; +static const KeyID kKeyAudioStop = 0xE0B2; +static const KeyID kKeyAudioPlay = 0xE0B3; +static const KeyID kKeyAppMail = 0xE0B4; +static const KeyID kKeyAppMedia = 0xE0B5; +static const KeyID kKeyAppUser1 = 0xE0B6; +static const KeyID kKeyAppUser2 = 0xE0B7; + +//@} + +#endif diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am new file mode 100644 index 0000000..4f1f135 --- /dev/null +++ b/lib/synergy/Makefile.am @@ -0,0 +1,67 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + libsynergy.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libsynergy.a +libsynergy_a_SOURCES = \ + CClipboard.cpp \ + CInputPacketStream.cpp \ + COutputPacketStream.cpp \ + CPrimaryScreen.cpp \ + CProtocolUtil.cpp \ + CSecondaryScreen.cpp \ + XScreen.cpp \ + XSynergy.cpp \ + CClipboard.h \ + CInputPacketStream.h \ + COutputPacketStream.h \ + CPrimaryScreen.h \ + CProtocolUtil.h \ + CSecondaryScreen.h \ + ClipboardTypes.h \ + IClient.h \ + IClipboard.h \ + IPrimaryScreenFactory.h \ + IPrimaryScreenReceiver.h \ + IScreen.h \ + IScreenEventHandler.h \ + IScreenReceiver.h \ + IScreenSaver.h \ + ISecondaryScreenFactory.h \ + IServer.h \ + KeyTypes.h \ + MouseTypes.h \ + OptionTypes.h \ + ProtocolTypes.h \ + XScreen.h \ + XSynergy.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + $(NULL) diff --git a/lib/synergy/Makefile.in b/lib/synergy/Makefile.in new file mode 100644 index 0000000..9af27e8 --- /dev/null +++ b/lib/synergy/Makefile.in @@ -0,0 +1,382 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AWK = @AWK@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +EXEEXT = @EXEEXT@ +HAVE_DOT = @HAVE_DOT@ +INET_ATON_LIBS = @INET_ATON_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +NANOSLEEP_LIBS = @NANOSLEEP_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + libsynergy.dsp \ + $(NULL) + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + + +noinst_LIBRARIES = libsynergy.a +libsynergy_a_SOURCES = \ + CClipboard.cpp \ + CInputPacketStream.cpp \ + COutputPacketStream.cpp \ + CPrimaryScreen.cpp \ + CProtocolUtil.cpp \ + CSecondaryScreen.cpp \ + XScreen.cpp \ + XSynergy.cpp \ + CClipboard.h \ + CInputPacketStream.h \ + COutputPacketStream.h \ + CPrimaryScreen.h \ + CProtocolUtil.h \ + CSecondaryScreen.h \ + ClipboardTypes.h \ + IClient.h \ + IClipboard.h \ + IPrimaryScreenFactory.h \ + IPrimaryScreenReceiver.h \ + IScreen.h \ + IScreenEventHandler.h \ + IScreenReceiver.h \ + IScreenSaver.h \ + ISecondaryScreenFactory.h \ + IServer.h \ + KeyTypes.h \ + MouseTypes.h \ + OptionTypes.h \ + ProtocolTypes.h \ + XScreen.h \ + XSynergy.h \ + $(NULL) + +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + $(NULL) + +subdir = lib/synergy +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libsynergy_a_AR = $(AR) cru +libsynergy_a_LIBADD = +am_libsynergy_a_OBJECTS = CClipboard.$(OBJEXT) \ + CInputPacketStream.$(OBJEXT) COutputPacketStream.$(OBJEXT) \ + CPrimaryScreen.$(OBJEXT) CProtocolUtil.$(OBJEXT) \ + CSecondaryScreen.$(OBJEXT) XScreen.$(OBJEXT) XSynergy.$(OBJEXT) +libsynergy_a_OBJECTS = $(am_libsynergy_a_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/CClipboard.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CInputPacketStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/COutputPacketStream.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CPrimaryScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CProtocolUtil.Po \ +@AMDEP_TRUE@ $(DEPDIR)/CSecondaryScreen.Po $(DEPDIR)/XScreen.Po \ +@AMDEP_TRUE@ $(DEPDIR)/XSynergy.Po +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +CXXFLAGS = @CXXFLAGS@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libsynergy_a_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libsynergy_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/synergy/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +AR = ar + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libsynergy.a: $(libsynergy_a_OBJECTS) $(libsynergy_a_DEPENDENCIES) + -rm -f libsynergy.a + $(libsynergy_a_AR) libsynergy.a $(libsynergy_a_OBJECTS) $(libsynergy_a_LIBADD) + $(RANLIB) libsynergy.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CClipboard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CInputPacketStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/COutputPacketStream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CPrimaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CProtocolUtil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/CSecondaryScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XScreen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/XSynergy.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/synergy/MouseTypes.h b/lib/synergy/MouseTypes.h new file mode 100644 index 0000000..e498855 --- /dev/null +++ b/lib/synergy/MouseTypes.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MOUSETYPES_H +#define MOUSETYPES_H + +#include "BasicTypes.h" + +//! Mouse button ID +/*! +Type to hold a mouse button identifier. +*/ +typedef UInt8 ButtonID; + +//! @name Mouse button identifiers +//@{ +static const ButtonID kButtonNone = 0; +static const ButtonID kButtonLeft = 1; +static const ButtonID kButtonMiddle = 2; +static const ButtonID kButtonRight = 3; +static const ButtonID kButtonExtra0 = 4; +//@} + +#endif diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h new file mode 100644 index 0000000..aaefe7f --- /dev/null +++ b/lib/synergy/OptionTypes.h @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef OPTIONTYPES_H +#define OPTIONTYPES_H + +#include "BasicTypes.h" +#include "stdvector.h" + +//! Option ID +/*! +Type to hold an option identifier. +*/ +typedef UInt32 OptionID; + +//! Option Value +/*! +Type to hold an option value. +*/ +typedef SInt32 OptionValue; + +// for now, options are just pairs of integers +typedef std::vector COptionsList; + +// macro for packing 4 character strings into 4 byte integers +#define OPTION_CODE(_s) \ + (static_cast(static_cast(_s[0]) << 24) | \ + static_cast(static_cast(_s[1]) << 16) | \ + static_cast(static_cast(_s[2]) << 8) | \ + static_cast(static_cast(_s[3]) )) + +//! @name Option identifiers +//@{ +static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); +static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); +static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); +static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); +static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); +static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); +static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); +static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); +static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); +static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); +static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); +static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); +//@} + +#undef OPTION_CODE + +#endif diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h new file mode 100644 index 0000000..539ddaf --- /dev/null +++ b/lib/synergy/ProtocolTypes.h @@ -0,0 +1,286 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef PROTOCOLTYPES_H +#define PROTOCOLTYPES_H + +#include "BasicTypes.h" + +// protocol version number +// 1.0: initial protocol +// 1.1: adds KeyCode to key press, release, and repeat +static const SInt16 kProtocolMajorVersion = 1; +static const SInt16 kProtocolMinorVersion = 1; + +// default contact port number +static const UInt16 kDefaultPort = 24800; + +// maximum total length for greeting returned by client +static const UInt32 kMaxHelloLength = 1024; + +// time between heartbeats (in seconds). negative value disables +// heartbeat. +static const double kHeartRate = -1.0; + +// number of skipped heartbeats that constitutes death +static const double kHeartBeatsUntilDeath = 3.0; + +// direction constants +enum EDirection { + kNoDirection, + kLeft, + kRight, + kTop, + kBottom, + kFirstDirection = kLeft, + kLastDirection = kBottom, + kNumDirections = kLastDirection - kFirstDirection + 1 +}; +enum EDirectionMask { + kNoDirMask = 0, + kLeftMask = 1 << kLeft, + kRightMask = 1 << kRight, + kTopMask = 1 << kTop, + kBottomMask = 1 << kBottom +}; + + +// +// message codes (trailing NUL is not part of code). in comments, $n +// refers to the n'th argument (counting from one). message codes are +// always 4 bytes optionally followed by message specific parameters +// except those for the greeting handshake. +// + +// +// positions and sizes are signed 16 bit integers. +// + +// +// greeting handshake messages +// + +// say hello to client; primary -> secondary +// $1 = protocol major version number supported by server. $2 = +// protocol minor version number supported by server. +static const char kMsgHello[] = "Synergy%2i%2i"; + +// respond to hello from server; secondary -> primary +// $1 = protocol major version number supported by client. $2 = +// protocol minor version number supported by client. $3 = client +// name. +static const char kMsgHelloBack[] = "Synergy%2i%2i%s"; + + +// +// command codes +// + +// no operation; secondary -> primary +static const char kMsgCNoop[] = "CNOP"; + +// close connection; primary -> secondary +static const char kMsgCClose[] = "CBYE"; + +// enter screen: primary -> secondary +// entering screen at screen position $1 = x, $2 = y. x,y are +// absolute screen coordinates. $3 = sequence number, which is +// used to order messages between screens. the secondary screen +// must return this number with some messages. $4 = modifier key +// mask. this will have bits set for each toggle modifier key +// that is activated on entry to the screen. the secondary screen +// should adjust its toggle modifiers to reflect that state. +static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i"; + +// leave screen: primary -> secondary +// leaving screen. the secondary screen should send clipboard +// data in response to this message for those clipboards that +// it has grabbed (i.e. has sent a kMsgCClipboard for and has +// not received a kMsgCClipboard for with a greater sequence +// number) and that were grabbed or have changed since the +// last leave. +static const char kMsgCLeave[] = "COUT"; + +// grab clipboard: primary <-> secondary +// sent by screen when some other app on that screen grabs a +// clipboard. $1 = the clipboard identifier, $2 = sequence number. +// secondary screens must use the sequence number passed in the +// most recent kMsgCEnter. the primary always sends 0. +static const char kMsgCClipboard[] = "CCLP%1i%4i"; + +// screensaver change: primary -> secondary +// screensaver on primary has started ($1 == 1) or closed ($1 == 0) +static const char kMsgCScreenSaver[] = "CSEC%1i"; + +// reset options: primary -> secondary +// client should reset all of its options to their defaults. +static const char kMsgCResetOptions[] = "CROP"; + +// resolution change acknowledgment: primary -> secondary +// sent by primary in response to a secondary screen's kMsgDInfo. +// this is sent for every kMsgDInfo, whether or not the primary +// had sent a kMsgQInfo. +static const char kMsgCInfoAck[] = "CIAK"; + + +// +// data codes +// + +// key pressed: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton +// the KeyButton identifies the physical key on the primary used to +// generate this key. the secondary should note the KeyButton along +// with the physical key it uses to generate the key press. on +// release, the secondary can then use the primary's KeyButton to +// find its corresponding physical key and release it. this is +// necessary because the KeyID on release may not be the KeyID of +// the press. this can happen with combining (dead) keys or if +// the keyboard layouts are not identical and the user releases +// a modifier key before releasing the modified key. +static const char kMsgDKeyDown[] = "DKDN%2i%2i%2i"; + +// key pressed 1.0: same as above but without KeyButton +static const char kMsgDKeyDown1_0[] = "DKDN%2i%2i"; + +// key auto-repeat: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton +static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i%2i"; + +// key auto-repeat 1.0: same as above but without KeyButton +static const char kMsgDKeyRepeat1_0[] = "DKRP%2i%2i%2i"; + +// key released: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton +static const char kMsgDKeyUp[] = "DKUP%2i%2i%2i"; + +// key released 1.0: same as above but without KeyButton +static const char kMsgDKeyUp1_0[] = "DKUP%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseDown[] = "DMDN%1i"; + +// mouse button released: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseUp[] = "DMUP%1i"; + +// mouse moved: primary -> secondary +// $1 = x, $2 = y. x,y are absolute screen coordinates. +static const char kMsgDMouseMove[] = "DMMV%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = delta. the delta should be +120 for one tick forward (away +// from the user) and -120 for one tick backward (toward the user). +static const char kMsgDMouseWheel[] = "DMWM%2i"; + +// clipboard data: primary <-> secondary +// $2 = sequence number, $3 = clipboard data. the sequence number +// is 0 when sent by the primary. secondary screens should use the +// sequence number from the most recent kMsgCEnter. $1 = clipboard +// identifier. +static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; + +// client data: secondary -> primary +// $1 = coordinate of leftmost pixel on secondary screen, +// $2 = coordinate of topmost pixel on secondary screen, +// $3 = width of secondary screen in pixels, +// $4 = height of secondary screen in pixels, +// $5 = size of warp zone, +// $6, $7 = the x,y position of the mouse on the secondary screen. +// +// the secondary screen must send this message in response to the +// kMsgQInfo message. it must also send this message when the +// screen's resolution changes. in this case, the secondary screen +// should ignore any kMsgDMouseMove messages until it receives a +// kMsgCInfoAck in order to prevent attempts to move the mouse off +// the new screen area. +static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i%2i%2i"; + +// set options: primary -> secondary +// client should set the given option/value pairs. $1 = option/value +// pairs. +static const char kMsgDSetOptions[] = "DSOP%4I"; + + +// +// query codes +// + +// query screen info: primary -> secondary +// client should reply with a kMsgDInfo. +static const char kMsgQInfo[] = "QINF"; + + +// +// error codes +// + +// incompatible versions: primary -> secondary +// $1 = major version of primary, $2 = minor version of primary. +static const char kMsgEIncompatible[] = "EICV%2i%2i"; + +// name provided when connecting is already in use: primary -> secondary +static const char kMsgEBusy[] = "EBSY"; + +// unknown client: primary -> secondary +// name provided when connecting is not in primary's screen +// configuration map. +static const char kMsgEUnknown[] = "EUNK"; + +// protocol violation: primary -> secondary +// primary should disconnect after sending this message. +static const char kMsgEBad[] = "EBAD"; + + +// +// structures +// + +//! Screen information +/*! +This class contains information about a screen. +*/ +class CClientInfo { +public: + //! Screen position + /*! + The position of the upper-left corner of the screen. This is + typically 0,0. + */ + SInt32 m_x, m_y; + + //! Screen size + /*! + The size of the screen in pixels. + */ + SInt32 m_w, m_h; + + //! Jump zone size + /*! + This is the size of the jump zone. The cursor jumps to the adjacent + screen when it comes within this many pixels of the edge of the screen. + */ + SInt32 m_zoneSize; + + //! Mouse position + /*! + The position of the cursor. This is not kept up-to-date so it's + only meaningful when receiving an update. + */ + SInt32 m_mx, m_my; +}; + +#endif + diff --git a/lib/synergy/XScreen.cpp b/lib/synergy/XScreen.cpp new file mode 100644 index 0000000..8a694bf --- /dev/null +++ b/lib/synergy/XScreen.cpp @@ -0,0 +1,53 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XScreen.h" + +// +// XScreenOpenFailure +// + +CString +XScreenOpenFailure::getWhat() const throw() +{ + return format("XScreenOpenFailure", "unable to open screen"); +} + + +// +// XScreenUnavailable +// + +XScreenUnavailable::XScreenUnavailable(double timeUntilRetry) : + m_timeUntilRetry(timeUntilRetry) +{ + // do nothing +} + +XScreenUnavailable::~XScreenUnavailable() +{ + // do nothing +} + +double +XScreenUnavailable::getRetryTime() const +{ + return m_timeUntilRetry; +} + +CString +XScreenUnavailable::getWhat() const throw() +{ + return format("XScreenUnavailable", "unable to open screen"); +} diff --git a/lib/synergy/XScreen.h b/lib/synergy/XScreen.h new file mode 100644 index 0000000..9966e6c --- /dev/null +++ b/lib/synergy/XScreen.h @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XSCREEN_H +#define XSCREEN_H + +#include "XBase.h" + +//! Generic screen exception +XBASE_SUBCLASS(XScreen, XBase); + +//! Cannot open screen exception +/*! +Thrown when a screen cannot be opened or initialized. +*/ +XBASE_SUBCLASS_WHAT(XScreenOpenFailure, XScreen); + +//! Screen unavailable exception +/*! +Thrown when a screen cannot be opened or initialized but retrying later +may be successful. +*/ +class XScreenUnavailable : public XScreenOpenFailure { +public: + /*! + \c timeUntilRetry is the suggested time the caller should wait until + trying to open the screen again. + */ + XScreenUnavailable(double timeUntilRetry); + virtual ~XScreenUnavailable(); + + //! @name manipulators + //@{ + + //! Get retry time + /*! + Returns the suggested time to wait until retrying to open the screen. + */ + double getRetryTime() const; + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + double m_timeUntilRetry; +}; + +#endif diff --git a/lib/synergy/XSynergy.cpp b/lib/synergy/XSynergy.cpp new file mode 100644 index 0000000..1e19945 --- /dev/null +++ b/lib/synergy/XSynergy.cpp @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XSynergy.h" +#include "CStringUtil.h" + +// +// XBadClient +// + +CString +XBadClient::getWhat() const throw() +{ + return "XBadClient"; +} + + +// +// XIncompatibleClient +// + +XIncompatibleClient::XIncompatibleClient(int major, int minor) : + m_major(major), + m_minor(minor) +{ + // do nothing +} + +int +XIncompatibleClient::getMajor() const throw() +{ + return m_major; +} + +int +XIncompatibleClient::getMinor() const throw() +{ + return m_minor; +} + +CString +XIncompatibleClient::getWhat() const throw() +{ + return format("XIncompatibleClient", "incompatible client %{1}.%{2}", + CStringUtil::print("%d", m_major).c_str(), + CStringUtil::print("%d", m_minor).c_str()); +} + + +// +// XDuplicateClient +// + +XDuplicateClient::XDuplicateClient(const CString& name) : + m_name(name) +{ + // do nothing +} + +const CString& +XDuplicateClient::getName() const throw() +{ + return m_name; +} + +CString +XDuplicateClient::getWhat() const throw() +{ + return format("XDuplicateClient", "duplicate client %{1}", m_name.c_str()); +} + + +// +// XUnknownClient +// + +XUnknownClient::XUnknownClient(const CString& name) : + m_name(name) +{ + // do nothing +} + +const CString& +XUnknownClient::getName() const throw() +{ + return m_name; +} + +CString +XUnknownClient::getWhat() const throw() +{ + return format("XUnknownClient", "unknown client %{1}", m_name.c_str()); +} diff --git a/lib/synergy/XSynergy.h b/lib/synergy/XSynergy.h new file mode 100644 index 0000000..2313119 --- /dev/null +++ b/lib/synergy/XSynergy.h @@ -0,0 +1,105 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XSYNERGY_H +#define XSYNERGY_H + +#include "XBase.h" + +//! Generic synergy exception +XBASE_SUBCLASS(XSynergy, XBase); + +//! Client error exception +/*! +Thrown when the client fails to follow the protocol. +*/ +XBASE_SUBCLASS_WHAT(XBadClient, XSynergy); + +//! Incompatible client exception +/*! +Thrown when a client attempting to connect has an incompatible version. +*/ +class XIncompatibleClient : public XSynergy { +public: + XIncompatibleClient(int major, int minor); + + //! @name accessors + //@{ + + //! Get client's major version number + int getMajor() const throw(); + //! Get client's minor version number + int getMinor() const throw(); + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + int m_major; + int m_minor; +}; + +//! Client already connected exception +/*! +Thrown when a client attempting to connect is using the same name as +a client that is already connected. +*/ +class XDuplicateClient : public XSynergy { +public: + XDuplicateClient(const CString& name); + + //! @name accessors + //@{ + + //! Get client's name + virtual const CString& + getName() const throw(); + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + CString m_name; +}; + +//! Client not in map exception +/*! +Thrown when a client attempting to connect is using a name that is +unknown to the server. +*/ +class XUnknownClient : public XSynergy { +public: + XUnknownClient(const CString& name); + + //! @name accessors + //@{ + + //! Get the client's name + virtual const CString& + getName() const throw(); + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + CString m_name; +}; + +#endif diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp new file mode 100644 index 0000000..08a3256 --- /dev/null +++ b/lib/synergy/libsynergy.dsp @@ -0,0 +1,222 @@ +# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libsynergy - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libsynergy.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libsynergy.mak" CFG="libsynergy - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libsynergy - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libsynergy - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "libsynergy - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "libsynergy - Win32 Release" +# Name "libsynergy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\XScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\ClipboardTypes.h +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IClient.h +# End Source File +# Begin Source File + +SOURCE=.\IClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenEventHandler.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreenFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IServer.h +# End Source File +# Begin Source File + +SOURCE=.\KeyTypes.h +# End Source File +# Begin Source File + +SOURCE=.\MouseTypes.h +# End Source File +# Begin Source File + +SOURCE=.\OptionTypes.h +# End Source File +# Begin Source File + +SOURCE=.\ProtocolTypes.h +# End Source File +# Begin Source File + +SOURCE=.\XScreen.h +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..e69de29 diff --git a/synergy.dsw b/synergy.dsw new file mode 100644 index 0000000..660e164 --- /dev/null +++ b/synergy.dsw @@ -0,0 +1,335 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "all"=".\all.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name launcher + End Project Dependency +}}} + +############################################################################### + +Project: "arch"=".\lib\arch\arch.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "base"=".\lib\base\base.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "client"=".\lib\client\client.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "common"=".\lib\common\common.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "exec"=".\cmd\exec.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synergyc + End Project Dependency + Begin Project Dependency + Project_Dep_Name synergys + End Project Dependency +}}} + +############################################################################### + +Project: "http"=".\lib\HTTP\http.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "io"=".\lib\io\io.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "launcher"=".\cmd\launcher\launcher.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name http + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency + Begin Project Dependency + Project_Dep_Name exec + End Project Dependency +}}} + +############################################################################### + +Project: "libsynergy"=".\lib\synergy\libsynergy.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "makehook"=".\lib\platform\makehook.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synrgyhk + End Project Dependency +}}} + +############################################################################### + +Project: "mt"=".\lib\mt\mt.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net"=".\lib\net\net.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "platform"=".\lib\platform\platform.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "server"=".\lib\server\server.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "synergyc"=".\cmd\synergyc\synergyc.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name client + End Project Dependency +}}} + +############################################################################### + +Project: "synergys"=".\cmd\synergys\synergys.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name http + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name makehook + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency +}}} + +############################################################################### + +Project: "synrgyhk"=".\lib\platform\synrgyhk.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +