diff --git a/ChangeLog b/ChangeLog index 7d1f8d2b..10ccab7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,1897 @@ +2006/04/02 12:16:23 crs +lib/platform/CXWindowsKeyState.cpp + +Fixed non-XKB handling of Mode_switch. There were two problems. +First the XkbLockGroup function was being called even for non-XKB +layouts. (And was being included in the compile even if the XKB +headers were not available!) Second, modifiers were assumed to +exist only the group in which a keysym was found and when looking +for modifiers for a key we only check modifiers in the same group. +So keys that needed Mode_switch (in group 1) would check to see if +Mode_switch was explictly mapped in group 1. It never is so that +would never work. Now we handle implicitly mapped modifiers. I +hadn't noticed this problem because my system (like most others) +uses XKB and when I forced non-XKB, the keys I tested worked anyway +through Multi_key. + +---------- +2006/04/01 22:25:33 crs +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp + +Fixed autorepeat on X11. Was autorepeating all keys sent from +server. Now autorepeating only those keys which are locally +configured in X to autorepeat. + +---------- +2006/04/01 21:37:24 crs +lib/platform/CMSWindowsKeyState.cpp + +Fixed autorepeat on win32 clients. Was synthesizing a key release +for each repeat. Win32 wants only repeat press events. + +---------- +2006/04/01 21:36:50 crs +cmd/launcher/CScreensLinks.cpp + +Fixed two bugs in screens and link dialog. First, the link controls +were not updated when reopening the dialog. Second, a change in any +link edit control would be discarded if the user pressed enter. + +---------- +2006/04/01 21:35:10 crs +cmd/launcher/CHotkeyOptions.cpp + +Fixed crash when creating a new hotkey but picking a key or mouse +button combination before clicking OK. + +---------- +2006/04/01 21:30:43 crs +configure.in +lib/arch/CArchNetworkBSD.cpp +lib/common/common.h + +Removed use of alloca() from unix and portable code. It's still +in win32 code but i don't have to play guessing games about +whether it's there or not on that platform. + +---------- +2006/04/01 17:53:27 crs +synergy.xcode/project.pbxproj + +Added new files to Xcode project. + +---------- +2006/04/01 17:41:59 crs +lib/client/CServerProxy.cpp +lib/net/CTCPSocket.cpp +lib/server/CClientProxy1_0.cpp + +Added more debugging output for network problems. + +---------- +2006/04/01 17:01:56 crs +lib/platform/COSXKeyState.cpp + +Fixed keymapping on OSX. Was checking keyboard tables for all +modifier combinations, including right-handed modifiers. It +turns out OSX doesn't set up the tables correctly for those +modifiers and acts if they have no effect. Since OSX never +generates right-handed modifiers this isn't too surprising. + +---------- +2006/04/01 15:32:19 crs +lib/server/CBaseClientProxy.cpp +lib/server/CBaseClientProxy.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/server/server.dsp + +Added new class to allow the server to keep information that every +screen has. The first such info is the cursor position when last +jumping from the screen. Using a hotkey to jump to a screen now +puts the cursor where it was the last time the user jumped from +that screen. + +---------- +2006/04/01 14:51:22 crs +lib/server/CInputFilter.cpp + +Fixed bug in reloading configurations. Was losing hotkeys. + +---------- +2006/04/01 14:49:41 crs +lib/platform/CXWindowsKeyState.cpp + +Fixed bug when recollecting the keyboard map on non-XKB keyboards. +Wasn't reseting a key map. It's unlikely anyone ever hit this bug. + +---------- +2006/04/01 13:39:09 crs +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp + +Fixed several uses of CXWindowsUtil::CErrorLock that take a flag. +Was checking the flag before destroying the lock object. That +doesn't reliably work because the X protocol is asynchronous. The +lock object ensures that the flag is correctly set in its d'tor by +synchronizing with the server. The X11 hotkey (un)registration was +one place where this was done. There were places in the X11 +screensaver and clipboard handling, too. + +---------- +2006/04/01 12:55:17 crs +lib/synergy/CKeyState.cpp + +Fixed failure to clear the state of which keys are pressed in +fakeAllKeysUp(). This fixes problems with synergy clients +thinking some keys are still down, causing weird key behavior. + +---------- +2006/04/01 12:53:31 crs +lib/common/Version.h + +Changed version to 1.3.1. + +---------- +2006/03/21 21:54:16 crs +lib/platform/CXWindowsUtil.cpp + +Add all #defines for including keysyms that we use. The #defines in +X11/keysym.h vary from platform to platform and cannot be relied on. + +---------- +2006/03/21 21:42:53 crs +lib/common/Version.h + +Changed version to 1.3.0. + +---------- +2006/03/21 21:38:52 crs +cmd/launcher/Makefile.am + +Added new files to makefile. + +---------- +2006/03/21 21:38:02 crs +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/CHotkeyOptions.cpp +cmd/launcher/CHotkeyOptions.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/synergyc.dsp +cmd/synergys/synergys.dsp +lib/arch/arch.dsp +lib/base/base.dsp +lib/client/client.dsp +lib/common/common.dsp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.cpp +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/server.dsp +lib/synergy/IKeyState.cpp +lib/synergy/IKeyState.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/libsynergy.dsp + +Added a hot key dialog to the win32 launcher, providing full support +for all features of hot keys. This required a bunch of small changes +to CInputFilter and stuff used by it (mainly getter methods). + +The hot key dialog uses dynamic_cast<> to determine the kinds of +conditions and actions for each rule in the configuration. This +required enabling RTTI in the project files. + +Also changed the 'Start' button to start the synergy service if +installed (i.e. synergy is configured to start when the computer +starts). + +---------- +2006/03/21 21:37:59 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h + +Changed AltGr handling on win32. Previously the server would send +ctrl and alt up events when sending a key that used AltGr then send +matching down events. Now we just clear the ctrl and alt bits in +the mask sent with the key; clients will temporarily release the +ctrl and alt modifiers as necessary to yield the key. If the key +doesn't need AltGr then the ctrl and alt bits are kept. We also +used to reserve the right alt key for AltGr even if the keyboard +layout didn't use it that way. This has been removed. The keyboard +mapping now presses the ctrl and alt keys for AltGr rather than use +the right alt. + +Also made getKeyID() a public method. + +---------- +2006/03/21 21:37:57 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/COSXScreen.cpp +lib/platform/CXWindowsScreen.cpp + +Improved log output when registering hot keys. + +---------- +2006/03/21 21:37:53 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Added "server" and "client" to synergy log windows. + +---------- +2006/03/21 21:37:52 crs +lib/synergy/CKeyMap.cpp + +Fixed bug in keyboard mapping when releasing modifiers. This caused +a problem with AltGr on win32. The left alt key was being +(synthetically) released but it was the right alt key that was down. + +---------- +2006/03/21 21:37:49 crs +doc/configuration.html + +Documentation fix. + +---------- +2006/03/20 23:13:11 crs +doc/images/warp.gif + +Replaced animated GIF demonstrating cursor warp with one that +doesn't show the cursor in the region between the monitors. +This is the original GIF with frames removed. Supplied by +user Brian A. + +---------- +2006/03/18 19:17:46 crs +synergy.xcode/project.pbxproj + +Updated Xcode project to build universal binaries. The PPC build +uses the 10.2.8 SDK while the i386 build uses the 10.4u SDK. Also +removed the "Default" configuration; "Development" is now the +default. + +---------- +2006/03/18 16:39:43 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp + +Improved handling of option key on OS X servers. Previously it +was always treated as the super modifier. Now it's treated as +AltGr if it generates a glyph, though AltGr is not sent to the +clients. So if option+key produces a glyph then key is sent +without the option or AltGr modifiers. If option+key does not +produce a glpyh then option is sent as the super modifier. +Note that combining an option+key combination that would produce +a glyph with the command or control modifiers will cause it to +not produce a glyph. In that case we send option as the super +modifier and we send the glyph that would've been produced on +the server had the option key not been pressed. So option+s +sends the beta key id with no modifiers but command+option+s +sends the "s" key id with the alt and super modifiers. + +This seems to handle the user expectations. However some users +may expect option+L to produce win+L on win32 clients. These +same users probably expect option+? to produce an upside down +question mark. But these two expectations are fundamentally at +odds. We cannot satisfy everyone because OS X doesn't have +enough modifier keys. + +---------- +2006/03/18 13:20:18 crs +lib/server/CInputFilter.cpp + +Fixed mousebutton condition. Wasn't working if num lock, caps lock +or scroll lock was on. + +---------- +2006/03/18 12:05:34 crs +lib/platform/CXWindowsScreen.cpp + +Applied patch from Jaco Kroon to fix a problem with mouse focus +on X11. + +---------- +2006/03/18 11:54:40 crs +doc/index.html + +Added support for index.html?child#anchor syntax. This will open +the index.html page then child in the page frame and jump to anchor. + +---------- +2006/03/12 20:24:43 crs +cmd/launcher/CAutoStart.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/synergyc.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/synergys.cpp +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/CArchLogUnix.cpp +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchLogWindows.h +lib/arch/IArchConsole.h +lib/arch/IArchLog.h +lib/base/CLog.cpp +lib/base/ILogOutputter.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h + +Added show() method to console and logs. + +---------- +2006/03/12 20:24:14 crs +dist/nullsoft/synergy.nsi + +Updated windows installer to install new documentation pages and +to put a shortcut on the desktop. + +---------- +2006/03/12 20:23:46 crs +cmd/launcher/synergy.ico +cmd/synergyc/synergyc.ico +cmd/synergyc/tb_error.ico +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/synergys.ico +cmd/synergys/tb_error.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico + +Updated icons on win32. + +---------- +2006/03/12 12:42:18 crs +doc/configuration.html +doc/faq.html +doc/running.html +doc/trouble.html + +More documentation fixes. + +---------- +2006/03/12 12:19:02 crs +doc/configuration.html +doc/faq.html +doc/Makefile.am +doc/toc.html +doc/trouble.html + +Added a page with typical problems and solutions. + +---------- +2006/03/12 10:25:15 crs +doc/tips.html + +More documentation fixes. + +---------- +2006/03/12 10:20:14 crs +doc/about.html +doc/configuration.html +doc/faq.html + +Documentation fixes. + +---------- +2006/03/12 09:34:16 crs +doc/toc.html + +Fixed link in table of contents. + +---------- +2006/03/11 15:01:00 crs +doc/banner.html +doc/border.html +doc/index.html +doc/synergy.css + +Adjusted how the border under the banner is drawn. + +---------- +2006/03/11 14:49:38 crs +doc/images/logo.gif + +Updated logo. + +---------- +2006/03/11 14:42:00 crs +doc/about.html +doc/authors.html +doc/autostart.html +doc/banner.html +doc/compiling.html +doc/configuration.html +doc/contact.html +doc/developer.html +doc/faq.html +doc/history.html +doc/home.html +doc/images/logo.gif +doc/images/warp.gif +doc/index.html +doc/license.html +doc/Makefile.am +doc/news.html +doc/roadmap.html +doc/running.html +doc/security.html +doc/synergy.css +doc/tips.html +doc/toc.html +doc/todo.html + +Updated documentation pages. They're now the web site pages except +they now use frames. + +---------- +2006/03/08 20:07:09 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/server/CInputFilter.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h + +Added beginnings of support for synthesizing keystrokes on server's +screen. It's partly implemented on win32; it just needs to track +the modifier keys and adjust them as appropriate when synthesizing +keys. It's not implemented on X11 or OS X. It's also currently +disabled (in CPrimaryClient.cpp). + +---------- +2006/03/08 20:05:38 crs +cmd/synergyc/CClientTaskBarReceiver.cpp +cmd/synergyc/CClientTaskBarReceiver.h +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc +lib/arch/CArch.cpp +lib/arch/CArchConsoleUnix.cpp +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/base/CLog.cpp +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/client/CClient.cpp +lib/client/CClient.h +lib/common/Version.cpp + +Replaced using win32 console for log with a dialog containing a rich +edit control. The user can close this window without quiting synergy +and can reopen the window using the tray icon menu. Also added menu +items to switch the current logging level. + +---------- +2006/02/22 19:21:21 crs +lib/platform/COSXScreen.cpp + +Removed bogus logging call. + +---------- +2006/02/20 19:46:47 crs +cmd/launcher/launcher.cpp +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h + +Fixed infinite loop of error dialogs in win32 launcher. + +---------- +2006/02/20 12:59:20 crs +doc/faq.html + +Added firewall info to faq12. Added faq19, a discussion of not taking +the foreground on win32. + +---------- +2006/02/20 12:46:18 crs +doc/authors.html +doc/autostart.html +doc/compiling.html +doc/configuration.html +doc/developer.html +doc/faq.html +doc/history.html +doc/index.html +doc/license.html +doc/news.html +doc/running.html +doc/security.html +doc/tips.html +doc/todo.html + +Changed !DOCTYPE to HTML 4.0 (from 3.2). + +---------- +2006/02/20 12:21:34 crs +doc/configuration.html +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp +lib/server/CServer.h + +Hot key overhaul. Added support for multiple actions per hot key. +Actions and conditions are now idempotent so they no longer track +the active state (on, off, toggled, etc). Actions can be assigned +to the activation or deactivation of a condition, basically a hot +key or mouse button being pressed (activation) or released +(deactivation). The keystroke and mousebutton actions map to both +and the new keyDown, keyUp, mouseDown and mouseUp map to one or the +other. The lock cursor to screen action now takes a mode: on, off +or toggle and the corresponding event state is respected by the +server. Removed the modifiers action. Mouse button actions now use +the new kKeySetModifiers and kKeyClearModifiers keys to set/reset +modifier state directly on the client. + +Conditions and actions are much simpler now that they're idempotent +and so is CInputFilter. Refactored CRule into a class since there's +now more to a rule than a condition and action. + +---------- +2006/02/20 11:29:41 crs +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/CKeyState.cpp + +Added support for kKeySetModifiers and kKeyClearModifiers keys. +The former activates the given modifiers and the latter deactivates +them. + +---------- +2006/02/20 11:25:44 crs +lib/synergy/KeyTypes.h + +Added special keys for setting/clearing modifiers. The intent +is to use these to set/reset modifiers for mouse button hot key +actions. + +---------- +2006/02/19 21:01:08 crs +lib/platform/COSXScreen.cpp + +Fixed OS X to send current keyboard modifiers with mouse button events. + +---------- +2006/02/19 13:26:54 crs +lib/client/CClient.cpp +lib/client/CClient.h + +Fixed two clipboard problems. + +First, synergy would blow an assert given the following sequence +of events: + enter client screen A + A takes clipboard + enter server screen B + B takes clipboard + clipboard on A changes (while we're not on the screen) + enter A + enter B +On entering B we find that the clipboard sender is not the owner +of the clipboard. This is because when A changed the clipboard +while we were on B, B ignored the grab from A. A now thinks it +owns the clipboard (even though B does). So when we leave A the +second time, it sends the clipboard (which contains what B sent +it) to B. The assertion is blown because B owns the clipboard +but A sends a clipboard with a valid sequence number. The fix +is simply that clients must reset their internal clipboard +ownership flag when told to set the clipboard by the server. + +Second, synergy clients would fail to send the clipboard to the +server given the following sequence of events: + enter client A + A takes the clipboard + enter screen B + B takes the clipboard + enter A + A takes the clipboard with the same contents as before +In an effort to reduce bandwidth, clients don't send unchanged +clipboard contents. Clients were incorrectly treating this +case as unchanged contents when, in fact, the contents are those +set by B not those of A. Clients now handle this case. + +---------- +2006/02/19 13:13:55 crs +lib/server/CServer.cpp + +Fixed error in log message. Was trying to report the sender of +a clipboard but was reporting the owner of the clipboard. + +---------- +2006/02/16 22:12:37 crs +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h + +Added a hack to work around how VMware handles modifier keys on X11 +when a virtual machine has focus. VMware removes all of the modifier +mappings so synergy doesn't know about any modifiers. The hack is to +use the last known good modifiers when there aren't any active +modifiers. + +---------- +2006/02/14 18:10:12 crs +lib/server/CServer.cpp + +Made switch in direction hot keys use the cursor's current position +when calculating what the neighbor is. This only affects layouts +using fractional edges. + +---------- +2006/02/12 16:49:16 crs +synergy.xcode/project.pbxproj + +Updated Xcode project. + +---------- +2006/02/12 16:40:02 crs +doc/configuration.html +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/KeyTypes.cpp + +Removed onCommandKey() from server. This was used only for handling +the ScrollLock key to lock the cursor to the screen but this has been +obsoleted by the hotkey support for locking the cursor. Also, +ScrollLock is now only added as a hotkey to lock the cursor if the +user hasn't configured some other hotkey to lock the cursor. + +---------- +2006/02/12 16:22:41 crs +doc/configuration.html +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/IKeyState.cpp +lib/synergy/IKeyState.h + +Added support for directing hotkey key actions to a particular screen +or screens or to broadcast to all screens. However, key action +keystrokes are never sent to the server screen. This should be fixed. + +---------- +2006/02/12 16:16:11 crs +doc/configuration.html +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/KeyTypes.cpp +lib/synergy/KeyTypes.h +lib/synergy/libsynergy.dsp +lib/synergy/Makefile.am +lib/synergy/mkspecialkeynames.pl +lib/synergy/SpecialKeyNameMap.h + +Moved and restructed key name maps. Also added names for all +non-alphanumeric ASCII characters and added support for \uXXXX +unicode character codes. + +---------- +2006/02/12 14:47:23 crs +lib/synergy/CKeyState.cpp + +Now allowing fake key presses from server with a server button ID +of 0. Hot keys that synthesize key events use a server button ID +of 0. Note that other code will prevent a client from processing +a hotkey press while another hotkey is pressed. The nature of +hotkeys should ensure that never happens except for modifier only +hotkeys. Worry about that later. + +---------- +2006/02/12 12:06:50 crs +lib/platform/COSXScreen.cpp + +Fixed 2 axis scrolling on OS X. + +---------- +2006/02/12 11:53:35 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_3.cpp +lib/server/CClientProxy1_3.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IClient.h +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/ISecondaryScreen.h +lib/synergy/ProtocolTypes.cpp +lib/synergy/ProtocolTypes.h + +Added support for horizontal scrolling. + +---------- +2006/02/12 10:08:49 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Applied patch from stkamp@users.sf.net that clamps mouse positions +to valid areas on OS X. It also improves using the local keyboard +with the remote mouse on OS X. + +---------- +2006/02/11 20:01:42 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp + +Fixed synthesis of ctrl+alt+del and handling of AltGr on win32 +client. + +---------- +2006/02/11 20:00:32 crs +lib/server/server.dsp + +Added CClientProxy1_3 to project. + +---------- +2006/02/06 19:27:45 crs +lib/server/CConfig.cpp + +Fixed handling of comments when parsing the configuration. +Had changed leading whitespace stripping which broke it. + +---------- +2006/02/05 19:42:55 crs +synergy.xcode/project.pbxproj + +Updated Xcode project. + +---------- +2006/02/05 18:48:35 crs +lib/server/CClientProxy1_3.cpp + +Fixed warning. + +---------- +2006/02/05 18:02:47 crs +lib/server/CInputFilter.cpp + +Fixed updates of input filters when configuration is changed. + +---------- +2006/02/05 17:55:45 crs +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_3.cpp + +More fixes for server side keep alives. + +---------- +2006/02/05 17:55:19 crs +lib/server/CServer.cpp + +Fixed sending of options to client. Wasn't reseting options first. + +---------- +2006/02/05 17:39:20 crs +lib/client/CServerProxy.cpp + +Fixed memory bug in releasing keep alive timer. + +---------- +2006/02/05 17:36:17 crs +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_3.cpp +lib/server/CClientProxy1_3.h +lib/server/CClientProxyUnknown.cpp + +Fixed server side handling of keep alives. + +---------- +2006/02/05 17:34:14 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h + +Fixed client side handling of keep alives. + +---------- +2006/02/05 16:56:00 crs +lib/synergy/ProtocolTypes.h + +Added comment for protocol version 1.3. + +---------- +2006/02/05 16:54:39 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_3.cpp +lib/server/CClientProxy1_3.h +lib/server/Makefile.am +lib/synergy/ProtocolTypes.cpp +lib/synergy/ProtocolTypes.h + +Deprecated heartbeat and added keep alive to replace it. While a +heartbeat was generated by clients and sent to the server, a keep +alive is sent from the server and echoed by the client. This +checks both directions of the connection. Either side will hang +up if the other hasn't been heard from in a reasonable amount of +time. This fixes a problem where clients would not hang up on +an unavailable server. + +---------- +2006/02/05 16:29:01 crs +cmd/launcher/Makefile.am + +Added CInfo files to makefile. + +---------- +2006/02/05 15:30:49 crs +lib/synergy/CKeyState.cpp + +Fixed handling of modifier keys that are held down while leaving +a client screen. Was correctly releasing those keys on the client +but wasn't reseting the active modifier state so as soon as a key +was pressed after reentering the client the modifiers were +reactivated. However, the user could only see the problem by +typing on the local keyboard because the modifier was correctly +adjusted for keys from the server. Now reseting modifier state +when leaving a client screen. + +---------- +2006/02/05 14:47:59 crs +cmd/launcher/CInfo.cpp +cmd/launcher/CInfo.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/resource.h + +Added two features to the win32 launcher. First there's now a +info dialog which reports some useful information. The other is +that configuration files are now re-read when the application is +activated and the file's modification time has changed. This +should help when users are hand editing the configuration file +while the launcher is running. + +---------- +2006/02/05 14:45:39 crs +lib/server/CConfig.cpp + +Fixed a bug in writing configuration files with fractional edges. + +---------- +2006/02/05 14:43:17 crs +cmd/launcher/CAddScreen.cpp +cmd/launcher/CScreensLinks.cpp +cmd/launcher/CScreensLinks.h +cmd/launcher/launcher.rc +cmd/launcher/resource.h +doc/configuration.html +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CServer.cpp +lib/server/CServer.h + +Added support for fractional edge links. This allows users to +configure a portion of an edge to map to a portion of another +edge, rather than linking entire edges. This does not allow +users to split a (presumably multimonitor) screen to allow +switching on reaching an interior edge. + +---------- +2006/02/01 21:34:28 crs +lib/synergy/CKeyMap.cpp + +Fixed parsing of modifier plus single character keystroke and +mousebutton entries in the configuration. Was discarding single +characters. + +---------- +2006/02/01 21:20:46 crs +lib/platform/COSXKeyState.cpp + +Fixed OS X keypad enter key. + +---------- +2006/01/29 20:50:54 crs +lib/server/CConfig.cpp +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp +lib/server/CServer.h + +Added support for performing an action when a screen connects. +Also added support for jumping to the screen that just connected. +The line 'connect() = switchToScreen()' in the global options will +cause the cursor to jump to a screen as soon as it connects. This +isn't especially useful but serves as an example. + +---------- +2006/01/29 19:56:31 crs +lib/platform/CXWindowsScreen.cpp + +Fixed X11 hot key grabbing. Previously was sensitive to CapsLock, +NumLock and ScrollLock and is now insensitive to all of those. + +---------- +2006/01/29 17:54:08 crs +lib/platform/COSXScreen.cpp + +Changed input suppress delay to 0. This might be right or it +might need to be a small positive value like 0.1. + +---------- +2006/01/29 15:52:44 crs +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp + +Fixed support for ScrollLock to lock the cursor to the screen. +It now works via the hotkey mechanism. + +---------- +2006/01/29 15:51:59 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h + +Fixed failure to run on win95 family. Was linked against +ToUnicodeEx which is not available on that family. Now looking +up that symbol at run time. + +---------- +2006/01/29 15:50:29 crs +lib/platform/COSXScreen.cpp + +Fixed minor bug in call to CGSetLocalEventsSuppressionInterval. + +---------- +2006/01/29 13:26:48 crs +lib/platform/CSynergyHook.cpp + +Win32 reports VK_RSHIFT as an extended key, which it is not. +We now correct this which fixes the right shift key when using +win32 servers. + +---------- +2006/01/29 12:47:31 crs +lib/synergy/CKeyMap.cpp + +Fixed handling of modifiers when synthesizing a modifier. We +were previously trying to match the desired modifier state +when synthesizing any key, even if some of those modifiers +weren't required (i.e. the key was insensitive to the state +of some of those modifiers). We no longer try to match the +state of non-required modifiers because doing so can break +the synthesis of some keys, particularly modifiers. For +example, previously pressing Control with NumLock on would +turn NumLock off, press Control, then turn NumLock back on. +If this Control was used with Alt on win32 to effect AltGr +then AltGr would not take effect. On X11 the Shift key was +improperly combined with NumLock, causing mouse keys to take +effect. + +---------- +2006/01/29 12:37:30 crs +lib/common/Version.h + +Changed to version 1.2.8. + +---------- +2005/12/18 18:00:56 crs +synergy.xcode/project.pbxproj + +Added new files to xcode project. + +---------- +2005/12/18 17:35:57 crs +doc/configuration.html +doc/news.html + +More documentation for 1.2.7. + +---------- +2005/12/18 16:51:52 crs +doc/news.html + +Fixed error in documentation. + +---------- +2005/12/18 16:15:42 crs +lib/platform/CSynergyHook.cpp + +Removed use of function from C standard library in the hook DLL. + +---------- +2005/12/18 16:11:15 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Added workaround for not receiving drag events for any mouse buttons +besides 1 and 2 (left and right). That appears to be a limitation +of the OS so we simply start polling the position while any buttons +besides 1 and 2 are down. Code based on a patch by Brian Kendall. + +---------- +2005/12/18 15:29:25 crs +lib/common/Version.h + +Changed version to 1.2.7. + +---------- +2005/12/18 15:12:39 crs +lib/platform/COSXKeyState.cpp + +Fixed mapping of unicode key layouts. Was discarding some +characters (backspace and return, in particular) that should +not have been discarded. + +---------- +2005/12/18 10:49:23 crs +cmd/launcher/launcher.rc +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp +lib/server/server.dsp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/IKeyState.cpp +lib/synergy/IKeyState.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/libsynergy.dsp +lib/synergy/SpecialKeyNameMap.h +synergy.dsw + +Added support for hot keys on win32. Also fixed memory leaks in +CInputFilter and changed CConfig to write hot keys in its +CInputFilter object to the options section. + +---------- +2005/12/15 18:57:38 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Fixed hot keys on OS X when on a secondary screen. This worked +in the original patch but broken during the modify/merge. It +was broken because we turn off all hot key processing by the OS +while on a secondary screen. The fix is to check on key press +and release if the key and modifier state matches a hot key. + +This is known to be broken on hot key release if you press the +hot key then change the modifiers before releasing the provoking +key. + +---------- +2005/12/14 21:51:01 crs +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added support for hotkeys on X11. + +---------- +2005/12/14 21:50:49 crs +lib/server/CServer.cpp + +When jumping to another screen was computing the center using the +size of the active screen instead of the size of the destination +screen. + +---------- +2005/12/14 21:50:09 crs +lib/server/CConfig.cpp + +Switched to ctype.h from cctype to fix linux build. + +---------- +2005/12/14 09:33:25 crs +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStringUtil.cpp +lib/base/CStringUtil.h + +Fixed bug in CStringUtil::vsprint. Was reusing va_list across +calls to vnsprintf, which is not allowed. Modified CStringUtil +and CLog to avoid doing that. This should fix a crash when +using --help on some platforms. Tthe --help message was the +only message long enough to exceed the static buffer length +which caused a va_list to get reused. + +---------- +2005/12/14 08:43:36 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/Makefile.am +lib/synergy/mkspecialkeynames.pl +lib/synergy/SpecialKeyNameMap.h + +Checkpoint of configurable hotkey support. Authored by Lorenz +Schori, modified and merged by me. Hotkeys are only implemented +on OS X so far; this change breaks the build on linux and win32. + +---------- +2005/11/29 21:22:02 crs +lib/platform/CXWindowsKeyState.cpp + +Fixed bug in X11 keyboard code. Wasn't checking for a NULL pointer. + +---------- +2005/11/28 22:12:49 crs +doc/running.html + +Added a little more detail to the documentation for for OS X users +not familiar with the shell. + +---------- +2005/11/28 20:59:39 crs +doc/autostart.html + +Added X11 autostart info for kdm. + +---------- +2005/11/27 16:41:06 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CSynergyHook.cpp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyState.cpp + +Fixed several win32 keyboard bugs. + +---------- +2005/11/27 16:30:50 crs +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h + +Fixed a couple of problems with key event synthesis. + +---------- +2005/11/25 18:19:28 crs +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Now sending a more significant fake mouse motion to xscreensaver +to keep it from activating. xscreensaver since 2.21 requires a +motion of at least 10 pixels to be considered significant. + +---------- +2005/11/25 14:42:30 crs +lib/platform/COSXClipboard.cpp +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Added periodic check for clipboard change on OS X. We're polling +because there doesn't seem to be any event to notify of a clipboard +change. + +---------- +2005/11/24 11:00:39 crs +cmd/launcher/launcher.cpp + +Fixed bug in win32 GUI. If synergy wasn't previously installed for +autostart then the GUI would incorrectly install it for autostart. +It was incorrectly installed in such a way that synergy would think +it wasn't installed and it would not function when the service +manager tried to start it. + +---------- +2005/11/20 22:34:06 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/CXWindowsScreen.cpp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h + +Converted OS X to new keyboard handling. Two known problems +with OS X: the thorn character is mapped incorrectly (because +OS X lies) and we're using every keyboard layout, not just the +enabled one, because we have no idea how to detect if a layout +is enabled or not. The latter problem only causes some startup +slowness. + +---------- +2005/11/20 22:29:01 crs +configure.in +lib/arch/CMultibyte.cpp + +Added support for converting clipboard data to Latin-1 encoding +if the default encoding is ASCII by switching to the en_US locale +at startup. + +---------- +2005/11/14 18:35:07 crs +lib/synergy/CKeyState.h + +Removed unnecessary headers. + +---------- +2005/11/13 17:08:45 crs +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h + +Added improved support for key combinations intended to perform a +keyboard shortcut rather than synthesize a particular key. + +---------- +2005/11/13 12:52:16 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h + +Finished X11 non-XKB keyboard mapping. Also added a convenience +function to CKeyMap used by both the X11 and win32 implemenetations. + +---------- +2005/11/13 10:31:32 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h +lib/synergy/KeyTypes.h +lib/synergy/libsynergy.dsp +lib/synergy/Makefile.am + +Checkpointing keyboard handling refactoring. This is a major change +to the client side handling of key events. This change converts the +X11 and win32 handling but not the OS X handling. + +The new class CKeyMap contains the information necessary to convert +a synergy key event to the platform events necessary to synthesize +the key. Platforms fill in a CKeyMap whenever their keyboard layouts +change using the addKeyEntry(), calling it once for each unique way +to synthesize a particular KeyID on the keyboard. The CKeyMap takes +it from there, first adding dead-key combinations and some other keys, +then doing the translation from KeyID to platform keystroke sequences. +Platforms no longer need to implement the KeyID to keystroke sequence +conversion, simplifying the platform implementations. + +This change also supports multiple keyboard layouts, typically used +to support various languages. If a key is not available on the active +layout but is on a different installed layout then synergy will switch +layouts automatically. + +The X11 version now fully supports the XKB extension. This should fix +problems with Mode_switch vs ISO_Level3_Shift. Non-XKB support is +incomplete in this checkpoint. + +---------- +2005/11/12 11:43:54 crs +lib/platform/CMSWindowsScreen.cpp + +Disabled code to periodically reinstall synergy in the clipboard +chain. It was causing an infinite recursion in the WM_DRAWCLIPBOARD +message handler. + +---------- +2005/11/12 11:28:25 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Changed a couple of logging messages to DEBUG2 from DEBUG. + +---------- +2005/11/12 11:27:55 crs +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/launcher.cpp + +Fixed saving of autostart configuration in win32 launcher. It +was erroring out when the user did not have permission to write +to that portion of the registry. Now it will only write the +configuration if synergy is installed for autostart. This is +still not quite right since it'll still error out if some other +user with sufficient permission has installed the autostart and +this user doesn't have enough permission. + +---------- +2005/11/12 11:22:34 crs +lib/common/Version.h + +Changed version to 1.2.6. + +---------- +2005/11/01 19:58:21 crs +acinclude.m4 + +Restored pthread signal configuration checks accidentally removed +in previous version. + +---------- +2005/10/12 21:37:58 crs +doc/running.html + +Updated setup documentation. + +---------- +2005/10/11 21:12:18 crs +lib/arch/CArchMultithreadPosix.cpp + +Fixed warning in posix multithread code. + +---------- +2005/10/11 20:59:29 crs +acinclude.m4 + +Updated autoconf script for detecting pthreads. + +---------- +2005/10/06 21:45:01 crs +configure.in +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added initial XKB code. This only checks for the extension in +the configuration, queries the extension at runtime and handles +mapping notify events. Still need to use the extension to get +the keyboard map. + +---------- +2005/10/02 17:40:56 crs +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsClipboard.h +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h + +Fixed X11 clipboard handling on 64-bit systems. Also fixed a +problem with detecting when a clipboard owner says a format is +unavailable. + +---------- +2005/10/01 19:26:57 crs +lib/server/CServer.cpp + +Fixed unnecessary comparison to NULL that chokes some compilers. + +---------- +2005/10/01 19:23:38 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +dist/nullsoft/synergy.nsi +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CSynergyHook.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h + +Fixed several win32 bugs. Removed the 'Save' button from the +launcher; configuration is now saved transparently. Autostart +configuration is now saved transparently whenever it changes +and autostart is enabled. Configuration is now saved when the +debug logging level changes. + +Now releasing synthetically pressed keys when leaving the client. +The existing code was checking for key down using a local key +button id then trying to release it using the same id as a server +key button id. + +Now looking up a local button id for key events that have a scan +code of 0. These are sent by some programs that synthesize events. + +Now periodically reinstalling the clipboard chain viewer. It appears +that the chain is breaking sometimes (probably due a badly behaved +or killed app) but there's no way to detect that so this is a +workaround. + +Now stopping and deleting the autostart services during uninstall. + +---------- +2005/10/01 18:55:59 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Added support for apple's mighty mouse. We now correctly handle +button 4 (and 5 if it exists). We now detect the mighty mouse's +scroll ball and convert vertical motion into scroll wheel events. +Also changed handling of scroll wheel scaling; the mac works +differently from other platforms so it's not perfect. + +---------- +2005/10/01 15:01:15 crs +cmd/launcher/Makefile.am + +Added new files to makefiles. + +---------- +2005/09/26 20:58:53 crs +lib/server/CConfig.cpp + +Fixed bug in output of configuration files. Was writing bottom-right +corner as "bottom-left" instead of "bottom-right". + +---------- +2005/09/26 20:55:24 crs +cmd/launcher/CAddScreen.cpp +cmd/launcher/CAddScreen.h +cmd/launcher/CScreensLinks.cpp +cmd/launcher/CScreensLinks.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Refactored code in win32 launcher GUI. + +---------- +2005/08/22 19:40:34 crs +lib/common/Version.h + +Changed version to 1.2.5. + +---------- +2005/08/07 10:32:54 crs +doc/autostart.html + +Updated OS X autostart documentation. + +---------- +2005/08/07 10:32:19 crs +lib/arch/CArchMultithreadPosix.cpp +lib/arch/IArchMultithread.h + +Added support for SIGUSR2. Not using it yet, though. + +---------- +2005/08/03 21:03:49 crs +cmd/synergyc/synergyc.cpp + +Client now quits after failing to connect if not restartable. +Was failing to quit. + +---------- +2005/08/03 20:14:42 crs +cmd/synergyc/Makefile.am +cmd/synergys/Makefile.am +configure.in +lib/arch/Makefile.am +lib/platform/Makefile.am + +Changed how we export MACOSX_DEPLOYMENT_TARGET to a hopefully more +portable solution. + +---------- +2005/08/02 21:42:12 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed --daemon fix when compiled on unix. + +---------- +2005/08/02 20:57:52 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsScreen.cpp +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Added option to suppress grabbing of the foreground window on win32 +synergy servers. Grabbing the foreground window prevents certain +apps from messing up low level keyboard events but doing that can +break certain workflow, particularly with games. Since the option +is only for win32 and it's in the launcher GUI and I hope to get rid +of it someday, there is no configuration documentation for it. + +---------- +2005/08/02 20:55:35 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed '--daemon' command line option on Windows NT family client and +server. Those platforms now ignore --daemon and --no-daemon like +they should. + +---------- +2005/08/01 22:34:05 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/server/CPrimaryClient.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h + +Fixed scroll-lock screen locking on linux servers. An earlier +change made the function that returns modifier state return the +shadowed state not the current state. On X windows we don't +shadow the state when on the primary screen so the synergy server +would never see the scroll-lock state turned on until it left the +primary screen. As a result scroll-lock would not lock to the +primary screen but would lock you to whatever secondary screen +you ended up on. + +Changed win32 and OSX versions to work like the linux version. +Win32 used to work anyway because we constantly shadow keyboard +state on that. OSX did not query current keyboard state at all +before and it still doesn't; this needs to be fixed. + +---------- +2005/08/01 22:29:48 crs +lib/synergy/libsynergy.dsp + +Added ProtocolTypes.cpp to VC++ project. + +---------- +2005/08/01 21:02:07 crs +lib/common/common.h +synergy.xcode/project.pbxproj + +Changes to get gcc3.3, 10.2.8 SDK builds working on OS X. + +---------- +2005/07/31 22:50:10 crs +configure.in +lib/platform/COSXScreenSaver.cpp +lib/platform/COSXScreenSaver.mm +lib/platform/COSXScreenSaverUtil.h +lib/platform/COSXScreenSaverUtil.m +lib/platform/Makefile.am + +Removed Objective-C++ file. Now using an Objective-C file instead +which works better with autoconf/automake. + +---------- +2005/07/31 22:49:03 crs +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.cpp +lib/synergy/ProtocolTypes.h + +Fixed warnings on gcc 4.0. + +---------- +2005/07/31 22:48:30 crs +lib/common/Version.h + +Changed version to 1.2.4. + +---------- +2005/07/26 21:37:41 crs +lib/common/Version.h + +Changed version to 1.2.3. + +---------- +2005/07/26 21:37:27 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h + +Now disabling the win32 foreground window on the server when +leaving the server screen. This prevents the system from +messing up keyboard events. The console (command prompt) and +remote desktop are known to interfere with the shift key when +they have the focus. This introduces a bug where the window +that was the foreground is raised to the top of the stacking +order when the cursor enters the server screen. This should +only be a problem for Mouse-X (activation follows mouse) users. +I don't know how Mouse-X changes the foreground/active window +without raising it; if I did I'd fix this bug. + +---------- +2005/07/26 21:24:13 crs +doc/configuration.html + +Fixed typo in documentation. + +---------- +2005/07/26 19:58:37 crs +cmd/launcher/launcher.cpp + +Now defaulting to WARNING as the default debug level on win32. +This is a workaround for NOTE and higher always popping up the +command window, which can't be hidden or closed. When the +user clicks 'Test', the debug level is forced to be at least +INFO to workaround this workaround. Future versions will do +away with the command window altogether so this weirdness isn't +too much of a problem. + +---------- +2005/07/26 19:50:44 crs +doc/faq.html +doc/tips.html + +Improved documentation of how to get ctrl+alt+pause working on +Windows NT,2k,XP. + +---------- +2005/07/21 21:25:51 crs +lib/common/common.h + +Forcing use of select() rather than poll() on OS X. Wasn't +getting a read event on a socket when the remote side closed +down when using poll(). Probably a bug in synergy code but +I couldn't find it. + +---------- +2005/07/21 21:24:28 crs +lib/arch/CArchNetworkBSD.cpp + +Fixed "resource temporarily unavailable" exception on OS X when +using the unblock pipe. + +---------- +2005/07/20 22:09:05 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergys/synergys.cpp +doc/configuration.html +lib/client/CClient.cpp +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/OptionTypes.h + +Added "dead" corners support. The cursor can't switch screens +from a dead corner. + +---------- +2005/07/19 20:43:51 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h + +Applied patch (from Lorenz Schori) to fix getting the keyboard +layout. It previously was failing for non-Apple keyboards. + +---------- +2005/07/19 20:23:29 crs +lib/platform/COSXKeyState.cpp + +Replaced NULL with 0 in arithmetic expression. + +---------- +2005/05/08 11:40:26 crs +lib/platform/CMSWindowsKeyState.cpp + +Applied patch 1113363 to support the kanji key on win32. + +---------- +2005/05/08 11:08:12 crs +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/IArchNetwork.h +lib/net/CTCPListenSocket.cpp + +Added support for SO_REUSEADDR. It is always enabled. + +---------- +2005/05/08 11:00:42 crs +cmd/synergys/synergys.cpp + +Fixed bug in retrying to initialize or start the server. Was +waiting the retry interval then doing nothing. + +---------- +2005/04/29 21:27:10 crs +lib/common/common.h +lib/platform/COSXScreen.cpp +synergy.xcode/project.pbxproj + +A few OSX build fixes. + +---------- +2005/04/28 22:05:51 crs +lib/common/common.h + +Now setting HAVE_SOCKLEN_T to 1 when on OSX and _SOCKLEN_T is +defined and HAVE_CONFIG_H isn't is set. + +---------- +2005/04/28 22:04:23 crs +cmd/synergyc/Makefile.am +cmd/synergys/Makefile.am +configure.in +lib/arch/Makefile.am +lib/platform/Makefile.am + +Added support for MACOSX_DEPLOYMENT_TARGET. It's set to 10.2. + +---------- +2005/04/28 22:02:47 crs +cmd/synergys/synergys.cpp +lib/client/CClient.cpp + +Now reporting sleep suspend/resume at INFO level. + +---------- +2005/04/25 22:12:04 crs +cmd/synergyc/Makefile.am +cmd/synergyc/synergyc.cpp +cmd/synergys/Makefile.am +cmd/synergys/synergys.cpp +configure.in +lib/base/CEvent.cpp +lib/base/CEvent.h +lib/base/CEventQueue.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/COSXScreenSaver.cpp +lib/platform/COSXScreenSaver.h +lib/platform/COSXScreenSaver.mm +lib/platform/Makefile.am +lib/platform/OSXScreenSaverControl.h +lib/server/CServer.cpp +lib/synergy/IScreen.cpp +lib/synergy/IScreen.h +synergy.xcode/project.pbxproj + +Added support on OS X for screensaver synchronization, +sleeping and fast user switching. OS X Server also now +captures global hot keys (cmd+tab, F9, etc.) and sends them +to the client. Changes mostly due to lorenz schori. Some +bug fixes (in lorenz's code and in synergy) and integration +into automake by crs (had to add support for Objective C++ +files) and the XCode project. + +This change also adds flags to events. Flags can cause events +to be dispatched immediately and to not free event data. Both +features are used in the new code. + +This change adds events on IScreen for notification of the +system going to sleep or waking up (or the user's session +being somehow suspended and restored). CClient and synergys +now listen for and respond to these events. CMSWindowsScreen +used to use IJobs for handling these events synchronously. It +now uses the new IScreen events and delivers them immediately. + +---------- 2005/01/26 18:45:45 crs lib/common/Version.h diff --git a/acinclude.m4 b/acinclude.m4 index 12d80bda..e7666464 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -300,6 +300,8 @@ dnl version AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h @@ -310,11 +312,11 @@ acx_pthread_ok=no # 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_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$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 @@ -322,7 +324,7 @@ if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" + CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different @@ -332,9 +334,10 @@ fi # 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. +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. -acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: @@ -353,6 +356,7 @@ acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -m # also defines -D_REENTRANT) # pthread: Linux, etcetera # --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) @@ -382,6 +386,13 @@ for flag in $acx_pthread_flags; do PTHREAD_CFLAGS="$flag" ;; + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" @@ -389,9 +400,9 @@ for flag in $acx_pthread_flags; do esac save_LIBS="$LIBS" - save_CXXFLAGS="$CXXFLAGS" + save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we @@ -409,7 +420,7 @@ for flag in $acx_pthread_flags; do [acx_pthread_ok=yes]) LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" + CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then @@ -425,69 +436,61 @@ fi if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - # Detect AIX lossage: threads are created detached by default - # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + # Detect AIX lossage: JOINABLE attribute is called 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]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) 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";; + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi - # Detect POSIX sigwait() + # Detect POSIX sigwait() AC_MSG_CHECKING([for POSIX sigwait]) AC_TRY_LINK([#include - #include ], - [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + #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" + save_CFLAGS2="$CFLAGS" + CFLAGS="$CFLAGS -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" + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) + CFLAGS="$save_CFLAGS2" 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 + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi fi - # Detect pthread signal functions + # Detect pthread signal functions AC_MSG_CHECKING([for pthread signal functions]) AC_TRY_LINK([#include - #include ], + #include ], [pthread_kill(pthread_self(), SIGTERM);], ok=yes, ok=no) AC_MSG_RESULT(${ok}) @@ -496,7 +499,7 @@ if test "x$acx_pthread_ok" = xyes; then fi LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" + CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with cc_r AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) @@ -516,6 +519,7 @@ else acx_pthread_ok=no $2 fi +AC_LANG_RESTORE ])dnl ACX_PTHREAD dnl enable maximum compiler warnings. must ignore unknown pragmas to diff --git a/cmd/launcher/CAddScreen.cpp b/cmd/launcher/CAddScreen.cpp new file mode 100644 index 00000000..bc65f283 --- /dev/null +++ b/cmd/launcher/CAddScreen.cpp @@ -0,0 +1,427 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 "CStringUtil.h" +#include "CArch.h" +#include "CAddScreen.h" +#include "LaunchUtil.h" +#include "resource.h" + +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; + +// +// CAddScreen +// + +CAddScreen* CAddScreen::s_singleton = NULL; + +CAddScreen::CAddScreen(HWND parent, CConfig* config, const CString& name) : + m_parent(parent), + m_config(config), + m_name(name) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CAddScreen::~CAddScreen() +{ + s_singleton = NULL; +} + +bool +CAddScreen::doModal() +{ + // do dialog + return (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + m_parent, dlgProc, (LPARAM)this) != 0); +} + +CString +CAddScreen::getName() const +{ + return m_name; +} + +void +CAddScreen::init(HWND hwnd) +{ + // set title + CString title; + if (m_name.empty()) { + title = getString(IDS_ADD_SCREEN); + } + else { + title = CStringUtil::format( + getString(IDS_EDIT_SCREEN).c_str(), + m_name.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)m_name.c_str()); + + // fill in aliases + CString aliases; + for (CConfig::all_const_iterator index = m_config->beginAll(); + index != m_config->endAll(); ++index) { + if (CStringUtil::CaselessCmp::equal(index->second, m_name) && + !CStringUtil::CaselessCmp::equal(index->second, index->first)) { + if (!aliases.empty()) { + aliases += "\r\n"; + } + aliases += index->first; + } + } + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); + + // set options + CConfig::CScreenOptions options; + getOptions(options); + CConfig::CScreenOptions::const_iterator index; + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + index = options.find(kOptionHalfDuplexCapsLock); + setItemChecked(child, (index != options.end() && index->second != 0)); + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + index = options.find(kOptionHalfDuplexNumLock); + setItemChecked(child, (index != options.end() && index->second != 0)); + child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); + index = options.find(kOptionHalfDuplexScrollLock); + setItemChecked(child, (index != 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 = options.find(s_modifiers[i].m_optionID); + KeyModifierID id = s_modifiers[i].m_modifierID; + if (index != options.end()) { + id = index->second; + } + SendMessage(child, CB_SETCURSEL, id - baseModifier, 0); + } + + // dead corners + UInt32 corners = 0; + index = options.find(kOptionScreenSwitchCorners); + if (index != options.end()) { + corners = index->second; + } + child = getItem(hwnd, IDC_ADD_DC_TOP_LEFT); + setItemChecked(child, (corners & kTopLeftMask) != 0); + child = getItem(hwnd, IDC_ADD_DC_TOP_RIGHT); + setItemChecked(child, (corners & kTopRightMask) != 0); + child = getItem(hwnd, IDC_ADD_DC_BOTTOM_LEFT); + setItemChecked(child, (corners & kBottomLeftMask) != 0); + child = getItem(hwnd, IDC_ADD_DC_BOTTOM_RIGHT); + setItemChecked(child, (corners & kBottomRightMask) != 0); + index = options.find(kOptionScreenSwitchCornerSize); + SInt32 size = 0; + if (index != options.end()) { + size = index->second; + } + char buffer[20]; + sprintf(buffer, "%d", size); + child = getItem(hwnd, IDC_ADD_DC_SIZE); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); +} + +bool +CAddScreen::save(HWND hwnd) +{ + // get the old aliases and options + CStringList oldAliases; + getAliases(oldAliases); + CConfig::CScreenOptions options; + getOptions(options); + + // extract name and aliases + CString newName; + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + newName = getWindowText(child); + CStringList newAliases; + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + tokenize(newAliases, getWindowText(child)); + + // name must be valid + if (!m_config->isValidScreenName(newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + newName.c_str())); + return false; + } + + // aliases must be valid + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (!m_config->isValidScreenName(*index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + index->c_str())); + return false; + } + } + + // 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 false; + } + + // 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 (m_config->isScreen(newName) && + !CStringUtil::CaselessCmp::equal(newName, m_name) && + !isNameInList(oldAliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + newName.c_str())); + return false; + } + + // 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 (m_config->isScreen(*index) && + !CStringUtil::CaselessCmp::equal(*index, m_name) && + !isNameInList(oldAliases, *index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + index->c_str())); + return false; + } + } + + // dead corner size must be non-negative + child = getItem(hwnd, IDC_ADD_DC_SIZE); + CString valueString = getWindowText(child); + int cornerSize = atoi(valueString.c_str()); + if (cornerSize < 0) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_CORNER_SIZE).c_str(), + valueString.c_str())); + SetFocus(child); + return false; + } + + // collect options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + if (isItemChecked(child)) { + options[kOptionHalfDuplexCapsLock] = 1; + } + else { + options.erase(kOptionHalfDuplexCapsLock); + } + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + if (isItemChecked(child)) { + options[kOptionHalfDuplexNumLock] = 1; + } + else { + options.erase(kOptionHalfDuplexNumLock); + } + child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); + if (isItemChecked(child)) { + options[kOptionHalfDuplexScrollLock] = 1; + } + else { + options.erase(kOptionHalfDuplexScrollLock); + } + + // save modifier options + 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) { + options[s_modifiers[i].m_optionID] = id; + } + else { + options.erase(s_modifiers[i].m_optionID); + } + } + + // save dead corner options + UInt32 corners = 0; + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_TOP_LEFT))) { + corners |= kTopLeftMask; + } + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_TOP_RIGHT))) { + corners |= kTopRightMask; + } + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_BOTTOM_LEFT))) { + corners |= kBottomLeftMask; + } + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_BOTTOM_RIGHT))) { + corners |= kBottomRightMask; + } + options[kOptionScreenSwitchCorners] = corners; + options[kOptionScreenSwitchCornerSize] = cornerSize; + + // save new data to config + if (m_name.empty()) { + // added screen + m_config->addScreen(newName); + } + else { + // edited screen + m_config->removeAliases(m_name); + m_config->removeOptions(m_name); + m_config->renameScreen(m_name, newName); + } + m_name = newName; + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + m_config->addAlias(m_name, *index); + } + for (CConfig::CScreenOptions::const_iterator + index = options.begin(); + index != options.end(); ++index) { + m_config->addOption(m_name, index->first, index->second); + } + + return true; +} + +BOOL +CAddScreen::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, 1); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAddScreen::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} + +void +CAddScreen::getAliases(CStringList& aliases) const +{ + for (CConfig::all_const_iterator index = m_config->beginAll(); + index != m_config->endAll(); ++index) { + if (CStringUtil::CaselessCmp::equal(index->second, m_name) && + !CStringUtil::CaselessCmp::equal(index->second, index->first)) { + aliases.push_back(index->first); + } + } +} + +void +CAddScreen::getOptions(CConfig::CScreenOptions& optionsOut) const +{ + const CConfig::CScreenOptions* options = m_config->getOptions(m_name); + if (options == NULL) { + optionsOut = CConfig::CScreenOptions(); + } + else { + optionsOut = *options; + } +} + +void +CAddScreen::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); +} + +bool +CAddScreen::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; +} diff --git a/cmd/launcher/CAddScreen.h b/cmd/launcher/CAddScreen.h new file mode 100644 index 00000000..e926c498 --- /dev/null +++ b/cmd/launcher/CAddScreen.h @@ -0,0 +1,74 @@ +/* + * 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 CADDSCREEN_H +#define CADDSCREEN_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Add screen dialog for Microsoft Windows launcher +class CAddScreen { +public: + CAddScreen(HWND parent, CConfig*, const CString& name); + ~CAddScreen(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. Return + \c true if the user accepted the changes, false otherwise. + */ + bool doModal(); + + //@} + //! @name accessors + //@{ + + CString getName() const; + + //@} + +private: + typedef std::vector CStringList; + + void getAliases(CStringList&) const; + void getOptions(CConfig::CScreenOptions&) const; + + static void tokenize(CStringList& tokens, const CString& src); + static bool isNameInList(const CStringList& tokens, + const CString& src); + + void init(HWND hwnd); + bool save(HWND hwnd); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAddScreen* s_singleton; + + HWND m_parent; + CConfig* m_config; + CString m_name; +}; + +#endif diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp index 4c72e8d4..ee5b45ea 100644 --- a/cmd/launcher/CAdvancedOptions.cpp +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -206,7 +206,7 @@ CAdvancedOptions::save(HWND hwnd) m_interface = iface; // save values to registry - HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath()); if (key != NULL) { CArchMiscWindows::setValue(key, "port", m_port); CArchMiscWindows::setValue(key, "name", m_screenName); diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index d5eee0ce..7b390a58 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -21,10 +21,10 @@ #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." +static const char* CLIENT_DAEMON_NAME = "Synergy Client"; +static const char* SERVER_DAEMON_NAME = "Synergy Server"; +static const char* CLIENT_DAEMON_INFO = "Uses a shared mouse and keyboard."; +static const char* SERVER_DAEMON_INFO = "Shares this system's mouse and keyboard with others."; // // CAutoStartOutputter @@ -40,6 +40,7 @@ public: // ILogOutputter overrides virtual void open(const char*) { } virtual void close() { } + virtual void show(bool) { } virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const { return ""; } @@ -63,14 +64,11 @@ CAutoStartOutputter::write(ELevel level, const char* message) CAutoStart* CAutoStart::s_singleton = NULL; -CAutoStart::CAutoStart(HWND parent, CConfig* config, const CString& cmdLine) : +CAutoStart::CAutoStart(HWND parent, bool isServer, const CString& cmdLine) : m_parent(parent), - m_config(config), - m_isServer(config != NULL), + m_isServer(isServer), m_cmdLine(cmdLine), - m_name((config != NULL) ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME), - m_userConfigSaved(false) - + m_name(isServer ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME) { assert(s_singleton == NULL); s_singleton = this; @@ -87,9 +85,6 @@ 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); @@ -98,10 +93,98 @@ CAutoStart::doModal() CLOG->pop_front(); } -bool -CAutoStart::wasUserConfigSaved() const +void +CAutoStart::reinstallDaemon(bool isClient, const CString& cmdLine) { - return m_userConfigSaved; + // get installation state + const char* name = (isClient ? CLIENT_DAEMON_NAME : SERVER_DAEMON_NAME); + bool installedSystem = ARCH->isDaemonInstalled(name, true); + bool installedUser = ARCH->isDaemonInstalled(name, false); + + // reinstall if anything is installed + if (installedSystem || installedUser) { + ARCH->installDaemon(name, + isClient ? CLIENT_DAEMON_INFO : SERVER_DAEMON_INFO, + getAppPath(isClient ? CLIENT_APP : SERVER_APP).c_str(), + cmdLine.c_str(), + NULL, + installedSystem); + } +} + +void +CAutoStart::uninstallDaemons(bool client) +{ + if (client) { + try { + ARCH->uninstallDaemon(CLIENT_DAEMON_NAME, true); + } + catch (...) { + } + try { + ARCH->uninstallDaemon(CLIENT_DAEMON_NAME, false); + } + catch (...) { + } + } + else { + try { + ARCH->uninstallDaemon(SERVER_DAEMON_NAME, true); + } + catch (...) { + } + try { + ARCH->uninstallDaemon(SERVER_DAEMON_NAME, false); + } + catch (...) { + } + } +} + +bool +CAutoStart::startDaemon() +{ + const char* name = NULL; + if (ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, true)) { + name = CLIENT_DAEMON_NAME; + } + else if (ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, true)) { + name = SERVER_DAEMON_NAME; + } + if (name == NULL) { + 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, SERVICE_START); + if (service == NULL) { + CloseServiceHandle(mgr); + return false; + } + + // start the service + BOOL okay = StartService(service, 0, NULL); + + // clean up + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + return (okay != 0); +} + +bool +CAutoStart::isDaemonInstalled() +{ + return (ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, false) || + ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, true) || + ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, false) || + ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, true)); } void @@ -177,22 +260,6 @@ CAutoStart::onInstall(bool allUsers) 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); diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h index e931530e..46aad100 100644 --- a/cmd/launcher/CAutoStart.h +++ b/cmd/launcher/CAutoStart.h @@ -20,14 +20,10 @@ #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(HWND parent, bool isServer, const CString& cmdLine); ~CAutoStart(); //! @name manipulators @@ -39,16 +35,34 @@ public: */ void doModal(); + //! Reinstall daemon + /*! + Reinstalls the currently installed daemon. + */ + static void reinstallDaemon(bool isClient, const CString& cmdLine); + + //! Uninstalls daemon + /*! + Uninstalls all installed client (\p client is \c true) or server daemons. + */ + static void uninstallDaemons(bool client); + + //! Starts an installed daemon + /*! + Returns \c true iff a daemon was started. This will only start daemons + installed for all users. + */ + static bool startDaemon(); + //@} //! @name accessors //@{ - //! Test if user configuration was saved + //! Tests if any daemons are installed /*! - Returns true if the user's configuration (as opposed to the system-wide - configuration) was saved successfully while in doModal(). + Returns \c true if any daemons are installed. */ - bool wasUserConfigSaved() const; + static bool isDaemonInstalled(); //@} @@ -65,14 +79,12 @@ 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 index 1777863b..b7c15801 100644 --- a/cmd/launcher/CGlobalOptions.cpp +++ b/cmd/launcher/CGlobalOptions.cpp @@ -79,6 +79,8 @@ CGlobalOptions::init(HWND hwnd) setItemChecked(child, true); child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES); setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND); + setItemChecked(child, false); // get the global options const CConfig::CScreenOptions* options = m_config->getOptions(""); @@ -122,6 +124,10 @@ CGlobalOptions::init(HWND hwnd) child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES); setItemChecked(child, (value != 0)); } + else if (id == kOptionWin32KeepForeground) { + child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND); + setItemChecked(child, (value != 0)); + } } } } @@ -187,6 +193,7 @@ CGlobalOptions::save(HWND hwnd) m_config->removeOption("", kOptionHeartbeat); m_config->removeOption("", kOptionScreenSaverSync); m_config->removeOption("", kOptionRelativeMouseMoves); + m_config->removeOption("", kOptionWin32KeepForeground); // add requested options child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); @@ -209,6 +216,10 @@ CGlobalOptions::save(HWND hwnd) if (isItemChecked(child)) { m_config->addOption("", kOptionRelativeMouseMoves, 1); } + child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND); + if (isItemChecked(child)) { + m_config->addOption("", kOptionWin32KeepForeground, 1); + } // save last values m_delayTime = newDelayTime; diff --git a/cmd/launcher/CHotkeyOptions.cpp b/cmd/launcher/CHotkeyOptions.cpp new file mode 100644 index 00000000..7e7a1b56 --- /dev/null +++ b/cmd/launcher/CHotkeyOptions.cpp @@ -0,0 +1,1864 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 "CMSWindowsKeyState.h" +#include "CConfig.h" +#include "CHotkeyOptions.h" +#include "CStringUtil.h" +#include "LaunchUtil.h" +#include "resource.h" + +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// +// CAdvancedOptions +// + +CHotkeyOptions* CHotkeyOptions::s_singleton = NULL; + +CHotkeyOptions::CHotkeyOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CHotkeyOptions::~CHotkeyOptions() +{ + s_singleton = NULL; +} + +void +CHotkeyOptions::doModal() +{ + // do dialog + m_inputFilter = m_config->getInputFilter(); + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +void +CHotkeyOptions::doInit(HWND hwnd) +{ + m_activeRuleIndex = (UInt32)-1; + fillHotkeys(hwnd); + openRule(hwnd); +} + +void +CHotkeyOptions::fillHotkeys(HWND hwnd, UInt32 select) +{ + HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + + SendMessage(rules, LB_RESETCONTENT, 0, 0); + for (UInt32 i = 0, n = m_inputFilter->getNumRules(); i < n; ++i) { + CInputFilter::CRule& rule = m_inputFilter->getRule(i); + SendMessage(rules, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)rule.getCondition()->format().c_str()); + } + + if (select < m_inputFilter->getNumRules()) { + SendMessage(rules, LB_SETCURSEL, select, 0); + } + + updateHotkeysControls(hwnd); +} + +void +CHotkeyOptions::updateHotkeysControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + bool selected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR); + + enableItem(hwnd, IDC_HOTKEY_ADD_HOTKEY, TRUE); + enableItem(hwnd, IDC_HOTKEY_EDIT_HOTKEY, selected); + enableItem(hwnd, IDC_HOTKEY_REMOVE_HOTKEY, selected); +} + +void +CHotkeyOptions::addHotkey(HWND hwnd) +{ + closeRule(hwnd); + CInputFilter::CCondition* condition = NULL; + if (editCondition(hwnd, condition)) { + m_inputFilter->addFilterRule(CInputFilter::CRule(condition)); + fillHotkeys(hwnd, m_inputFilter->getNumRules() - 1); + } + else { + delete condition; + } + openRule(hwnd); +} + +void +CHotkeyOptions::removeHotkey(HWND hwnd) +{ + UInt32 ruleIndex = m_activeRuleIndex; + closeRule(hwnd); + + m_inputFilter->removeFilterRule(ruleIndex); + UInt32 n = m_inputFilter->getNumRules(); + if (n > 0 && ruleIndex >= n) { + ruleIndex = n - 1; + } + fillHotkeys(hwnd, ruleIndex); + + openRule(hwnd); +} + +void +CHotkeyOptions::editHotkey(HWND hwnd) +{ + // save selected item in action list + HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS); + LRESULT aIndex = SendMessage(actions, LB_GETCURSEL, 0, 0); + + UInt32 index = m_activeRuleIndex; + closeRule(hwnd); + + CInputFilter::CRule& rule = m_inputFilter->getRule(index); + CInputFilter::CCondition* condition = rule.getCondition()->clone(); + if (editCondition(hwnd, condition)) { + rule.setCondition(condition); + fillHotkeys(hwnd, index); + } + else { + delete condition; + } + + openRule(hwnd); + + // restore selected item in action list + if (aIndex != LB_ERR) { + SendMessage(actions, LB_SETCURSEL, aIndex, 0); + } +} + +void +CHotkeyOptions::fillActions(HWND hwnd, UInt32 select) +{ + HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS); + SendMessage(actions, LB_RESETCONTENT, 0, 0); + if (m_activeRuleIndex != (UInt32)-1) { + UInt32 n = m_activeRule.getNumActions(true); + UInt32 n2 = m_activeRule.getNumActions(false); + for (UInt32 i = 0; i < n; ++i) { + const CInputFilter::CAction& action = + m_activeRule.getAction(true, i); + CString line("A "); + line += action.format(); + SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)line.c_str()); + SendMessage(actions, LB_SETITEMDATA, (WPARAM)i, (LPARAM)i); + } + for (UInt32 i = 0; i < n2; ++i) { + const CInputFilter::CAction& action = + m_activeRule.getAction(false, i); + CString line("D "); + line += action.format(); + SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)line.c_str()); + SendMessage(actions, LB_SETITEMDATA, (WPARAM)i + n, + (LPARAM)(i | 0x80000000u)); + } + + if (select < n + n2) { + SendMessage(actions, LB_SETCURSEL, select, 0); + } + } + + updateActionsControls(hwnd); +} + +void +CHotkeyOptions::updateActionsControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + bool active = (m_activeRuleIndex != (UInt32)-1); + + child = getItem(hwnd, IDC_HOTKEY_ACTIONS); + bool selected = + (active && (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR)); + + enableItem(hwnd, IDC_HOTKEY_ADD_ACTION, active); + enableItem(hwnd, IDC_HOTKEY_EDIT_ACTION, selected); + enableItem(hwnd, IDC_HOTKEY_REMOVE_ACTION, selected); +} + +void +CHotkeyOptions::addAction(HWND hwnd) +{ + CInputFilter::CAction* action = NULL; + bool onActivate = true; + if (editAction(hwnd, action, onActivate)) { + m_activeRule.adoptAction(action, onActivate); + + UInt32 actionIndex = m_activeRule.getNumActions(true) - 1; + if (!onActivate) { + actionIndex += m_activeRule.getNumActions(false); + } + fillActions(hwnd, actionIndex); + } + else { + delete action; + } +} + +void +CHotkeyOptions::removeAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + UInt32 actionIndex = + (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0); + bool onActivate = ((actionIndex & 0x80000000u) == 0); + actionIndex &= ~0x80000000u; + + m_activeRule.removeAction(onActivate, actionIndex); + + actionIndex = static_cast(index); + UInt32 n = m_activeRule.getNumActions(true) + + m_activeRule.getNumActions(false); + if (n > 0 && actionIndex >= n) { + actionIndex = n - 1; + } + fillActions(hwnd, actionIndex); + } +} + +void +CHotkeyOptions::editAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + UInt32 actionIndex = + (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0); + bool onActivate = ((actionIndex & 0x80000000u) == 0); + actionIndex &= ~0x80000000u; + + CInputFilter::CAction* action = + m_activeRule.getAction(onActivate, actionIndex).clone(); + bool newOnActivate = onActivate; + if (editAction(hwnd, action, newOnActivate)) { + if (onActivate == newOnActivate) { + m_activeRule.replaceAction(action, onActivate, actionIndex); + actionIndex = static_cast(index); + } + else { + m_activeRule.removeAction(onActivate, actionIndex); + m_activeRule.adoptAction(action, newOnActivate); + actionIndex = m_activeRule.getNumActions(true) - 1; + if (!newOnActivate) { + actionIndex += m_activeRule.getNumActions(false); + } + } + fillActions(hwnd, actionIndex); + } + else { + delete action; + } + } +} + +bool +CHotkeyOptions::editCondition(HWND hwnd, CInputFilter::CCondition*& condition) +{ + return CConditionDialog::doModal(hwnd, condition); +} + +bool +CHotkeyOptions::editAction(HWND hwnd, CInputFilter::CAction*& action, + bool& onActivate) +{ + return CActionDialog::doModal(hwnd, m_config, action, onActivate); +} + +void +CHotkeyOptions::openRule(HWND hwnd) +{ + // get the active rule and copy it, merging down/up pairs of keystroke + // and mouse button actions into single actions for the convenience of + // of the user. + HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + // copy the rule as is + m_activeRuleIndex = (SInt32)index; + m_activeRule = m_inputFilter->getRule(m_activeRuleIndex); + + // look for actions to combine + for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) { + // get next activate action + const CInputFilter::CAction* action = + &m_activeRule.getAction(true, i); + + // check if it's a key or mouse action + const CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(action); + const CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(action); + if (keyAction == NULL && mouseAction == NULL) { + continue; + } + + // check for matching deactivate action + UInt32 j = (UInt32)-1; + CInputFilter::CAction* newAction = NULL; + if (keyAction != NULL) { + j = findMatchingAction(keyAction); + if (j != (UInt32)-1) { + // found a match + const IPlatformScreen::CKeyInfo* oldInfo = + keyAction->getInfo(); + IPlatformScreen::CKeyInfo* newInfo = + IKeyState::CKeyInfo::alloc(*oldInfo); + newAction = new CKeystrokeDownUpAction(newInfo); + } + } + else if (mouseAction != NULL) { + j = findMatchingAction(mouseAction); + if (j != (UInt32)-1) { + // found a match + const IPlatformScreen::CButtonInfo* oldInfo = + mouseAction->getInfo(); + IPlatformScreen::CButtonInfo* newInfo = + IPrimaryScreen::CButtonInfo::alloc(*oldInfo); + newAction = new CMouseButtonDownUpAction(newInfo); + } + } + + // perform merge + if (newAction != NULL) { + m_activeRule.replaceAction(newAction, true, i); + m_activeRule.removeAction(false, j); + } + } + } + else { + m_activeRuleIndex = (UInt32)-1; + } + fillActions(hwnd); +} + +void +CHotkeyOptions::closeRule(HWND) +{ + // copy rule back to input filter, expanding merged actions into the + // two component actions. + if (m_activeRuleIndex != (UInt32)-1) { + // expand merged rules + for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) { + // get action + const CInputFilter::CAction* action = + &m_activeRule.getAction(true, i); + + // check if it's a merged key or mouse action + const CKeystrokeDownUpAction* keyAction = + dynamic_cast(action); + const CMouseButtonDownUpAction* mouseAction = + dynamic_cast(action); + if (keyAction == NULL && mouseAction == NULL) { + continue; + } + + // expand + if (keyAction != NULL) { + const IPlatformScreen::CKeyInfo* oldInfo = + keyAction->getInfo(); + IPlatformScreen::CKeyInfo* newInfo = + IKeyState::CKeyInfo::alloc(*oldInfo); + CInputFilter::CKeystrokeAction* downAction = + new CInputFilter::CKeystrokeAction(newInfo, true); + newInfo = IKeyState::CKeyInfo::alloc(*oldInfo); + CInputFilter::CKeystrokeAction* upAction = + new CInputFilter::CKeystrokeAction(newInfo, false); + m_activeRule.replaceAction(downAction, true, i); + m_activeRule.adoptAction(upAction, false); + } + else if (mouseAction != NULL) { + const IPlatformScreen::CButtonInfo* oldInfo = + mouseAction->getInfo(); + IPlatformScreen::CButtonInfo* newInfo = + IPrimaryScreen::CButtonInfo::alloc(*oldInfo); + CInputFilter::CMouseButtonAction* downAction = + new CInputFilter::CMouseButtonAction(newInfo, true); + newInfo = IPrimaryScreen::CButtonInfo::alloc(*oldInfo); + CInputFilter::CMouseButtonAction* upAction = + new CInputFilter::CMouseButtonAction(newInfo, false); + m_activeRule.replaceAction(downAction, true, i); + m_activeRule.adoptAction(upAction, false); + } + } + + // copy it back + m_inputFilter->getRule(m_activeRuleIndex) = m_activeRule; + } + m_activeRuleIndex = (UInt32)-1; +} + +UInt32 +CHotkeyOptions::findMatchingAction( + const CInputFilter::CKeystrokeAction* src) const +{ + for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) { + const CInputFilter::CKeystrokeAction* dst = + dynamic_cast( + &m_activeRule.getAction(false, i)); + if (dst != NULL && + IKeyState::CKeyInfo::equal(src->getInfo(), dst->getInfo())) { + return i; + } + } + return (UInt32)-1; +} + +UInt32 +CHotkeyOptions::findMatchingAction( + const CInputFilter::CMouseButtonAction* src) const +{ + for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) { + const CInputFilter::CMouseButtonAction* dst = + dynamic_cast( + &m_activeRule.getAction(false, i)); + if (dst != NULL && + IPrimaryScreen::CButtonInfo::equal( + src->getInfo(), dst->getInfo())) { + return i; + } + } + return (UInt32)-1; +} + +BOOL +CHotkeyOptions::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: + case IDCANCEL: + closeRule(hwnd); + EndDialog(hwnd, 0); + return TRUE; + + case IDC_HOTKEY_HOTKEYS: + switch (HIWORD(wParam)) { + case LBN_DBLCLK: + editHotkey(hwnd); + return TRUE; + + case LBN_SELCHANGE: { + HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0); + if (m_activeRuleIndex != (UInt32)index) { + closeRule(hwnd); + updateHotkeysControls(hwnd); + openRule(hwnd); + } + return TRUE; + } + } + break; + + case IDC_HOTKEY_ADD_HOTKEY: + addHotkey(hwnd); + return TRUE; + + case IDC_HOTKEY_REMOVE_HOTKEY: + removeHotkey(hwnd); + return TRUE; + + case IDC_HOTKEY_EDIT_HOTKEY: + editHotkey(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTIONS: + switch (HIWORD(wParam)) { + case LBN_DBLCLK: + editAction(hwnd); + return TRUE; + + case LBN_SELCHANGE: + updateActionsControls(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ADD_ACTION: + addAction(hwnd); + return TRUE; + + case IDC_HOTKEY_REMOVE_ACTION: + removeAction(hwnd); + return TRUE; + + case IDC_HOTKEY_EDIT_ACTION: + editAction(hwnd); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CHotkeyOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} + + +// +// CHotkeyOptions::CConditionDialog +// + +CInputFilter::CCondition* + CHotkeyOptions::CConditionDialog::s_condition = NULL; +CInputFilter::CCondition* + CHotkeyOptions::CConditionDialog::s_lastGoodCondition = NULL; +WNDPROC CHotkeyOptions::CConditionDialog::s_editWndProc = NULL; + +bool +CHotkeyOptions::CConditionDialog::doModal(HWND parent, + CInputFilter::CCondition*& condition) +{ + s_condition = condition; + if (s_condition != NULL) { + s_lastGoodCondition = s_condition->clone(); + } + else { + s_lastGoodCondition = NULL; + } + int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_CONDITION), + parent, dlgProc); + + condition = s_condition; + delete s_lastGoodCondition; + s_condition = NULL; + s_lastGoodCondition = NULL; + + // user effectively cancelled if the condition is NULL + if (condition == NULL) { + n = 0; + } + + return (n == 1); +} + +void +CHotkeyOptions::CConditionDialog::doInit(HWND hwnd) +{ + // subclass edit control + HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY); + s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC); + SetWindowLong(child, GWL_WNDPROC, (LONG)editProc); + + // fill control + fillHotkey(hwnd); +} + +void +CHotkeyOptions::CConditionDialog::fillHotkey(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY); + if (s_condition != NULL) { + setWindowText(child, s_condition->format().c_str()); + } + else { + setWindowText(child, ""); + } +} + +void +CHotkeyOptions::CConditionDialog::onButton(HWND hwnd, ButtonID button) +{ + delete s_condition; + s_condition = + new CInputFilter::CMouseButtonCondition(button, getModifiers()); + + fillHotkey(GetParent(hwnd)); +} + +void +CHotkeyOptions::CConditionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + // ignore key repeats + if ((lParam & 0xc0000000u) == 0x40000000u) { + return; + } + + // ignore key releases if the condition is complete and for the tab + // key (in case we were just tabbed to) + if ((lParam & 0x80000000u) != 0) { + if (isGoodCondition() || wParam == VK_TAB) { + return; + } + } + + KeyID key = kKeyNone; + KeyModifierMask mask = getModifiers(); + switch (wParam) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + break; + + case VK_TAB: + // allow tabbing out of control + if ((mask & (KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) == 0) { + HWND next = hwnd; + if ((mask & KeyModifierShift) == 0) { + do { + next = GetWindow(next, GW_HWNDNEXT); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDFIRST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + else { + do { + next = GetWindow(next, GW_HWNDPREV); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDLAST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + SetFocus(next); + return; + } + // fall through + + default: + key = CMSWindowsKeyState::getKeyID(wParam, + static_cast((lParam & 0x1ff0000u) >> 16)); + switch (key) { + case kKeyNone: + // could be a character + key = getChar(wParam, lParam); + if (key == kKeyNone) { + return; + } + break; + + case kKeyShift_L: + case kKeyShift_R: + case kKeyControl_L: + case kKeyControl_R: + case kKeyAlt_L: + case kKeyAlt_R: + case kKeyMeta_L: + case kKeyMeta_R: + case kKeySuper_L: + case kKeySuper_R: + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: + // bogus + return; + } + break; + } + + delete s_condition; + s_condition = new CInputFilter::CKeystrokeCondition(key, mask); + + fillHotkey(GetParent(hwnd)); +} + +KeyID +CHotkeyOptions::CConditionDialog::getChar(WPARAM wParam, LPARAM lParam) +{ + BYTE keyState[256]; + UINT virtualKey = (UINT)wParam; + UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16); + GetKeyboardState(keyState); + + // reset modifier state + keyState[VK_SHIFT] = 0; + keyState[VK_LSHIFT] = 0; + keyState[VK_RSHIFT] = 0; + keyState[VK_CONTROL] = 0; + keyState[VK_LCONTROL] = 0; + keyState[VK_RCONTROL] = 0; + keyState[VK_MENU] = 0; + keyState[VK_LMENU] = 0; + keyState[VK_RMENU] = 0; + keyState[VK_LWIN] = 0; + keyState[VK_RWIN] = 0; + + // translate virtual key to character + int n; + KeyID id; + if (CArchMiscWindows::isWindows95Family()) { + // XXX -- how do we get characters not in Latin-1? + WORD ascii; + n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0); + id = static_cast(ascii & 0xffu); + } + else { + typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey, + UINT wScanCode, + PBYTE lpKeyState, + LPWSTR pwszBuff, + int cchBuff, + UINT wFlags); + ToUnicode_t s_ToUnicode = NULL; + if (s_ToUnicode == NULL) { + HMODULE userModule = GetModuleHandle("user32.dll"); + s_ToUnicode = + (ToUnicode_t)GetProcAddress(userModule, "ToUnicode"); + } + + WCHAR unicode[2]; + n = s_ToUnicode(virtualKey, scanCode, keyState, + unicode, sizeof(unicode) / sizeof(unicode[0]), + 0); + id = static_cast(unicode[0]); + } + switch (n) { + case -1: + // no hot keys on dead keys + return kKeyNone; + + default: + case 0: + // unmapped + return kKeyNone; + + case 1: + return id; + } +} + +KeyModifierMask +CHotkeyOptions::CConditionDialog::getModifiers() +{ + KeyModifierMask mask = 0; + if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) { + mask |= KeyModifierShift; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + mask |= KeyModifierControl; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + mask |= KeyModifierAlt; + } + if ((GetKeyState(VK_LWIN) & 0x8000) != 0 || + (GetKeyState(VK_RWIN) & 0x8000) != 0) { + mask |= KeyModifierSuper; + } + return mask; +} + +bool +CHotkeyOptions::CConditionDialog::isGoodCondition() +{ + CInputFilter::CKeystrokeCondition* keyCondition = + dynamic_cast(s_condition); + return (keyCondition == NULL || keyCondition->getKey() != kKeyNone); +} + +BOOL CALLBACK +CHotkeyOptions::CConditionDialog::dlgProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +LRESULT CALLBACK +CHotkeyOptions::CConditionDialog::editProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_LBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonLeft); + } + else { + SetFocus(hwnd); + } + return 0; + + case WM_MBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonMiddle); + } + return 0; + + case WM_RBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonRight); + } + return 0; + + case WM_XBUTTONDOWN: + if (GetFocus() == hwnd) { + switch (HIWORD(wParam)) { + case XBUTTON1: + onButton(hwnd, kButtonExtra0 + 0); + break; + + case XBUTTON2: + onButton(hwnd, kButtonExtra0 + 1); + break; + } + } + return 0; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + onKey(hwnd, wParam, lParam); + return 0; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_CHAR: + case WM_SYSCHAR: + case WM_DEADCHAR: + case WM_SYSDEADCHAR: + return 0; + + case WM_SETFOCUS: + if (s_condition != NULL) { + delete s_lastGoodCondition; + s_lastGoodCondition = s_condition->clone(); + } + break; + + case WM_KILLFOCUS: + if (!isGoodCondition()) { + delete s_condition; + if (s_lastGoodCondition != NULL) { + s_condition = s_lastGoodCondition->clone(); + } + else { + s_condition = NULL; + } + } + fillHotkey(GetParent(hwnd)); + break; + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + default: + break; + } + return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam); +} + + +// +// CHotkeyOptions::CActionDialog +// + +CConfig* CHotkeyOptions::CActionDialog::s_config = NULL; +bool CHotkeyOptions::CActionDialog::s_onActivate = false; +CInputFilter::CAction* + CHotkeyOptions::CActionDialog::s_action = NULL; +CInputFilter::CAction* + CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL; +WNDPROC CHotkeyOptions::CActionDialog::s_editWndProc = NULL; + +bool +CHotkeyOptions::CActionDialog::doModal(HWND parent, CConfig* config, + CInputFilter::CAction*& action, bool& onActivate) +{ + s_config = config; + s_onActivate = onActivate; + s_action = action; + if (s_action != NULL) { + s_lastGoodAction = s_action->clone(); + } + else { + s_lastGoodAction = NULL; + } + + int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_ACTION), + parent, dlgProc); + + onActivate = s_onActivate; + action = s_action; + delete s_lastGoodAction; + s_action = NULL; + s_lastGoodAction = NULL; + + return (n == 1); +} + +void +CHotkeyOptions::CActionDialog::doInit(HWND hwnd) +{ + // subclass edit control + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY); + s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC); + SetWindowLong(child, GWL_WNDPROC, (LONG)editProc); + setWindowText(getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY), ""); + fillHotkey(hwnd); + + // fill screens + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST); + SendMessage(child, CB_RESETCONTENT, 0, 0); + for (CConfig::const_iterator index = s_config->begin(); + index != s_config->end(); ) { + const CString& name = *index; + ++index; + if (index != s_config->end()) { + SendMessage(child, CB_INSERTSTRING, + (WPARAM)-1, (LPARAM)name.c_str()); + } + else { + SendMessage(child, CB_ADDSTRING, 0, (LPARAM)name.c_str()); + } + } + SendMessage(child, CB_SETCURSEL, 0, 0); + + // fill directions + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_LEFT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_RIGHT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_TOP).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_BOTTOM).c_str()); + SendMessage(child, CB_SETCURSEL, 0, 0); + + // fill lock modes + child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_LOCK_MODE_OFF).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_LOCK_MODE_ON).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_LOCK_MODE_TOGGLE).c_str()); + SendMessage(child, CB_SETCURSEL, 0, 0); + + // select when + if (s_onActivate) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_ACTIVATE); + } + else { + child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_DEACTIVATE); + } + setItemChecked(child, true); + + // select mode + child = NULL; + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_action); + CInputFilter::CLockCursorToScreenAction* lockAction = + dynamic_cast(s_action); + CInputFilter::CSwitchToScreenAction* switchToAction = + dynamic_cast(s_action); + CInputFilter::CSwitchInDirectionAction* switchInAction = + dynamic_cast(s_action); + if (keyAction != NULL) { + if (dynamic_cast(s_action) != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP); + } + else if (keyAction->isOnPress()) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN); + } + else { + child = getItem(hwnd, IDC_HOTKEY_ACTION_UP); + } + } + else if (mouseAction != NULL) { + if (dynamic_cast(s_action) != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP); + } + else if (keyAction->isOnPress()) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN); + } + else { + child = getItem(hwnd, IDC_HOTKEY_ACTION_UP); + } + } + else if (lockAction != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); + SendMessage(child, CB_SETCURSEL, lockAction->getMode(), 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK); + } + else if (switchToAction != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST); + DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1, + (LPARAM)switchToAction->getScreen().c_str()); + if (i == CB_ERR) { + i = 0; + } + SendMessage(child, CB_SETCURSEL, i, 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO); + } + else if (switchInAction != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST); + SendMessage(child, CB_SETCURSEL, + switchInAction->getDirection() - kLeft, 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN); + } + if (child != NULL) { + setItemChecked(child, true); + } + + updateControls(hwnd); +} + +void +CHotkeyOptions::CActionDialog::fillHotkey(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY); + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_action); + if (keyAction != NULL || mouseAction != NULL) { + setWindowText(child, s_action->format().c_str()); + } + else { + setWindowText(child, ""); + } + + // can only set screens in key actions + enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL); +} + +void +CHotkeyOptions::CActionDialog::updateControls(HWND hwnd) +{ + // determine which mode we're in + UInt32 mode = 0; + if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP)) || + isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN)) || + isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) { + mode = 1; + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO))) { + mode = 2; + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN))) { + mode = 3; + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) { + mode = 4; + } + + // enable/disable all mode specific controls + enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1); + enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2); + enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3); + enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4); + + // can only set screens in key actions + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL); +} + +void +CHotkeyOptions::CActionDialog::onButton(HWND hwnd, ButtonID button) +{ + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(button, getModifiers()); + delete s_action; + HWND parent = GetParent(hwnd); + if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CMouseButtonDownUpAction(info); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CMouseButtonAction(info, true); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CMouseButtonAction(info, false); + } + else { + s_action = NULL; + } + + fillHotkey(parent); +} + +void +CHotkeyOptions::CActionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + // ignore key repeats + if ((lParam & 0xc0000000u) == 0x40000000u) { + return; + } + + // ignore key releases if the action is complete and for the tab + // key (in case we were just tabbed to) + if ((lParam & 0x80000000u) != 0) { + if (isGoodAction() || wParam == VK_TAB) { + return; + } + } + + KeyID key = kKeyNone; + KeyModifierMask mask = getModifiers(); + switch (wParam) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + break; + + case VK_TAB: + // allow tabbing out of control + if ((mask & (KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) == 0) { + HWND next = hwnd; + if ((mask & KeyModifierShift) == 0) { + do { + next = GetWindow(next, GW_HWNDNEXT); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDFIRST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + else { + do { + next = GetWindow(next, GW_HWNDPREV); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDLAST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + SetFocus(next); + return; + } + // fall through + + default: + key = CMSWindowsKeyState::getKeyID(wParam, + static_cast((lParam & 0x1ff0000u) >> 16)); + switch (key) { + case kKeyNone: + // could be a character + key = getChar(wParam, lParam); + if (key == kKeyNone) { + return; + } + break; + + case kKeyShift_L: + case kKeyShift_R: + case kKeyControl_L: + case kKeyControl_R: + case kKeyAlt_L: + case kKeyAlt_R: + case kKeyMeta_L: + case kKeyMeta_R: + case kKeySuper_L: + case kKeySuper_R: + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: + // bogus + return; + } + break; + } + + // get old screen list + std::set screens; + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + if (keyAction == NULL) { + keyAction = + dynamic_cast(s_lastGoodAction); + } + if (keyAction != NULL) { + IKeyState::CKeyInfo::split(keyAction->getInfo()->m_screens, screens); + } + + // create new action + IPlatformScreen::CKeyInfo* info = + IKeyState::CKeyInfo::alloc(key, mask, 0, 0, screens); + delete s_action; + HWND parent = GetParent(hwnd); + if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CKeystrokeDownUpAction(info); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CKeystrokeAction(info, true); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CKeystrokeAction(info, false); + } + else { + s_action = NULL; + } + + fillHotkey(parent); +} + +void +CHotkeyOptions::CActionDialog::onLockAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); + LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) { + delete s_action; + s_action = new CInputFilter::CLockCursorToScreenAction( + (CInputFilter::CLockCursorToScreenAction::Mode)index); + } +} + +void +CHotkeyOptions::CActionDialog::onSwitchToAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST); + CString screen = getWindowText(child); + delete s_action; + s_action = new CInputFilter::CSwitchToScreenAction(screen); +} + +void +CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST); + LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) { + delete s_action; + s_action = new CInputFilter::CSwitchInDirectionAction( + (EDirection)(index + kLeft)); + } +} + +KeyID +CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam) +{ + BYTE keyState[256]; + UINT virtualKey = (UINT)wParam; + UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16); + GetKeyboardState(keyState); + + // reset modifier state + keyState[VK_SHIFT] = 0; + keyState[VK_LSHIFT] = 0; + keyState[VK_RSHIFT] = 0; + keyState[VK_CONTROL] = 0; + keyState[VK_LCONTROL] = 0; + keyState[VK_RCONTROL] = 0; + keyState[VK_MENU] = 0; + keyState[VK_LMENU] = 0; + keyState[VK_RMENU] = 0; + keyState[VK_LWIN] = 0; + keyState[VK_RWIN] = 0; + + // translate virtual key to character + int n; + KeyID id; + if (CArchMiscWindows::isWindows95Family()) { + // XXX -- how do we get characters not in Latin-1? + WORD ascii; + n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0); + id = static_cast(ascii & 0xffu); + } + else { + typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey, + UINT wScanCode, + PBYTE lpKeyState, + LPWSTR pwszBuff, + int cchBuff, + UINT wFlags); + ToUnicode_t s_ToUnicode = NULL; + if (s_ToUnicode == NULL) { + HMODULE userModule = GetModuleHandle("user32.dll"); + s_ToUnicode = + (ToUnicode_t)GetProcAddress(userModule, "ToUnicode"); + } + + WCHAR unicode[2]; + n = s_ToUnicode(virtualKey, scanCode, keyState, + unicode, sizeof(unicode) / sizeof(unicode[0]), + 0); + id = static_cast(unicode[0]); + } + switch (n) { + case -1: + // no hot keys on dead keys + return kKeyNone; + + default: + case 0: + // unmapped + return kKeyNone; + + case 1: + return id; + } +} + +KeyModifierMask +CHotkeyOptions::CActionDialog::getModifiers() +{ + KeyModifierMask mask = 0; + if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) { + mask |= KeyModifierShift; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + mask |= KeyModifierControl; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + mask |= KeyModifierAlt; + } + if ((GetKeyState(VK_LWIN) & 0x8000) != 0 || + (GetKeyState(VK_RWIN) & 0x8000) != 0) { + mask |= KeyModifierSuper; + } + return mask; +} + +bool +CHotkeyOptions::CActionDialog::isGoodAction() +{ + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_action); + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + return (mouseAction == NULL || keyAction == NULL || + keyAction->getInfo()->m_key != kKeyNone); +} + +void +CHotkeyOptions::CActionDialog::convertAction(HWND hwnd) +{ + if (s_lastGoodAction != NULL) { + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_lastGoodAction); + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_lastGoodAction); + if (mouseAction != NULL) { + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(*mouseAction->getInfo()); + delete s_action; + if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CMouseButtonDownUpAction(info); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CMouseButtonAction(info, true); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CMouseButtonAction(info, false); + } + else { + free(info); + s_action = NULL; + } + } + else if (keyAction != NULL) { + IPlatformScreen::CKeyInfo* info = + IKeyState::CKeyInfo::alloc(*keyAction->getInfo()); + delete s_action; + if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CKeystrokeDownUpAction(info); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CKeystrokeAction(info, true); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CKeystrokeAction(info, false); + } + else { + free(info); + s_action = NULL; + } + } + } +} + +bool +CHotkeyOptions::CActionDialog::isDownUpAction() +{ + return (dynamic_cast(s_action) != NULL || + dynamic_cast(s_action) != NULL); +} + +BOOL CALLBACK +CHotkeyOptions::CActionDialog::dlgProc(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 (isDownUpAction()) { + s_onActivate = true; + } + EndDialog(hwnd, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_HOTKEY_ACTION_ON_ACTIVATE: + s_onActivate = true; + return TRUE; + + case IDC_HOTKEY_ACTION_ON_DEACTIVATE: + s_onActivate = false; + return TRUE; + + case IDC_HOTKEY_ACTION_DOWNUP: + case IDC_HOTKEY_ACTION_DOWN: + case IDC_HOTKEY_ACTION_UP: + convertAction(hwnd); + fillHotkey(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_LOCK: + onLockAction(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_SWITCH_TO: + onSwitchToAction(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_SWITCH_IN: + onSwitchInAction(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_LOCK_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onLockAction(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ACTION_SWITCH_TO_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onSwitchToAction(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ACTION_SWITCH_IN_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onSwitchInAction(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ACTION_SCREENS: + CScreensDialog::doModal(hwnd, s_config, + dynamic_cast(s_action)); + fillHotkey(hwnd); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +LRESULT CALLBACK +CHotkeyOptions::CActionDialog::editProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_LBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonLeft); + } + else { + SetFocus(hwnd); + } + return 0; + + case WM_MBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonMiddle); + } + return 0; + + case WM_RBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonRight); + } + return 0; + + case WM_XBUTTONDOWN: + if (GetFocus() == hwnd) { + switch (HIWORD(wParam)) { + case XBUTTON1: + onButton(hwnd, kButtonExtra0 + 0); + break; + + case XBUTTON2: + onButton(hwnd, kButtonExtra0 + 1); + break; + } + } + return 0; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + onKey(hwnd, wParam, lParam); + return 0; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_CHAR: + case WM_SYSCHAR: + case WM_DEADCHAR: + case WM_SYSDEADCHAR: + return 0; + + case WM_SETFOCUS: + if (s_action != NULL) { + delete s_lastGoodAction; + s_lastGoodAction = s_action->clone(); + } + break; + + case WM_KILLFOCUS: + if (!isGoodAction()) { + delete s_action; + if (s_lastGoodAction != NULL) { + s_action = s_lastGoodAction->clone(); + } + else { + s_action = NULL; + } + } + else if (s_action != NULL) { + delete s_lastGoodAction; + s_lastGoodAction = s_action->clone(); + } + fillHotkey(GetParent(hwnd)); + break; + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + default: + break; + } + return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam); +} + + +// +// CHotkeyOptions::CScreensDialog +// + +CConfig* CHotkeyOptions::CScreensDialog::s_config = NULL; +CInputFilter::CKeystrokeAction* + CHotkeyOptions::CScreensDialog::s_action = NULL; +CHotkeyOptions::CScreensDialog::CScreens + CHotkeyOptions::CScreensDialog::s_nonTargets; +CHotkeyOptions::CScreensDialog::CScreens + CHotkeyOptions::CScreensDialog::s_targets; +CString CHotkeyOptions::CScreensDialog::s_allScreens; + +void +CHotkeyOptions::CScreensDialog::doModal(HWND parent, CConfig* config, + CInputFilter::CKeystrokeAction* action) +{ + s_allScreens = getString(IDS_ALL_SCREENS); + s_config = config; + s_action = action; + DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_SCREENS), + parent, dlgProc); + s_config = NULL; + s_action = NULL; +} + +void +CHotkeyOptions::CScreensDialog::doInit(HWND hwnd) +{ + s_nonTargets.clear(); + s_targets.clear(); + + // get screens from config + s_nonTargets.insert("*"); + for (CConfig::const_iterator i = s_config->begin(); + i != s_config->end(); ++i) { + s_nonTargets.insert(*i); + } + + // get screens in action + IKeyState::CKeyInfo::split(s_action->getInfo()->m_screens, s_targets); + + // remove screens in action from screens in config + for (CScreens::const_iterator i = s_targets.begin(); + i != s_targets.end(); ++i) { + s_nonTargets.erase(*i); + } + + // fill dialog + fillScreens(hwnd); + updateControls(hwnd); +} + +void +CHotkeyOptions::CScreensDialog::doFini(HWND) +{ + // put screens into action + const IPlatformScreen::CKeyInfo* oldInfo = s_action->getInfo(); + IPlatformScreen::CKeyInfo* newInfo = + IKeyState::CKeyInfo::alloc(oldInfo->m_key, + oldInfo->m_mask, 0, 0, s_targets); + s_action->adoptInfo(newInfo); +} + +void +CHotkeyOptions::CScreensDialog::fillScreens(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CScreens::const_iterator i = s_nonTargets.begin(); + i != s_nonTargets.end(); ++i) { + CString name = *i; + if (name == "*") { + name = s_allScreens; + } + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + } + + child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CScreens::const_iterator i = s_targets.begin(); + i != s_targets.end(); ++i) { + CString name = *i; + if (name == "*") { + name = s_allScreens; + } + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + } + if (s_targets.empty()) { + // if no targets then add a special item so the user knows + // what'll happen + CString activeScreenLabel = getString(IDS_ACTIVE_SCREEN); + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)activeScreenLabel.c_str()); + } +} + +void +CHotkeyOptions::CScreensDialog::updateControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC); + bool canAdd = (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0); + child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST); + bool canRemove = (!s_targets.empty() && + (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0)); + + enableItem(hwnd, IDC_HOTKEY_SCREENS_ADD, canAdd); + enableItem(hwnd, IDC_HOTKEY_SCREENS_REMOVE, canRemove); +} + +void +CHotkeyOptions::CScreensDialog::add(HWND hwnd) +{ + CScreens selected; + getSelected(hwnd, IDC_HOTKEY_SCREENS_SRC, s_nonTargets, selected); + for (CScreens::const_iterator i = selected.begin(); + i != selected.end(); ++i) { + s_targets.insert(*i); + s_nonTargets.erase(*i); + } + fillScreens(hwnd); + updateControls(hwnd); +} + +void +CHotkeyOptions::CScreensDialog::remove(HWND hwnd) +{ + CScreens selected; + getSelected(hwnd, IDC_HOTKEY_SCREENS_DST, s_targets, selected); + for (CScreens::const_iterator i = selected.begin(); + i != selected.end(); ++i) { + s_nonTargets.insert(*i); + s_targets.erase(*i); + } + fillScreens(hwnd); + updateControls(hwnd); +} + +void +CHotkeyOptions::CScreensDialog::getSelected(HWND hwnd, UINT id, + const CScreens& inScreens, CScreens& outScreens) +{ + // get the selected item indices + HWND child = getItem(hwnd, id); + UInt32 n = (UInt32)SendMessage(child, LB_GETSELCOUNT, 0, 0); + int* index = new int[n]; + SendMessage(child, LB_GETSELITEMS, (WPARAM)n, (LPARAM)index); + + // get the items in a vector + std::vector tmpList; + for (CScreens::const_iterator i = inScreens.begin(); + i != inScreens.end(); ++i) { + tmpList.push_back(*i); + } + + // get selected items into the output set + outScreens.clear(); + for (UInt32 i = 0; i < n; ++i) { + outScreens.insert(tmpList[index[i]]); + } + + // clean up + delete[] index; +} + +BOOL CALLBACK +CHotkeyOptions::CScreensDialog::dlgProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + doFini(hwnd); + EndDialog(hwnd, 0); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_HOTKEY_SCREENS_ADD: + add(hwnd); + return TRUE; + + case IDC_HOTKEY_SCREENS_REMOVE: + remove(hwnd); + return TRUE; + + case IDC_HOTKEY_SCREENS_SRC: + case IDC_HOTKEY_SCREENS_DST: + switch (HIWORD(wParam)) { + case LBN_SELCANCEL: + case LBN_SELCHANGE: + updateControls(hwnd); + return TRUE; + } + break; + } + break; + + case WM_CTLCOLORLISTBOX: + if (s_targets.empty() && + (HWND)lParam == getItem(hwnd, IDC_HOTKEY_SCREENS_DST)) { + // override colors + HDC dc = (HDC)wParam; + SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT)); + return (BOOL)GetSysColorBrush(COLOR_WINDOW); + } + break; + + default: + break; + } + + return FALSE; +} diff --git a/cmd/launcher/CHotkeyOptions.h b/cmd/launcher/CHotkeyOptions.h new file mode 100644 index 00000000..b63fcf2b --- /dev/null +++ b/cmd/launcher/CHotkeyOptions.h @@ -0,0 +1,225 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 CHOTKEYOPTIONS_H +#define CHOTKEYOPTIONS_H + +#include "CString.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CInputFilter.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Hotkey options dialog for Microsoft Windows launcher +class CHotkeyOptions { +public: + CHotkeyOptions(HWND parent, CConfig*); + ~CHotkeyOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + //@} + +private: + void doInit(HWND hwnd); + + void fillHotkeys(HWND hwnd, UInt32 select = (UInt32)-1); + void updateHotkeysControls(HWND hwnd); + + void addHotkey(HWND hwnd); + void removeHotkey(HWND hwnd); + void editHotkey(HWND hwnd); + + void fillActions(HWND hwnd, UInt32 select = (UInt32)-1); + void updateActionsControls(HWND hwnd); + + void addAction(HWND hwnd); + void removeAction(HWND hwnd); + void editAction(HWND hwnd); + + bool editCondition(HWND hwnd, CInputFilter::CCondition*&); + bool editAction(HWND hwnd, CInputFilter::CAction*&, + bool& onActivate); + + void openRule(HWND hwnd); + void closeRule(HWND hwnd); + UInt32 findMatchingAction( + const CInputFilter::CKeystrokeAction*) const; + UInt32 findMatchingAction( + const CInputFilter::CMouseButtonAction*) const; + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + + // special actions we use to combine matching down/up actions into a + // single action for the convenience of the user. + class CKeystrokeDownUpAction : public CInputFilter::CKeystrokeAction { + public: + CKeystrokeDownUpAction(IPlatformScreen::CKeyInfo* adoptedInfo) : + CInputFilter::CKeystrokeAction(adoptedInfo, true) { } + + // CAction overrides + virtual CInputFilter::CAction* clone() const + { + IKeyState::CKeyInfo* info = IKeyState::CKeyInfo::alloc(*getInfo()); + return new CKeystrokeDownUpAction(info); + } + + protected: + // CKeystrokeAction overrides + virtual const char* formatName() const { return "keystroke"; } + }; + class CMouseButtonDownUpAction : public CInputFilter::CMouseButtonAction { + public: + CMouseButtonDownUpAction(IPrimaryScreen::CButtonInfo* adoptedInfo) : + CInputFilter::CMouseButtonAction(adoptedInfo, true) { } + + // CAction overrides + virtual CInputFilter::CAction* clone() const + { + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(*getInfo()); + return new CMouseButtonDownUpAction(info); + } + + protected: + // CMouseButtonAction overrides + virtual const char* formatName() const { return "mousebutton"; } + }; + + class CConditionDialog { + public: + static bool doModal(HWND parent, CInputFilter::CCondition*&); + + private: + static void doInit(HWND hwnd); + static void fillHotkey(HWND hwnd); + + static void onButton(HWND hwnd, ButtonID button); + static void onKey(HWND hwnd, WPARAM wParam, LPARAM lParam); + static KeyID getChar(WPARAM wParam, LPARAM lParam); + static KeyModifierMask + getModifiers(); + + static bool isGoodCondition(); + + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK editProc(HWND hwnd, UINT, WPARAM, LPARAM); + + private: + static CInputFilter::CCondition* + s_condition; + static CInputFilter::CCondition* + s_lastGoodCondition; + static WNDPROC s_editWndProc; + }; + + class CActionDialog { + public: + static bool doModal(HWND parent, CConfig* config, + CInputFilter::CAction*&, bool& onActivate); + + private: + static void doInit(HWND hwnd); + static void fillHotkey(HWND hwnd); + static void updateControls(HWND hwnd); + + static void onButton(HWND hwnd, ButtonID button); + static void onKey(HWND hwnd, WPARAM wParam, LPARAM lParam); + static void onLockAction(HWND hwnd); + static void onSwitchToAction(HWND hwnd); + static void onSwitchInAction(HWND hwnd); + + static KeyID getChar(WPARAM wParam, LPARAM lParam); + static KeyModifierMask + getModifiers(); + + static bool isGoodAction(); + static void convertAction(HWND hwnd); + + static bool isDownUpAction(); + + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK editProc(HWND hwnd, UINT, WPARAM, LPARAM); + + private: + static CConfig* s_config; + static bool s_onActivate; + static CInputFilter::CAction* + s_action; + static CInputFilter::CAction* + s_lastGoodAction; + static WNDPROC s_editWndProc; + }; + +// public to allow CActionDialog to use it +public: + class CScreensDialog { + public: + static void doModal(HWND parent, CConfig* config, + CInputFilter::CKeystrokeAction*); + + // public due to compiler brokenness + typedef std::set CScreens; + + private: + + static void doInit(HWND hwnd); + static void doFini(HWND hwnd); + static void fillScreens(HWND hwnd); + static void updateControls(HWND hwnd); + + static void add(HWND hwnd); + static void remove(HWND hwnd); + + static void getSelected(HWND hwnd, UINT id, + const CScreens& inScreens, CScreens& outScreens); + + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + + private: + static CConfig* s_config; + static CInputFilter::CKeystrokeAction* s_action; + static CScreens s_nonTargets; + static CScreens s_targets; + static CString s_allScreens; + }; + +private: + static CHotkeyOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + CInputFilter* m_inputFilter; + CInputFilter::CRule m_activeRule; + UInt32 m_activeRuleIndex; +}; + +#endif diff --git a/cmd/launcher/CInfo.cpp b/cmd/launcher/CInfo.cpp new file mode 100644 index 00000000..f2292db8 --- /dev/null +++ b/cmd/launcher/CInfo.cpp @@ -0,0 +1,111 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 "ProtocolTypes.h" +#include "CStringUtil.h" +#include "Version.h" +#include "CArch.h" +#include "CInfo.h" +#include "LaunchUtil.h" +#include "resource.h" + +// +// CInfo +// + +CInfo* CInfo::s_singleton = NULL; + +CInfo::CInfo(HWND parent) : + m_parent(parent) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CInfo::~CInfo() +{ + s_singleton = NULL; +} + +void +CInfo::doModal() +{ + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_INFO), + m_parent, dlgProc, (LPARAM)this); +} + +void +CInfo::init(HWND hwnd) +{ + // collect info + CString version = + CStringUtil::format(getString(IDS_TITLE).c_str(), VERSION); + CString hostname = ARCH->getHostName(); + CString address = ARCH->addrToString(ARCH->nameToAddr(hostname)); + CString userConfig = ARCH->getUserDirectory(); + if (!userConfig.empty()) { + userConfig = ARCH->concatPath(userConfig, CONFIG_NAME); + } + CString sysConfig = ARCH->getSystemDirectory(); + if (!sysConfig.empty()) { + sysConfig = ARCH->concatPath(sysConfig, CONFIG_NAME); + } + + // set info + HWND child; + child = getItem(hwnd, IDC_INFO_VERSION); + setWindowText(child, version); + child = getItem(hwnd, IDC_INFO_HOSTNAME); + setWindowText(child, hostname); + child = getItem(hwnd, IDC_INFO_IP_ADDRESS); + setWindowText(child, address); + child = getItem(hwnd, IDC_INFO_USER_CONFIG); + setWindowText(child, userConfig); + child = getItem(hwnd, IDC_INFO_SYS_CONFIG); + setWindowText(child, sysConfig); + + // focus on okay button + SetFocus(getItem(hwnd, IDOK)); +} + +BOOL +CInfo::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CInfo::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CInfo.h b/cmd/launcher/CInfo.h new file mode 100644 index 00000000..2d85ec7d --- /dev/null +++ b/cmd/launcher/CInfo.h @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 CINFO_H +#define CINFO_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +//! Info dialog for Microsoft Windows launcher +class CInfo { +public: + CInfo(HWND parent); + ~CInfo(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + //@} + +private: + void init(HWND hwnd); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CInfo* s_singleton; + + HWND m_parent; +}; + +#endif diff --git a/cmd/launcher/CScreensLinks.cpp b/cmd/launcher/CScreensLinks.cpp new file mode 100644 index 00000000..dbbca67b --- /dev/null +++ b/cmd/launcher/CScreensLinks.cpp @@ -0,0 +1,855 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 "CScreensLinks.h" +#include "CAddScreen.h" +#include "LaunchUtil.h" +#include "resource.h" + +// +// CScreensLinks +// + +CScreensLinks* CScreensLinks::s_singleton = NULL; + +CScreensLinks::CScreensLinks(HWND parent, CConfig* config) : + m_parent(parent), + m_mainConfig(config), + m_config(&m_scratchConfig) +{ + assert(s_singleton == NULL); + s_singleton = this; + + // get formatting strings + m_linkFormat = getString(IDS_LINK_FORMAT); + m_intervalFormat = getString(IDS_LINK_INTERVAL_FORMAT); + m_newLinkLabel = getString(IDS_NEW_LINK); + m_sideLabel[kLeft - kFirstDirection] = getString(IDS_SIDE_LEFT); + m_sideLabel[kRight - kFirstDirection] = getString(IDS_SIDE_RIGHT); + m_sideLabel[kTop - kFirstDirection] = getString(IDS_SIDE_TOP); + m_sideLabel[kBottom - kFirstDirection] = getString(IDS_SIDE_BOTTOM); + + // GDI objects + m_redPen = CreatePen(PS_INSIDEFRAME, 1, RGB(255, 0, 0)); +} + +CScreensLinks::~CScreensLinks() +{ + DeleteObject(m_redPen); + s_singleton = NULL; +} + +void +CScreensLinks::doModal() +{ + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_SCREENS_LINKS), + m_parent, dlgProc, (LPARAM)this); +} + +void +CScreensLinks::init(HWND hwnd) +{ + // get initial config + m_scratchConfig = *m_mainConfig; + + // fill side list box (in EDirection order) + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE); + SendMessage(child, CB_ADDSTRING, 0, (LPARAM)TEXT("---")); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_LEFT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_RIGHT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_TOP).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_BOTTOM).c_str()); + + // create error boxes + m_srcSideError = createErrorBox(hwnd); + m_srcScreenError = createErrorBox(hwnd); + m_dstScreenError = createErrorBox(hwnd); + resizeErrorBoxes(); + + m_selectedLink = -1; + m_editedLink = CEdgeLink(); + m_edgeLinks.clear(); + updateScreens(hwnd, ""); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); +} + +bool +CScreensLinks::save(HWND /*hwnd*/) +{ + *m_mainConfig = m_scratchConfig; + return true; +} + +BOOL +CScreensLinks::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return TRUE; + + case WM_SIZE: + resizeErrorBoxes(); + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + SetFocus(getItem(hwnd, IDOK)); + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_SCREENS_SCREENS: + switch (HIWORD(wParam)) { + case LBN_DBLCLK: + editScreen(hwnd); + return TRUE; + + case LBN_SELCHANGE: + updateScreensControls(hwnd); + updateLinkView(hwnd); + return TRUE; + + case LBN_SELCANCEL: + updateScreensControls(hwnd); + updateLinkView(hwnd); + return TRUE; + } + break; + + case IDC_SCREENS_ADD_SCREEN: + addScreen(hwnd); + return TRUE; + + case IDC_SCREENS_REMOVE_SCREEN: + removeScreen(hwnd); + return TRUE; + + case IDC_SCREENS_EDIT_SCREEN: + editScreen(hwnd); + return TRUE; + + case IDC_SCREENS_LINKS: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + editLink(hwnd); + return TRUE; + + case LBN_SELCANCEL: + editLink(hwnd); + return TRUE; + } + break; + + case IDC_SCREENS_ADD_LINK: + addLink(hwnd); + return TRUE; + + case IDC_SCREENS_REMOVE_LINK: + removeLink(hwnd); + return TRUE; + + case IDC_SCREENS_SRC_SIDE: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + changeSrcSide(hwnd); + break; + } + break; + + case IDC_SCREENS_SRC_SCREEN: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + changeSrcScreen(hwnd); + break; + } + break; + + case IDC_SCREENS_DST_SCREEN: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + changeDstScreen(hwnd); + break; + } + break; + + case IDC_SCREENS_SRC_START: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalStart(hwnd, LOWORD(wParam), + m_editedLink.m_srcInterval); + break; + } + break; + + case IDC_SCREENS_SRC_END: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalEnd(hwnd, LOWORD(wParam), + m_editedLink.m_srcInterval); + break; + } + break; + + case IDC_SCREENS_DST_START: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalStart(hwnd, LOWORD(wParam), + m_editedLink.m_dstInterval); + break; + } + break; + + case IDC_SCREENS_DST_END: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalEnd(hwnd, LOWORD(wParam), + m_editedLink.m_dstInterval); + break; + } + break; + } + + break; + + case WM_CTLCOLORSTATIC: + switch (GetDlgCtrlID((HWND)lParam)) { + case IDC_SCREENS_OVERLAP_ERROR: + SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE)); + SetTextColor((HDC)wParam, RGB(255, 0, 0)); + return (BOOL)GetSysColorBrush(COLOR_3DFACE); + } + break; + + // error outlines + case WM_DRAWITEM: { + DRAWITEMSTRUCT* di = (DRAWITEMSTRUCT*)lParam; + if (di->CtlType == ODT_STATIC) { + HGDIOBJ oldPen = SelectObject(di->hDC, m_redPen); + HGDIOBJ oldBrush = SelectObject(di->hDC, + GetStockObject(NULL_BRUSH)); + Rectangle(di->hDC, di->rcItem.left, di->rcItem.top, + di->rcItem.right, di->rcItem.bottom); + SelectObject(di->hDC, oldPen); + SelectObject(di->hDC, oldBrush); + return TRUE; + } + break; + } + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CScreensLinks::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} + +CString +CScreensLinks::getSelectedScreen(HWND hwnd) const +{ + HWND child = getItem(hwnd, IDC_SCREENS_SCREENS); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + return CString(); + } + + LRESULT size = SendMessage(child, LB_GETTEXTLEN, index, 0); + char* buffer = new char[size + 1]; + SendMessage(child, LB_GETTEXT, index, (LPARAM)buffer); + buffer[size] = '\0'; + CString result(buffer); + delete[] buffer; + return result; +} + +void +CScreensLinks::addScreen(HWND hwnd) +{ + CAddScreen dialog(hwnd, m_config, ""); + if (dialog.doModal()) { + updateScreens(hwnd, dialog.getName()); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::editScreen(HWND hwnd) +{ + CString oldName = getSelectedScreen(hwnd); + CAddScreen dialog(hwnd, m_config, oldName); + if (dialog.doModal()) { + CString newName = dialog.getName(); + + // rename screens in the edge list + if (newName != oldName) { + for (size_t i = 0; i < m_edgeLinks.size(); ++i) { + m_edgeLinks[i].rename(oldName, newName); + } + m_editedLink.rename(oldName, newName); + } + + updateScreens(hwnd, newName); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::removeScreen(HWND hwnd) +{ + // remove screen from config (this also removes aliases) + m_config->removeScreen(getSelectedScreen(hwnd)); + + // update dialog + updateScreens(hwnd, ""); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); +} + +void +CScreensLinks::addLink(HWND hwnd) +{ + if (m_editedLink.connect(m_config)) { + m_editedLink = CEdgeLink(); + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::editLink(HWND hwnd) +{ + // get selection + HWND child = getItem(hwnd, IDC_SCREENS_LINKS); + DWORD i = SendMessage(child, LB_GETCURSEL, 0, 0); + if (i != LB_ERR && i != (DWORD)m_edgeLinks.size()) { + // existing link + m_selectedLink = (SInt32)SendMessage(child, LB_GETITEMDATA, i, 0); + m_editedLink = m_edgeLinks[m_selectedLink]; + } + else { + // new link + m_selectedLink = -1; + m_editedLink = CEdgeLink(); + } + updateLinksControls(hwnd); +} + +void +CScreensLinks::removeLink(HWND hwnd) +{ + if (m_editedLink.disconnect(m_config)) { + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::updateScreens(HWND hwnd, const CString& selectName) +{ + HWND child; + + // set screen list + child = getItem(hwnd, IDC_SCREENS_SCREENS); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CConfig::const_iterator index = m_config->begin(); + index != m_config->end(); ) { + const CString& name = *index; + ++index; + if (index != m_config->end()) { + SendMessage(child, LB_INSERTSTRING, + (WPARAM)-1, (LPARAM)name.c_str()); + } + else { + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)name.c_str()); + } + } + + // find the named screen + if (!selectName.empty()) { + DWORD i = SendMessage(child, LB_FINDSTRINGEXACT, + (UINT)-1, (LPARAM)selectName.c_str()); + if (i != LB_ERR) { + SendMessage(child, LB_SETSEL, TRUE, i); + } + } +} + +void +CScreensLinks::updateScreensControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_SCREENS); + bool screenSelected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR); + + enableItem(hwnd, IDC_SCREENS_ADD_SCREEN, TRUE); + enableItem(hwnd, IDC_SCREENS_EDIT_SCREEN, screenSelected); + enableItem(hwnd, IDC_SCREENS_REMOVE_SCREEN, screenSelected); +} + +void +CScreensLinks::updateLinks(HWND hwnd) +{ + HWND links = getItem(hwnd, IDC_SCREENS_LINKS); + HWND srcScreens = getItem(hwnd, IDC_SCREENS_SRC_SCREEN); + HWND dstScreens = getItem(hwnd, IDC_SCREENS_DST_SCREEN); + + // get old selection + CEdgeLink oldLink; + if (m_selectedLink != -1) { + oldLink = m_edgeLinks[m_selectedLink]; + } + + // clear links and screens + SendMessage(links, LB_RESETCONTENT, 0, 0); + SendMessage(srcScreens, CB_RESETCONTENT, 0, 0); + SendMessage(dstScreens, CB_RESETCONTENT, 0, 0); + m_edgeLinks.clear(); + + // add "no screen" items + SendMessage(srcScreens, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)TEXT("----")); + SendMessage(dstScreens, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)TEXT("----")); + + // add links and screens + for (CConfig::const_iterator i = m_config->begin(); + i != m_config->end(); ++i) { + const CString& name = *i; + + // add screen + SendMessage(srcScreens, CB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + SendMessage(dstScreens, CB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + + // add links for screen + for (CConfig::link_const_iterator j = m_config->beginNeighbor(name), + n = m_config->endNeighbor(name); + j != n; ++j) { + DWORD k = m_edgeLinks.size(); + m_edgeLinks.push_back(CEdgeLink(name, *j)); + SendMessage(links, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)formatLink(m_edgeLinks.back()).c_str()); + SendMessage(links, LB_SETITEMDATA, (WPARAM)k, (LPARAM)k); + } + } + + // add "new link" item to sort + SendMessage(links, LB_ADDSTRING, 0, (LPARAM)m_newLinkLabel.c_str()); + + // remove the "new link" item then insert it on the end + DWORD i = SendMessage(links, LB_FINDSTRINGEXACT, + (UINT)-1, (LPARAM)m_newLinkLabel.c_str()); + if (i != LB_ERR) { + SendMessage(links, LB_DELETESTRING, i, 0); + } + SendMessage(links, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)getString(IDS_NEW_LINK).c_str()); + SendMessage(links, LB_SETITEMDATA, (WPARAM)m_edgeLinks.size(), + (LPARAM)-1); + + // select the same link as before + SendMessage(links, LB_SETCURSEL, (WPARAM)m_edgeLinks.size(), 0); + if (m_selectedLink != -1) { + m_selectedLink = -1; + for (size_t j = 0; j < m_edgeLinks.size(); ++j) { + if (m_edgeLinks[j] == oldLink) { + // found matching link + m_selectedLink = j; + for (size_t k = 0; k < m_edgeLinks.size(); ++k) { + if (SendMessage(links, LB_GETITEMDATA, k, 0) == (int)j) { + SendMessage(links, LB_SETCURSEL, k, 0); + break; + } + } + break; + } + } + + // if we can't find the link anymore then reset edited link + if (m_selectedLink == -1) { + m_editedLink = CEdgeLink(); + } + } +} + +void +CScreensLinks::updateLinksControls(HWND hwnd) +{ + // get selection. select "new link" if nothing is selected. + HWND child = getItem(hwnd, IDC_SCREENS_LINKS); + if (m_selectedLink == -1) { + SendMessage(child, LB_SETCURSEL, m_edgeLinks.size(), 0); + } + + // enable/disable remove button + enableItem(hwnd, IDC_SCREENS_REMOVE_LINK, m_selectedLink != -1); + + // fill link entry controls from m_editedLink + updateLinkEditControls(hwnd, m_editedLink); + updateLinkValid(hwnd, m_editedLink); + updateLinkView(hwnd); +} + +void +CScreensLinks::changeSrcSide(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE); + m_editedLink.m_srcSide = (EDirection)SendMessage(child, CB_GETCURSEL, 0, 0); + updateLink(hwnd); +} + +void +CScreensLinks::changeSrcScreen(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SCREEN); + m_editedLink.m_srcName = getWindowText(child); + updateLink(hwnd); +} + +void +CScreensLinks::changeDstScreen(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_DST_SCREEN); + m_editedLink.m_dstName = getWindowText(child); + updateLink(hwnd); +} + +void +CScreensLinks::changeIntervalStart(HWND hwnd, int id, CConfig::CInterval& i) +{ + int x = (int)GetDlgItemInt(hwnd, id, NULL, FALSE); + if (x < 0) { + x = 0; + } + else if (x > 99) { + x = 99; + } + + i.first = 0.01f * (float)x; + if (i.first >= i.second) { + i.second = 0.01f * (float)(x + 1); + } + + updateLinkIntervalControls(hwnd, m_editedLink); + updateLink(hwnd); +} + +void +CScreensLinks::changeIntervalEnd(HWND hwnd, int id, CConfig::CInterval& i) +{ + int x = (int)GetDlgItemInt(hwnd, id, NULL, FALSE); + if (x < 1) { + x = 1; + } + else if (x > 100) { + x = 100; + } + + i.second = 0.01f * (float)x; + if (i.first >= i.second) { + i.first = 0.01f * (float)(x - 1); + } + + updateLinkIntervalControls(hwnd, m_editedLink); + updateLink(hwnd); +} + +void +CScreensLinks::selectScreen(HWND hwnd, int id, const CString& name) +{ + HWND child = getItem(hwnd, id); + DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1, + (LPARAM)name.c_str()); + if (i == CB_ERR) { + // no match, select no screen + SendMessage(child, CB_SETCURSEL, 0, 0); + } + else { + SendMessage(child, CB_SETCURSEL, i, 0); + } +} + +void +CScreensLinks::updateLinkEditControls(HWND hwnd, const CEdgeLink& link) +{ + // fill link entry controls from link + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE); + SendMessage(child, CB_SETCURSEL, link.m_srcSide, 0); + selectScreen(hwnd, IDC_SCREENS_SRC_SCREEN, link.m_srcName); + selectScreen(hwnd, IDC_SCREENS_DST_SCREEN, link.m_dstName); + updateLinkIntervalControls(hwnd, link); +} + +void +CScreensLinks::updateLinkIntervalControls(HWND hwnd, const CEdgeLink& link) +{ + HWND child; + + // src interval + child = getItem(hwnd, IDC_SCREENS_SRC_START); + setWindowText(child, formatIntervalValue(link.m_srcInterval.first)); + child = getItem(hwnd, IDC_SCREENS_SRC_END); + setWindowText(child, formatIntervalValue(link.m_srcInterval.second)); + + // dst interval + child = getItem(hwnd, IDC_SCREENS_DST_START); + setWindowText(child, formatIntervalValue(link.m_dstInterval.first)); + child = getItem(hwnd, IDC_SCREENS_DST_END); + setWindowText(child, formatIntervalValue(link.m_dstInterval.second)); +} + +void +CScreensLinks::updateLink(HWND hwnd) +{ + updateLinkValid(hwnd, m_editedLink); + + // update link in config + if (m_selectedLink != -1 && m_editedLinkIsValid) { + // editing an existing link and entry is valid + if (m_edgeLinks[m_selectedLink].disconnect(m_config)) { + // successfully removed old link + if (!m_editedLink.connect(m_config)) { + // couldn't set new link so restore old link + m_edgeLinks[m_selectedLink].connect(m_config); + } + else { + m_edgeLinks[m_selectedLink] = m_editedLink; + updateLinks(hwnd); + updateLinkEditControls(hwnd, m_editedLink); + } + } + } + + updateLinkView(hwnd); +} + +void +CScreensLinks::updateLinkValid(HWND hwnd, const CEdgeLink& link) +{ + m_editedLinkIsValid = true; + + // check source side and screen + if (link.m_srcSide == kNoDirection) { + m_editedLinkIsValid = false; + ShowWindow(m_srcSideError, SW_SHOWNA); + } + else { + ShowWindow(m_srcSideError, SW_HIDE); + } + if (!m_config->isCanonicalName(link.m_srcName)) { + m_editedLinkIsValid = false; + ShowWindow(m_srcScreenError, SW_SHOWNA); + } + else { + ShowWindow(m_srcScreenError, SW_HIDE); + } + + // check for overlap. if editing a link we must remove it, then + // check for overlap and restore the old link. + bool overlap = false; + if (m_editedLinkIsValid) { + if (m_selectedLink == -1) { + if (link.overlaps(m_config)) { + m_editedLinkIsValid = false; + overlap = true; + } + } + else { + if (m_edgeLinks[m_selectedLink].disconnect(m_config)) { + overlap = link.overlaps(m_config); + m_edgeLinks[m_selectedLink].connect(m_config); + if (overlap) { + m_editedLinkIsValid = false; + } + } + } + } + ShowWindow(getItem(hwnd, IDC_SCREENS_OVERLAP_ERROR), + overlap ? SW_SHOWNA : SW_HIDE); + + // check dst screen + if (!m_config->isCanonicalName(link.m_dstName)) { + m_editedLinkIsValid = false; + ShowWindow(m_dstScreenError, SW_SHOWNA); + } + else { + ShowWindow(m_dstScreenError, SW_HIDE); + } + + // update add link button + enableItem(hwnd, IDC_SCREENS_ADD_LINK, + m_selectedLink == -1 && m_editedLinkIsValid); +} + +void +CScreensLinks::updateLinkView(HWND /*hwnd*/) +{ + // XXX -- draw visual of selected screen, highlighting selected link +} + +HWND +CScreensLinks::createErrorBox(HWND parent) +{ + return CreateWindow(TEXT("STATIC"), TEXT(""), + WS_CHILD | SS_OWNERDRAW, + 0, 0, 1, 1, + parent, (HMENU)-1, + s_instance, NULL); +} + +void +CScreensLinks::resizeErrorBoxes() +{ + HWND hwnd = GetParent(m_srcSideError); + resizeErrorBox(m_srcSideError, getItem(hwnd, IDC_SCREENS_SRC_SIDE)); + resizeErrorBox(m_srcScreenError, getItem(hwnd, IDC_SCREENS_SRC_SCREEN)); + resizeErrorBox(m_dstScreenError, getItem(hwnd, IDC_SCREENS_DST_SCREEN)); +} + +void +CScreensLinks::resizeErrorBox(HWND box, HWND assoc) +{ + RECT rect; + GetWindowRect(assoc, &rect); + MapWindowPoints(NULL, GetParent(box), (POINT*)&rect, 2); + SetWindowPos(box, HWND_TOP, rect.left - 1, rect.top - 1, + rect.right - rect.left + 2, + rect.bottom - rect.top + 2, SWP_NOACTIVATE); +} + +CString +CScreensLinks::formatIntervalValue(float x) const +{ + return CStringUtil::print("%d", (int)(x * 100.0f + 0.5f)); +} + +CString +CScreensLinks::formatInterval(const CConfig::CInterval& i) const +{ + if (i.first == 0.0f && i.second == 1.0f) { + return ""; + } + else { + CString start = formatIntervalValue(i.first); + CString end = formatIntervalValue(i.second); + return CStringUtil::format(m_intervalFormat.c_str(), + start.c_str(), end.c_str()); + } +} + +CString +CScreensLinks::formatLink(const CEdgeLink& link) const +{ + CString srcInterval = formatInterval(link.m_srcInterval); + CString dstInterval = formatInterval(link.m_dstInterval); + return CStringUtil::format(m_linkFormat.c_str(), + link.m_srcName.c_str(), srcInterval.c_str(), + m_sideLabel[link.m_srcSide - kFirstDirection].c_str(), + link.m_dstName.c_str(), dstInterval.c_str()); +} + +// +// CScreensLinks::CEdgeLink +// + +CScreensLinks::CEdgeLink::CEdgeLink() : + m_srcName(), + m_srcSide(kNoDirection), + m_srcInterval(0.0f, 1.0f), + m_dstName(), + m_dstInterval(0.0f, 1.0f) +{ + // do nothing +} + +CScreensLinks::CEdgeLink::CEdgeLink(const CString& name, + const CConfigLink& link) : + m_srcName(name), + m_srcSide(link.first.getSide()), + m_srcInterval(link.first.getInterval()), + m_dstName(link.second.getName()), + m_dstInterval(link.second.getInterval()) +{ + // do nothing +} + +bool +CScreensLinks::CEdgeLink::connect(CConfig* config) +{ + return config->connect(m_srcName, m_srcSide, + m_srcInterval.first, m_srcInterval.second, + m_dstName, + m_dstInterval.first, m_dstInterval.second); +} + +bool +CScreensLinks::CEdgeLink::disconnect(CConfig* config) +{ + return config->disconnect(m_srcName, m_srcSide, 0.5f * + (m_srcInterval.first + m_srcInterval.second)); +} + +void +CScreensLinks::CEdgeLink::rename(const CString& oldName, const CString& newName) +{ + if (m_srcName == oldName) { + m_srcName = newName; + } + if (m_dstName == oldName) { + m_dstName = newName; + } +} + +bool +CScreensLinks::CEdgeLink::overlaps(const CConfig* config) const +{ + return config->hasNeighbor(m_srcName, m_srcSide, + m_srcInterval.first, m_srcInterval.second); +} + +bool +CScreensLinks::CEdgeLink::operator==(const CEdgeLink& x) const +{ + return (m_srcName == x.m_srcName && + m_srcSide == x.m_srcSide && + m_srcInterval == x.m_srcInterval && + m_dstName == x.m_dstName && + m_dstInterval == x.m_dstInterval); +} diff --git a/cmd/launcher/CScreensLinks.h b/cmd/launcher/CScreensLinks.h new file mode 100644 index 00000000..8ffd0089 --- /dev/null +++ b/cmd/launcher/CScreensLinks.h @@ -0,0 +1,138 @@ +/* + * 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 CSCREENSLINKS_H +#define CSCREENSLINKS_H + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +//! Screens and links dialog for Microsoft Windows launcher +class CScreensLinks { +public: + CScreensLinks(HWND parent, CConfig*); + ~CScreensLinks(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + + //@} + +private: + typedef std::pair CConfigLink; + struct CEdgeLink { + public: + CEdgeLink(); + CEdgeLink(const CString& name, const CConfigLink&); + + bool connect(CConfig*); + bool disconnect(CConfig*); + void rename(const CString& oldName, const CString& newName); + + bool overlaps(const CConfig* config) const; + bool operator==(const CEdgeLink&) const; + + public: + CString m_srcName; + EDirection m_srcSide; + CConfig::CInterval m_srcInterval; + CString m_dstName; + CConfig::CInterval m_dstInterval; + }; + typedef std::vector CEdgeLinkList; + + void init(HWND hwnd); + bool save(HWND hwnd); + + CString getSelectedScreen(HWND hwnd) const; + void addScreen(HWND hwnd); + void editScreen(HWND hwnd); + void removeScreen(HWND hwnd); + void addLink(HWND hwnd); + void editLink(HWND hwnd); + void removeLink(HWND hwnd); + + void updateScreens(HWND hwnd, const CString& name); + void updateScreensControls(HWND hwnd); + void updateLinks(HWND hwnd); + void updateLinksControls(HWND hwnd); + + void changeSrcSide(HWND hwnd); + void changeSrcScreen(HWND hwnd); + void changeDstScreen(HWND hwnd); + void changeIntervalStart(HWND hwnd, int id, + CConfig::CInterval&); + void changeIntervalEnd(HWND hwnd, int id, + CConfig::CInterval&); + + void selectScreen(HWND hwnd, int id, const CString& name); + void updateLinkEditControls(HWND hwnd, + const CEdgeLink& link); + void updateLinkIntervalControls(HWND hwnd, + const CEdgeLink& link); + void updateLink(HWND hwnd); + void updateLinkValid(HWND hwnd, const CEdgeLink& link); + + void updateLinkView(HWND hwnd); + + HWND createErrorBox(HWND parent); + void resizeErrorBoxes(); + void resizeErrorBox(HWND box, HWND assoc); + + CString formatIntervalValue(float) const; + CString formatInterval(const CConfig::CInterval&) const; + CString formatLink(const CEdgeLink&) const; + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CScreensLinks* s_singleton; + + HWND m_parent; + CConfig* m_mainConfig; + CConfig m_scratchConfig; + CConfig* m_config; + + CString m_linkFormat; + CString m_intervalFormat; + CString m_newLinkLabel; + CString m_sideLabel[kNumDirections]; + CEdgeLinkList m_edgeLinks; + SInt32 m_selectedLink; + CEdgeLink m_editedLink; + bool m_editedLinkIsValid; + HPEN m_redPen; + HWND m_srcSideError; + HWND m_srcScreenError; + HWND m_dstScreenError; +}; + +#endif diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index 240fd1d9..05b517e7 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -19,7 +19,7 @@ #include "resource.h" #include "stdfstream.h" -#define CONFIG_NAME "synergy.sgc" +size_t s_showingDialog = 0; CString getString(DWORD id) @@ -37,24 +37,36 @@ void showError(HWND hwnd, const CString& msg) { CString title = getString(IDS_ERROR); + ++s_showingDialog; MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); + --s_showingDialog; } void askOkay(HWND hwnd, const CString& title, const CString& msg) { + ++s_showingDialog; MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); + --s_showingDialog; } bool askVerify(HWND hwnd, const CString& msg) { CString title = getString(IDS_VERIFY); + ++s_showingDialog; int result = MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OKCANCEL | MB_APPLMODAL); + --s_showingDialog; return (result == IDOK); } +bool +isShowingDialog() +{ + return (s_showingDialog != 0); +} + void setWindowText(HWND hwnd, const CString& msg) { @@ -109,6 +121,39 @@ getAppPath(const CString& appName) return appPath; } +static +void +getFileTime(const CString& path, time_t& t) +{ + struct _stat s; + if (_stat(path.c_str(), &s) != -1) { + t = s.st_mtime; + } +} + +bool +isConfigNewer(time_t& oldTime, bool userConfig) +{ + time_t newTime = oldTime; + if (userConfig) { + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + getFileTime(path, newTime); + } + } + else { + CString path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + getFileTime(path, newTime); + } + } + bool result = (newTime > oldTime); + oldTime = newTime; + return result; +} + static bool loadConfig(const CString& pathname, CConfig& config) @@ -127,7 +172,7 @@ loadConfig(const CString& pathname, CConfig& config) } bool -loadConfig(CConfig& config) +loadConfig(CConfig& config, time_t& t, bool& userConfig) { // load configuration bool configLoaded = false; @@ -137,6 +182,8 @@ loadConfig(CConfig& config) path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; + userConfig = true; + getFileTime(path, t); } else { // try the system-wide config file @@ -145,6 +192,8 @@ loadConfig(CConfig& config) path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; + userConfig = false; + getFileTime(path, t); } } } @@ -170,7 +219,7 @@ saveConfig(const CString& pathname, const CConfig& config) } bool -saveConfig(const CConfig& config, bool sysOnly) +saveConfig(const CConfig& config, bool sysOnly, time_t& t) { // try saving the user's configuration if (!sysOnly) { @@ -178,6 +227,7 @@ saveConfig(const CConfig& config, bool sysOnly) if (!path.empty()) { path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { + getFileTime(path, t); return true; } } @@ -189,6 +239,7 @@ saveConfig(const CConfig& config, bool sysOnly) if (!path.empty()) { path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { + getFileTime(path, t); return true; } } diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h index 48889264..75954046 100644 --- a/cmd/launcher/LaunchUtil.h +++ b/cmd/launcher/LaunchUtil.h @@ -19,9 +19,12 @@ #define WINDOWS_LEAN_AND_MEAN #include +#include +#include #define CLIENT_APP "synergyc.exe" #define SERVER_APP "synergys.exe" +#define CONFIG_NAME "synergy.sgc" class CConfig; @@ -35,6 +38,7 @@ void showError(HWND hwnd, const CString& msg); void askOkay(HWND hwnd, const CString& title, const CString& msg); bool askVerify(HWND hwnd, const CString& msg); +bool isShowingDialog(); void setWindowText(HWND hwnd, const CString& msg); CString getWindowText(HWND hwnd); @@ -47,8 +51,10 @@ bool isItemChecked(HWND); CString getAppPath(const CString& appName); -bool loadConfig(CConfig& config); -bool saveConfig(const CConfig& config, bool sysOnly); +bool isConfigNewer(time_t&, bool userConfig); +bool loadConfig(CConfig& config, time_t&, bool& userConfig); +bool saveConfig(const CConfig& config, + bool sysOnly, time_t&); const TCHAR* const* getSettingsPath(); diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index 4998a89a..729aeb79 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -14,14 +14,22 @@ NULL = MSWINDOWS_SOURCE_FILES = \ + CAddScreen.cpp \ CAdvancedOptions.cpp \ CAutoStart.cpp \ CGlobalOptions.cpp \ + CHotkeyOptions.cpp \ + CInfo.cpp \ + CScreensLinks.cpp \ LaunchUtil.cpp \ launcher.cpp \ + CAddScreen.h \ CAdvancedOptions.h \ CAutoStart.h \ CGlobalOptions.h \ + CHotkeyOptions.h \ + CInfo.h \ + CScreensLinks.h \ LaunchUtil.h \ resource.h \ launcher.rc \ diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 66019f01..07eab676 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -20,29 +20,23 @@ #include "CStringUtil.h" #include "CArch.h" #include "CArchMiscWindows.h" +#include "XArch.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 "CAddScreen.h" +#include "CAdvancedOptions.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" +#include "CHotkeyOptions.h" +#include "CInfo.h" +#include "CScreensLinks.h" typedef std::vector CStringList; -class CScreenInfo { -public: - CString m_screen; - CStringList m_aliases; - CConfig::CScreenOptions m_options; -}; - class CChildWaitInfo { public: HWND m_dialog; @@ -52,29 +46,6 @@ public: 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" }, @@ -84,16 +55,30 @@ static const char* s_debugName[][2] = { { TEXT("Debug1"), "DEBUG1" }, { TEXT("Debug2"), "DEBUG2" } }; -static const int s_defaultDebug = 3; // INFO +static const int s_defaultDebug = 1; // WARNING +static const int s_minTestDebug = 3; // INFO HINSTANCE s_instance = NULL; static CGlobalOptions* s_globalOptions = NULL; static CAdvancedOptions* s_advancedOptions = NULL; +static CHotkeyOptions* s_hotkeyOptions = NULL; +static CScreensLinks* s_screensLinks = NULL; +static CInfo* s_info = NULL; + +static bool s_userConfig = true; +static time_t s_configTime = 0; +static CConfig s_lastConfig; static const TCHAR* s_mainClass = TEXT("GoSynergy"); static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); +enum SaveMode { + SAVE_QUITING, + SAVE_NORMAL, + SAVE_QUIET +}; + // // program arguments // @@ -108,7 +93,6 @@ public: public: static CArgs* s_instance; CConfig m_config; - CConfig m_oldConfig; CStringList m_screens; }; @@ -119,40 +103,6 @@ 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) @@ -161,44 +111,6 @@ isClientChecked(HWND hwnd) 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) @@ -206,248 +118,10 @@ 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); + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SCREENS, !client); + enableItem(hwnd, IDC_MAIN_OPTIONS, !client); + enableItem(hwnd, IDC_MAIN_HOTKEYS, !client); } static @@ -505,7 +179,7 @@ execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo) static CString -getCommandLine(HWND hwnd, bool testing) +getCommandLine(HWND hwnd, bool testing, bool silent) { CString cmdLine; @@ -527,9 +201,11 @@ getCommandLine(HWND hwnd, bool testing) HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); server = getWindowText(child); if (!ARG->m_config.isValidScreenName(server)) { - showError(hwnd, CStringUtil::format( + if (!silent) { + showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SERVER_NAME).c_str(), server.c_str())); + } SetFocus(child); return CString(); } @@ -539,9 +215,11 @@ getCommandLine(HWND hwnd, bool testing) // don't bother to check the addresses though that'd be // more accurate. if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) { - showError(hwnd, CStringUtil::format( + if (!silent) { + showError(hwnd, CStringUtil::format( getString(IDS_SERVER_IS_CLIENT).c_str(), server.c_str())); + } SetFocus(child); return CString(); } @@ -549,8 +227,16 @@ getCommandLine(HWND hwnd, bool testing) // debug level. always include this. if (true) { - HWND child = getItem(hwnd, IDC_MAIN_DEBUG); - DWORD debug = SendMessage(child, CB_GETCURSEL, 0, 0); + HWND child = getItem(hwnd, IDC_MAIN_DEBUG); + int debug = (int)SendMessage(child, CB_GETCURSEL, 0, 0); + + // if testing then we force the debug level to be no less than + // s_minTestDebug. what's the point of testing if you can't + // see the debugging info? + if (testing && debug < s_minTestDebug) { + debug = s_minTestDebug; + } + cmdLine += " --debug "; cmdLine += s_debugName[debug][1]; } @@ -562,17 +248,29 @@ getCommandLine(HWND hwnd, bool testing) } static -HANDLE -launchApp(HWND hwnd, bool testing, DWORD* threadID) +bool +launchApp(HWND hwnd, bool testing, HANDLE* thread, DWORD* threadID) { + if (thread != NULL) { + *thread = NULL; + } + if (threadID != NULL) { + *threadID = 0; + } + + // start daemon if it's installed and we're not testing + if (!testing && CAutoStart::startDaemon()) { + return true; + } + // 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); + CString cmdLine = getCommandLine(hwnd, testing, false); if (cmdLine.empty()) { - return NULL; + return false; } // start child @@ -581,19 +279,21 @@ launchApp(HWND hwnd, bool testing, DWORD* threadID) showError(hwnd, CStringUtil::format( getString(IDS_STARTUP_FAILED).c_str(), getErrorString(GetLastError()).c_str())); - return NULL; + return false; } // don't need process handle CloseHandle(procInfo.hProcess); - // save thread ID if desired + // save thread handle and thread ID if desired + if (thread != NULL) { + *thread = procInfo.hThread; + } if (threadID != NULL) { *threadID = procInfo.dwThreadId; } - // return thread handle - return procInfo.hThread; + return true; } static @@ -701,9 +401,11 @@ initMainWindow(HWND hwnd) setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION)); // load configuration - bool configLoaded = loadConfig(ARG->m_config); - ARG->m_oldConfig = ARG->m_config; - enableSaveControls(hwnd); + bool configLoaded = + loadConfig(ARG->m_config, s_configTime, s_userConfig); + if (configLoaded) { + s_lastConfig = ARG->m_config; + } // get settings from registry bool isServer = configLoaded; @@ -739,18 +441,6 @@ initMainWindow(HWND hwnd) 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) / @@ -759,16 +449,21 @@ initMainWindow(HWND hwnd) } SendMessage(child, CB_SETCURSEL, debugLevel, 0); - // update neighbor combo boxes + // update controls enableMainWindowControls(hwnd); - updateNeighbors(hwnd); } static -void -saveMainWindow(HWND hwnd) +bool +saveMainWindow(HWND hwnd, SaveMode mode, CString* cmdLineOut = NULL) { - HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + DWORD errorID = 0; + CString arg; + CString cmdLine; + + // save dialog state + bool isClient = isClientChecked(hwnd); + HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath()); if (key != NULL) { HWND child; child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); @@ -776,214 +471,78 @@ saveMainWindow(HWND hwnd) 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::setValue(key, "isServer", isClient ? 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); + // save user's configuration + if (!s_userConfig || ARG->m_config != s_lastConfig) { + time_t t; + if (!saveConfig(ARG->m_config, false, t)) { + errorID = IDS_SAVE_FAILED; + arg = getErrorString(GetLastError()); + goto failed; } - else { - title = CStringUtil::format( - getString(IDS_EDIT_SCREEN).c_str(), - info->m_screen.c_str()); + if (s_userConfig) { + s_configTime = t; + s_lastConfig = ARG->m_config; } - 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)); - child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); - index = info->m_options.find(kOptionHalfDuplexScrollLock); - 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; + // save autostart configuration + if (CAutoStart::isDaemonInstalled()) { + if (s_userConfig || ARG->m_config != s_lastConfig) { + time_t t; + if (!saveConfig(ARG->m_config, true, t)) { + errorID = IDS_AUTOSTART_SAVE_FAILED; + arg = getErrorString(GetLastError()); + goto failed; } - - // 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; - } + if (!s_userConfig) { + s_configTime = t; + s_lastConfig = ARG->m_config; } - - // 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); - } - child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); - if (isItemChecked(child)) { - info->m_options[kOptionHalfDuplexScrollLock] = 1; - } - else { - info->m_options.erase(kOptionHalfDuplexScrollLock); - } - - // save modifier options - 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; + // get autostart command + cmdLine = getCommandLine(hwnd, false, mode == SAVE_QUITING); + if (cmdLineOut != NULL) { + *cmdLineOut = cmdLine; + } + if (cmdLine.empty()) { + return (mode == SAVE_QUITING); + } + + // save autostart command + if (CAutoStart::isDaemonInstalled()) { + try { + CAutoStart::reinstallDaemon(isClient, cmdLine); + CAutoStart::uninstallDaemons(!isClient); + } + catch (XArchDaemon& e) { + errorID = IDS_INSTALL_GENERIC_ERROR; + arg = e.what(); + goto failed; + } + } + + return true; + +failed: + CString errorMessage = + CStringUtil::format(getString(errorID).c_str(), arg.c_str()); + if (mode == SAVE_QUITING) { + errorMessage += "\n"; + errorMessage += getString(IDS_UNSAVED_DATA_REALLY_QUIT); + if (askVerify(hwnd, errorMessage)) { + return true; + } + } + else if (mode == SAVE_NORMAL) { + showError(hwnd, errorMessage); + } + return false; } static @@ -991,18 +550,46 @@ LRESULT CALLBACK mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { + case WM_ACTIVATE: + if (LOWORD(wParam) != WA_INACTIVE) { + // activated + + // see if the configuration changed + if (isConfigNewer(s_configTime, s_userConfig)) { + CString message = getString(IDS_CONFIG_CHANGED); + if (askVerify(hwnd, message)) { + time_t configTime; + bool userConfig; + CConfig newConfig; + if (loadConfig(newConfig, configTime, userConfig) && + userConfig == s_userConfig) { + ARG->m_config = newConfig; + s_lastConfig = ARG->m_config; + } + else { + message = getString(IDS_LOAD_FAILED); + showError(hwnd, message); + s_lastConfig = CConfig(); + } + } + } + } + else { + // deactivated; write configuration + if (!isShowingDialog()) { + saveMainWindow(hwnd, SAVE_QUIET); + } + } + break; + 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; - } + // save data + if (saveMainWindow(hwnd, SAVE_QUITING)) { + // quit + PostQuitMessage(0); } - - // quit - PostQuitMessage(0); return 0; case IDOK: @@ -1011,139 +598,72 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 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())); + if (saveMainWindow(hwnd, SAVE_NORMAL)) { + // launch child app + DWORD threadID; + HANDLE thread; + if (!launchApp(hwnd, testing, &thread, &threadID)) { 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); - // 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 + if (thread != NULL) { + CloseHandle(thread); + } - // clean up - CloseHandle(thread); - } - else { - // don't need thread handle - CloseHandle(thread); + // notify of success + askOkay(hwnd, getString(IDS_STARTED_TITLE), + getString(IDS_STARTED)); - // notify of success - askOkay(hwnd, getString(IDS_STARTED_TITLE), - getString(IDS_STARTED)); - - // quit - PostQuitMessage(0); + // quit + PostQuitMessage(0); + } } return 0; } case IDC_MAIN_AUTOSTART: { - // construct command line - CString cmdLine = getCommandLine(hwnd, false); - if (!cmdLine.empty()) { + CString cmdLine; + if (saveMainWindow(hwnd, SAVE_NORMAL, &cmdLine)) { // run dialog - CAutoStart autoStart(hwnd, - isClientChecked(hwnd) ? NULL : &ARG->m_config, - cmdLine); + CAutoStart autoStart(hwnd, !isClientChecked(hwnd), 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; - } + case IDC_MAIN_SCREENS: + s_screensLinks->doModal(); break; case IDC_MAIN_OPTIONS: s_globalOptions->doModal(); - enableSaveControls(hwnd); break; case IDC_MAIN_ADVANCED: s_advancedOptions->doModal(isClientChecked(hwnd)); - enableSaveControls(hwnd); + break; + + case IDC_MAIN_HOTKEYS: + s_hotkeyOptions->doModal(); + break; + + case IDC_MAIN_INFO: + s_info->doModal(); break; } @@ -1154,7 +674,7 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } int WINAPI -WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) +WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow) { CArch arch(instance); CLOG; @@ -1162,6 +682,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) s_instance = instance; + // if "/uninstall" is on the command line then just stop and + // uninstall the service and quit. this is the only option + // but we ignore any others. + if (CString(cmdLine).find("/uninstall") != CString::npos) { + CAutoStart::uninstallDaemons(false); + CAutoStart::uninstallDaemons(true); + return 0; + } + // register main window (dialog) class WNDCLASSEX classInfo; classInfo.cbSize = sizeof(classInfo); @@ -1190,8 +719,11 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) // prep windows initMainWindow(mainWindow); - s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config); + s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config); s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config); + s_hotkeyOptions = new CHotkeyOptions(mainWindow, &ARG->m_config); + s_screensLinks = new CScreensLinks(mainWindow, &ARG->m_config); + s_info = new CInfo(mainWindow); // show window ShowWindow(mainWindow, nCmdShow); @@ -1219,8 +751,5 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) } } while (!done); - // save values to registry - saveMainWindow(mainWindow); - return msg.wParam; } diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index b1ce64d4..f799afc9 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # 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\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\launcher.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\launcher.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # 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\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\launcher.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\launcher.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -95,6 +95,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CAddScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CAdvancedOptions.cpp # End Source File # Begin Source File @@ -107,6 +111,18 @@ SOURCE=.\CGlobalOptions.cpp # End Source File # Begin Source File +SOURCE=.\CHotkeyOptions.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\CScreensLinks.cpp +# End Source File +# Begin Source File + SOURCE=.\launcher.cpp # End Source File # Begin Source File @@ -123,6 +139,10 @@ SOURCE=.\LaunchUtil.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CAddScreen.h +# End Source File +# Begin Source File + SOURCE=.\CAdvancedOptions.h # End Source File # Begin Source File @@ -135,6 +155,18 @@ SOURCE=.\CGlobalOptions.h # End Source File # Begin Source File +SOURCE=.\CHotkeyOptions.h +# End Source File +# Begin Source File + +SOURCE=.\CInfo.h +# End Source File +# Begin Source File + +SOURCE=.\CScreensLinks.h +# End Source File +# Begin Source File + SOURCE=.\LaunchUtil.h # End Source File # Begin Source File diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 53f15770..802f3d3e 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -7,10 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "winres.h" -#if !defined(IDC_STATIC) -#define IDC_STATIC (-1) -#endif +#include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -55,55 +52,41 @@ END // Dialog // -IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 262 +IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 199 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.", + LTEXT "Choose to share or use a shared keyboard and mouse, 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, + GROUPBOX "",IDC_STATIC,7,29,286,36 + GROUPBOX "",IDC_STATIC,7,72,286,36 + GROUPBOX "Options",IDC_STATIC,7,115,286,56 + CONTROL "&Use another computer's shared keyboard and mouse (client)", + IDC_MAIN_CLIENT_RADIO,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,29,205,10 + CONTROL "Share this computer's keyboard and mouse (server)", + IDC_MAIN_SERVER_RADIO,"Button",BS_AUTORADIOBUTTON,11,72, + 177,10 + LTEXT "Other Computer's &Host Name:", + IDC_MAIN_CLIENT_SERVER_NAME_LABEL,12,46,94,8 + EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,111,44,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,75, - 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,75, - 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,75, - 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,75, - 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 | + LTEXT "&Screens && Links:",IDC_MAIN_SERVER_SCREENS_LABEL,12,89, + 54,8 + PUSHBUTTON "Configure...",IDC_MAIN_SCREENS,71,86,50,14 + PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,129,50,14 + PUSHBUTTON "Hot &Keys...",IDC_MAIN_HOTKEYS,68,129,50,14 + PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,124,129,50,14 + PUSHBUTTON "&AutoStart...",IDC_MAIN_AUTOSTART,180,129,50,14 + LTEXT "&Logging Level:",IDC_STATIC,12,154,48,8 + COMBOBOX IDC_MAIN_DEBUG,68,151,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 + PUSHBUTTON "&Info",IDC_MAIN_INFO,7,178,50,14 + DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,179,50,14 + PUSHBUTTON "Start",IDOK,187,179,50,14 + PUSHBUTTON "Quit",IDCANCEL,243,179,50,14 END IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 254 @@ -114,33 +97,46 @@ 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 | + EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,24,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - GROUPBOX "Options",IDC_STATIC,7,72,178,80 + GROUPBOX "Options",IDC_STATIC,7,55,178,54 LTEXT "If your Caps, Num, or Scroll 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 - CONTROL "Half-duplex Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,134,165,10 - GROUPBOX "Modifiers",IDC_STATIC,7,155,178,65 - LTEXT "Shift",IDC_STATIC,13,171,15,8 - COMBOBOX IDC_ADD_MOD_SHIFT,37,168,48,60,CBS_DROPDOWNLIST | + IDC_STATIC,13,65,165,25 + CONTROL "&Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,93,51,10 + CONTROL "&Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,69,93,51,10 + CONTROL "Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,125,93,51,10 + GROUPBOX "Modifiers",IDC_STATIC,7,113,178,65 + LTEXT "Shift",IDC_STATIC,13,129,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,126,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Ctrl",IDC_STATIC,13,186,11,8 - COMBOBOX IDC_ADD_MOD_CTRL,37,184,48,60,CBS_DROPDOWNLIST | + LTEXT "Ctrl",IDC_STATIC,13,144,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,142,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Alt",IDC_STATIC,13,202,9,8 - COMBOBOX IDC_ADD_MOD_ALT,37,200,48,60,CBS_DROPDOWNLIST | + LTEXT "Alt",IDC_STATIC,13,160,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,158,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Meta",IDC_STATIC,101,170,17,8 - COMBOBOX IDC_ADD_MOD_META,125,168,48,60,CBS_DROPDOWNLIST | + LTEXT "Meta",IDC_STATIC,101,128,17,8 + COMBOBOX IDC_ADD_MOD_META,125,126,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Super",IDC_STATIC,101,186,20,8 - COMBOBOX IDC_ADD_MOD_SUPER,125,184,48,60,CBS_DROPDOWNLIST | + LTEXT "Super",IDC_STATIC,101,144,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,142,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Dead Corners",IDC_STATIC,7,183,178,43 + LTEXT "Don't switch in these corners:",IDC_STATIC,14,198,52,18 + CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME,68,193,47,28 + CONTROL "",IDC_ADD_DC_TOP_LEFT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,76,197,16,8 + CONTROL "",IDC_ADD_DC_TOP_RIGHT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,98,197,16,8 + CONTROL "",IDC_ADD_DC_BOTTOM_LEFT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,76,210,16,8 + CONTROL "",IDC_ADD_DC_BOTTOM_RIGHT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,98,210,16,8 + LTEXT "Size",IDC_STATIC,120,202,14,8 + EDITTEXT IDC_ADD_DC_SIZE,139,200,40,12,ES_AUTOHSCROLL | ES_NUMBER DEFPUSHBUTTON "OK",IDOK,79,233,50,14 PUSHBUTTON "Cancel",IDCANCEL,135,233,50,14 END @@ -175,7 +171,7 @@ BEGIN IDC_STATIC,7,43,181,17 END -IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 269 +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 290 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Options" FONT 8, "MS Sans Serif" @@ -207,12 +203,15 @@ BEGIN IDC_STATIC,7,176,193,8 CONTROL "Synchronize screen savers",IDC_GLOBAL_SCREENSAVER_SYNC, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,192,101,10 - LTEXT "Experimental: Relative mouse moves on secondary screens.", - IDC_STATIC,7,213,193,8 + LTEXT "Relative mouse moves on secondary screens.",IDC_STATIC, + 7,213,193,8 CONTROL "Use relative mouse moves",IDC_GLOBAL_RELATIVE_MOVES, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,229,99,10 - DEFPUSHBUTTON "OK",IDOK,94,248,50,14 - PUSHBUTTON "Cancel",IDCANCEL,150,248,50,14 + CONTROL "Don't take foreground window on Windows servers", + IDC_GLOBAL_LEAVE_FOREGROUND,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,250,177,10 + DEFPUSHBUTTON "OK",IDOK,94,269,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,269,50,14 END IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 186 @@ -238,6 +237,158 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,173,165,50,14 END +IDD_SCREENS_LINKS DIALOG DISCARDABLE 0, 0, 354, 213 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Screens & Links" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Screens:",IDC_STATIC,7,7,29,8 + LISTBOX IDC_SCREENS_SCREENS,7,18,100,36,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_SCREENS_ADD_SCREEN,7,57,17,14 + PUSHBUTTON "-",IDC_SCREENS_REMOVE_SCREEN,28,57,17,14 + PUSHBUTTON "Edit",IDC_SCREENS_EDIT_SCREEN,49,57,24,14 + LTEXT "&Links:",IDC_STATIC,7,83,20,8 + LISTBOX IDC_SCREENS_LINKS,7,94,339,59,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_SCREENS_SRC_START,7,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "to",IDC_STATIC,25,158,8,8 + EDITTEXT IDC_SCREENS_SRC_END,33,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "% of the",IDC_STATIC,52,158,27,8 + COMBOBOX IDC_SCREENS_SRC_SIDE,80,156,48,69,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "of",IDC_STATIC,129,158,8,8 + COMBOBOX IDC_SCREENS_SRC_SCREEN,139,156,59,53,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "goes to",IDC_STATIC,200,158,24,8 + EDITTEXT IDC_SCREENS_DST_START,225,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "to",IDC_STATIC,243,158,8,8 + EDITTEXT IDC_SCREENS_DST_END,251,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "% of",IDC_STATIC,270,158,15,8 + COMBOBOX IDC_SCREENS_DST_SCREEN,287,156,59,53,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_SCREENS_ADD_LINK,7,172,17,14 + PUSHBUTTON "-",IDC_SCREENS_REMOVE_LINK,28,172,17,14 + DEFPUSHBUTTON "OK",IDOK,241,192,50,14 + PUSHBUTTON "Cancel",IDCANCEL,297,192,50,14 + LTEXT "Source edge overlaps an existing edge.", + IDC_SCREENS_OVERLAP_ERROR,72,175,126,8,NOT WS_VISIBLE | + NOT WS_GROUP +END + +IDD_INFO DIALOG DISCARDABLE 0, 0, 186, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Info" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Version:",IDC_STATIC,7,7,26,8 + EDITTEXT IDC_INFO_VERSION,52,7,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "Hostname:",IDC_STATIC,7,19,35,8 + EDITTEXT IDC_INFO_HOSTNAME,52,19,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "IP Address:",IDC_STATIC,7,31,37,8 + EDITTEXT IDC_INFO_IP_ADDRESS,52,31,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "User Config:",IDC_STATIC,7,43,40,8 + EDITTEXT IDC_INFO_USER_CONFIG,52,43,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "Sys Config:",IDC_STATIC,7,55,36,8 + EDITTEXT IDC_INFO_SYS_CONFIG,52,55,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + DEFPUSHBUTTON "OK",IDOK,129,74,50,14 +END + +IDD_HOTKEY_OPTIONS DIALOG DISCARDABLE 0, 0, 360, 151 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hot Keys" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Hot Keys:",IDC_STATIC,7,7,32,8 + LISTBOX IDC_HOTKEY_HOTKEYS,7,18,169,88,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_HOTKEY_ADD_HOTKEY,7,109,17,14 + PUSHBUTTON "-",IDC_HOTKEY_REMOVE_HOTKEY,28,109,17,14 + PUSHBUTTON "Edit",IDC_HOTKEY_EDIT_HOTKEY,49,109,24,14 + LTEXT "&Actions:",IDC_STATIC,183,7,26,8 + LISTBOX IDC_HOTKEY_ACTIONS,183,18,169,88,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_HOTKEY_ADD_ACTION,183,109,17,14 + PUSHBUTTON "-",IDC_HOTKEY_REMOVE_ACTION,204,109,17,14 + PUSHBUTTON "Edit",IDC_HOTKEY_EDIT_ACTION,225,109,24,14 + DEFPUSHBUTTON "OK",IDOK,302,130,50,14 +END + +IDD_HOTKEY_CONDITION DIALOG DISCARDABLE 0, 0, 183, 58 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hot Key" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Enter &new hot key or mouse button:",IDC_STATIC,7,7,113, + 8 + EDITTEXT IDC_HOTKEY_CONDITION_HOTKEY,7,17,169,12,ES_WANTRETURN + DEFPUSHBUTTON "OK",IDOK,70,37,50,14 + PUSHBUTTON "Cancel",IDCANCEL,126,37,50,14 +END + +IDD_HOTKEY_ACTION DIALOG DISCARDABLE 0, 0, 183, 202 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Action" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Action:",IDC_STATIC,7,7,23,8 + CONTROL "Press:",IDC_HOTKEY_ACTION_DOWN,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,7,19,35,10 + CONTROL "Release:",IDC_HOTKEY_ACTION_UP,"Button", + BS_AUTORADIOBUTTON,7,31,44,10 + CONTROL "Press && Release:",IDC_HOTKEY_ACTION_DOWNUP,"Button", + BS_AUTORADIOBUTTON,7,43,69,10 + CONTROL "Switch To Screen:",IDC_HOTKEY_ACTION_SWITCH_TO,"Button", + BS_AUTORADIOBUTTON,7,85,75,10 + CONTROL "Switch In Direction:",IDC_HOTKEY_ACTION_SWITCH_IN, + "Button",BS_AUTORADIOBUTTON,7,101,77,10 + CONTROL "Lock Cursor to Screen:",IDC_HOTKEY_ACTION_LOCK,"Button", + BS_AUTORADIOBUTTON,7,117,89,10 + LTEXT "&Hot key or mouse button:",IDC_STATIC,7,55,80,8 + EDITTEXT IDC_HOTKEY_ACTION_HOTKEY,7,67,152,12,ES_WANTRETURN + PUSHBUTTON "...",IDC_HOTKEY_ACTION_SCREENS,162,67,14,12 + COMBOBOX IDC_HOTKEY_ACTION_SWITCH_TO_LIST,87,83,89,62, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_HOTKEY_ACTION_SWITCH_IN_LIST,106,99,70,66, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_HOTKEY_ACTION_LOCK_LIST,106,115,70,58, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Action takes place &when:",IDC_STATIC,7,137,81,8 + CONTROL "Hot key is pressed",IDC_HOTKEY_ACTION_ON_ACTIVATE, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,149,74,10 + CONTROL "Hot key is released",IDC_HOTKEY_ACTION_ON_DEACTIVATE, + "Button",BS_AUTORADIOBUTTON,7,161,76,10 + DEFPUSHBUTTON "OK",IDOK,70,181,50,14 + PUSHBUTTON "Cancel",IDCANCEL,126,181,50,14 +END + +IDD_HOTKEY_SCREENS DIALOG DISCARDABLE 0, 0, 237, 79 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Target Screens" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Available screens:",IDC_STATIC,7,7,58,8 + LISTBOX IDC_HOTKEY_SCREENS_SRC,7,17,100,36,LBS_NOINTEGRALHEIGHT | + LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_HOTKEY_SCREENS_DST,130,17,100,36, + LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "-->",IDC_HOTKEY_SCREENS_ADD,109,21,17,14 + PUSHBUTTON "<--",IDC_HOTKEY_SCREENS_REMOVE,109,38,17,14 + DEFPUSHBUTTON "OK",IDOK,124,58,50,14 + PUSHBUTTON "Cancel",IDCANCEL,180,58,50,14 + LTEXT "&Send action to screens:",IDC_STATIC,130,7,76,8 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -252,7 +403,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 293 TOPMARGIN, 7 - BOTTOMMARGIN, 255 + BOTTOMMARGIN, 192 END IDD_ADD, DIALOG @@ -284,7 +435,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 200 TOPMARGIN, 7 - BOTTOMMARGIN, 262 + BOTTOMMARGIN, 283 END IDD_ADVANCED_OPTIONS, DIALOG @@ -294,6 +445,54 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 179 END + + IDD_SCREENS_LINKS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 347 + TOPMARGIN, 7 + BOTTOMMARGIN, 206 + END + + IDD_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_HOTKEY_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 353 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_HOTKEY_CONDITION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 176 + TOPMARGIN, 7 + BOTTOMMARGIN, 51 + END + + IDD_HOTKEY_ACTION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 176 + TOPMARGIN, 7 + BOTTOMMARGIN, 195 + END + + IDD_HOTKEY_SCREENS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 230 + TOPMARGIN, 7 + BOTTOMMARGIN, 72 + END END #endif // APSTUDIO_INVOKED @@ -358,13 +557,37 @@ BEGIN 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_SERVER_IS_CLIENT "Please enter the name of the computer sharing a\nkeyboard and mouse, not the name of this computer,\nin the Other Computer's Host Name field." IDS_ADD_SCREEN "Add Screen" IDS_EDIT_SCREEN "Edit Screen %{1}" IDS_ERROR_CODE "Error code: %{1}" IDS_AUTOSTART_PERMISSION_ALL "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." IDS_INVALID_INTERFACE_NAME "The interface '%{1}' is invalid: %{2}" + IDS_INVALID_CORNER_SIZE "The dead corner size %{1} is invalid; it must be 0 or higher." + IDS_NEW_LINK "[New Link]" + IDS_SIDE_LEFT "left of" + IDS_SIDE_RIGHT "right of" + IDS_SIDE_TOP "above" + IDS_SIDE_BOTTOM "below" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_LINK_FORMAT "%{4}%{5} is %{3} %{1}%{2}" + IDS_LINK_INTERVAL_FORMAT "(%{1},%{2})" + IDS_EDGE_LEFT "left" + IDS_EDGE_RIGHT "right" + IDS_EDGE_TOP "top" + IDS_EDGE_BOTTOM "bottom" + IDS_AUTOSTART_SAVE_FAILED "Failed to save autostart configuration: %{1}" + IDS_LOAD_FAILED "Failed to load configuration." + IDS_CONFIG_CHANGED "Configuration changed on disk. Reload?" + IDS_LOCK_MODE_OFF "off" + IDS_LOCK_MODE_ON "on" + IDS_LOCK_MODE_TOGGLE "toggle" + IDS_ALL_SCREENS "All Screens" + IDS_ACTIVE_SCREEN "Active Screen" END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index caa48052..e4e7b487 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -44,6 +44,26 @@ #define IDS_ERROR_CODE 39 #define IDS_AUTOSTART_PERMISSION_ALL 40 #define IDS_INVALID_INTERFACE_NAME 41 +#define IDS_INVALID_CORNER_SIZE 42 +#define IDS_NEW_LINK 43 +#define IDS_SIDE_LEFT 44 +#define IDS_SIDE_RIGHT 45 +#define IDS_SIDE_TOP 46 +#define IDS_SIDE_BOTTOM 47 +#define IDS_LINK_FORMAT 48 +#define IDS_LINK_INTERVAL_FORMAT 49 +#define IDS_EDGE_LEFT 50 +#define IDS_EDGE_RIGHT 51 +#define IDS_EDGE_TOP 52 +#define IDS_EDGE_BOTTOM 53 +#define IDS_AUTOSTART_SAVE_FAILED 54 +#define IDS_LOAD_FAILED 55 +#define IDS_CONFIG_CHANGED 56 +#define IDS_LOCK_MODE_OFF 57 +#define IDS_LOCK_MODE_ON 58 +#define IDS_LOCK_MODE_TOGGLE 59 +#define IDS_ALL_SCREENS 60 +#define IDS_ACTIVE_SCREEN 61 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 @@ -51,45 +71,36 @@ #define IDD_AUTOSTART 105 #define IDD_ADVANCED_OPTIONS 106 #define IDD_GLOBAL_OPTIONS 107 +#define IDD_SCREENS_LINKS 110 +#define IDD_INFO 111 +#define IDD_HOTKEY_OPTIONS 112 +#define IDD_HOTKEY_CONDITION 113 +#define IDD_HOTKEY_ACTION 114 +#define IDD_HOTKEY_SCREENS 115 #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_CLIENT_SERVER_NAME_LABEL 1002 +#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1003 +#define IDC_MAIN_SERVER_SCREENS_LABEL 1004 +#define IDC_MAIN_SCREENS 1005 +#define IDC_MAIN_OPTIONS 1006 +#define IDC_MAIN_ADVANCED 1007 +#define IDC_MAIN_AUTOSTART 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_MAIN_HOTKEYS 1010 +#define IDC_MAIN_DEBUG 1011 #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_ADD_HD_SCROLL_CHECK 1039 -#define IDC_MAIN_DEBUG 1040 #define IDC_ADVANCED_INTERFACE_EDIT 1040 #define IDC_GLOBAL_DELAY_CHECK 1041 #define IDC_GLOBAL_DELAY_TIME 1042 @@ -105,15 +116,68 @@ #define IDC_GLOBAL_SCREENSAVER_SYNC 1047 #define IDC_GLOBAL_RELATIVE_MOVES 1048 #define IDC_ADVANCED_DEFAULTS 1049 +#define IDC_GLOBAL_LEAVE_FOREGROUND 1049 +#define IDC_ADD_DC_SIZE 1052 +#define IDC_ADD_DC_TOP_LEFT 1053 +#define IDC_ADD_DC_TOP_RIGHT 1054 +#define IDC_ADD_DC_BOTTOM_LEFT 1055 +#define IDC_ADD_DC_BOTTOM_RIGHT 1056 +#define IDC_SCREENS_SRC_SIDE 1057 +#define IDC_SCREENS_SRC_START 1058 +#define IDC_SCREENS_SRC_END 1059 +#define IDC_SCREENS_SRC_SCREEN 1060 +#define IDC_SCREENS_SCREENS 1061 +#define IDC_SCREENS_ADD_SCREEN 1062 +#define IDC_SCREENS_LINKS 1063 +#define IDC_SCREENS_DST_START 1064 +#define IDC_SCREENS_DST_END 1065 +#define IDC_SCREENS_DST_SCREEN 1066 +#define IDC_SCREENS_REMOVE_SCREEN 1067 +#define IDC_SCREENS_EDIT_SCREEN 1068 +#define IDC_SCREENS_ADD_LINK 1069 +#define IDC_SCREENS_REMOVE_LINK 1070 +#define IDC_SCREENS_OVERLAP_ERROR 1071 +#define IDC_INFO_VERSION 1073 +#define IDC_MAIN_INFO 1074 +#define IDC_INFO_HOSTNAME 1076 +#define IDC_HOTKEY_HOTKEYS 1076 +#define IDC_INFO_IP_ADDRESS 1077 +#define IDC_HOTKEY_ADD_HOTKEY 1077 +#define IDC_INFO_USER_CONFIG 1078 +#define IDC_HOTKEY_REMOVE_HOTKEY 1078 +#define IDC_INFO_SYS_CONFIG 1079 +#define IDC_HOTKEY_EDIT_HOTKEY 1079 +#define IDC_HOTKEY_ACTIONS 1080 +#define IDC_HOTKEY_CONDITION_HOTKEY 1080 +#define IDC_HOTKEY_ACTION_DOWNUP 1081 +#define IDC_HOTKEY_ADD_ACTION 1082 +#define IDC_HOTKEY_ACTION_DOWN 1082 +#define IDC_HOTKEY_REMOVE_ACTION 1083 +#define IDC_HOTKEY_ACTION_UP 1083 +#define IDC_HOTKEY_EDIT_ACTION 1084 +#define IDC_HOTKEY_ACTION_HOTKEY 1085 +#define IDC_HOTKEY_ACTION_SWITCH_TO_LIST 1086 +#define IDC_HOTKEY_ACTION_SWITCH_TO 1087 +#define IDC_HOTKEY_ACTION_SWITCH_IN 1088 +#define IDC_HOTKEY_ACTION_LOCK 1089 +#define IDC_HOTKEY_ACTION_SWITCH_IN_LIST 1090 +#define IDC_HOTKEY_ACTION_LOCK_LIST 1091 +#define IDC_HOTKEY_ACTION_ON_ACTIVATE 1092 +#define IDC_HOTKEY_ACTION_ON_DEACTIVATE 1093 +#define IDC_HOTKEY_ACTION_SCREENS 1094 +#define IDC_HOTKEY_SCREENS_SRC 1095 +#define IDC_HOTKEY_SCREENS_DST 1096 +#define IDC_HOTKEY_SCREENS_ADD 1097 +#define IDC_HOTKEY_SCREENS_REMOVE 1098 // 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_RESOURCE_VALUE 116 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1052 +#define _APS_NEXT_CONTROL_VALUE 1098 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/cmd/launcher/synergy.ico b/cmd/launcher/synergy.ico index 39e0ff8d..89f965f4 100644 Binary files a/cmd/launcher/synergy.ico and b/cmd/launcher/synergy.ico differ diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp index ddd8ec5a..025b43f6 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -15,8 +15,10 @@ #include "CClientTaskBarReceiver.h" #include "CClient.h" #include "CLock.h" +#include "CStringUtil.h" #include "IEventQueue.h" #include "CArch.h" +#include "Version.h" // // CClientTaskBarReceiver @@ -48,6 +50,8 @@ CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg) } } else { + m_server = client->getServerAddress().getHostname(); + if (client->isConnected()) { m_state = kConnected; } @@ -108,19 +112,23 @@ CClientTaskBarReceiver::getToolTip() const { switch (m_state) { case kNotRunning: - return "Synergy: Not running"; + return CStringUtil::print("%s: Not running", kAppVersion); case kNotWorking: - return CString("Synergy: ") + m_errorMessage; + return CStringUtil::print("%s: %s", + kAppVersion, m_errorMessage.c_str()); case kNotConnected: - return CString("Synergy: Not connected: ") + m_errorMessage; + return CStringUtil::print("%s: Not connected: %s", + kAppVersion, m_errorMessage.c_str()); case kConnecting: - return "Synergy: Connecting..."; + return CStringUtil::print("%s: Connecting to %s...", + kAppVersion, m_server.c_str()); case kConnected: - return "Synergy: Connected"; + return CStringUtil::print("%s: Connected to %s", + kAppVersion, m_server.c_str()); default: return ""; diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h index a9fe50c2..0e3440e1 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.h +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -77,6 +77,7 @@ protected: private: EState m_state; CString m_errorMessage; + CString m_server; }; #endif diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index c53ea1c8..5c31f4a5 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -139,6 +139,9 @@ CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) SetForegroundWindow(m_window); HMENU menu = GetSubMenu(m_menu, 0); SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + HMENU logLevelMenu = GetSubMenu(menu, 3); + CheckMenuRadioItem(logLevelMenu, 0, 6, + CLOG->getFilter() - CLog::kERROR, MF_BYPOSITION); int n = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD | @@ -157,6 +160,38 @@ CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) copyLog(); break; + case IDC_TASKBAR_SHOW_LOG: + ARCH->showConsole(true); + break; + + case IDC_TASKBAR_LOG_LEVEL_ERROR: + CLOG->setFilter(CLog::kERROR); + break; + + case IDC_TASKBAR_LOG_LEVEL_WARNING: + CLOG->setFilter(CLog::kWARNING); + break; + + case IDC_TASKBAR_LOG_LEVEL_NOTE: + CLOG->setFilter(CLog::kNOTE); + break; + + case IDC_TASKBAR_LOG_LEVEL_INFO: + CLOG->setFilter(CLog::kINFO); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG: + CLOG->setFilter(CLog::kDEBUG); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG1: + CLOG->setFilter(CLog::kDEBUG1); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG2: + CLOG->setFilter(CLog::kDEBUG2); + break; + case IDC_TASKBAR_QUIT: quit(); break; diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 783472c2..89942666 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -67,6 +67,12 @@ synergyc_SOURCES = \ $(COMMON_SOURCE_FILES) \ $(CARBON_SOURCE_FILES) \ $(NULL) +synergyc_LDFLAGS = \ + -framework ScreenSaver \ + -framework IOKit \ + -framework ApplicationServices \ + -framework Foundation \ + $(NULL) endif synergyc_LDADD = \ $(top_builddir)/lib/client/libclient.a \ diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index 1a5455a1..eeee6e1e 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -16,13 +16,21 @@ #define IDC_TASKBAR_QUIT 40001 #define IDC_TASKBAR_STATUS 40002 #define IDC_TASKBAR_LOG 40003 +#define IDC_TASKBAR_SHOW_LOG 40004 +#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009 +#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010 +#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011 +#define IDC_TASKBAR_LOG_LEVEL_INFO 40012 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40004 +#define _APS_NEXT_COMMAND_VALUE 40016 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 2b548be8..eabddb49 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -60,10 +60,6 @@ typedef int (*StartupFunc)(int, char**); static bool startClient(); static void parse(int argc, const char* const* argv); -#if WINAPI_MSWINDOWS -static void handleSystemSuspend(void*); -static void handleSystemResume(void*); -#endif // // program arguments @@ -108,9 +104,7 @@ CScreen* createScreen() { #if WINAPI_MSWINDOWS - return new CScreen(new CMSWindowsScreen(false, - new CFunctionJob(&handleSystemSuspend), - new CFunctionJob(&handleSystemResume))); + return new CScreen(new CMSWindowsScreen(false)); #elif WINAPI_XWINDOWS return new CScreen(new CXWindowsScreen(ARG->m_display, false)); #elif WINAPI_CARBON @@ -199,26 +193,6 @@ handleScreenError(const CEvent&, void*) EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } -#if WINAPI_MSWINDOWS -static -void -handleSystemSuspend(void*) -{ - LOG((CLOG_NOTE "system suspending")); - s_suspened = true; - s_client->disconnect(NULL); -} - -static -void -handleSystemResume(void*) -{ - LOG((CLOG_NOTE "system resuming")); - s_suspened = false; - startClient(); -} -#endif - static CScreen* openClientScreen() @@ -252,9 +226,7 @@ handleClientRestart(const CEvent&, void* vtimer) EVENTQUEUE->removeHandler(CEvent::kTimer, timer); // reconnect - if (!s_suspened) { - startClient(); - } + startClient(); } static @@ -287,6 +259,7 @@ handleClientFailed(const CEvent& e, void*) updateStatus(CString("Failed to connect to server: ") + info->m_what); if (!ARG->m_restartable || !info->m_retry) { LOG((CLOG_ERR "failed to connect to server: %s", info->m_what)); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } else { LOG((CLOG_WARN "failed to connect to server: %s", info->m_what)); @@ -441,7 +414,11 @@ static int daemonMainLoop(int, const char**) { - CSystemLogger sysLogger(DAEMON_NAME); +#if SYSAPI_WIN32 + CSystemLogger sysLogger(DAEMON_NAME, false); +#else + CSystemLogger sysLogger(DAEMON_NAME, true); +#endif return mainLoop(); } @@ -449,6 +426,10 @@ static int standardStartup(int argc, char** argv) { + if (!ARG->m_daemon) { + ARCH->showConsole(false); + } + // parse command line parse(argc, argv); @@ -758,6 +739,7 @@ public: // ILogOutputter overrides virtual void open(const char*) { } virtual void close() { } + virtual void show(bool) { } virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const { return ""; } }; @@ -802,11 +784,24 @@ static int daemonNTStartup(int, char**) { - CSystemLogger sysLogger(DAEMON_NAME); + CSystemLogger sysLogger(DAEMON_NAME, false); bye = &byeThrow; return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } +static +int +foregroundStartup(int argc, char** argv) +{ + ARCH->showConsole(false); + + // parse command line + parse(argc, argv); + + // never daemonize + return mainLoop(); +} + static void showError(HINSTANCE instance, const char* title, UINT id, const char* arg) @@ -820,12 +815,23 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { try { + CArchMiscWindows::setIcons((HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED), + (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED)); CArch arch(instance); CMSWindowsScreen::init(instance); CLOG; CThread::getCurrentThread().setPriority(-14); CArgs args; + // set title on log window + ARCH->openConsole((CString(kAppVersion) + " " + "Client").c_str()); + // 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 @@ -833,8 +839,13 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. StartupFunc startup = &standardStartup; - if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - startup = &daemonNTStartup; + if (!CArchMiscWindows::isWindows95Family()) { + if (__argc <= 1) { + startup = &daemonNTStartup; + } + else { + startup = &foregroundStartup; + } } // send PRINT and FATAL output to a message box diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp index ff793b14..4238ea03 100644 --- a/cmd/synergyc/synergyc.dsp +++ b/cmd/synergyc/synergyc.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # 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 /Fd..\..\gen\build\synergyc.pdb +# ADD CPP /nologo /MT /W4 /GR /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"..\..\gen\build\synergyc.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -70,7 +70,7 @@ LINK32=link.exe # 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 /Fd..\..\gen\debug\synergyc.pdb +# ADD CPP /nologo /MTd /W4 /Gm /GR /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"..\..\gen\debug\synergyc.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/synergyc/synergyc.ico b/cmd/synergyc/synergyc.ico index 23d9a090..89f965f4 100644 Binary files a/cmd/synergyc/synergyc.ico and b/cmd/synergyc/synergyc.ico differ diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index e168a3b3..519e8e48 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -87,7 +87,25 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + POPUP "Set Log Level" + BEGIN + MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR + + MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING + + MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE + + MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO + + MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG + + MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1 + + MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2 + + END MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END diff --git a/cmd/synergyc/tb_error.ico b/cmd/synergyc/tb_error.ico index a304dc18..746a87c9 100644 Binary files a/cmd/synergyc/tb_error.ico and b/cmd/synergyc/tb_error.ico differ diff --git a/cmd/synergyc/tb_idle.ico b/cmd/synergyc/tb_idle.ico index 9ba599de..4e13a264 100644 Binary files a/cmd/synergyc/tb_idle.ico and b/cmd/synergyc/tb_idle.ico differ diff --git a/cmd/synergyc/tb_run.ico b/cmd/synergyc/tb_run.ico index 86aa9f3a..88e160cb 100644 Binary files a/cmd/synergyc/tb_run.ico and b/cmd/synergyc/tb_run.ico differ diff --git a/cmd/synergyc/tb_wait.ico b/cmd/synergyc/tb_wait.ico index 3e2edd5f..257be0a1 100644 Binary files a/cmd/synergyc/tb_wait.ico and b/cmd/synergyc/tb_wait.ico differ diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index a1011fda..21a61954 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -157,6 +157,9 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) SetForegroundWindow(m_window); HMENU menu = GetSubMenu(m_menu, 0); SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + HMENU logLevelMenu = GetSubMenu(menu, 3); + CheckMenuRadioItem(logLevelMenu, 0, 6, + CLOG->getFilter() - CLog::kERROR, MF_BYPOSITION); int n = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD | @@ -175,6 +178,10 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) copyLog(); break; + case IDC_TASKBAR_SHOW_LOG: + ARCH->showConsole(true); + break; + case IDC_RELOAD_CONFIG: EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(), IEventQueue::getSystemTarget())); @@ -185,6 +192,34 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) IEventQueue::getSystemTarget())); break; + case IDC_TASKBAR_LOG_LEVEL_ERROR: + CLOG->setFilter(CLog::kERROR); + break; + + case IDC_TASKBAR_LOG_LEVEL_WARNING: + CLOG->setFilter(CLog::kWARNING); + break; + + case IDC_TASKBAR_LOG_LEVEL_NOTE: + CLOG->setFilter(CLog::kNOTE); + break; + + case IDC_TASKBAR_LOG_LEVEL_INFO: + CLOG->setFilter(CLog::kINFO); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG: + CLOG->setFilter(CLog::kDEBUG); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG1: + CLOG->setFilter(CLog::kDEBUG1); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG2: + CLOG->setFilter(CLog::kDEBUG2); + break; + case IDC_TASKBAR_QUIT: quit(); break; diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp index ee04d79e..6555b214 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -15,8 +15,10 @@ #include "CServerTaskBarReceiver.h" #include "CServer.h" #include "CLock.h" +#include "CStringUtil.h" #include "IEventQueue.h" #include "CArch.h" +#include "Version.h" // // CServerTaskBarReceiver @@ -113,16 +115,17 @@ CServerTaskBarReceiver::getToolTip() const { switch (m_state) { case kNotRunning: - return "Synergy: Not running"; + return CStringUtil::print("%s: Not running", kAppVersion); case kNotWorking: - return std::string("Synergy: ") + m_errorMessage; - + return CStringUtil::print("%s: %s", + kAppVersion, m_errorMessage.c_str()); + case kNotConnected: - return "Synergy: Waiting for clients"; + return CStringUtil::print("%s: Waiting for clients", kAppVersion); case kConnected: - return "Synergy: Connected"; + return CStringUtil::print("%s: Connected", kAppVersion); default: return ""; diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index c45e6b4f..3e02739d 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -67,6 +67,12 @@ synergys_SOURCES = \ $(COMMON_SOURCE_FILES) \ $(CARBON_SOURCE_FILES) \ $(NULL) +synergys_LDFLAGS = \ + -framework ScreenSaver \ + -framework IOKit \ + -framework ApplicationServices \ + -framework Foundation \ + $(NULL) endif synergys_LDADD = \ $(top_builddir)/lib/server/libserver.a \ diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index f3127816..0ad5868a 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -19,13 +19,21 @@ #define IDC_TASKBAR_LOG 40005 #define IDC_RELOAD_CONFIG 40006 #define IDC_FORCE_RECONNECT 40007 +#define IDC_TASKBAR_SHOW_LOG 40008 +#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009 +#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010 +#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011 +#define IDC_TASKBAR_LOG_LEVEL_INFO 40012 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40008 +#define _APS_NEXT_COMMAND_VALUE 40016 #define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index a8530dec..6821a4e2 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -121,7 +121,7 @@ CScreen* createScreen() { #if WINAPI_MSWINDOWS - return new CScreen(new CMSWindowsScreen(true, NULL, NULL)); + return new CScreen(new CMSWindowsScreen(true)); #elif WINAPI_XWINDOWS return new CScreen(new CXWindowsScreen(ARG->m_display, true)); #elif WINAPI_CARBON @@ -148,6 +148,16 @@ createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) // platform independent main // +enum EServerState { + kUninitialized, + kInitializing, + kInitializingToStart, + kInitialized, + kStarting, + kStarted +}; + +static EServerState s_serverState = kUninitialized; static CServer* s_server = NULL; static CScreen* s_serverScreen = NULL; static CPrimaryClient* s_primaryClient = NULL; @@ -155,6 +165,8 @@ static CClientListener* s_listener = NULL; static CServerTaskBarReceiver* s_taskBarReceiver = NULL; static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; static CEvent::Type s_forceReconnectEvent = CEvent::kUnknown; +static bool s_suspended = false; +static CEventQueueTimer* s_timer = NULL; CEvent::Type getReloadConfigEvent() @@ -224,6 +236,10 @@ handleScreenError(const CEvent&, void*) EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } + +static void handleSuspend(const CEvent& event, void*); +static void handleResume(const CEvent& event, void*); + static CScreen* openServerScreen() @@ -233,6 +249,14 @@ openServerScreen() screen->getEventTarget(), new CFunctionEventJob( &handleScreenError)); + EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleSuspend)); + EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleResume)); return screen; } @@ -243,6 +267,10 @@ closeServerScreen(CScreen* screen) if (screen != NULL) { EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), screen->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(), + screen->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getResumeEvent(), + screen->getEventTarget()); delete screen; } } @@ -319,80 +347,180 @@ closeServer(CServer* server) delete server; } +static bool initServer(); static bool startServer(); static void -retryStartHandler(const CEvent&, void* vtimer) +stopRetryTimer() +{ + if (s_timer != NULL) { + EVENTQUEUE->deleteTimer(s_timer); + EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + s_timer = NULL; + } +} + +static +void +retryHandler(const CEvent&, void*) { // discard old timer - CEventQueueTimer* timer = reinterpret_cast(vtimer); - EVENTQUEUE->deleteTimer(timer); - EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + assert(s_timer != NULL); + stopRetryTimer(); - // try starting the server again - LOG((CLOG_DEBUG1 "retry starting server")); - startServer(); + // try initializing/starting the server again + switch (s_serverState) { + case kUninitialized: + case kInitialized: + case kStarted: + assert(0 && "bad internal server state"); + break; + + case kInitializing: + LOG((CLOG_DEBUG1 "retry server initialization")); + s_serverState = kUninitialized; + if (!initServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + break; + + case kInitializingToStart: + LOG((CLOG_DEBUG1 "retry server initialization")); + s_serverState = kUninitialized; + if (!initServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + else if (s_serverState == kInitialized) { + LOG((CLOG_DEBUG1 "starting server")); + if (!startServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + } + break; + + case kStarting: + LOG((CLOG_DEBUG1 "retry starting server")); + s_serverState = kInitialized; + if (!startServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + break; + } } static bool -startServer() +initServer() { + // skip if already initialized or initializing + if (s_serverState != kUninitialized) { + return true; + } + double retryTime; CScreen* serverScreen = NULL; CPrimaryClient* primaryClient = NULL; - CClientListener* listener = NULL; try { CString name = ARG->m_config->getCanonicalName(ARG->m_name); serverScreen = openServerScreen(); primaryClient = openPrimaryClient(name, serverScreen); - listener = openClientListener(ARG->m_config->getSynergyAddress()); - s_server = openServer(*ARG->m_config, primaryClient); s_serverScreen = serverScreen; s_primaryClient = primaryClient; - s_listener = listener; + s_serverState = kInitialized; updateStatus(); - LOG((CLOG_NOTE "started server")); return true; } catch (XScreenUnavailable& e) { LOG((CLOG_WARN "cannot open primary screen: %s", e.what())); - closeClientListener(listener); closePrimaryClient(primaryClient); closeServerScreen(serverScreen); updateStatus(CString("cannot open primary screen: ") + e.what()); retryTime = e.getRetryTime(); } - catch (XSocketAddressInUse& e) { - LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); - closeClientListener(listener); - closePrimaryClient(primaryClient); - closeServerScreen(serverScreen); - updateStatus(CString("cannot listen for clients: ") + e.what()); - retryTime = 10.0; - } catch (XScreenOpenFailure& e) { LOG((CLOG_CRIT "cannot open primary screen: %s", e.what())); - closeClientListener(listener); closePrimaryClient(primaryClient); closeServerScreen(serverScreen); return false; } catch (XBase& e) { LOG((CLOG_CRIT "failed to start server: %s", e.what())); - closeClientListener(listener); closePrimaryClient(primaryClient); closeServerScreen(serverScreen); return false; } + + if (ARG->m_restartable) { + // install a timer and handler to retry later + assert(s_timer == NULL); + LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); + s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, + new CFunctionEventJob(&retryHandler, NULL)); + s_serverState = kInitializing; + return true; + } + else { + // don't try again + return false; + } +} + +static +bool +startServer() +{ + // skip if already started or starting + if (s_serverState == kStarting || s_serverState == kStarted) { + return true; + } + + // initialize if necessary + if (s_serverState != kInitialized) { + if (!initServer()) { + // hard initialization failure + return false; + } + if (s_serverState == kInitializing) { + // not ready to start + s_serverState = kInitializingToStart; + return true; + } + assert(s_serverState == kInitialized); + } + + double retryTime; + CClientListener* listener = NULL; + try { + listener = openClientListener(ARG->m_config->getSynergyAddress()); + s_server = openServer(*ARG->m_config, s_primaryClient); + s_listener = listener; + updateStatus(); + LOG((CLOG_NOTE "started server")); + s_serverState = kStarted; + return true; + } + catch (XSocketAddressInUse& e) { + LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); + closeClientListener(listener); + updateStatus(CString("cannot listen for clients: ") + e.what()); + retryTime = 10.0; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start server: %s", e.what())); + closeClientListener(listener); + return false; + } if (ARG->m_restartable) { // install a timer and handler to retry later + assert(s_timer == NULL); LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); - CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, - new CFunctionEventJob(&retryStartHandler, timer)); + s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, + new CFunctionEventJob(&retryHandler, NULL)); + s_serverState = kStarting; return true; } else { @@ -405,14 +533,63 @@ static void stopServer() { - closeClientListener(s_listener); - closeServer(s_server); - closePrimaryClient(s_primaryClient); - closeServerScreen(s_serverScreen); - s_server = NULL; - s_listener = NULL; - s_primaryClient = NULL; - s_serverScreen = NULL; + if (s_serverState == kStarted) { + closeClientListener(s_listener); + closeServer(s_server); + s_server = NULL; + s_listener = NULL; + s_serverState = kInitialized; + } + else if (s_serverState == kStarting) { + stopRetryTimer(); + s_serverState = kInitialized; + } + assert(s_server == NULL); + assert(s_listener == NULL); +} + +static +void +cleanupServer() +{ + stopServer(); + if (s_serverState == kInitialized) { + closePrimaryClient(s_primaryClient); + closeServerScreen(s_serverScreen); + s_primaryClient = NULL; + s_serverScreen = NULL; + s_serverState = kUninitialized; + } + else if (s_serverState == kInitializing || + s_serverState == kInitializingToStart) { + stopRetryTimer(); + s_serverState = kUninitialized; + } + assert(s_primaryClient == NULL); + assert(s_serverScreen == NULL); + assert(s_serverState == kUninitialized); +} + +static +void +handleSuspend(const CEvent&, void*) +{ + if (!s_suspended) { + LOG((CLOG_INFO "suspend")); + stopServer(); + s_suspended = true; + } +} + +static +void +handleResume(const CEvent&, void*) +{ + if (s_suspended) { + LOG((CLOG_INFO "resume")); + startServer(); + s_suspended = false; + } } static @@ -517,7 +694,7 @@ mainLoop() IEventQueue::getSystemTarget()); EVENTQUEUE->removeHandler(getReloadConfigEvent(), IEventQueue::getSystemTarget()); - stopServer(); + cleanupServer(); updateStatus(); LOG((CLOG_NOTE "stopped server")); @@ -528,7 +705,11 @@ static int daemonMainLoop(int, const char**) { - CSystemLogger sysLogger(DAEMON_NAME); +#if SYSAPI_WIN32 + CSystemLogger sysLogger(DAEMON_NAME, false); +#else + CSystemLogger sysLogger(DAEMON_NAME, true); +#endif return mainLoop(); } @@ -536,6 +717,10 @@ static int standardStartup(int argc, char** argv) { + if (!ARG->m_daemon) { + ARCH->showConsole(false); + } + // parse command line parse(argc, argv); @@ -952,6 +1137,7 @@ public: // ILogOutputter overrides virtual void open(const char*) { } virtual void close() { } + virtual void show(bool) { } virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const { return ""; } }; @@ -997,11 +1183,27 @@ static int daemonNTStartup(int, char**) { - CSystemLogger sysLogger(DAEMON_NAME); + CSystemLogger sysLogger(DAEMON_NAME, false); bye = &byeThrow; return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } +static +int +foregroundStartup(int argc, char** argv) +{ + ARCH->showConsole(false); + + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // never daemonize + return mainLoop(); +} + static void showError(HINSTANCE instance, const char* title, UINT id, const char* arg) @@ -1015,12 +1217,23 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { try { + CArchMiscWindows::setIcons((HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED), + (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED)); CArch arch(instance); CMSWindowsScreen::init(instance); CLOG; CThread::getCurrentThread().setPriority(-14); CArgs args; + // set title on log window + ARCH->openConsole((CString(kAppVersion) + " " + "Server").c_str()); + // 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 @@ -1028,8 +1241,13 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. StartupFunc startup = &standardStartup; - if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - startup = &daemonNTStartup; + if (!CArchMiscWindows::isWindows95Family()) { + if (__argc <= 1) { + startup = &daemonNTStartup; + } + else { + startup = &foregroundStartup; + } } // send PRINT and FATAL output to a message box diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp index 7fdf1f01..a8d791de 100644 --- a/cmd/synergys/synergys.dsp +++ b/cmd/synergys/synergys.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # 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\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergys.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergys.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -70,7 +70,7 @@ LINK32=link.exe # 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\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergys.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergys.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/synergys/synergys.ico b/cmd/synergys/synergys.ico index 23d9a090..89f965f4 100644 Binary files a/cmd/synergys/synergys.ico and b/cmd/synergys/synergys.ico differ diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index c8f7da21..117b1852 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -73,7 +73,25 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + POPUP "Set Log Level" + BEGIN + MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR + + MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING + + MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE + + MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO + + MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG + + MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1 + + MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2 + + END MENUITEM "Reload Configuration", IDC_RELOAD_CONFIG MENUITEM "Force Reconnect", IDC_FORCE_RECONNECT MENUITEM SEPARATOR diff --git a/cmd/synergys/tb_error.ico b/cmd/synergys/tb_error.ico index a304dc18..746a87c9 100644 Binary files a/cmd/synergys/tb_error.ico and b/cmd/synergys/tb_error.ico differ diff --git a/cmd/synergys/tb_idle.ico b/cmd/synergys/tb_idle.ico index 9ba599de..4e13a264 100644 Binary files a/cmd/synergys/tb_idle.ico and b/cmd/synergys/tb_idle.ico differ diff --git a/cmd/synergys/tb_run.ico b/cmd/synergys/tb_run.ico index 86aa9f3a..88e160cb 100644 Binary files a/cmd/synergys/tb_run.ico and b/cmd/synergys/tb_run.ico differ diff --git a/cmd/synergys/tb_wait.ico b/cmd/synergys/tb_wait.ico index e94db3c3..257be0a1 100644 Binary files a/cmd/synergys/tb_wait.ico and b/cmd/synergys/tb_wait.ico differ diff --git a/configure.in b/configure.in index 41727153..b33bf5f1 100644 --- a/configure.in +++ b/configure.in @@ -53,10 +53,19 @@ AM_CONDITIONAL(CARBON, test x$acx_host_winapi = xCARBON) AM_CONDITIONAL(XWINDOWS, test x$acx_host_winapi = xXWINDOWS) dnl checks for programs +AC_PROG_CC AC_PROG_CXX AC_PROG_RANLIB AC_CHECK_PROG(HAVE_DOT, dot, YES, NO) +dnl AC_PROG_OBJC doesn't exist. Borrow some ideas from KDE. +dnl AC_MSG_CHECKING(for an Objective-C compiler) +OBJC="${CC}" +OBJCFLAGS="${CFLAGS}" +AC_SUBST(OBJC) +AC_SUBST(OBJCFLAGS) +_AM_DEPENDENCIES(OBJC) + dnl do checks using C++ AC_LANG_CPLUSPLUS @@ -88,7 +97,7 @@ 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 alloca.h]) +AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h locale.h wchar.h]) AC_CHECK_HEADERS([sys/socket.h sys/select.h]) AC_CHECK_HEADERS([sys/utsname.h]) AC_CHECK_HEADERS([istream ostream sstream]) @@ -109,6 +118,33 @@ if test x"$acx_host_winapi" = xXWINDOWS; then , AC_MSG_ERROR(You must have the XTest headers to compile synergy)) + acx_have_xkb=no + AC_CHECK_LIB(X11, + XkbQueryExtension, + [acx_have_xkb=yes], + [acx_have_xkb=no], + [$X_LIBS $X_EXTRA_LIBS]) + if test x"$acx_have_xkb" = xyes; then + AC_CHECK_HEADERS([X11/XKBlib.h X11/extensions/XKBstr.h], + [acx_have_xkb=yes], + [acx_have_xkb=no], + [#include ]) + if test x"$acx_have_xkb" = xyes; then + AC_TRY_COMPILE([ + #include + #include + ],[ + XkbQueryExtension(0, 0, 0, 0, 0, 0); + ], + [acx_have_xkb=yes], + [acx_have_xkb=no]) + fi + fi + if test x"$acx_have_xkb" = xyes; then + AC_DEFINE(HAVE_XKB_EXTENSION, 1, + [Define this if the XKB extension is available.]) + fi + acx_have_xinerama=yes AC_CHECK_LIB(Xinerama, XineramaQueryExtension, @@ -149,7 +185,7 @@ if test x"$acx_host_winapi" = xXWINDOWS; then AC_MSG_CHECKING(for prototypes in X11/extensions/dpms.h) acx_have_dpms_protos=no AC_TRY_COMPILE([ - #include + #include extern "C" { #include } @@ -207,8 +243,25 @@ ACX_CXX_WARNINGS_ARE_ERRORS dnl adjust compiler and linker variables CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $ARCH_CFLAGS" +OBJCXXFLAGS="$OBJCXXFLAGS $CXXFLAGS $ARCH_CFLAGS" LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $ARCH_LIBS $LIBS" +dnl we need to have an environment variable set when building on OS X. +dnl i'm not sure of the right way to do that. writing 'export ...' to +dnl the makefiles isn't portable. here we'll hijack XXXDEPMODE (where +dnl XXX depends on the language) to insert setting the environment +dnl variable when running the compiler. we'd like to put that in CC, +dnl CXX and OBJC but that breaks depcomp. let's hope this works. +if test x"$acx_host_winapi" = xCARBON; then + MACOSX_DEPLOYMENT_TARGET="10.2" + CCDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $CCDEPMODE" + CXXDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $CXXDEPMODE" + OBJCDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $OBJCDEPMODE" +else + MACOSX_DEPLOYMENT_TARGET="5" + CXXDEPMODE="FOO=$MACOSX_DEPLOYMENT_TARGET $CXXDEPMODE" +fi + AC_OUTPUT([ Makefile cmd/Makefile diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index e7d61938..bbc26cfc 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -62,20 +62,31 @@ Section "Synergy (required)" File COPYING.txt File ChangeLog.txt File ${DEPTH}\doc\PORTING + File ${DEPTH}\doc\about.html File ${DEPTH}\doc\authors.html File ${DEPTH}\doc\autostart.html + File ${DEPTH}\doc\banner.html File ${DEPTH}\doc\compiling.html File ${DEPTH}\doc\configuration.html + File ${DEPTH}\doc\contact.html File ${DEPTH}\doc\developer.html File ${DEPTH}\doc\faq.html File ${DEPTH}\doc\history.html + File ${DEPTH}\doc\home.html File ${DEPTH}\doc\index.html File ${DEPTH}\doc\license.html File ${DEPTH}\doc\news.html + File ${DEPTH}\doc\roadmap.html File ${DEPTH}\doc\running.html File ${DEPTH}\doc\security.html + File ${DEPTH}\doc\synergy.css File ${DEPTH}\doc\tips.html - File ${DEPTH}\doc\todo.html + File ${DEPTH}\doc\toc.html + File ${DEPTH}\doc\trouble.html + + SetOutPath $INSTDIR\images + File ${DEPTH}\doc\images\logo.gif + File ${DEPTH}\doc\images\warp.gif ; Write the installation path into the registry WriteRegStr HKLM SOFTWARE\Synergy "Install_Dir" "$INSTDIR" @@ -95,18 +106,26 @@ Section "Start Menu Shortcuts" CreateDirectory "$SMPROGRAMS\Synergy" CreateShortCut "$SMPROGRAMS\Synergy\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0 CreateShortCut "$SMPROGRAMS\Synergy\README.lnk" "$INSTDIR\index.html" - CreateShortCut "$SMPROGRAMS\Synergy\FAQ.lnk" "$INSTDIR\faq.html" CreateShortCut "$SMPROGRAMS\Synergy\Synergy Folder.lnk" "$INSTDIR" CreateShortCut "$SMPROGRAMS\Synergy\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 SectionEnd +; Optional section (can be disabled by the user) +Section "Desktop Icon" + + CreateShortCut "$DESKTOP\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0 + +SectionEnd + ;-------------------------------- ; Uninstaller Section "Uninstall" - + ; Stop and uninstall the daemons + ExecWait '"$INSTDIR\synergy.exe" /uninstall' + ; Remove autorun registry keys for synergy DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Server" DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Client" @@ -136,6 +155,7 @@ Section "Uninstall" ; Remove shortcuts, if any Delete "$SMPROGRAMS\Synergy\*.*" + Delete "$DESKTOP\Synergy.lnk" ; Remove directories used RMDir "$SMPROGRAMS\Synergy" diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index a4b368ba..0d2b6f48 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -3,7 +3,7 @@ Name: @PACKAGE@ Version: @VERSION@ Release: 1 License: GPL -Packager: Chris Schoeneman +Packager: Chris Schoeneman Group: System Environment/Daemons Prefixes: /usr/bin Source: @PACKAGE@-@VERSION@.tar.gz @@ -41,19 +41,26 @@ rm -rf $RPM_BUILD_ROOT %doc INSTALL %doc NEWS %doc README +%doc doc/about.html %doc doc/authors.html %doc doc/autostart.html +%doc doc/banner.html +%doc doc/border.html %doc doc/compiling.html %doc doc/configuration.html +%doc doc/contact.html %doc doc/developer.html %doc doc/faq.html %doc doc/history.html +%doc doc/home.html %doc doc/index.html %doc doc/license.html %doc doc/news.html +%doc doc/roadmap.html %doc doc/running.html %doc doc/security.html %doc doc/tips.html -%doc doc/todo.html +%doc doc/toc.html +%doc doc/trouble.html %doc doc/synergy.css %doc examples/synergy.conf diff --git a/doc/Makefile.am b/doc/Makefile.am index 45494563..2efec24c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -17,20 +17,29 @@ EXTRA_DIST = \ PORTING \ doxygen.cfg.in \ synergy.css \ + about.html \ authors.html \ autostart.html \ + banner.html \ + border.html \ compiling.html \ configuration.html \ + contact.html \ developer.html \ faq.html \ history.html \ + home.html \ index.html \ license.html \ news.html \ + roadmap.html \ running.html \ security.html \ tips.html \ - todo.html \ + toc.html \ + trouble.html \ + images/logo.gif \ + images/warp.gif \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/doc/about.html b/doc/about.html new file mode 100644 index 00000000..aadd5764 --- /dev/null +++ b/doc/about.html @@ -0,0 +1,55 @@ + + + + + + + + About Synergy + + +

+With synergy, all the computers on your desktop form a single virtual +screen. You use the mouse and keyboard of only one of the computers +while you use all of the monitors on all of the computers. +You tell synergy how many screens you have and their positions relative +to one another. Synergy then detects when the mouse moves off +the edge of a screen and jumps it instantly to the neighboring screen. +The keyboard works normally on each screen; input goes to whichever +screen has the cursor. +

+In this example, the user is moving the mouse from left to right. +When the cursor reaches the right edge of the left screen it jumps +instantly to the left edge of the right screen. +

+

+

+You can arrange screens side-by-side, above and below one another, +or any combination. You can even have a screen jump to the opposite +edge of itself. Synergy also understands multiple screens attached +to the same computer. +

+Running a game and don't want synergy to jump screens? No problem. +Just toggle Scroll Lock. Synergy keeps the cursor on the same screen +when Scroll Lock is on. (This can be configured to another hot key.) +

+Do you wish you could cut and paste between computers? Now you can! +Just copy text, HTML, or an image as you normally would on one screen +then switch to another screen and paste it. It's as if all your +computers shared a single clipboard (and separate primary selection for +you X11 users). It even converts newlines to each computer's native +form so cut and paste between different operating systems works +seamlessly. And it does it all in Unicode so any text can be copied. +

+

+Do you use a screen saver? With synergy all your screen savers act in +concert. When one starts they all start. When one stops they all +stop. And, if you require a password to unlock the screen, you'll +only have to enter a password on one screen. +

+If you regularly use multiple computers on one desk, give synergy a +try. You'll wonder how you ever lived without it. +

+ + + diff --git a/doc/authors.html b/doc/authors.html index f908c0bf..bbf3fd10 100644 --- a/doc/authors.html +++ b/doc/authors.html @@ -1,38 +1,65 @@ - + - - Synergy Authors + + + + + Synergy Authors -

Synergy Authors

+

Synergy Authors

+

- + + + + - + + + + - + + + + - + + + + - + + + + - + + + +
Chris SchoenemanChris Schoeneman crs23@users.sourceforge.no_spam.net  Creator, owner, primary developer
Ryan BreenRyan Breen ryan@ryanbreen.no_spam.com  Initial Mac OS X port
Guido PoschtaGuido Poschta moolder@gmx.no_spam.net  Windows installer
Bertrand Landry HetuBertrand Landry Hetu bertrand@landryhetu.no_spam.com  Mac OS X port
Tom ChadwickTom Chadwick vttom@users.sourceforge.no_spam.net  PageUp/PageDown on X servers without mouse wheel support
Brent PriddyBrent Priddy toopriddy@users.sourceforge.no_spam.net  Re-resolving server hostname on each connection
+

+To avoid spam bots, the above email addresses have ".no_spam" +hidden near the end. If you copy and paste the text be sure to +remove it.

+ diff --git a/doc/autostart.html b/doc/autostart.html index 9a194ce2..0343048a 100644 --- a/doc/autostart.html +++ b/doc/autostart.html @@ -1,41 +1,41 @@ - + - - Synergy Autostart Guide + + + + + Synergy Autostart Guide -

Starting synergy automatically

+

Starting synergy automatically

+

You can configure synergy to start automatically when the computer starts or when you log in. The steps to do that are different on each platform. Note that changing these configurations doesn't actually start or stop synergy. The changes take effect the next time you start your computer or log in. -

- -

Windows

-

+

+

Windows

+

Start synergy and 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 or not start automatically when the computer starts or when you log in. You need Administrator access rights to start synergy automatically when the computer starts. 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 tell 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 @@ -46,10 +46,9 @@ 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. -

- -

Unix

-

+

+

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 @@ -57,34 +56,33 @@ 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. -

-

+

Typically, you need to edit three script files. The first file will start synergy before a user logs in, the second will kill that copy of synergy, and the third will start it again after the user logs in. -

-

+

The contents of the scripts varies greatly between systems so there's no one definite place where you should insert your edits. However, these scripts often exit before reaching the bottom so put the edits near the top of the script. -

-

+

The location and names of these files depend on the operating system and display manager you're using. A good guess for the -location is /etc/X11. Typical file names -are: -

-

+location is /etc/X11. If you use kdm +then try looking in /etc/kde3 or +/usr/kde/version/share/config. +Typical file names are: +

+ - - - - + + + +
      xdm    gdm
1 xdm/Xsetup gdm/Init/Default (*)
2 xdm/Xstartup gdm/PostLogin/Default (*)
3 xdm/Xsession gdm/Sessions/Default (*, **)
      xdm    kdm    gdm
1 xdm/Xsetup kdm/Xsetup gdm/Init/Default (*)
2 xdm/Xstartup kdm/Xstartup gdm/PostLogin/Default (*)
3 xdm/Xsession kdm/Xsession gdm/Sessions/Default (*, **)
-

-

+ +

*) The Default file is used if no other suitable file is found. gdm will try displayname (e.g. :0) @@ -95,8 +93,7 @@ in that order, before and instead of Default. xdm/Xsession or dm/Xsession if gdm/Sessions/Default doesn't exist. -

-

+

For a synergy client, add the following to the first file: /usr/bin/killall synergyc @@ -105,15 +102,13 @@ For a synergy client, add the following to the first file: Of course, the path to synergyc depends on where you installed it so adjust as necessary. -

-

+

Add to the second file: /usr/bin/killall synergyc sleep 1 -

-

+

And to the third file: /usr/bin/killall synergyc @@ -124,8 +119,7 @@ Note that <options>-f or --no-daemon or the script will never exit and you won't be able to log in. -

-

+

The changes are the same for the synergy server except replace synergyc with synergys and use the appropriate synergys command @@ -135,38 +129,32 @@ file in root's home directory then in /etc. Make sure it exists in one of those places or use the --config config-pathname option to specify its location. -

-

+

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. -

-

+

If you're configuring synergy to start only after you log in then edit your .xsession file. Add just what you would add to the third file above. -

- -

Mac OS X

-

+

+

Mac OS X

+

[By Tor Slettnes] -

-

+

There are three different ways to automatically start Synergy (client or server) on Mac OS X: -

-

+

  1. The first method involves creating a StartupItem at the system level, which is executed when the machine starts up or shuts down. This script will run in the background, and relaunch synergy as needed. -

    -

    +

    Pros:
    @@ -180,14 +168,12 @@ There are three different ways to automatically start Synergy
  2. -

    -

    +

  3. The second method will launch Synergy from the LoginWindow application, once a particular user has logged in. -

    -

    +

    Pros:
    @@ -202,13 +188,11 @@ There are three different ways to automatically start Synergy
  4. -

    -

    +

  5. The third method is to launch a startup script from the "Startup Items" tab under System Preferences -> Accounts. -

    -

    +

    Pros:
    @@ -222,17 +206,14 @@ There are three different ways to automatically start Synergy
-

-

+

The text below describes how to implement a Synergy client using the first two methods simultaneously. This way, Synergy is always running, and the clipboard is available when someone is logged in. A Mac OS X Synergy server setup will be quite similar. -

-

+

1. Create a System Level Startup Item -

-

+

  • Open a Terminal window, and become root: @@ -250,8 +231,7 @@ logged in. A Mac OS X Synergy server setup will be quite similar. In this folder, create a new script file by the same name as the directory itself, Synergy. This script should contain the following text: -

    -

    +

    #!/bin/sh . /etc/rc.common @@ -291,12 +271,10 @@ RestartService ()   RunService "$1" -

    -

    +

    However, replace synergy-server with the actual name or IP address of your Synergy server. -

    -

    +

    Note that this scripts takes care not to start Synergy if another instance is currently running. This allows it to run in the background even when synergy is also @@ -312,39 +290,32 @@ RunService "$1"

  • In the same folder, create a file named StartupParameters.plist containing: -

    -

    +

    { Description = "Synergy Client"; - Provides = ("synergy-client"); - Requires = "Network"; + Provides = ("Synergy"); + Requires = ("Network"); OrderPreference = "None"; }

-

-

+

That's it! If you want to test this setup, you can run the -startup script as follow: -

-

+startup script as follows: +

# /Library/StartupItems/Synergy/Synergy start -

-

+

Any errors, as well as output from Synergy, will be shown in your terminal window. -

-

+

Next time you reboot, Synergy should start automatically. -

-

+

2. Run Synergy When a User Logs In -

-

+

Each time a user successfully logs in via the console, the LoginWindow application creates a unique session cookie and stores it in the environment variable @@ -352,57 +323,48 @@ cookie and stores it in the environment variable to work, Synergy needs access to this environment variable. In other words, Synergy needs to be launched (directly or indirectly) via the LoginWindow application. -

-

+

However, in order to kill any synergy processes started at the system level (as described above), we need root access. Thus, launching Synergy within the User's environment (e.g. via the Startup Items tab in System Preferences -> Accounts) is not an option that work in conjunction with the method above. -

-

+

Fortunately, the LoginWindow application provides a "hook" for running a custom program (as root, with the username provided as the first and only argument) once a user has authenticated, but before the user is logged in. -

-

+

Unfortunately, only one such hook is available. If you have already installed a Login Hook, you may need to add the text from below to your existing script, rather than creating a new one. -

-

+

  • Launch a Terminal window, and become root: $ sudo su - +
  • -

    -

    +

  • Find out if a LoginHook already exists: - # defaults read /Library/Preferences/com.apple.loginwindow LoginHook + # defaults read com.apple.loginwindow LoginHook -

    -

    This will either show the full path to a script or executable file, or the text: - The domain/default pair of (/Library/Preferences/com.apple.loginwindow, LoginHook) does not exist + The domain/default pair of (com.apple.loginwindow, LoginHook) does not exist In the former case, you need to modify your existing script, and/or create a "superscript" which in turn calls your existing script plus the one we will create here. -

    -

    +

    The rest of this text assumes that this item did not already exist, and that we will create a new script.

  • -

    -

  • Create a folder in which we will store our custom startup script: @@ -410,16 +372,13 @@ one. # mkdir -p /Library/LoginWindow
  • -

    -

  • In this folder, create a new script file (let's name it LoginHook.sh), containing the following text: -

    -

    +

    #!/bin/sh -prog=(/usr/local/bin/synergyc -n $(hostname -s) --camp ip-address-of-server) +prog=(/usr/local/bin/synergyc -n $(hostname -s) ip-address-of-server)   ### Stop any currently running Synergy client killall ${prog[0]##*/} @@ -428,19 +387,23 @@ killall ${prog[0]##*/} exec "${prog[@]}"

  • -

    -

    +

  • + Make this script executable: + + # chmod 755 /Library/LoginWindow/LoginHook.sh + +
  • Create a login hook to call the script you just created: - # defaults write /Library/Preferences/com.apple.loginwindow \ - LoginHook /Library/LoginWindow/LoginHook.sh + # defaults write com.apple.loginwindow LoginHook /Library/LoginWindow/LoginHook.sh - You can instead type the above all on one line but remove the backslash.
-

-

+

+More information on setting up login hooks can be found at +Apple. +

When running the Synergy client, you may need to use the IP address of the Synergy server rather than its host name. Specifically, unless you have listed the server in your @@ -448,21 +411,18 @@ local /etc/hosts file or in your local NetInfo database, name services (i.e. DNS) may not yet be available by the time you log in after power-up. synergyc will quit if it cannot resolve the server name. -

-

+

(This is not an issue with the previous method, because the -StartupItems.plist file specifies that this +StartupParameters.plist file specifies that this script should not be run until "network" is available). -

-

+

3. Good Luck! -

-

+

Remember to look in your system log on both your server and your client(s) for clues to any problems you may have (/var/log/system.log on your OS X box, typically /var/log/syslog on Linux boxes).

- + diff --git a/doc/banner.html b/doc/banner.html new file mode 100644 index 00000000..eed66918 --- /dev/null +++ b/doc/banner.html @@ -0,0 +1,16 @@ + + + + + + + + Synergy Header + + + + + +
Synergy
+ + diff --git a/doc/border.html b/doc/border.html new file mode 100644 index 00000000..ce45847a --- /dev/null +++ b/doc/border.html @@ -0,0 +1,14 @@ + + + + + + + + Synergy + + + +
+ + diff --git a/doc/compiling.html b/doc/compiling.html index 49f2c764..3fe5c393 100644 --- a/doc/compiling.html +++ b/doc/compiling.html @@ -1,39 +1,40 @@ - + - - Building and Installing Synergy + + + + + Building and Installing Synergy -

Prerequisites for building

+

Prerequisites for building

+

To build synergy from the sources you'll need the following: -

  • Windows -
      -
    • VC++ 6.0 or up -
    - +
      +
    • VC++ 6.0 or up +
    +

  • Unix -
      -
    • gcc 2.95 or up -
    • X11R4 or up headers and libraries -
    - +
      +
    • gcc 2.95 or up +
    • X11R4 or up headers and libraries +
    +

  • Mac OS X -
      -
    • XCode; or gcc 2.95 or up -
    • Carbon development headers and libraries -
    +
      +
    • XCode; or gcc 2.95 or up +
    • Carbon development headers and libraries +
-

- -

Configuring the build

-

+

+

Configuring the build

+

This step is not necessary when using VC++ or XCode. -

-

+

To configure the build for your platform use the configure script:

   ./configure
@@ -47,80 +48,73 @@ 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

+

+

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 ./build. -

    - +

  • Unix or Mac OS X without XCode -

    +

    Simply enter:

       make
       
    This will build the client and server and leave them in their respective source directories. -

    - +

  • Mac OS X with XCode -

    +

    Start XCode and open the synergy.xcode project. Build the all project using the Deployment flavor. -

    - +

- -

Installing

+

+

Installing

  • Windows -

    - You'll need NSIS, the +

    + You'll need NSIS, the Nullsoft Scriptable Install System. Build All - Release then build Installer - Release. This creates SynergyInstaller.exe in the build directory. Run this to install synergy. -

    -

    +

    Alternatively, you can simply copy the following files from the debug or build directory to a directory you choose (perhaps under the Program Files directory): -

    • synergy.exe
    • synergyc.exe
    • synergys.exe
    • synrgyhk.dll
    -

    - +

  • Unix or Mac OS X without XCode -

    +

       make install
       
    will install the client and server into /usr/local/bin unless you specified a different directory when you ran configure. -

    - +

  • Mac OS X with XCode -

    +

    Copy the following files from ./build to a convenient location: - +

    • synergyc
    • synergys
    -

    +

    + diff --git a/doc/configuration.html b/doc/configuration.html index ca3f55f8..48b2cc2d 100644 --- a/doc/configuration.html +++ b/doc/configuration.html @@ -1,27 +1,30 @@ - + - - Synergy Configuration Guide + + + + + Synergy Configuration Guide -

    Synergy Configuration File Format

    +

    Synergy Configuration File Format

    +

    The synergy server requires configuration. It will try certain pathnames to load the configuration file if you don't specify a path using the --config command line option. synergys --help reports those pathnames. -

    -

    +

    The configuration file is a plain text file. Use any text editor to create the configuration file. The file is broken into sections and each section has the form: -

    +
       section: name
         args
       end
    -
    + Comments are introduced by # and continue to the end of the line. name must be one of the following: @@ -36,8 +39,7 @@ configuration file is case-sensitive so Section, SECTION, and section are all different and only the last is valid. Screen names are the exception; screen names are case-insensitive. -

    -

    +

    The file is parsed top to bottom and names cannot be used before they've been defined in the screens or aliases sections. So the @@ -45,9 +47,9 @@ they've been defined in the screens or must appear after the screens and links cannot refer to aliases unless the aliases appear before the links. -

    -

    screens

    -

    +

    +

    screens

    +

    args 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. (This @@ -60,30 +62,28 @@ screen can specify a number of options. Options have the form name = value and are listed one per line after the screen name. -

    -

    +

    Example: -

    +
         section: screens
    -    	moe:
    -    	larry:
    +     moe:
    +     larry:
                 halfDuplexCapsLock = true
                 halfDuplexNumLock = true
    -    	curly:
    +     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 modifier key. -

    -

    +

    A 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 @@ -93,22 +93,32 @@ A screen can have the following options: on the server screen. If it acts strangely on one screen then that screen may need the option set to true. -

      +

    • halfDuplexNumLock = {true|false} -

      +

      This is identical to halfDuplexCapsLock except it applies to the Num Lock key. -

      +

    • halfDuplexScrollLock = {true|false} -

      +

      This is identical to halfDuplexCapsLock - except it applies to the Scroll Lock key. Note that synergy uses - Scroll Lock to keep the cursor on the current screen. That is, - when Scroll Lock is toggled on, the cursor is locked to the screen - that it's currently on. Use it to prevent accidental switching. -

      + except it applies to the Scroll Lock key. Note that, by default, + synergy uses Scroll Lock to keep the cursor on the current screen. That + is, when Scroll Lock is toggled on, the cursor is locked to the screen + that it's currently on. You can use that to prevent accidental switching. + You can also configure other hot keys to do that; see + lockCursorToScreen. +

      +

    • switchCorners = <corners> +

      + See switchCorners below. +

      +

    • switchCornerSize = N +

      + See switchCornerSize below. +

    • 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 @@ -118,33 +128,31 @@ A screen can have the following options: 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 + 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). -

      +

    -

    - -

    aliases

    -

    +

    +

    aliases

    +

    args 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 @@ -152,102 +160,163 @@ A screen can have the following options: 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 + larry.stooges.com and can connect as either name. Screen curly is also known as shemp (hey, it's just an example). -

    - -

    links

    -

    +

    +

    links

    +

    args 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 {left|right|up|down} = - name. A link indicates which screen is - adjacent in the given direction. -

    -

    + form {left|right|up|down}[<range>] = + name[<range>]. A link indicates which + screen is adjacent in the given direction. +

    + Each side of a link can specify a range which defines a portion + of an edge. A range on the direction is the portion of edge you can + leave from while a range on the screen is the portion of edge you'll + enter into. Ranges are optional and default to the entire edge. All + ranges on a particular direction of a particular screen must not + overlap. +

    + A <range> is written as (<start>,<end>). + Both start and end + are percentages in the range 0 to 100, inclusive. The start must be + less than the end. 0 is the left or top of an edge and 100 is the + right or bottom. +

    Example: -

    +
         section: links
             moe:
    -            right = larry
    -            up    = curly
    +            right        = larry
    +            up(50,100)   = curly(0,50)
             larry:
    -            left  = moe
    -            up    = curly
    +            left         = moe
    +            up(0,50)     = curly(50,100)
             curly:
    -            down  = larry
    +            down(0,50)   = moe
    +            down(50,100) = larry(0,50)
         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, + the left half of + curly is above the right half of + 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. -

    - -

    options

    -

    + larry (edges are not necessarily symmetric + so you have to provide both directions), the right half of + curly is above the left half of + larry, all of moe + is below the left half of curly, and the + left half of larry is below the right half of + curly. +

    + Note that links do not have to be + symmetrical; for instance, here the edge between + moe and curly + maps to different ranges depending on if you're going up or down. + In fact links don't have to be bidirectional. You can configure + the right of moe to go to + larry without a link from the left of + larry to moe. + It's possible to configure a screen with no outgoing links; the + cursor will get stuck on that screen unless you have a hot key + configured to switch off of that screen. +

    +

    options

    +

    args is a list of lines of the form name = value. These set the global options. -

    -

    +

    Example: -

    +
         section: options
             heartbeat = 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. -

      +

      +

    • switchCorners = <corners> +

      + Synergy won't switch screens when the mouse reaches the edge of + the screen if it's in a listed corner. The size of all corners + is given by the switchCornerSize + option. +

      + Corners are specified by a list using the following names: +

        +
      • none -- no corners +
      • top-left -- the top left corner +
      • top-right -- the top right corner +
      • bottom-left -- the bottom left corner +
      • bottom-right -- the bottom right corner +
      • left -- top and bottom left corners +
      • right -- top and bottom right corners +
      • top -- left and right top corners +
      • bottom -- left and right bottom corners +
      • all -- all corners +
      +

      + The first name in the list is one of the above names and defines + the initial set of corners. Subsequent names are prefixed with + + or - to add the corner to or remove the corner from the set, + respectively. For example: +

      + + all -left +top-left + +

      + starts will all corners, removes the left corners (top and bottom) + then adds the top-left back in, resulting in the top-left, + bottom-left and bottom-right corners. +

      +

    • switchCornerSize = N +

      + Sets the size of all corners in pixels. The cursor must be within + N pixels of the corner to be considered + to be in the corner. +

    • 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 @@ -255,29 +324,337 @@ A screen can have the following options: 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. -

      +

      + 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. +

    • relativeMouseMoves = {true|false} -

      - If set to true then secondary - screens move the mouse using relative rather than - absolute mouse moves when and only when Scroll Lock is - toggled on (i.e. the cursor is locked to the screen). - This is intended to make synergy work better with certain - games. If set to false or not - set then all mouse moves are absolute. -

      +

      + If set to true then secondary + screens move the mouse using relative rather than absolute + mouse moves when and only when the cursor is locked to the + screen (by Scroll Lock or a configured + hot key). + This is intended to make synergy work better with certain + games. If set to false or not + set then all mouse moves are absolute. +

      +

    • keystroke(key) = actions +

      + Binds the key combination key to the + given actions. key + is an optional list of modifiers (shift, + control, alt, + meta or super) + optionally followed by a character or a key name, all separated by + + (plus signs). You must have either + modifiers or a character/key name or both. See below for + valid key names. +

      + Actions are described below. +

      + Keyboard hot keys are handled while the cursor is on the primary + screen and secondary screens. Separate actions can be assigned + to press and release. +

      +

    • mousebutton(button) = actions +

      + Binds the modifier and mouse button combination + button to the given + actions. button + is an optional list of modifiers (shift, + control, alt, + meta or super) + followed by a button number. The primary button (the + left button for right handed users) is button 1, the middle button + is 2, etc. +

      + Actions are described below. +

      + Mouse button actions are not handled while the cursor is on the + primary screen. You cannot use these to perform an action while + on the primary screen. Separate actions can be assigned to press + and release. +

    You can use both the switchDelay and switchDoubleTap options at the same time. Synergy will switch when either requirement is satisfied. +

    +Actions are two lists of individual actions separated +by commas. The two lists are separated by a semicolon. Either list can be +empty and if the second list is empty then the semicolon is optional. The +first list lists actions to take when the condition becomes true (e.g. the +hot key or mouse button is pressed) and the second lists actions to take +when the condition becomes false (e.g. the hot key or button is released). +The condition becoming true is called activation and becoming false is +called deactivation. +Allowed individual actions are: +

      +
    • keystroke(key[,screens]) +
    • keyDown(key[,screens]) +
    • keyUp(key[,screens]) +

      + Synthesizes the modifiers and key given in key + which has the same form as described in the + keystroke option. If given, + screens lists the screen or screens to + direct the event to, regardless of the active screen. If not + given then the event is directed to the active screen only. +

      + keyDown synthesizes a key press and + keyUp synthesizes a key release. + keystroke synthesizes a key press on + activation and a release on deactivation and is equivalent to + a keyDown on activation and + keyUp on deactivation. +

      + screens is either * + to indicate all screens or a colon (:) separated list of screen + names. (Note that the screen name must have already been encountered + in the configuration file so you'll probably want to put actions at + the bottom of the file.) +

      +

    • mousebutton(button) +
    • mouseDown(button) +
    • mouseUp(button) +

      + Synthesizes the modifiers and mouse button given in + button + which has the same form as described in the + mousebutton option. +

      + mouseDown synthesizes a mouse press and + mouseUp synthesizes a mouse release. + mousebutton synthesizes a mouse press on + activation and a release on deactivation and is equivalent to + a mouseDown on activation and + mouseUp on deactivation. +

      +

    • lockCursorToScreen(mode) +

      + Locks the cursor to or unlocks the cursor from the active screen. + mode can be off + to unlock the cursor, on to lock the + cursor, or toggle to toggle the current + state. The default is toggle. If the + configuration has no lockCursorToScreen + action and Scroll Lock is not used as a hot key then Scroll Lock + toggles cursor locking. +

      +

    • switchToScreen(screen) +

      + Jump to screen with name or alias screen. +

      +

    • switchInDirection(dir) +

      + Switch to the screen in the direction dir, + which may be one of left, + right, up or + down. +

      +

    +

    +Examples: +

      +
    • keystroke(alt+left) = switchInDirection(left) +

      + Switches to the screen to left when the left arrow key is pressed + in combination with the Alt key. +

      +

    • keystroke(shift+control+alt+super) = switchToScreen(moe) +

      + Switches to screen moe when all of the + Shift, Control, Alt, and Super modifier keys are pressed together. +

      +

    • keystroke(alt+f1) = ; lockCursorToScreen(toggle) +

      + Toggles locking the cursor to the screen when Alt+F1 is released. +

      +

    • mousebutton(2) = mouseDown(control+1) ; mouseUp(control+1) +

      + While on a secondary screen clicking the middle mouse button will + become a Control click of the primary button. +

      +

    • keystroke(super+f1) = keystroke(super+L,larry), keystroke(control+alt+delete,curly) +

      + Pressing Super+F1 (on any screen) will synthesize Super+L on screen + larry and Control+Alt+Delete on screen + curly. +

      +

    +

    +Valid key names are: +

      +
    • AppMail +
    • AppMedia +
    • AppUser1 +
    • AppUser2 +
    • AudioDown +
    • AudioMute +
    • AudioNext +
    • AudioPlay +
    • AudioPrev +
    • AudioStop +
    • AudioUp +
    • BackSpace +
    • Begin +
    • Break +
    • Cancel +
    • CapsLock +
    • Clear +
    • Delete +
    • Down +
    • Eject +
    • End +
    • Escape +
    • Execute +
    • F1 +
    • F2 +
    • F3 +
    • F4 +
    • F5 +
    • F6 +
    • F7 +
    • F8 +
    • F9 +
    • F10 +
    • F11 +
    • F12 +
    • F13 +
    • F14 +
    • F15 +
    • F16 +
    • F17 +
    • F18 +
    • F19 +
    • F20 +
    • F21 +
    • F22 +
    • F23 +
    • F24 +
    • F25 +
    • F26 +
    • F27 +
    • F28 +
    • F29 +
    • F30 +
    • F31 +
    • F32 +
    • F33 +
    • F34 +
    • F35 +
    • Find +
    • Help +
    • Home +
    • Insert +
    • KP_0 +
    • KP_1 +
    • KP_2 +
    • KP_3 +
    • KP_4 +
    • KP_5 +
    • KP_6 +
    • KP_7 +
    • KP_8 +
    • KP_9 +
    • KP_Add +
    • KP_Begin +
    • KP_Decimal +
    • KP_Delete +
    • KP_Divide +
    • KP_Down +
    • KP_End +
    • KP_Enter +
    • KP_Equal +
    • KP_F1 +
    • KP_F2 +
    • KP_F3 +
    • KP_F4 +
    • KP_Home +
    • KP_Insert +
    • KP_Left +
    • KP_Multiply +
    • KP_PageDown +
    • KP_PageUp +
    • KP_Right +
    • KP_Separator +
    • KP_Space +
    • KP_Subtract +
    • KP_Tab +
    • KP_Up +
    • Left +
    • LeftTab +
    • Linefeed +
    • Menu +
    • NumLock +
    • PageDown +
    • PageUp +
    • Pause +
    • Print +
    • Redo +
    • Return +
    • Right +
    • ScrollLock +
    • Select +
    • Sleep +
    • Space +
    • SysReq +
    • Tab +
    • Undo +
    • Up +
    • WWWBack +
    • WWWFavorites +
    • WWWForward +
    • WWWHome +
    • WWWRefresh +
    • WWWSearch +
    • WWWStop +
    • Space +
    • Exclaim +
    • DoubleQuote +
    • Number +
    • Dollar +
    • Percent +
    • Ampersand +
    • Apostrophe +
    • ParenthesisL +
    • ParenthesisR +
    • Asterisk +
    • Plus +
    • Comma +
    • Minus +
    • Period +
    • Slash +
    • Colon +
    • Semicolon +
    • Less +
    • Equal +
    • Greater +
    • Question +
    • At +
    • BracketL +
    • Backslash +
    • BracketR +
    • Circumflex +
    • Underscore +
    • Grave +
    • BraceL +
    • Bar +
    • BraceR +
    • Tilde +
    +Additionally, a name of the form \uXXXX where +XXXX is a hexadecimal number is interpreted as +a unicode character code. +Key and modifier names are case-insensitive. Keys that don't exist on +the keyboard or in the default keyboard layout will not work.

    + diff --git a/doc/contact.html b/doc/contact.html new file mode 100644 index 00000000..1b378b85 --- /dev/null +++ b/doc/contact.html @@ -0,0 +1,44 @@ + + + + + + + + Synergy Contact Info + + +

    +Use the following addresses to contact the synergy project: +

    + + + + + + + + + + + + + + + + +
    Bug reports: Add Synergy Bug
    Help: synergy-help@groundhog.pair..no_spamcom
    General: crs23@users.sourceforge..no_spamnet
    +

    +To avoid spam bots, the above email addresses have ".no_spam" +hidden near the end. If you copy and paste the text be sure to +remove it. +

    +Please check the + +bug list before reporting a bug. You may also find answers at the +synergy forums. +Emails for help asking questions answered on this site will go unanswered. +

    + + + diff --git a/doc/developer.html b/doc/developer.html index 7391dd2a..acfdff9a 100644 --- a/doc/developer.html +++ b/doc/developer.html @@ -1,27 +1,27 @@ - + - - Synergy Developer's Guide + + + + + Synergy Developer Documentation -

    Developer's Guide

    Synergy is reasonably well commented so reading the source code should be enough to understand particular pieces. See the -doc/PORTING +doc/PORTING file in the synergy source code for more high-level information. -

    - -

    How it works

    -

    +

    +

    How it works

    +

    The theory behind synergy is simple: the server captures mouse, keyboard, clipboard, and screen saver events and forwards them to one or more clients. If input is directed to the server itself then the input is delivered normally. In practice, however, many complications arise. -

    -

    +

    First, different keyboard mappings can produce different characters. Synergy attempts to generate the same character on the client as would've been generated on the server, including appropriate modifier @@ -34,8 +34,7 @@ keyboard. For example, if the client or server can't distinguish between the left and right shift keys then synergy can't be certain to synthesize the shift on the same side of the keyboard as the user pressed. -

    -

    +

    Second, different systems have different clipboards and clipboard formats. The X window system has a system-wide selection and clipboard (and yet other buffers) while Microsoft Windows has only @@ -43,15 +42,13 @@ a system-wide clipboard. Synergy has to choose which of these buffers correspond to one another. Furthermore, different systems use different text encodings and line breaks. Synergy mediates and converts between them. -

    -

    +

    Finally, there are no standards across operating systems for some operations that synergy requires. Among these are: intercepting and synthesizing events; enabling, disabling, starting and stopping the screen saver; detecting when the screen saver starts; reading and writing the clipboard(s). -

    -

    +

    All this means that synergy must be customized to each operating system (or windowing system in the case of X windows). Synergy breaks platform differences into two groups. The first includes @@ -60,25 +57,25 @@ multithreading, network I/O, multi-byte and wide character conversion, time and sleeping, message display and logging, and running a process detached from a terminal. This code lives in lib/arch. -

    -

    +

    The second includes screen and window management handling, user event handling, event synthesis, the clipboards, and the screen saver. This code lives in lib/platform. -

    -

    +

    For both groups, there are particular classes or interfaces that must be inherited and implemented for each platform. See the -doc/PORTING file in -the synergy source code for more information. -

    -

    Auto-generated Documentation

    -

    +doc/PORTING file in the synergy source +code for more information. +

    +

    Auto-generated Documentation

    +

    Synergy can automatically generate documentation from the comments -in the code using doxygen. -Use "make doxygen" to build it yourself +in the code using doxygen. +Use make doxygen to build it yourself from the source code into the doc/doxygen/html directory.

    +

    + diff --git a/doc/faq.html b/doc/faq.html index 1b355594..b9696391 100644 --- a/doc/faq.html +++ b/doc/faq.html @@ -1,14 +1,18 @@ - + - - Synergy FAQ + + + + + Synergy Frequently Asked Questions +

    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? @@ -27,27 +31,42 @@
      4. I get the error 'Xlib: No protocol specified'. Why?
      5. The cursor goes to secondary screen but won't come back. Why?
      6. The cursor wraps from one edge of the screen to the opposite. Why? +
      7. How do I stop my game from minimizing when I leave the screen?
      -

      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 + primary screens 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.) + must be configured to autostart when the computer starts for + this to work.) +

          + On a primary screen running on an OS X system, you can use + ctrl+command+del. Using the pause key isn't necessary since OS X + doesn't treat ctrl+command+del differently. And using the pause + key isn't usually possible because there isn't one on most OS X + systems. Use command instead of option/alt because + the command key, not the option/alt key, maps to alt on windows. + The reason is because the command key is in the same physical + location and performs the same general function (menu shortcuts) + as alt on a windows system. This mapping can be modified in + the configuration. +

          + On mac laptops, the key labeled "delete" is actually backspace + and ctrl+command+delete won't work. However fn+delete really + is delete so fn+ctrl+command+delete will act as ctrl+alt+del + on a windows secondary screen.

          -
        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?

          @@ -60,7 +79,6 @@ 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 @@ -70,7 +88,6 @@ 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. @@ -78,33 +95,30 @@ 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:server-hostname:24800 server-hostname
           
          -

          +

          where server-hostname is the name of the - SSH/synergy server. + 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 is - OpenSSH. For + OpenSSH. For Windows there's a port of OpenSSH using - Cygwin. + Cygwin.

          -
        6. What should I call my screens in the configuration?

          You can use any unique name in the configuration file for each @@ -115,8 +129,7 @@ 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. -

          -

          +

          Some systems are configured to report the fully qualified domain name as the hostname. For those systems it will be easier to use the FQDN as the screen name. Also note that a Mac OS X system @@ -124,32 +137,29 @@ xyz.local. If that's the case for you then use xyz.local as the screen name.

          -
        7. Why do my Caps-Lock, Num-Lock, Scroll-Lock keys act funny?

          Some systems treat the Caps-Lock, Num-Lock, and Scroll-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 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. -

          -

          + 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 any or all of these lines as appropriate: -

          + problem, any or all of these lines as appropriate: +

                   halfDuplexCapsLock = true
                   halfDuplexNumLock = true
                   halfDuplexScrollLock = true
           
          -

          +

          Then restart synergy on the server or reload the configuration.

          -
        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) @@ -157,13 +167,11 @@ 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/ISO_Level3_Shift work?

          Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. @@ -173,7 +181,6 @@ layout cannot be generated by synergy.) There is experimental support for ISO_Level3_Shift in 1.1.3.

          -
        11. Why isn't synergy ported to platform XYZ?

          Probably because the developers don't have access to platform XYZ @@ -181,7 +188,6 @@ 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 @@ -192,36 +198,33 @@ 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. + host name/address or port. See the + troublshooting page for more help.

          -
        13. Linking fails on Solaris. What's wrong?

          - Did you add -

          + Did you add +

                   --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib
           
          -

          +

          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 should not happen with 1.1.3 and up. Earlier versions of synergy would not allow switching screens when a key was down and sometimes it would believe a key was down when it was not.

          -
        16. I get the error 'Xlib: No protocol specified'. Why?

          You're running synergy without authorization to connect to the @@ -229,7 +232,6 @@ logged in as non-root. Just run synergy as the same user that's logged in.

          -
        17. The cursor goes to secondary screen but won't come back. Why?

          Your configuration is incorrect. You must indicate the neighbors @@ -237,7 +239,6 @@ the left of 'Orange' does not mean that 'Orange' is to the right of 'Apple'. You must provide both in the configuration.

          -
        18. The cursor wraps from one edge of the screen to the opposite. Why?

          Because you told it to. If you list 'Orange' to be to the left of @@ -245,6 +246,21 @@ make it jump to the right edge. Remove the offending line from the configuration if you don't want that behavior.

          - +
        19. How do I stop my game from minimizing when I leave the screen? +

          + Many full screen applications, particularly games, automatically + minimize when they're no longer the active (foreground) application + on Microsoft Windows. The synergy server normally becomes the foreground + when you switch to another screen in order to more reliably capture all + user input causing those full screen applications to minimize. To + prevent synergy from stealing the foreground just click "Options..." + and check "Don't take foreground window on Windows servers." If you + turn this on then be aware that synergy may not function correctly when + certain programs, particularly the command prompt, are the foreground + when you switch to other screens. Simply make a different program the + foreground before switching to work around that. +

          +
        + diff --git a/doc/history.html b/doc/history.html index dedbb016..48f921c3 100644 --- a/doc/history.html +++ b/doc/history.html @@ -1,12 +1,16 @@ - + - - Synergy History + + + + + Synergy History -

        Synergy History

        +

        Synergy History

        +

        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 @@ -16,11 +20,11 @@ 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/doc/home.html b/doc/home.html new file mode 100644 index 00000000..df0775db --- /dev/null +++ b/doc/home.html @@ -0,0 +1,61 @@ + + + + + + + + Synergy + + +

        +

        Introduction

        +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 monitor(s). +

        +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. Learn more +about how it works. +

        +Synergy is open source and released under the +GNU Public License (GPL). +

        +

        System Requirements

        +

        +

          +
        • Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family) +
        • Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family) +
        • Mac OS X 10.2 or higher +
        • Unix +
            +
          • X Windows version 11 revision 4 or up +
          • XTEST extension
            + (use "xdpyinfo | grep XTEST" to check for XTEST) +
          +
        +All systems must support TCP/IP networking. +

        +"Unix" includes Linux, Solaris, Irix and other variants. Synergy has +only been extensively tested on Linux and may not work completely or +at all on other versions of Unix. Patches are welcome (including +patches that package binaries) at the +patches page. +

        +The Mac OS X port is incomplete. It does not synchronize the screen saver, +only text clipboard data works (i.e. HTML and bitmap data do not work), +the cursor won't hide when not on the screen, and there may be problems +with mouse wheel acceleration. Other problems should be +filed as bugs. +

        + + + diff --git a/doc/index.html b/doc/index.html index 22a0dfe7..9e9ef302 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,96 +1,33 @@ - + + + + - Synergy Introduction + Synergy - -

        Synergy

        -

        Introduction

        -

        -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 monitor(s). -

        -

        -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). -

        -

        Links

        -

        -Local
        -Getting started:
        -how to run synergy
        -how to build synergy
        -
        -Using synergy:
        -FAQ
        -tips on using synergy
        -autostart guide
        -configuration file format guide
        -
        -Future directions:
        -roadmap to future enhancements
        -
        -For developers:
        -developer's guide
        -
        -Security:
        -important note about security with synergy
        -
        -Miscellaneous documents:
        -the authors of synergy
        -the history of synergy
        -the synergy license terms
        -news about synergy
        -
        -Internet
        -synergy home page
        -synergy project page
        -synergy bug list
        -synergy community forums
        -

        -

        System Requirements

        -

        -

          -
        • Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family) -
        • Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family) -
        • Mac OS X 10.2 or higher -
        • Unix -
            -
          • X Windows version 11 revision 4 or up -
          • XTEST extension
            - (use "xdpyinfo | grep XTEST" to check for XTEST) -
          -
        -All systems must support TCP/IP networking. -

        -

        -"Unix" includes Linux, Solaris, Irix and other variants. Synergy has -only been extensively tested on Linux and may not work completely or -at all on other versions of Unix. Patches are welcome (including -patches that package binaries) at the -patches page. -

        -

        -The Mac OS X port is incomplete. It does not synchronize the screen saver, -only text clipboard data works (i.e. HTML and bitmap data do not work), -non-US English keyboards are untested and probably don't work, and there -may be problems with mouse pointer and mouse wheel acceleration. Other -problems should be filed as bugs. -

        - + + diff --git a/doc/license.html b/doc/license.html index 0c4eaa0b..5e748123 100644 --- a/doc/license.html +++ b/doc/license.html @@ -1,25 +1,29 @@ - + - - Synergy License and Copyright + + + + + Synergy License and Copyright -

        Synergy License and Copyright

        +

        Synergy License and Copyright

        +

        Synergy is copyright (C) 2002 Chris Schoeneman.
        Synergy is distributed under the GNU GENERAL PUBLIC LICENSE. -

        -

        GNU GENERAL PUBLIC 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

        -

        +

        +

        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 @@ -29,55 +33,48 @@ 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

        -

        +

        +

        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, @@ -87,16 +84,14 @@ 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 @@ -104,31 +99,26 @@ 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 @@ -139,10 +129,9 @@ above, provided that you also meet all of these conditions: 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 @@ -152,50 +141,43 @@ 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 @@ -206,15 +188,13 @@ 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 @@ -222,8 +202,7 @@ 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 @@ -232,8 +211,7 @@ 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 @@ -241,8 +219,7 @@ 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 @@ -255,14 +232,12 @@ 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 @@ -273,12 +248,10 @@ 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 @@ -286,14 +259,12 @@ 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 @@ -301,8 +272,7 @@ 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 @@ -310,11 +280,9 @@ 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 @@ -325,8 +293,7 @@ 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 @@ -338,9 +305,9 @@ 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/doc/news.html b/doc/news.html index fac5f2a8..b3729ec6 100644 --- a/doc/news.html +++ b/doc/news.html @@ -1,37 +1,179 @@ - + - - Synergy News + + + + + Synergy News -

        Synergy News

        - -Jan-26-2005 - Synergy 1.2.2 released -

        +Apr-02-2006 - Synergy 1.3.1 released +

        +Made following changes: +

          +
        • Hot key screen switching now restores last cursor position +
        • Fixed loss of hot keys when reloading configuration +
        • Fixed autorepeating on win32 (no longer sending repeating key releases) +
        • Fixed autorepeating on X11 (non-repeating keys were repeating) +
        • Fixed AltGr issues on X11 +
        • Fixed modifier mapping bug on OS X client (caused wrong characters) +
        • Fixed one way for modifiers to get stuck active on all platforms +
        • Fixed bugs in win32 GUI +
        • Removed alloca() from unix code (should fix FreeBSD build) +
        • Added more debugging output for network problems +
        • Fixed failure to detect some errors on X11 +
        +

        +Mar-22-2006 - Synergy 1.3.0 released +

        +Made following additions: +

          +
        • Console window on win32 can now be closed (reopened from tray menu) +
        • Can now change logging level on the fly from win32 tray menu +
        • Added client keep alive (lost connections are now detected reliably) +
        • Added support for linking portions of screen edges +
        • Added version number to UI in win32 +
        • Added GUI for hot key configuration on win32 +
        • Hot keys can now perform actions on press and/or release +
        • Added key down, key up, mouse down, and mouse up hot key actions +
        • Key actions can be directed to particular screens +
        • Hot keys can each perform multiple actions +
        +

        +Made following fixes: +

          +
        • Fixed AltGr key mappings (again) +
        • Fixed assertion when pasting on X11 +
        • Fixed modifier keys in VMware on X11 +
        • OS X server now treats sends option/alt as AltGr or super depending on key +
        • Improved handling of AltGr on win32 +
        • Fixed not removing client when connection is lost +
        • Clients now detect loss of connection to server and reconnect +
        • Fixed mouse jumping on OS X multimonitor systems +
        • Closing console on win32 no longer quits synergy +
        • Fixed Num Lock breaking certain keys +
        • Fixed Scroll Lock not locking cursor to screen +
        • Fixed mapping of delete key on X11 +
        • Fixed loss of clipboard after a particular copy/paste sequence +
        • Fixed compatibility with windows 95/98/Me (ToUnicodeEx) +
        • Fixed bad argument to function on OS X +
        • Fixed error parsing comments in configuration +
        • Fixed autorepeat on win32 servers +
        • Fixed X11 keyboard focus bug when reentering screen +
        • Fixed (suppressed) hot key autorepeating +
        • Fixed mousebutton action when Caps/Num/Scroll Lock were on +
        • Added documentation on firewalls +
        • Fixed documentation formatting on IE6 +
        +

        +Hot keys support has one known major bug: key actions cannot be directed +to the server (primary) screen. The configuration file syntax has changed +from earlier versions; users will have to modify the configurations by +hand. +

        +Dec-18-2005 - Synergy 1.2.7 released +

        +Made following changes: +

          +
        • Added preliminary support for configurable hot keys (Lorenz Schori) +
        • Major rewrite of keyboard handling code +
        • Fixed non-US keyboard handling (AltGr and ISO_Level3_Shift) +
        • Now supporting all installed keyboard layouts simultaneously +
        • Fixed bug in handling remapped caps-lock on X11 +
        • Fixed control and alt keys getting stuck on on X11 +
        • Fixed desktop focus problems requiring extra clicks on win32 +
        • Fixed alt key event getting passed to server when on client on win32 +
        • Synergy would prevent alt+numpad character entry; this is fixed +
        • Fixed suppression of xscreensaver 2.21 on X11 +
        • Fixed middle mouse button dragging on OSX server (Brian Kendall) +
        • Fixed caps/num/scroll lock toggles getting out of sync +
        • Enhanced support for converting clipboard text to the Latin-1 encoding +
        • Added autostart documentation for KDE users +
        • Added more details about using Terminal for OSX users +
        • Fixed crash when using --help on certain platforms +
        +

        +The hot key support is known to have bugs. The configuration file +syntax for hot keys is likely to change and the documentation for it +is minimal. The graphical UI on windows does not provide any support +for editing hot keys. +

        +Nov-12-2005 - Synergy 1.2.6 released +

        +Made following changes: +

          +
        • Fixed permission problem saving autostart configuration in win32 launcher +
        • Disabled buggy fix for loss of clipboard change detection +
        • Restored pthread signal autoconf code +
        +

        +Oct-17-2005 - Synergy 1.2.5 released +

        +Made following changes: +

          +
        • Win32 launcher now saves configuration automatically +
        • Fixed failure to save autostart configuration on win32 +
        • Fixed output bottom-right configuration flag +
        • Now properly releasing keys when leaving a client screen +
        • Fixed stuck-Alt on win32 +
        • Fixed 64-bit problem with clipboard on X11 +
        • Fixed BadAtom bug on X11 +
        • Fixed loss of detection of clipboard changes on win32 +
        • Added support for the MightyMouse +
        • Added support for buttons 4 and 5 on OSX +
        • Now shutting down win32 services when uninstalling +
        +

        +Aug-07-2005 - Synergy 1.2.4 released +

        +Made following changes: +

          +
        • Fixed gcc 4.0 warnings +
        • Fixed autoconf/automake problems +
        • Fixed scroll-lock on X windows +
        • Added option to suppress foreground window grabbing on win32 +
        • Fixed --daemon option on win32 client +
        • Fixed --no-restart on client +
        • Updated OS X autostart documentation +
        +

        +Jul-27-2005 - Synergy 1.2.3 released +

        +Made following changes: +

          +
        • Added OS X screensaver synchronization support (Lorenz Schori) +
        • Added OS X sleep support (Lorenz Schori) +
        • Added OS X fast user switching support (Lorenz Schori) +
        • Fixed international keyboard support on OS X (Lorenz Schori) +
        • Now capturing global hotkeys (e.g. cmd+tab, etc) on OS X (Lorenz Schori) +
        • Added support for SO_REUSEADDR (Don Eisele) +
        • Added "dead" corners feature +
        • Fixed "resource temporarily unavailable" warning when quiting on OS X +
        • Win32 now defaults to WARNING log level to avoid console window +
        • Now disabling foreground window on win32 when leaving server (Brent Priddy) +
        +

        +Jan-26-2005 - Synergy 1.2.2 released +

        Made following changes: -

        • Fixed major OS X modifier key handling bug
        • Fixed handling of ISO_Level3_Shift on X11
        - +

        Jan-04-2005 - Synergy 1.2.1 released - -

        +

        Made following changes: -

        • Fixed major OS X keyboard handling bug
        • Fixed some minor documentation bugs
        - +

        Dec-30-2004 - Synergy 1.2.0 released - -

        +

        Made following changes: -

        • Improved support for moving laptops between networks (Brent Priddy)
        • Added ISO_Level3_Shift support on X windows @@ -42,23 +184,19 @@ Made following changes:
        • Fixed memory leak on OS X
        • Added OS X autostart documentation (Tor Slettnes)
        - +

        Nov-12-2004 - Synergy 1.1.10 released - -

        +

        Made following changes: -

        • Fixed race in condition variable wrapper; caused synergy to hang randomly -
        • Fixed modifier key and caps-lock handling on OS X +
        • Fixed modifier key and caps-lock handling on OSX
        • System info log message now filtered like all other messages
        - +

        Nov-07-2004 - Synergy 1.1.9 released - -

        +

        Made following changes: -

        • Fixed compiler error on gcc 3.4 and later
        • Worked around minor gcc -O3 compiler bug @@ -89,12 +227,10 @@ Made following changes:
        • Added -display option for X11
        • Added support for X11 compose key (Multi_key)
        - +

        Aug-05-2004 - Synergy 1.1.8 released - -

        +

        Made following changes: -

        • Removed key event capture on X11 (was breaking terminal keyboard input)
        • Worked around win32 command prompt stealing shift key events @@ -110,12 +246,10 @@ Made following changes:
        • Fixed mouse wheel drift on OS X client
        • Reorganized documentation and converted to HTML
        - +

        Jun-13-2004 - Synergy 1.1.7 released - -

        +

        Made following changes: -

        • Added OS X precompiled header file forgotten in last build
        • Fixed bug in fix for 'unexpected async reply' on X11 @@ -125,12 +259,10 @@ Made following changes:
        • Fixed error in conversion from multibyte to wide characters
        • Maybe fixed win32 screen saver detection
        - +

        May-26-2004 - Synergy 1.1.6 released - -

        +

        Made following changes: -

        • Added preliminary Mac OS X support (client and server)
        • Fixed ctrl+alt+del emulation on win32 @@ -141,21 +273,17 @@ Made following changes:
        • Fixed reference count bug
        • Keyboard input focus now restored on X11 (fixes loss of input in some games)
        - -

        +

        The OS X port does not yet support: -

        • HTML and bitmap clipboard data
        • Screen saver synchronization
        • Non-US English keyboards
        - +

        May-05-2004 - Synergy 1.1.5 released - -

        +

        Made following changes: -

        • No longer switching screens when a mouse button is down
        • Worked around win32 mouse hook bug, fixing switch on double tap @@ -171,24 +299,20 @@ Made following changes:
        • Partial support for MSYS/MinGW builds (NOT COMPLETE)
        • Partial merge of OS X port (NOT COMPLETE)
        - +

        Mar-31-2004 - Synergy 1.1.4 released - -

        +

        Made following changes: -

        • Fixed lookup of hosts by name of win32
        • Reverted tray icon code to 1.0.15 version; seems to fix the bugs
        • Fixed crash when caps, num, or scroll lock not in key map on X11
        • Fixed double tap and wait to switch features
        - +

        Mar-28-2004 - Synergy 1.1.3 released - -

        +

        Made following changes: -

        • Major code refactoring; reduced use of threads, added event queue
        • Removed unused HTTP support code @@ -199,17 +323,13 @@ Made following changes:
        • Improved keyboard handling and bug fixes
        • Fixed dead key handling
        - -

        +

        Note: the tray icon on windows is known to not work correctly when running the synergy server on Windows 95/95/Me. -

        - +

        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 @@ -217,20 +337,18 @@ Made following changes:
        • 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 - -

        +

        +Made following changes: +

        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 @@ -244,12 +362,11 @@ Made following changes:
        • 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 @@ -268,12 +385,48 @@ Made following changes:
        • Added support for "Internet" and "Multimedia" keys
        • Fixed jumping from client to itself (mouse wrapping)
        - +

        +Apr-26-2003 - Added roadmap +

        +There's now a roadmap for Synergy +describing the plans for further development. +

        +Apr-26-2003 - Added Paypal donation page +

        +There's now a donate button for those +who'd like to make a monetary contribution to the further +development of Synergy. +

        +Apr-26-2003 - Development update +

        +Synergy 1.0.8 will include fixes for the following problems. +These are already fixed and some are in development version 1.0.7. +

        +

          +
        • Mouse events at edge of screen are stolen +
        • Windows key doesn't work on clients +
        • Alt+[Shift+]Tab, Alt+[Shift+]Esc, Ctrl+Esc don't work on Win 95/98/Me +
        • Scroll lock doesn't lock to Windows server screen +
        • Screen flashes every 5 seconds on some X11 systems +
        • Synergy doesn't work properly with Xinerama +
        • Screen names with underscores are not allowed +
        +

        +Synergy 1.0.8 will probably include fixes for these problems: +

        +

          +
        • AltGr/Mode_switch doesn't work +
        • Non-ASCII keys aren't supported +
        • Synergy performs badly on a busy Windows system +
        • Unexpected key repeats on X11 clients +
        +

        +Synergy 1.0.8 should be available in the first half of May. +

        Mar-27-2003 - Synergy 1.0.6 released - -

        +

        Made following changes: -

        +

        • Added tray icon on win32
        • Fixed multi-monitor support on win32 @@ -285,14 +438,33 @@ Made following changes:
        • Fixed problem sending the CLIPBOARD to motif/lesstif apps
        • Win32 launcher now remembers non-config-file state
        - +

        +In addition, the version number scheme has been changed. Given a +version number X.Y.Z, release versions will always have Y and Z +even while development versions will have Y and Z odd. +

        +Mar-27-2003 - Synergy featured in Linux Journal. +

        +The April 2003 issue of Linux Journal includes an article on Synergy. +Written by Chris Schoeneman, it describes configuring synergy between +two linux systems. +

        +Mar-27-2003 - Contributions to Synergy. +

        +Many thanks to Girard Thibaut for providing a version of the win32 +launch dialog translated into French. I hope to integrate these +changes into future releases. +

        +Thanks also to "wrhodes" who provided source files for +building an InstallShield installer for Synergy. They'll be +integrated into an upcoming release. +

        Feb-18-2003 - Synergy 1.0.3 released - -

        +

        Made following changes: -

        +

          -
        • Support for X11 keymaps with only uppercase letters +
        • Added 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 @@ -300,31 +472,28 @@ Made following changes:
        • 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) @@ -333,19 +502,16 @@ Made following changes:
        • 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) @@ -358,12 +524,11 @@ Made following changes:
        • 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) @@ -376,12 +541,11 @@ Made following changes:
        • 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 @@ -389,51 +553,47 @@ Made following changes:
        • 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 +
        • The Pause/Break and keypad 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/doc/roadmap.html b/doc/roadmap.html new file mode 100644 index 00000000..7f8681bc --- /dev/null +++ b/doc/roadmap.html @@ -0,0 +1,92 @@ + + + + + + + + Synergy Roadmap + + +

        +

        Synergy Roadmap

        +

        +This page describes the planned development of Synergy. There are +no dates or deadlines. Instead, you'll find the features to come +and the rough order they'll arrive. +

        +

        Short term

        +

        +Synergy should work seamlessly. When it works correctly, it works +transparently so you don't even think about it. When it breaks, +you're forced out of the illusion of a unified desktop. The first +priority is fixing those bugs that break the illusion. +

        +Some of these bugs are pretty minor and some people would rather +have new features first. But I'd rather fix the current +foundation before building on it. That's not to say features +won't get added until after bug fixes; sometimes it's just too +tempting to code up a feature. +

        +The highest priority feature is currently splitting synergy into +front-ends and a back-end. The back-end does the real work. The +front-ends are console, GUI, or background applications that +communicate with the back-end, either controlling it or receiving +notifications from it. +

        +On win32, there'd be a front-end for the tray icon and a dialog to +start, stop, and control the back-end. OS X and X11 would have +similar front-ends. Splitting out the front-end has the added +benefit on X11 of keeping the back-end totally independent of +choice of GUI toolkit (KDE, Gnome, etc.) +

        +One can also imagine a front-end that does nothing but put monitors +into power-saving mode when the cursor is not on them. If you have +one monitor auto-senses two inputs, this would automatically switch +the display when you move the cursor to one screen or another. +

        +

        Medium term

        +

        +Some features fit well into Synergy's current design and may simply +enhance it's current capabilities. +

        +

          +
        • Configurable hot key to pop up a screen switch menu +
        • Configure screen saver synchronization on or off +
        • Graphical interface configuration and control on all platforms +
        • Graphical status feedback on all platforms +
        • More supported clipboard formats (particularly rich text) +
        +

        +A popup menu would be new for Synergy, which currently doesn't have +to do any drawing. That opens up many possibilities. Ideally, +front-ends request hot keys from the back-end and then tell the back +end what to do when they're invoked. This keeps the back-end +independent of the user interface. +

        +

        Long term

        +

        +Two features stand out as long term goals: +

        +

          +
        • Support N computers on +M monitors +
        • Drag and drop across computers +
        +

        +The first feature means sharing a monitor or monitors the way the +keyboard and mouse are shared. With this, Synergy would be a full +KVM solution. Not only would it support a few computers sharing +one screen (still using the mouse to roll from one screen to +another), but it should also support dozens of computers to provide +a solution for server farm administrators. In this capacity, it +may need to support text (as opposed to bitmap graphics) screens. +

        +The second feature would enhance the unified desktop illusion. It +would make it possible to drag a file and possibly other objects +to another screen. The object would be copied (or moved). I expect +this to be a very tricky feature. +

        + + + diff --git a/doc/running.html b/doc/running.html index 6f6ced05..4ad2d594 100644 --- a/doc/running.html +++ b/doc/running.html @@ -1,69 +1,72 @@ - + - - Guide to Running Synergy + + + + + Synergy User Guide -

        Running Synergy

        +

        Running Synergy

        +

        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. -

        - -

        Step 1 - Choose a server

        -

        +

        +

        Step 1 - Choose a server

        +

        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. -

        - -

        Step 2 - Install the software

        -

        +

        +

        Step 2 - Install the software

        +

        Second, you install the software. Choose the appropriate package and install it. For example, on Windows you would run SynergyInstaller. You must install the -software on all the computers that will share the mouse and keyboard. -

        - -

        Step 3 - Configure and start the server

        -

        +software on all the computers that will share the mouse and keyboard +(clients and server). On OS X you'll just have a folder with some +documentation and two programs. You can put this folder anywhere. +

        +

        Step 3 - Configure and start the server

        +

        Next you configure the server. You'll tell synergy the name of the primary and secondary screens, which screens are next to which, and choose desired options. On Windows there's a dialog box for setting the configuration. On other systems you'll create a simple text file. -

        -

        +

        + Note that when you tell synergy that screen A is to the left of screen B this does not imply that B is to the right of A. You must explicitly indicate both relations. If you don't do both then when you're running synergy you'll find you're unable to leave one of the screens. -

        -

        +

        Windows
        On Windows run synergy by double clicking on the synergy file. This brings up a dialog. Configure the server:

          -
        • Click the Server radio button -
        • Click Add to add the server to the +
        • Click the Share this computer's keyboard and mouse (server) radio button +
        • Click the Screens & Links Configure... button +
        • Click the + button to add the server to the Screens list
          • Enter the name of server (the computer's name is the recommended name)
          • Optionally enter other names the server is known by
          • Click OK
          -
        • Use Add to add your other computers +
        • Use the + button to add your other computers
          • Using a computer's name as its screen name is recommended -
          • Choose desired screen options on the Add dialog +
          • Choose desired screen options on the Add Screen dialog
          -
        • Use the controls under Layout to link screens together +
        • Use the controls under Links 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 --- @@ -71,6 +74,7 @@ Configure the server:
          • Choose the screens to the right, above and below the server
          • Repeat the above steps for all the other screens
          +
        • Click OK to close the Screens & Links dialog
        • Use Options... to set desired options
        • If the server's screen name is not the server's computer name:
            @@ -80,16 +84,14 @@ Configure the server:
          • 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 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. -

        - -

        +correct them and try Test again. See Step 5 +for typical errors. +

        Unix or Mac OS X
        Create a text file named synergy.conf with the following: @@ -119,34 +121,41 @@ 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. See the configuration guide for more configuration possibilities. -

        -

        +

        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. +

        +On unix type the command below in a shell. If synergys is not in your +PATH then use the full pathname.

             synergys -f --config synergy.conf
         
        +On OS X open Terminal in the Utilities folder in the Applications folder. +Drag the synergys program from the synergy folder onto the Terminal window. +The path to the synergys program will appear. Add the following to the +same line, type a space at the end of the line but don't press enter: +
        +    -f --config 
        +
        +Now drag the synergy.conf file onto the Terminal window and press enter. Check the reported messages for errors. Use ctrl+c to stop synergy if it didn't stop automatically, correct any problems, and start it again. -

        - -

        Step 4 - Start the clients

        -

        +

        +

        Step 4 - Start the clients

        +

        Next you start the client on each computer that will share the server's keyboard and mouse. -

        -

        +

        Windows
        On Windows run synergy by double clicking on the synergy file. This brings up a dialog. Configure the client: -

          -
        • Click the Client radio button -
        • Enter the server's computer name in Server Host Name +
        • Click the Use another computer's shared keyboard and mouse (client) radio button +
        • Enter the server's computer name next to Other Computer's Host Name
          • This is not the server's screen name, unless you made that the server's host name as recommended @@ -158,45 +167,81 @@ Configure the client:
          • Click OK
        -

        -

        +

        Now click Test. -

        -

        +

        Unix or Mac OS X
        -To start a client, enter the following: +To start a client on unix, enter the following:

             synergyc -f server-host-name
         
        where server-host-name is replaced by the host -name of the computer running the synergy server. -

        -

        Step 5 - Test

        -

        +name of the computer running the synergy server. If synergyc is not in +your PATH then use the full pathname. +

        +On OS X open Terminal in the Utilities folder in the Applications folder. +Drag the synergyc program from the synergy folder onto the Terminal window. +The path to the synergys program will appear. Add the following to the +same line and press enter: +

        +    -f server-host-name
        +
        +

        +When you added the client to the server's configuration you chose a +name for the client. If that name was not client's host name then +you must tell the client the name you used. Instead of the above +command use this instead: +

        +    synergyc -f --name name server-host-name
        +
        +where name is the name for the client in +the server's configuration. (On OS X drag the synergyc program to the +Terminal window rather than typing synergyc.) +

        +

        Step 5 - Test

        +

        Clients should immediately report a successful connection or one or -more error messages. Here are the typical problems and possible -solutions: +more error messages. Some typical problems and possible solutions are +below. See the troubleshooting and the +FAQ pages for more help.

        • failed to open screen (X11 only) -

          +

          Check permission to open the X display;
          - check that the DISPLAY environment variable is set;
          + check that the DISPLAY environment variable is set
          use the --display command line option. -

          +

          +

        • address already in use +

          + Another program (maybe another copy of synergy) is using the synergy port; + stop the other program or choose a different port in the + Advanced... dialog. If you change the port + you must make the same change on all of the clients, too. +

          +

        • connection forcefully rejected +

          + The synergy client successfully contacted the server but synergy wasn't + running or it's running on a different port. You may also see this if + there's a firewall blocking the host or port. Make sure synergy is + running on the server and check for a firewall. +

        • already connected -

          +

          Check that the synergy client isn't already running. -

          +

        • refused client -

          +

          Add the client to the server's configuration file. -

          +

          +

        • connection timed out +

          + Check that server-host-name is correct.
          + Check that you don't have a firewall blocking the server or synergy port. +

        • connection failed -

          - check that server-host-name is - correct;
          the server cannot open the desired port, stop - the program using that port (24800) and restart the server. -

          +

          + Check that server-host-name is correct. +

        If you get the error "Xlib: No protocol specified" you're probably running synergy as root while logged in as another user. @@ -204,33 +249,34 @@ X11 may prevent this for security reasons. Either run synergy as the same user that's logged in or (not recommended) use "xhost +" to allow anyone to connect to the display. -

        -

        +

        When successful you should be able to move the mouse off the appropriate edges of your server's screen and have it appear on a client screen. Try to move the mouse to each screen and check all the configured links. Check the mouse buttons and wheel and try the keyboard on each client. You can also cut-and-paste text, HTML, and images across computers (HTML and images are not supported on OS X yet). -

        -

        Step 6 - Run

        -

        +

        +

        Step 6 - Run

        +

        Once everything works correctly, stop all the clients then the server. Then start the server with the Start button on Windows and without the -f option on Unix -and Mac OS X. Finally start the clients similarly. -

        -

        +and Mac OS X. Finally start the clients similarly. On Windows before +clicking Start you may want to set the +Logging Level to +Warning so the logging window doesn't pop +up (because you currently can't close it, just minimize it). +

        You can also configure synergy to start automatically when your computer starts or when you log in. See the autostart guide for more information. -

        -

        Command Line Options Guide

        -

        +

        +

        Command Line Options Guide

        +

        Common Command Line Options
        The following options are supported by synergys and synergyc. -

        @@ -242,16 +288,16 @@ and synergyc. - - - - - + + + + + @@ -276,10 +322,9 @@ and synergyc. - +
         -d,--daemon run as a daemon (Unix) or background (Windows)
          --display display  connect to X server at display (X11 only)
         -f, --no-daemon run in the foreground
          --display display  connect to X server at display (X11 only)
         -n, --name name  --version print version information and exit
        -

        -

        +

        Debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, INFO, @@ -291,28 +336,24 @@ 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 (usually because it died). -

        -

        +

        Server Command Line Options
        -

        +

             synergys [options]
         
        The server accepts the common options and: -

        -

        +

        @@ -325,8 +366,7 @@ The server accepts the common options and:
         -a,  read configuration from pathname
        -

        -

        +

        address has one of the following forms:

             hostname
        @@ -338,10 +378,9 @@ interface on the server system (e.g. somehost
         or 192.168.1.100).  port
         is a port number from 1 to 65535.  hostname defaults to
         the system's hostname and port defaults to 24800.
        -

        -

        +

        Client Command Line Options
        -

        +

             synergyc [options] address[:port]
         
        @@ -351,4 +390,5 @@ port on the server to connect to. The client accepts the common options.

        + diff --git a/doc/security.html b/doc/security.html index db8ad3f5..c8013c27 100644 --- a/doc/security.html +++ b/doc/security.html @@ -1,39 +1,37 @@ - + - - Synergy Security Guide + + + + + Synergy Network Security Guide -

        Authentication and Encryption

        +

        Authentication and Encryption

        Synergy does not do any authentication or encryption. Any computer can connect to the synergy server if it provides a screen name known to the server, and all data is transferred between the server and the clients unencrypted which means that anyone can, say, extract the key presses used to type a password. Therefore, synergy should not be used on untrusted networks. -

        -

        +

        However, there are tools that can add authentication and encryption to synergy without modifying either those tools or synergy. One such tool is SSH (which stands for secure shell). A free implementation -of SSH is called OpenSSH and runs +of SSH is called OpenSSH and runs on Linux, many Unixes, and Windows (in combination with -Cygwin). -

        - -

        Configuring the Server

        -

        +Cygwin). +

        +

        Configuring the Server

        Install the OpenSSH server on the same computer as the synergy server. Configure the OpenSSH server as usual (synergy doesn't demand any special options in OpenSSH) and start it. Start the synergy server as usual; the synergy server requires no special options to work with OpenSSH. -

        - -

        Configuring the Clients

        -

        +

        +

        Configuring the Clients

        Install the OpenSSH client on each synergy client computer. Then, on each client, start the OpenSSH client using port forwarding:

        @@ -53,4 +51,5 @@ Synergy will then run normally except all communication is passed
         through OpenSSH which decrypts/encrypts it on behalf of synergy.
         

        + diff --git a/doc/synergy.css b/doc/synergy.css index 9085c706..8d831aa0 100644 --- a/doc/synergy.css +++ b/doc/synergy.css @@ -53,8 +53,9 @@ pre { font-variant: small-caps; font-size: 400%; width: 100%; - padding: 0px 0px 0px 5px; - border-bottom: solid #6699ff 1px; + padding: 0px; + margin: 0px; + border: 0px; } .banner a { color: #000000; @@ -63,6 +64,15 @@ pre { text-decoration: none; color: #000000; } +.bannerb { + color: #ffffff; + background-color: #ffffff; + width: 100%; + height: 1px; + padding: 0px; + margin: 0px; + border-bottom: solid #6699ff 1px; +} .nav { font-size: x-small; @@ -71,6 +81,7 @@ pre { padding: 2px 0px 2px 0px; margin: 0px; + border-bottom: solid #d4d4d4 300px; } .nav a:hover { text-decoration: none; @@ -82,7 +93,7 @@ pre { text-indent: 1em; } .nav .section { - width: 100%; + width: 120px; text-indent: 0em; border-top: 0px; border-left: 0px; diff --git a/doc/tips.html b/doc/tips.html index 175c094a..9f8e9e24 100644 --- a/doc/tips.html +++ b/doc/tips.html @@ -1,45 +1,42 @@ - + - - Synergy Tips and Tricks + + + + + Synergy Tips and Tricks -

        Tips and Tricks

        +

        +

        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. -

          - + (Synergy must be configured to autostart when the computer starts + 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 the Scroll Lock is toggled on. Use - this to prevent unintentional switching. -

          - + this to prevent unintentional switching. You can configure other + hot keys to do this instead; see + lockCursorToScreen. +

        • -

          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 @@ -49,45 +46,36 @@ 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 security guide 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. -

          +

        - +

        + diff --git a/doc/toc.html b/doc/toc.html new file mode 100644 index 00000000..3c43bd7c --- /dev/null +++ b/doc/toc.html @@ -0,0 +1,43 @@ + + + + + + + + Synergy TOC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/trouble.html b/doc/trouble.html new file mode 100644 index 00000000..f2923a5b --- /dev/null +++ b/doc/trouble.html @@ -0,0 +1,204 @@ + + + + + + + + Synergy Troubleshooting + + +

        +

        Synergy Troubleshooting

        +

        Problems

        +
          +
        1. Cannot read configuration +
        2. Connection forcefully rejected +
        3. Connection timed out +
        4. Cannot listen for clients +
        5. Unknown screen name "XXX" +
        6. Server refused client with name "XXX" +
          A client with name "XXX" is not in the map +
        7. Server already has a connected client with name "XXX" +
          A client with name "XXX" is already connected +
        8. Server has incompatible version +
        9. The cursor goes to secondary screen but won't come back +
        +

        Solutions

        +
          +
        1. Cannot read configuration +

          +There's an error in the configuration file. This error is always +accompanied by another message describing the problem. Use that +message and the configuration documentation +to determine the fix. +

          +
        2. Connection forcefully rejected +

          +The client was able to contact the server computer but the server was +not listening for clients. Possible reasons are: +

          +
            +
          • The client is using the wrong server +

            +Make sure the client is using the hostname or IP address of the computer +running the synergy server. +

            +
          • Synergy isn't running on the server +

            +Make sure the synergy server is running on the server computer. Make +sure the server is ready to accept connections. If another program is +using synergy's port (24800 by default) then synergy can't start unless +you specify a different port. +

            +
          • The client is using the wrong port +

            +Synergy uses port 24800 by default but you can specify a different port. +If you do use a different port you must use that port on the server and +all clients. +

            +
          +
        3. Connection timed out +

          +The most likely reasons for this are: +

          +
            +
          • A firewall +

            +A firewall is a program or device that deliberately blocks network +connections for security reasons. Typically, they'll silently drop +packets they don't want rather than sending a rejection to the sender. +This makes it more difficult for intruders to break in. +

            +When synergy traffic hits a firewall and gets dropped, eventually the +synergy client will give up waiting for a response and time out. To +allow synergy traffic through first find all the firewalls on the +network between and on the synergy client and server computers. +

            +A firewall on the server or any network device between the server and +any client should allow packets to TCP port 24800. (Port 24800 is the +default; use whichever port you've selected.) You'll have to consult +the manual for your operating system, device, or firewall software to +find out how to do this. +

            +Usually you'll won't need to adjust a firewall on client machines. +That's because firewalls normally allow incoming traffic on any port +they've initiated a connection on. The reasoning is, of course, if +you started a conversation you probably want to hear the reply. +

            +
          • The network is down or busy +

            +Correct the network problem and try again. You might try +ping to see if the two computers can see +each other on the network. +

            +
          • The server is frozen +

            +If the synergy server is running but locked up or very busy then the +client may get this message. If the server is locked up then you'll +probably have to restart it. If it's just very busy then the client +should successfully connect automatically once the server settles down. +

            +
          +
        4. Cannot listen for clients +

          +Synergy tried to start listening for clients but the network port is +unavailable for some reason. Typical reasons are: +

          +
            +
          • No network devices +

            +You must have a TCP/IP network device installed and enabled to use +synergy. +

            +
          • A synergy server is already running +

            +Check that a synergy server isn't already running. +

            +
          • Another program is using synergy's port +

            +Only one program at a time can listen for connections on a given port. +If the specific error is that the address is already in use and you've +ruled out the other causes, then it's likely another program is already +using synergy's port. By default synergy uses port 24800. Try having +synergy use a different port number, like 24801 or 24900. Note that +the server and all clients must use the same port number. Alternatively, +find the other program and stop it or have it use another port. +

            +
          +
        5. Unknown screen name "XXX" +

          +This error can be reported when reading the configuration; see +cannot read configuration. If the configuration +was read successfully and you get this error then it means that the +server's screen is not in the configuration. All screens must be listed +in the configuration. +

          +A common reason for this is when you haven't used the system's hostname +as its screen name. By default, synergy uses the hostname as the screen +name. If you used a different screen name in the configuration then you +must tell synergy what that name is. Let's say the hostname is +frederick but the configuration defines a screen +named fred. Then you must tell the server +that its screen name is fred by using the +--name fred command line option or setting +the screen name in the advanced options dialog to +fred. +

          +Alternatively, you can specify one name as an alias of another. See +the configuration documentation +for details. +

          +Another common reason for this is a mismatch between what you think the +hostname is and what synergy thinks it is. Typically this is a problem +with fully qualified domain names (FQDN). Perhaps you think your system +is named fred but synergy thinks it's +fred.nowhere.com or +fred.local. You can use either solution above +to fix this. +

          +
        6. Server refused client with name "XXX" +
          A client with name "XXX" is not in the map +

          +The client is using a screen name not in the server's configuration. +This is essentially the same problem as Unknown +screen name "XXX" and has the same solutions: specify another +screen name or add an alias. +

          +
        7. Server already has a connected client with name "XXX" +
          A client with name "XXX" is already connected +

          +This happens when: +

          +
            +
          • Two clients try use the same screen name +

            +Each client must have a unique screen name. Configure at least one +client to use a different screen name. +

            +
          • One client reconnects without cleanly disconnecting +

            +It's possible for a client to disconnect without the server knowing, +usually by being disconnected from the network or possibly by going +to sleep or even crashing. The server is left thinking the client is +still connected so when the client reconnects the server will think +this is a different client using the same name. Synergy will usually +detect and correct this problem within a few seconds. If it doesn't +then restart the server. +

            +
          +
        8. Server has incompatible version +

          +You're using different versions of synergy on the client and server. +You should use the same version on all systems. +

          +
        9. The cursor goes to secondary screen but won't come back +

          +This is FAQ #17 and is also mentioned in +the documentation for using synergy +and configuration. +

          +
        + + + diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 37dee72b..80c613ab 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -122,7 +122,7 @@ CArch::CArch(ARCH_ARGS* args) m_sleep = new ARCH_SLEEP; m_string = new ARCH_STRING; m_time = new ARCH_TIME; - m_console = new ARCH_CONSOLE; + m_console = new ARCH_CONSOLE(args); m_daemon = new ARCH_DAEMON; m_taskbar = new ARCH_TASKBAR(args); @@ -170,6 +170,12 @@ CArch::closeConsole() m_console->closeConsole(); } +void +CArch::showConsole(bool showIfEmpty) +{ + m_console->showConsole(showIfEmpty); +} + void CArch::writeConsole(const char* str) { @@ -254,6 +260,12 @@ CArch::closeLog() m_log->closeLog(); } +void +CArch::showLog(bool showIfEmpty) +{ + m_log->showLog(showIfEmpty); +} + void CArch::writeLog(ELevel level, const char* msg) { @@ -488,6 +500,12 @@ CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay) return m_net->setNoDelayOnSocket(s, noDelay); } +bool +CArch::setReuseAddrOnSocket(CArchSocket s, bool reuse) +{ + return m_net->setReuseAddrOnSocket(s, reuse); +} + std::string CArch::getHostName() { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 8a42bd0c..644f015c 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -74,6 +74,7 @@ public: // IArchConsole overrides virtual void openConsole(const char*); virtual void closeConsole(); + virtual void showConsole(bool showIfEmpty); virtual void writeConsole(const char*); virtual const char* getNewlineForConsole(); @@ -99,6 +100,7 @@ public: // IArchLog overrides virtual void openLog(const char*); virtual void closeLog(); + virtual void showLog(bool showIfEmpty); virtual void writeLog(ELevel, const char*); // IArchMultithread overrides @@ -143,6 +145,7 @@ public: const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); virtual CArchNetAddress copyAddr(CArchNetAddress); diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp index 79c4ae2c..dcb6e961 100644 --- a/lib/arch/CArchConsoleUnix.cpp +++ b/lib/arch/CArchConsoleUnix.cpp @@ -19,7 +19,7 @@ // CArchConsoleUnix // -CArchConsoleUnix::CArchConsoleUnix() +CArchConsoleUnix::CArchConsoleUnix(void*) { // do nothing } @@ -41,6 +41,12 @@ CArchConsoleUnix::closeConsole() // do nothing } +void +CArchConsoleUnix::showConsole(bool) +{ + // do nothing +} + void CArchConsoleUnix::writeConsole(const char* str) { diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h index 5e560fec..f93630bd 100644 --- a/lib/arch/CArchConsoleUnix.h +++ b/lib/arch/CArchConsoleUnix.h @@ -22,12 +22,13 @@ //! Unix implementation of IArchConsole class CArchConsoleUnix : public IArchConsole { public: - CArchConsoleUnix(); + CArchConsoleUnix(void*); virtual ~CArchConsoleUnix(); // IArchConsole overrides virtual void openConsole(const char* title); virtual void closeConsole(); + virtual void showConsole(bool); virtual void writeConsole(const char*); virtual const char* getNewlineForConsole(); }; diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index 6796a454..14d418ac 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -15,90 +15,100 @@ #include "CArchConsoleWindows.h" #include "IArchMultithread.h" #include "CArch.h" -#include +#include "CArchMiscWindows.h" +#include + +#define SYNERGY_MSG_CONSOLE_OPEN WM_APP + 0x0021 +#define SYNERGY_MSG_CONSOLE_CLOSE WM_APP + 0x0022 +#define SYNERGY_MSG_CONSOLE_SHOW WM_APP + 0x0023 +#define SYNERGY_MSG_CONSOLE_WRITE WM_APP + 0x0024 +#define SYNERGY_MSG_CONSOLE_CLEAR WM_APP + 0x0025 // // CArchConsoleWindows // -CArchThread CArchConsoleWindows::s_thread = 0; +CArchConsoleWindows* CArchConsoleWindows::s_instance = NULL; +HINSTANCE CArchConsoleWindows::s_appInstance = NULL; -CArchConsoleWindows::CArchConsoleWindows() : - m_output(NULL) +CArchConsoleWindows::CArchConsoleWindows(void* appInstance) : + m_show(false), + m_maxLines(1000), + m_numCharacters(0), + m_maxCharacters(65536) { - s_thread = ARCH->newCurrentThread(); + // save the singleton instance + s_instance = this; - m_mutex = ARCH->newMutex(); + // 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(&CArchConsoleWindows::threadEntry, this); + + // wait for child thread + while (!m_ready) { + ARCH->waitCondVar(m_condVar, m_mutex, -1.0); + } + + // ready + ARCH->unlockMutex(m_mutex); - // dummy write to stderr to create locks in stdio from the main - // thread. if we open the console from another thread then we - // can deadlock in stdio when trying to write from a 3rd thread. - // writes to stderr without a console don't go anywhere so the - // user won't notice this. - fprintf(stderr, "\n"); } CArchConsoleWindows::~CArchConsoleWindows() { + if (m_thread != NULL) { + PostMessage(m_hwnd, WM_QUIT, 0, 0); + ARCH->wait(m_thread, -1.0); + ARCH->closeThread(m_thread); + } + ARCH->closeCondVar(m_condVar); ARCH->closeMutex(m_mutex); - ARCH->closeThread(s_thread); + s_instance = NULL; } 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); + SetWindowText(m_frame, title); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_OPEN, 0, 0); } void CArchConsoleWindows::closeConsole() { - ARCH->lockMutex(m_mutex); - if (m_output != NULL) { - if (FreeConsole()) { - m_output = NULL; - } - } - ARCH->unlockMutex(m_mutex); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLOSE, 0, 0); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLEAR, 0, 0); +} + +void +CArchConsoleWindows::showConsole(bool showIfEmpty) +{ + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_SHOW, showIfEmpty ? 1 : 0, 0); } void CArchConsoleWindows::writeConsole(const char* str) { - fprintf(stderr, "%s", str); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_WRITE, + reinterpret_cast(str), 0); } const char* @@ -107,21 +117,322 @@ CArchConsoleWindows::getNewlineForConsole() return "\r\n"; } -BOOL WINAPI -CArchConsoleWindows::signalHandler(DWORD ctrlType) +void +CArchConsoleWindows::clearBuffer() { - // terminate app and skip remaining handlers - switch (ctrlType) { - case CTRL_C_EVENT: - ARCH->raiseSignal(CArch::kINTERRUPT); - return TRUE; + m_buffer.clear(); + m_numCharacters = 0; + SetWindowText(m_hwnd, ""); +} - case CTRL_BREAK_EVENT: - ARCH->raiseSignal(CArch::kTERMINATE); - return TRUE; +void +CArchConsoleWindows::appendBuffer(const char* msg) +{ + bool wasEmpty = m_buffer.empty(); + + // get current selection + CHARRANGE selection; + SendMessage(m_hwnd, EM_EXGETSEL, 0, reinterpret_cast(&selection)); + + // remove tail of buffer + size_t removedCharacters = 0; + while (m_buffer.size() >= m_maxLines) { + removedCharacters += m_buffer.front().size(); + m_buffer.pop_front(); + } + + // remove lines from top of control + if (removedCharacters > 0) { + CHARRANGE range; + range.cpMin = 0; + range.cpMax = static_cast(removedCharacters); + SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range)); + SendMessage(m_hwnd, EM_REPLACESEL, FALSE, reinterpret_cast("")); + + // adjust selection + if (selection.cpMin < static_cast(removedCharacters) || + selection.cpMax < static_cast(removedCharacters)) { + selection.cpMin = 0; + selection.cpMax = 0; + } + else { + selection.cpMin -= static_cast(removedCharacters); + selection.cpMax -= static_cast(removedCharacters); + } + + m_numCharacters -= removedCharacters; + } + + // append message + m_buffer.push_back(msg); + size_t newNumCharacters = m_numCharacters + m_buffer.back().size(); + + // add line to bottom of control + if (newNumCharacters > m_maxCharacters) { + m_maxCharacters = newNumCharacters; + SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters); + } + CHARRANGE range; + range.cpMin = m_numCharacters; + range.cpMax = m_numCharacters; + SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range)); + SendMessage(m_hwnd, EM_REPLACESEL, FALSE, + reinterpret_cast(m_buffer.back().c_str())); + + // adjust selection + bool atEnd = false; + if (selection.cpMax == static_cast(m_numCharacters)) { + selection.cpMin = static_cast(newNumCharacters); + selection.cpMax = static_cast(newNumCharacters); + atEnd = true; + } + + // restore the selection + SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&selection)); + if (atEnd) { + SendMessage(m_hwnd, EM_SCROLLCARET, 0, 0); + } + + if (wasEmpty && m_show) { + ShowWindow(m_frame, TRUE); + } + + m_numCharacters = newNumCharacters; +} + +void +CArchConsoleWindows::setSize(int width, int height) +{ + DWORD style = GetWindowLong(m_frame, GWL_STYLE); + DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE); + RECT rect; + rect.left = 100; + rect.top = 100; + rect.right = rect.left + width * m_wChar; + rect.bottom = rect.top + height * m_hChar; + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + SetWindowPos(m_frame, NULL, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); +} + +LRESULT +CArchConsoleWindows::wndProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_CLOSE: + ShowWindow(m_frame, FALSE); + m_show = false; + return 0; + + case SYNERGY_MSG_CONSOLE_OPEN: + return 0; + + case SYNERGY_MSG_CONSOLE_CLOSE: + SendMessage(m_frame, WM_CLOSE, 0, 0); + m_show = false; + return 0; + + case SYNERGY_MSG_CONSOLE_SHOW: + m_show = true; + if (wParam != 0 || !m_buffer.empty()) { + ShowWindow(m_frame, TRUE); + } + return 0; + + case SYNERGY_MSG_CONSOLE_WRITE: + appendBuffer(reinterpret_cast(wParam)); + return 0; + + case SYNERGY_MSG_CONSOLE_CLEAR: + clearBuffer(); + return 0; + + case WM_SIZE: + if (hwnd == m_frame) { + MoveWindow(m_hwnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); + } + break; + + case WM_SIZING: + if (hwnd == m_frame) { + // get window vs client area info + int wBase = 40 * m_wChar; + int hBase = 40 * m_hChar; + DWORD style = GetWindowLong(m_frame, GWL_STYLE); + DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE); + RECT rect; + rect.left = 100; + rect.top = 100; + rect.right = rect.left + wBase; + rect.bottom = rect.top + hBase; + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + wBase = rect.right - rect.left - wBase; + hBase = rect.bottom - rect.top - hBase; + + // get closest size that's a multiple of the character size + RECT* newRect = (RECT*)lParam; + int width = (newRect->right - newRect->left - wBase) / m_wChar; + int height = (newRect->bottom - newRect->top - hBase) / m_hChar; + width = width * m_wChar + wBase; + height = height * m_hChar + hBase; + + // adjust sizing rect + switch (wParam) { + case WMSZ_LEFT: + case WMSZ_TOPLEFT: + case WMSZ_BOTTOMLEFT: + newRect->left = newRect->right - width; + break; + + case WMSZ_RIGHT: + case WMSZ_TOPRIGHT: + case WMSZ_BOTTOMRIGHT: + newRect->right = newRect->left + width; + break; + } + switch (wParam) { + case WMSZ_TOP: + case WMSZ_TOPLEFT: + case WMSZ_TOPRIGHT: + newRect->top = newRect->bottom - height; + break; + + case WMSZ_BOTTOM: + case WMSZ_BOTTOMLEFT: + case WMSZ_BOTTOMRIGHT: + newRect->bottom = newRect->top + height; + break; + } + return TRUE; + } + break; default: - ARCH->raiseSignal(CArch::kINTERRUPT); - return TRUE; + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CArchConsoleWindows::staticWndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + // forward the message + if (s_instance != NULL) { + return s_instance->wndProc(hwnd, msg, wParam, lParam); + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); } } + +void +CArchConsoleWindows::threadMainLoop() +{ + LoadLibrary("RICHED32.DLL"); + + // get the app icons + HICON largeIcon, smallIcon; + CArchMiscWindows::getIcons(largeIcon, smallIcon); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = 0; + classInfo.lpfnWndProc = &CArchConsoleWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchConsoleWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = largeIcon; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyConsole"); + classInfo.hIconSm = smallIcon; + ATOM windowClass = RegisterClassEx(&classInfo); + + // create frame window + m_frame = CreateWindowEx(0, + reinterpret_cast(windowClass), + TEXT("Synergy Log"), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, + NULL, + NULL, + s_appInstance, + NULL); + + // create log window + m_hwnd = CreateWindowEx(0, + "RichEdit", + TEXT(""), + WS_CHILD | WS_VISIBLE | WS_VSCROLL | + ES_MULTILINE | ES_READONLY, + 0, 0, 1, 1, + m_frame, + (HMENU)1, + s_appInstance, + NULL); + + // select font and get info + HDC hdc = GetDC(m_hwnd); + HGDIOBJ oldFont = SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT)); + TEXTMETRIC metrics; + GetTextMetrics(hdc, &metrics); + CHARFORMAT format; + format.cbSize = sizeof(format); + format.dwMask = CFM_CHARSET | CFM_COLOR | CFM_FACE | + CFM_OFFSET | CFM_SIZE | CFM_PROTECTED | + CFM_BOLD | CFM_ITALIC | + CFM_STRIKEOUT | CFM_UNDERLINE; + format.dwEffects = 0; + format.yHeight = metrics.tmHeight; + format.yOffset = 0; + format.crTextColor = RGB(0, 0, 0); + format.bCharSet = DEFAULT_CHARSET; + format.bPitchAndFamily = FIXED_PITCH | FF_MODERN; + GetTextFace(hdc, sizeof(format.szFaceName), format.szFaceName); + SelectObject(hdc, oldFont); + ReleaseDC(m_hwnd, hdc); + + // prep window + SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters); + SendMessage(m_hwnd, EM_SETCHARFORMAT, 0, reinterpret_cast(&format)); + SendMessage(m_hwnd, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255)); + m_wChar = metrics.tmAveCharWidth; + m_hChar = metrics.tmHeight + metrics.tmExternalLeading; + setSize(80, 25); + + // signal ready + ARCH->lockMutex(m_mutex); + m_ready = true; + ARCH->broadcastCondVar(m_condVar); + ARCH->unlockMutex(m_mutex); + + // handle failure + if (m_hwnd == NULL) { + UnregisterClass(reinterpret_cast(windowClass), s_appInstance); + return; + } + + // main loop + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // clean up + DestroyWindow(m_hwnd); + UnregisterClass(reinterpret_cast(windowClass), s_appInstance); +} + +void* +CArchConsoleWindows::threadEntry(void* self) +{ + reinterpret_cast(self)->threadMainLoop(); + return NULL; +} diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h index bf9f0a32..0d59e6ef 100644 --- a/lib/arch/CArchConsoleWindows.h +++ b/lib/arch/CArchConsoleWindows.h @@ -19,6 +19,7 @@ #include "IArchConsole.h" #include "IArchMultithread.h" +#include "stddeque.h" #include #define ARCH_CONSOLE CArchConsoleWindows @@ -26,23 +27,51 @@ //! Win32 implementation of IArchConsole class CArchConsoleWindows : public IArchConsole { public: - CArchConsoleWindows(); + CArchConsoleWindows(void*); virtual ~CArchConsoleWindows(); // IArchConsole overrides virtual void openConsole(const char* title); virtual void closeConsole(); + virtual void showConsole(bool showIfEmpty); virtual void writeConsole(const char*); virtual const char* getNewlineForConsole(); private: - static BOOL WINAPI signalHandler(DWORD); + void clearBuffer(); + void appendBuffer(const char*); + void setSize(int width, int height); + + LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK + staticWndProc(HWND, UINT, WPARAM, LPARAM); + void threadMainLoop(); + static void* threadEntry(void*); private: - static CArchThread s_thread; + typedef std::deque MessageBuffer; + static CArchConsoleWindows* s_instance; + static HINSTANCE s_appInstance; + + // multithread data CArchMutex m_mutex; - HANDLE m_output; + CArchCond m_condVar; + bool m_ready; + CArchThread m_thread; + + // child thread data + HWND m_frame; + HWND m_hwnd; + LONG m_wChar; + LONG m_hChar; + bool m_show; + + // messages + size_t m_maxLines; + size_t m_maxCharacters; + size_t m_numCharacters; + MessageBuffer m_buffer; }; #endif diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index cd88659e..ab42ceab 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -136,10 +136,11 @@ CArchDaemonWindows::installDaemon(const char* name, NULL); if (service == NULL) { // can't create service - // FIXME -- handle ERROR_SERVICE_EXISTS DWORD err = GetLastError(); - CloseServiceHandle(mgr); - throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + if (err != ERROR_SERVICE_EXISTS) { + CloseServiceHandle(mgr); + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } } // done with service and manager @@ -148,7 +149,7 @@ CArchDaemonWindows::installDaemon(const char* name, // open the registry key for this service HKEY key = openNTServicesKey(); - key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::addKey(key, name); if (key == NULL) { // can't open key DWORD err = GetLastError(); @@ -165,7 +166,7 @@ CArchDaemonWindows::installDaemon(const char* name, CArchMiscWindows::setValue(key, _T("Description"), description); // set command line - key = CArchMiscWindows::openKey(key, _T("Parameters")); + key = CArchMiscWindows::addKey(key, _T("Parameters")); if (key == NULL) { // can't open key DWORD err = GetLastError(); @@ -224,7 +225,7 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) } // open the service. oddly, you must open a service to delete it. - SC_HANDLE service = OpenService(mgr, name, DELETE); + SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP); if (service == NULL) { DWORD err = GetLastError(); CloseServiceHandle(mgr); @@ -234,6 +235,10 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); } + // stop the service. we don't care if we fail. + SERVICE_STATUS status; + ControlService(service, SERVICE_CONTROL_STOP, &status); + // delete the service const bool okay = (DeleteService(service) == 0); const DWORD err = GetLastError(); @@ -244,6 +249,10 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) // handle failure. ignore error if service isn't installed anymore. if (!okay && isDaemonInstalled(name, allUsers)) { + if (err == ERROR_IO_PENDING) { + // this seems to be a spurious error + return; + } if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); } @@ -317,7 +326,7 @@ CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) } bool -CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) +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. @@ -338,10 +347,10 @@ CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) } CloseServiceHandle(mgr); - // check if we can open the registry key for this service + // check if we can open the registry key HKEY key = openNTServicesKey(); - key = CArchMiscWindows::openKey(key, name); - key = CArchMiscWindows::openKey(key, _T("Parameters")); +// key = CArchMiscWindows::addKey(key, name); +// key = CArchMiscWindows::addKey(key, _T("Parameters")); CArchMiscWindows::closeKey(key); return (key != NULL); @@ -415,7 +424,7 @@ CArchDaemonWindows::openNTServicesKey() NULL }; - return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); + return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); } HKEY @@ -430,7 +439,7 @@ CArchDaemonWindows::open95ServicesKey() NULL }; - return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); + return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); } HKEY @@ -445,7 +454,7 @@ CArchDaemonWindows::openUserStartupKey() NULL }; - return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); + return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames); } bool diff --git a/lib/arch/CArchLogUnix.cpp b/lib/arch/CArchLogUnix.cpp index c75696e3..093d89f9 100644 --- a/lib/arch/CArchLogUnix.cpp +++ b/lib/arch/CArchLogUnix.cpp @@ -41,6 +41,12 @@ CArchLogUnix::closeLog() closelog(); } +void +CArchLogUnix::showLog(bool) +{ + // do nothing +} + void CArchLogUnix::writeLog(ELevel level, const char* msg) { diff --git a/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h index b3717844..91070b45 100644 --- a/lib/arch/CArchLogUnix.h +++ b/lib/arch/CArchLogUnix.h @@ -28,6 +28,7 @@ public: // IArchLog overrides virtual void openLog(const char* name); virtual void closeLog(); + virtual void showLog(bool); virtual void writeLog(ELevel, const char*); }; diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp index 6d21d6ba..0ac89131 100644 --- a/lib/arch/CArchLogWindows.cpp +++ b/lib/arch/CArchLogWindows.cpp @@ -47,6 +47,12 @@ CArchLogWindows::closeLog() } } +void +CArchLogWindows::showLog(bool) +{ + // do nothing +} + void CArchLogWindows::writeLog(ELevel level, const char* msg) { diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h index d2385765..e8812536 100644 --- a/lib/arch/CArchLogWindows.h +++ b/lib/arch/CArchLogWindows.h @@ -31,6 +31,7 @@ public: // IArchLog overrides virtual void openLog(const char* name); virtual void closeLog(); + virtual void showLog(bool showIfEmpty); virtual void writeLog(ELevel, const char*); private: diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index f900f741..e2fb2dce 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -33,6 +33,8 @@ typedef DWORD EXECUTION_STATE; CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; DWORD CArchMiscWindows::s_busyState = 0; CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL; +HICON CArchMiscWindows::s_largeIcon = NULL; +HICON CArchMiscWindows::s_smallIcon = NULL; void CArchMiscWindows::init() @@ -87,6 +89,20 @@ CArchMiscWindows::isWindowsModern() return result; } +void +CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon) +{ + s_largeIcon = largeIcon; + s_smallIcon = smallIcon; +} + +void +CArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon) +{ + largeIcon = s_largeIcon; + smallIcon = s_smallIcon; +} + int CArchMiscWindows::runDaemon(RunFunc runFunc) { @@ -113,6 +129,30 @@ CArchMiscWindows::getDaemonQuitMessage() HKEY CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) +{ + return openKey(key, keyName, false); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +{ + return openKey(key, keyNames, false); +} + +HKEY +CArchMiscWindows::addKey(HKEY key, const TCHAR* keyName) +{ + return openKey(key, keyName, true); +} + +HKEY +CArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames) +{ + return openKey(key, keyNames, true); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create) { // ignore if parent is NULL if (key == NULL) { @@ -123,7 +163,7 @@ CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) HKEY newKey; LONG result = RegOpenKeyEx(key, keyName, 0, KEY_WRITE | KEY_QUERY_VALUE, &newKey); - if (result != ERROR_SUCCESS) { + if (result != ERROR_SUCCESS && create) { DWORD disp; result = RegCreateKeyEx(key, keyName, 0, TEXT(""), 0, KEY_WRITE | KEY_QUERY_VALUE, @@ -140,11 +180,11 @@ CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) } HKEY -CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create) { for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { // open next key - key = openKey(key, keyNames[i]); + key = openKey(key, keyNames[i], create); } return key; } diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 973f9b4e..95a1d136 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -56,6 +56,18 @@ public: */ static bool isWindowsModern(); + //! Set the application icons + /*! + Set the application icons. + */ + static void setIcons(HICON largeIcon, HICON smallIcon); + + //! Get the application icons + /*! + Get the application icons. + */ + static void getIcons(HICON& largeIcon, HICON& smallIcon); + //! Run the daemon /*! Delegates to CArchDaemonWindows. @@ -86,6 +98,12 @@ public: //! Open and return a registry key, closing the parent key static HKEY openKey(HKEY parent, const TCHAR* const* keyPath); + //! Open/create and return a registry key, closing the parent key + static HKEY addKey(HKEY parent, const TCHAR* child); + + //! Open/create and return a registry key, closing the parent key + static HKEY addKey(HKEY parent, const TCHAR* const* keyPath); + //! Close a key static void closeKey(HKEY); @@ -144,6 +162,13 @@ public: static void removeBusyState(DWORD busyModes); private: + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* child, bool create); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* const* keyPath, + bool create); + //! Read a string value from the registry static std::string readBinaryOrString(HKEY, const TCHAR* name, DWORD type); @@ -159,6 +184,8 @@ private: static CDialogs* s_dialogs; static DWORD s_busyState; static STES_t s_stes; + static HICON s_largeIcon; + static HICON s_smallIcon; }; #endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 5750b51d..ec11fc50 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -49,6 +49,7 @@ setSignalSet(sigset_t* sigset) sigaddset(sigset, SIGHUP); sigaddset(sigset, SIGINT); sigaddset(sigset, SIGTERM); + sigaddset(sigset, SIGUSR2); } // @@ -771,7 +772,7 @@ CArchMultithreadPosix::threadSignalHandler(void*) for (;;) { // wait #if HAVE_POSIX_SIGWAIT - int signal; + int signal = 0; sigwait(&sigset, &signal); #else sigwait(&sigset); @@ -791,6 +792,10 @@ CArchMultithreadPosix::threadSignalHandler(void*) ARCH->raiseSignal(kHANGUP); break; + case SIGUSR2: + ARCH->raiseSignal(kUSER); + break; + default: // ignore break; diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 2432c9e0..6b36cff5 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -31,9 +31,6 @@ #if HAVE_POLL # include -# if HAVE_ALLOCA_H -# include -# endif #else # if HAVE_SYS_SELECT_H # include @@ -47,13 +44,6 @@ # include #endif -#if HAVE_ALLOCA_H -# define freea(x_) -#else -# define alloca(x_) malloc(x_) -# define freea(x_) free(x_) -#endif - static const int s_family[] = { PF_UNSPEC, PF_INET @@ -291,8 +281,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } // allocate space for translated query - struct pollfd* pfd = reinterpret_cast( - alloca((1 + num) * sizeof(struct pollfd))); + struct pollfd* pfd = new struct pollfd[1 + num]; // translate query for (int i = 0; i < num; ++i) { @@ -322,7 +311,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) n = poll(pfd, n, t); // reset the unblock pipe - if (unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) { + if (n > 0 && unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) { // the unblock event was signalled. flush the pipe. char dummy[100]; do { @@ -338,10 +327,10 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) if (errno == EINTR) { // interrupted system call ARCH->testCancelThread(); - freea(pfd); + delete[] pfd; return 0; } - freea(pfd); + delete[] pfd; throwError(errno); } @@ -362,7 +351,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } } - freea(pfd); + delete[] pfd; return n; } @@ -452,7 +441,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) SELECT_TYPE_ARG5 timeout2P); // reset the unblock pipe - if (unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) { + if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) { // the unblock event was signalled. flush the pipe. char dummy[100]; do { @@ -594,6 +583,29 @@ CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) return (oflag != 0); } +bool +CArchNetworkBSD::setReuseAddrOnSocket(CArchSocket s, bool reuse) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = sizeof(oflag); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, + (optval_t*)&oflag, &size) == -1) { + throwError(errno); + } + + int flag = reuse ? 1 : 0; + size = sizeof(flag); + if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, + (optval_t*)&flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + std::string CArchNetworkBSD::getHostName() { diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 3915b3ff..bba60272 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -73,6 +73,7 @@ public: const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); virtual CArchNetAddress copyAddr(CArchNetAddress); diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 0ed630e0..ac40596a 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -612,6 +612,30 @@ CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay) return (oflag != 0); } +bool +CArchNetworkWinsock::setReuseAddrOnSocket(CArchSocket s, bool reuse) +{ + assert(s != NULL); + + // get old state + BOOL oflag; + int size = sizeof(oflag); + if (getsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_REUSEADDR, &oflag, &size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + // set new state + BOOL flag = reuse ? 1 : 0; + size = sizeof(flag); + if (setsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_REUSEADDR, &flag, size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + return (oflag != 0); +} + std::string CArchNetworkWinsock::getHostName() { diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h index 8124019c..3912ba5b 100644 --- a/lib/arch/CArchNetworkWinsock.h +++ b/lib/arch/CArchNetworkWinsock.h @@ -70,6 +70,7 @@ public: const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); virtual CArchNetAddress copyAddr(CArchNetAddress); diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp index 2b9bb8ca..517d72d6 100644 --- a/lib/arch/CMultibyte.cpp +++ b/lib/arch/CMultibyte.cpp @@ -19,6 +19,9 @@ #include "CArch.h" #include #include +#if HAVE_LOCALE_H +# include +#endif #if HAVE_WCHAR_H || defined(_MSC_VER) # include #elif __APPLE__ @@ -56,6 +59,15 @@ static CArchMutex s_mutex = NULL; ARCH_STRING::ARCH_STRING() { s_mutex = ARCH->newMutex(); + +#if HAVE_LOCALE_H + // see if we can convert a Latin-1 character + char mb[MB_LEN_MAX]; + if (wctomb(mb, 0xe3) == -1) { + // can't convert. try another locale so we can convert latin-1. + setlocale(LC_CTYPE, "en_US"); + } +#endif } ARCH_STRING::~ARCH_STRING() diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h index c9da4100..2befb196 100644 --- a/lib/arch/IArchConsole.h +++ b/lib/arch/IArchConsole.h @@ -43,6 +43,15 @@ public: */ virtual void closeConsole() = 0; + //! Show the console + /*! + Causes the console to become visible. This generally only makes sense + for a console in a graphical user interface. Other implementations + will do nothing. Iff \p showIfEmpty is \c false then the implementation + may optionally only show the console if it's not empty. + */ + virtual void showConsole(bool showIfEmpty) = 0; + //! Write to the console /*! Writes the given string to the console, opening it if necessary. diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h index 0c259187..7655ff95 100644 --- a/lib/arch/IArchLog.h +++ b/lib/arch/IArchLog.h @@ -52,6 +52,15 @@ public: */ virtual void closeLog() = 0; + //! Show the log + /*! + Causes the log to become visible. This generally only makes sense + for a log in a graphical user interface. Other implementations + will do nothing. Iff \p showIfEmpty is \c false then the implementation + may optionally only show the log if it's not empty. + */ + virtual void showLog(bool showIfEmpty) = 0; + //! Write to the log /*! Writes the given string to the log with the given level. diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index 55db7af9..b7b72293 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -80,6 +80,7 @@ public: kINTERRUPT, //!< Interrupt (e.g. Ctrl+C) kTERMINATE, //!< Terminate (e.g. Ctrl+Break) kHANGUP, //!< Hangup (SIGHUP) + kUSER, //!< User (SIGUSR2) kNUM_SIGNALS }; //! Type of signal handler function diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h index 2c821afb..007fb442 100644 --- a/lib/arch/IArchNetwork.h +++ b/lib/arch/IArchNetwork.h @@ -226,6 +226,13 @@ public: */ virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0; + //! Turn address reuse on or off on socket + /*! + Allows the address this socket is bound to to be reused while in the + TIME_WAIT state. Returns the previous state. + */ + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse) = 0; + //! Return local host's name virtual std::string getHostName() = 0; diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index 73575be9..37c9bcd8 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\arch.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\arch.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\arch.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\arch.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/base/CEvent.cpp b/lib/base/CEvent.cpp index 5d828989..bfdf88ed 100644 --- a/lib/base/CEvent.cpp +++ b/lib/base/CEvent.cpp @@ -22,15 +22,17 @@ CEvent::CEvent() : m_type(kUnknown), m_target(NULL), - m_data(NULL) + m_data(NULL), + m_flags(0) { // do nothing } -CEvent::CEvent(Type type, void* target, void* data) : +CEvent::CEvent(Type type, void* target, void* data, Flags flags) : m_type(type), m_target(target), - m_data(data) + m_data(data), + m_flags(flags) { // do nothing } @@ -53,6 +55,12 @@ CEvent::getData() const return m_data; } +CEvent::Flags +CEvent::getFlags() const +{ + return m_flags; +} + CEvent::Type CEvent::registerType(const char* name) { @@ -82,7 +90,9 @@ CEvent::deleteData(const CEvent& event) break; default: - free(event.getData()); + if ((event.getFlags() & kDontFreeData) == 0) { + free(event.getData()); + } break; } } diff --git a/lib/base/CEvent.h b/lib/base/CEvent.h index 272369ba..8b637cef 100644 --- a/lib/base/CEvent.h +++ b/lib/base/CEvent.h @@ -33,6 +33,13 @@ public: kLast //!< Must be last }; + typedef UInt32 Flags; + enum { + kNone = 0x00, //!< No flags + kDeliverImmediately = 0x01, //!< Dispatch and free event immediately + kDontFreeData = 0x02 //!< Don't free data in deleteData + }; + CEvent(); //! Create \c CEvent with data @@ -41,9 +48,10 @@ public: The \p data must be POD (plain old data) allocated by malloc(), which means it cannot have a constructor, destructor or be composed of any types that do. \p target is the intended - recipient of the event. + recipient of the event. \p flags is any combination of \c Flags. */ - CEvent(Type type, void* target = NULL, void* data = NULL); + CEvent(Type type, void* target = NULL, void* data = NULL, + UInt32 flags = kNone); //! @name manipulators //@{ @@ -97,12 +105,19 @@ public: */ void* getData() const; + //! Get event flags + /*! + Returns the event flags. + */ + Flags getFlags() const; + //@} private: Type m_type; void* m_target; void* m_data; + Flags m_flags; }; #endif diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index 16a0d52b..d0a93391 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -206,18 +206,24 @@ CEventQueue::addEvent(const CEvent& event) default: break; } - - CArchMutexLock lock(m_mutex); - - // store the event's data locally - UInt32 eventID = saveEvent(event); - - // add it - if (!m_buffer->addEvent(eventID)) { - // failed to send event - removeEvent(eventID); + + if ((event.getFlags() & CEvent::kDeliverImmediately) != 0) { + dispatchEvent(event); CEvent::deleteData(event); } + else { + CArchMutexLock lock(m_mutex); + + // store the event's data locally + UInt32 eventID = saveEvent(event); + + // add it + if (!m_buffer->addEvent(eventID)) { + // failed to send event + removeEvent(eventID); + CEvent::deleteData(event); + } + } } CEventQueueTimer* diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 1cc007da..7c73ac87 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -100,42 +100,7 @@ CLog::getInstance() } 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 +CLog::print(const char* file, int line, const char* fmt, ...) const { // check if fmt begins with a priority argument int priority = 4; @@ -151,35 +116,61 @@ CLog::printt(const char* file, int line, const char* fmt, ...) const // 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; + int pPad = g_priorityPad; + if (file != NULL) { + sprintf(stack, "%d", line); + pPad += strlen(file) + 1 /* comma */ + + strlen(stack) + 1 /* colon */ + 1 /* space */; + } - // 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); + // compute suffix padding length + int sPad = m_maxNewlineLength; + + // print to buffer, leaving space for a newline at the end and prefix + // at the beginning. + char* buffer = stack; + int len = (int)(sizeof(stack) / sizeof(stack[0])); + while (true) { + // try printing into the buffer + va_list args; + va_start(args, fmt); + int n = ARCH->vsnprintf(buffer + pPad, len - pPad - sPad, fmt, args); + va_end(args); + + // if the buffer wasn't big enough then make it bigger and try again + if (n < 0 || n > (int)len) { + if (buffer != stack) { + delete[] buffer; + } + len *= 2; + buffer = new char[len]; + } + + // if the buffer was big enough then continue + else { + break; + } + } // 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; + if (file != NULL) { + sprintf(buffer + g_priorityPad, "%s,%d:", file, line); + buffer[pPad - 1] = ' '; + + // discard file and line if priority < 0 + if (priority < 0) { + message += pPad - g_priorityPad; + } } // output buffer output(priority, message); // clean up - if (buffer != stack) + if (buffer != stack) { delete[] buffer; + } } void @@ -199,6 +190,8 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) if (newlineLength > m_maxNewlineLength) { m_maxNewlineLength = newlineLength; } + outputter->open(kAppVersion); + outputter->show(false); } void @@ -279,9 +272,6 @@ CLog::output(int priority, char* msg) const // put an appropriate newline at the end strcpy(end, outputter->getNewline()); - // open the outputter - outputter->open(kApplication); - // write message outputter->write(static_cast(priority), msg + g_maxPriorityLength - n); @@ -294,9 +284,6 @@ CLog::output(int priority, char* msg) const // put an appropriate newline at the end strcpy(end, 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)) { diff --git a/lib/base/CLog.h b/lib/base/CLog.h index b25e0ba6..391480e2 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -102,18 +102,13 @@ public: //! @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. + preceded by the filename and line number. If \c file is NULL then + neither the file nor the line are printed. */ - void printt(const char* file, int line, + void print(const char* file, int line, const char* format, ...) const; //! Get the minimum priority level. @@ -175,9 +170,9 @@ without the leading \c k. For example, \c CLOG_INFO. The special 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. +nothing. If \c NDEBUG is not defined during the build then it expands +to a call to CLog::print that prints the filename and line number, +otherwise it expands to a call that doesn't. */ #if defined(NOLOGGING) @@ -187,10 +182,10 @@ which includes the filename and line number. #elif defined(NDEBUG) #define LOG(_a1) CLOG->print _a1 #define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 -#define CLOG_TRACE +#define CLOG_TRACE NULL, 0, #else -#define LOG(_a1) CLOG->printt _a1 -#define LOGC(_a1, _a2) if (_a1) CLOG->printt _a2 +#define LOG(_a1) CLOG->print _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 #define CLOG_TRACE __FILE__, __LINE__, #endif diff --git a/lib/base/CStringUtil.cpp b/lib/base/CStringUtil.cpp index 08dc61c8..46361932 100644 --- a/lib/base/CStringUtil.cpp +++ b/lib/base/CStringUtil.cpp @@ -115,55 +115,38 @@ CStringUtil::vformat(const char* fmt, va_list args) 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* buffer = tmp; + int len = (int)(sizeof(tmp) / sizeof(tmp[0])); + CString result; + while (buffer != NULL) { + // try printing into the buffer + va_list args; + va_start(args, fmt); + int n = ARCH->vsnprintf(buffer, len, fmt, args); + va_end(args); -char* -CStringUtil::vsprint(char* buffer, int len, - int prefix, int suffix, const char* fmt, va_list args) -{ - assert(len > 0); + // if the buffer wasn't big enough then make it bigger and try again + if (n < 0 || n > len) { + if (buffer != tmp) { + delete[] buffer; + } + len *= 2; + buffer = new char[len]; + } - // 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; + // if it was big enough then save the string and don't try again + else { + result = buffer; + if (buffer != tmp) { + delete[] buffer; + } + buffer = NULL; + } } - // 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; + return result; } diff --git a/lib/base/CStringUtil.h b/lib/base/CStringUtil.h index 1ad5824c..8ee86647 100644 --- a/lib/base/CStringUtil.h +++ b/lib/base/CStringUtil.h @@ -48,28 +48,6 @@ public: */ 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. diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h index 585598c2..2be4dcc9 100644 --- a/lib/base/ILogOutputter.h +++ b/lib/base/ILogOutputter.h @@ -45,6 +45,15 @@ public: */ virtual void close() = 0; + //! Show the outputter + /*! + Causes the output to become visible. This generally only makes sense + for a logger in a graphical user interface. Other implementations + will do nothing. Iff \p showIfEmpty is \c false then the implementation + may optionally only show the log if it's not empty. + */ + virtual void show(bool showIfEmpty) = 0; + //! Write a message with level /*! Writes \c message, which has the given \c level, to a log. diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp index 5f7a40ba..f556d53f 100644 --- a/lib/base/LogOutputters.cpp +++ b/lib/base/LogOutputters.cpp @@ -41,6 +41,12 @@ CStopLogOutputter::close() // do nothing } +void +CStopLogOutputter::show(bool) +{ + // do nothing +} + bool CStopLogOutputter::write(ELevel, const char*) { @@ -80,6 +86,12 @@ CConsoleLogOutputter::close() ARCH->closeConsole(); } +void +CConsoleLogOutputter::show(bool showIfEmpty) +{ + ARCH->showConsole(showIfEmpty); +} + bool CConsoleLogOutputter::write(ELevel, const char* msg) { @@ -120,6 +132,12 @@ CSystemLogOutputter::close() ARCH->closeLog(); } +void +CSystemLogOutputter::show(bool showIfEmpty) +{ + ARCH->showLog(showIfEmpty); +} + bool CSystemLogOutputter::write(ELevel level, const char* msg) { @@ -162,22 +180,27 @@ CSystemLogOutputter::getNewline() const // CSystemLogger // -CSystemLogger::CSystemLogger(const char* title) +CSystemLogger::CSystemLogger(const char* title, bool blockConsole) : + m_stop(NULL) { // redirect log messages + if (blockConsole) { + m_stop = new CStopLogOutputter; + CLOG->insert(m_stop); + } 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; + if (m_stop != NULL) { + CLOG->remove(m_stop); + delete m_stop; + } } @@ -221,6 +244,12 @@ CBufferedLogOutputter::close() m_buffer.clear(); } +void +CBufferedLogOutputter::show(bool) +{ + // do nothing +} + bool CBufferedLogOutputter::write(ELevel, const char* message) { diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h index 64befe5f..a8087593 100644 --- a/lib/base/LogOutputters.h +++ b/lib/base/LogOutputters.h @@ -34,6 +34,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; }; @@ -51,6 +52,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; }; @@ -67,6 +69,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; }; @@ -81,7 +84,7 @@ the scope. */ class CSystemLogger { public: - CSystemLogger(const char* title); + CSystemLogger(const char* title, bool blockConsole); ~CSystemLogger(); private: @@ -117,6 +120,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; diff --git a/lib/base/base.dsp b/lib/base/base.dsp index f1eb30aa..542cfe8d 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\base.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\base.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\base.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\base.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index d339156f..1e450a17 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -47,17 +47,32 @@ CClient::CClient(const CString& name, const CNetworkAddress& address, m_stream(NULL), m_timer(NULL), m_server(NULL), - - m_active(false) + m_ready(false), + m_active(false), + m_suspended(false), + m_connectOnResume(false) { assert(m_socketFactory != NULL); assert(m_screen != NULL); - // do nothing + // register suspend/resume event handlers + EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleSuspend)); + EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleResume)); } CClient::~CClient() { + EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(), + getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getResumeEvent(), + getEventTarget()); + cleanupTimer(); cleanupScreen(); cleanupConnecting(); @@ -72,6 +87,10 @@ CClient::connect() if (m_stream != NULL) { return; } + if (m_suspended) { + m_connectOnResume = true; + return; + } try { // resolve the server hostname. do this every time we connect @@ -111,8 +130,10 @@ CClient::connect() void CClient::disconnect(const char* msg) { + m_connectOnResume = false; cleanupTimer(); cleanupScreen(); + cleanupConnecting(); cleanupConnection(); if (msg != NULL) { sendConnectionFailedEvent(msg); @@ -142,6 +163,12 @@ CClient::isConnecting() const return (m_timer != NULL); } +CNetworkAddress +CClient::getServerAddress() const +{ + return m_serverAddress; +} + CEvent::Type CClient::getConnectedEvent() { @@ -216,13 +243,16 @@ void CClient::setClipboard(ClipboardID id, const IClipboard* clipboard) { m_screen->setClipboard(id, clipboard); + m_ownClipboard[id] = false; + m_sentClipboard[id] = false; } void CClient::grabClipboard(ClipboardID id) { m_screen->grabClipboard(id); - m_ownClipboard[id] = false; + m_ownClipboard[id] = false; + m_sentClipboard[id] = false; } void @@ -275,9 +305,9 @@ CClient::mouseRelativeMove(SInt32 dx, SInt32 dy) } void -CClient::mouseWheel(SInt32 delta) +CClient::mouseWheel(SInt32 xDelta, SInt32 yDelta) { - m_screen->mouseWheel(delta); + m_screen->mouseWheel(xDelta, yDelta); } void @@ -330,8 +360,9 @@ CClient::sendClipboard(ClipboardID id) // marshall the data CString data = clipboard.marshall(); - // save and send data if different - if (data != m_dataClipboard[id]) { + // save and send data if different or not yet sent + if (!m_sentClipboard[id] || data != m_dataClipboard[id]) { + m_sentClipboard[id] = true; m_dataClipboard[id] = data; m_server->onClipboardChanged(id, &clipboard); } @@ -490,6 +521,7 @@ CClient::handleConnected(const CEvent&, void*) // reset clipboard state for (ClipboardID id = 0; id < kClipboardEnd; ++id) { m_ownClipboard[id] = false; + m_sentClipboard[id] = false; m_timeClipboard[id] = 0; } } @@ -513,6 +545,7 @@ CClient::handleConnectTimeout(const CEvent&, void*) { cleanupTimer(); cleanupConnecting(); + cleanupConnection(); delete m_stream; m_stream = NULL; LOG((CLOG_DEBUG1 "connection timed out")); @@ -557,6 +590,7 @@ CClient::handleClipboardGrabbed(const CEvent& event, void*) // we now own the clipboard and it has not been sent to the server m_ownClipboard[info->m_id] = true; + m_sentClipboard[info->m_id] = false; m_timeClipboard[info->m_id] = 0; // if we're not the active screen then send the clipboard now, @@ -605,3 +639,24 @@ CClient::handleHello(const CEvent&, void*) m_stream->getEventTarget())); } } + +void +CClient::handleSuspend(const CEvent&, void*) +{ + LOG((CLOG_INFO "suspend")); + m_suspended = true; + bool wasConnected = isConnected(); + disconnect(NULL); + m_connectOnResume = wasConnected; +} + +void +CClient::handleResume(const CEvent&, void*) +{ + LOG((CLOG_INFO "resume")); + m_suspended = false; + if (m_connectOnResume) { + m_connectOnResume = false; + connect(); + } +} diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 5087759b..4bd11c66 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -89,6 +89,13 @@ public: */ bool isConnecting() const; + //! Get address of server + /*! + Returns the address of the server the client is connected (or wants + to connect) to. + */ + CNetworkAddress getServerAddress() const; + //! Get connected event type /*! Returns the connected event type. This is sent when the client has @@ -136,7 +143,7 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); - virtual void mouseWheel(SInt32 delta); + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); @@ -162,7 +169,9 @@ private: void handleShapeChanged(const CEvent&, void*); void handleClipboardGrabbed(const CEvent&, void*); void handleHello(const CEvent&, void*); - + void handleSuspend(const CEvent& event, void*); + void handleResume(const CEvent& event, void*); + private: CString m_name; CNetworkAddress m_serverAddress; @@ -174,7 +183,10 @@ private: CServerProxy* m_server; bool m_ready; bool m_active; + bool m_suspended; + bool m_connectOnResume; bool m_ownClipboard[kClipboardEnd]; + bool m_sentClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index a44347db..acef43b2 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -32,7 +32,6 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_client(client), m_stream(stream), - m_timer(NULL), m_seqNum(0), m_compressMouse(false), m_compressMouseRelative(false), @@ -41,7 +40,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_dxMouse(0), m_dyMouse(0), m_ignoreMouse(false), - m_heartRate(0.0), + m_keepAliveAlarm(0.0), + m_keepAliveAlarmTimer(NULL), m_parser(&CServerProxy::parseHandshakeMessage) { assert(m_client != NULL); @@ -58,32 +58,40 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : &CServerProxy::handleData)); // send heartbeat - installHeartBeat(kHeartRate); + setKeepAliveRate(kKeepAliveRate); } CServerProxy::~CServerProxy() { - installHeartBeat(-1.0); + setKeepAliveRate(-1.0); EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), m_stream->getEventTarget()); } void -CServerProxy::installHeartBeat(double heartRate) +CServerProxy::resetKeepAliveAlarm() { - if (m_timer != NULL) { - EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); - EVENTQUEUE->deleteTimer(m_timer); + if (m_keepAliveAlarmTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_keepAliveAlarmTimer); + EVENTQUEUE->deleteTimer(m_keepAliveAlarmTimer); + m_keepAliveAlarmTimer = NULL; } - m_heartRate = heartRate; - if (m_heartRate > 0.0) { - m_timer = EVENTQUEUE->newTimer(m_heartRate, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + if (m_keepAliveAlarm > 0.0) { + m_keepAliveAlarmTimer = + EVENTQUEUE->newOneShotTimer(m_keepAliveAlarm, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_keepAliveAlarmTimer, new TMethodEventJob(this, - &CServerProxy::handleHeartBeat)); + &CServerProxy::handleKeepAliveAlarm)); } } +void +CServerProxy::setKeepAliveRate(double rate) +{ + m_keepAliveAlarm = rate * kKeepAlivesUntilDeath; + resetKeepAliveAlarm(); +} + void CServerProxy::handleData(const CEvent&, void*) { @@ -105,7 +113,7 @@ CServerProxy::handleData(const CEvent&, void*) break; case kUnknown: - LOG((CLOG_ERR "invalid message from server")); + LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3])); m_client->disconnect("invalid message from server"); return; @@ -143,6 +151,12 @@ CServerProxy::parseHandshakeMessage(const UInt8* code) resetOptions(); } + else if (memcmp(code, kMsgCKeepAlive, 4) == 0) { + // echo keep alives and reset alarm + CProtocolUtil::writef(m_stream, kMsgCKeepAlive); + resetKeepAliveAlarm(); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { // accept and discard no-op } @@ -222,6 +236,12 @@ CServerProxy::parseMessage(const UInt8* code) keyRepeat(); } + else if (memcmp(code, kMsgCKeepAlive, 4) == 0) { + // echo keep alives and reset alarm + CProtocolUtil::writef(m_stream, kMsgCKeepAlive); + resetKeepAliveAlarm(); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { // accept and discard no-op } @@ -290,9 +310,10 @@ CServerProxy::parseMessage(const UInt8* code) } void -CServerProxy::handleHeartBeat(const CEvent&, void*) +CServerProxy::handleKeepAliveAlarm(const CEvent&, void*) { - CProtocolUtil::writef(m_stream, kMsgCNoop); + LOG((CLOG_NOTE "server is dead")); + m_client->disconnect("server is not responding"); } void @@ -699,12 +720,12 @@ CServerProxy::mouseWheel() flushCompressedMouse(); // parse - SInt16 delta; - CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &delta); - LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); + SInt16 xDelta, yDelta; + CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &xDelta, &yDelta); + LOG((CLOG_DEBUG2 "recv mouse wheel %+d,%+d", xDelta, yDelta)); // forward - m_client->mouseWheel(delta); + m_client->mouseWheel(xDelta, yDelta); } void @@ -728,11 +749,8 @@ CServerProxy::resetOptions() // forward m_client->resetOptions(); - // reset heart rate and send heartbeat if necessary - installHeartBeat(kHeartRate); - if (m_heartRate >= 0.0) { - CProtocolUtil::writef(m_stream, kMsgCNoop); - } + // reset keep alive + setKeepAliveRate(kKeepAliveRate); // reset modifier translation table for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) { @@ -770,11 +788,8 @@ CServerProxy::setOptions() id = kKeyModifierIDSuper; } else if (options[i] == kOptionHeartbeat) { - // update heart rate and send heartbeat if necessary - installHeartBeat(1.0e-3 * static_cast(options[i + 1])); - if (m_heartRate >= 0.0) { - CProtocolUtil::writef(m_stream, kMsgCNoop); - } + // update keep alive + setKeepAliveRate(1.0e-3 * static_cast(options[i + 1])); } if (id != kKeyModifierIDNull) { m_modifierTranslationTable[id] = diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index 51263792..f6642696 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -59,7 +59,8 @@ private: void sendInfo(const CClientInfo&); - void installHeartBeat(double); + void resetKeepAliveAlarm(); + void setKeepAliveRate(double); // modifier key translation KeyID translateKey(KeyID) const; @@ -67,7 +68,7 @@ private: // event handlers void handleData(const CEvent&, void*); - void handleHeartBeat(const CEvent&, void*); + void handleKeepAliveAlarm(const CEvent&, void*); // message handlers void enter(); @@ -93,7 +94,6 @@ private: CClient* m_client; IStream* m_stream; - CEventQueueTimer* m_timer; UInt32 m_seqNum; @@ -105,7 +105,9 @@ private: bool m_ignoreMouse; KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; - double m_heartRate; + + double m_keepAliveAlarm; + CEventQueueTimer* m_keepAliveAlarmTimer; MessageParser m_parser; }; diff --git a/lib/client/client.dsp b/lib/client/client.dsp index 1764b7be..6fe46b93 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\client.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /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"..\..\gen\build\client.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\client.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /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"..\..\gen\debug\client.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/common/Version.cpp b/lib/common/Version.cpp index 2347e1df..86fcdef7 100644 --- a/lib/common/Version.cpp +++ b/lib/common/Version.cpp @@ -14,9 +14,9 @@ #include "Version.h" -const char* kApplication = "synergy"; +const char* kApplication = "Synergy"; const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; const char* kWebsite = "http://synergy2.sourceforge.net/"; const char* kVersion = VERSION; -const char* kAppVersion = "synergy " VERSION; +const char* kAppVersion = "Synergy " VERSION; diff --git a/lib/common/Version.h b/lib/common/Version.h index 0eadf6fc..1f98a6a2 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.3.0" +# define VERSION "1.3.1" #endif // important strings diff --git a/lib/common/common.dsp b/lib/common/common.dsp index 55714483..f4f4b8a2 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\common.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\common.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\common.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\common.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/common/common.h b/lib/common/common.h index c8f26831..9f51d4e3 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -19,6 +19,11 @@ #if HAVE_CONFIG_H # include "config.h" + + // don't use poll() on mac +# if defined(__APPLE__) +# undef HAVE_POLL +# endif #else // we may not have run configure on win32 # if defined(_WIN32) @@ -31,7 +36,6 @@ # define SYSAPI_UNIX 1 # define WINAPI_CARBON 1 -# define HAVE_ALLOCA_H 1 # define HAVE_CXX_BOOL 1 # define HAVE_CXX_CASTS 1 # define HAVE_CXX_EXCEPTIONS 1 @@ -48,20 +52,26 @@ # define HAVE_POSIX_SIGWAIT 1 # define HAVE_PTHREAD 1 # define HAVE_PTHREAD_SIGNAL 1 +# include +# include +# if defined(_SOCKLEN_T) +# define HAVE_SOCKLEN_T 1 +# endif # define HAVE_SSTREAM 1 # define HAVE_STDINT_H 1 # define HAVE_STDLIB_H 1 # define HAVE_STRINGS_H 1 # define HAVE_STRING_H 1 # define HAVE_SYS_SELECT_H 1 +# define HAVE_SYS_SOCKET_H 1 # define HAVE_SYS_STAT_H 1 # define HAVE_SYS_TIME_H 1 # define HAVE_SYS_TYPES_H 1 # define HAVE_SYS_UTSNAME_H 1 # define HAVE_UNISTD_H 1 # define HAVE_VSNPRINTF 1 -# define HAVE_WCHAR_H 1 -# define HAVE_SYS_SOCKET_H 1 +/* disable this so we can build with the 10.2.8 SDK */ +/*# define HAVE_WCHAR_H 1*/ # define SELECT_TYPE_ARG1 int # define SELECT_TYPE_ARG234 (fd_set *) diff --git a/lib/io/io.dsp b/lib/io/io.dsp index aea8848e..5d117b62 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\io.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\io.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\io.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\io.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index eae9e001..6d7400ed 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\mt.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\mt.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\mt.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\mt.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 296fa42c..938bf95a 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -59,6 +59,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr) { try { CLock lock(m_mutex); + ARCH->setReuseAddrOnSocket(m_socket, true); ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); CSocketMultiplexer::getInstance()->addSocket(this, diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index c6c87789..8e313176 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -485,8 +485,9 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, sendEvent(getDisconnectedEvent()); needNewJob = true; } - catch (XArchNetwork&) { + catch (XArchNetwork& e) { // other write error + LOG((CLOG_WARN "error writing socket: %s", e.what().c_str())); onDisconnected(); sendEvent(getOutputErrorEvent()); sendEvent(getDisconnectedEvent()); @@ -531,8 +532,9 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, onDisconnected(); needNewJob = true; } - catch (XArchNetwork&) { + catch (XArchNetwork& e) { // ignore other read error + LOG((CLOG_WARN "error reading socket: %s", e.what().c_str())); } } diff --git a/lib/net/net.dsp b/lib/net/net.dsp index 53b1a438..9080dbdb 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\net.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\net.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\net.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\net.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index ec56d2c1..4a1c4474 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -14,6 +14,7 @@ #include "CMSWindowsDesks.h" #include "CMSWindowsScreen.h" +#include "CSynergyHook.h" #include "IScreenSaver.h" #include "XScreen.h" #include "CLock.h" @@ -45,8 +46,8 @@ #define WM_NCXBUTTONDOWN 0x00AB #define WM_NCXBUTTONUP 0x00AC #define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 +#define MOUSEEVENTF_XDOWN 0x0080 +#define MOUSEEVENTF_XUP 0x0100 #define XBUTTON1 0x0001 #define XBUTTON2 0x0002 #endif @@ -67,7 +68,7 @@ #define SYNERGY_MSG_FAKE_BUTTON SYNERGY_HOOK_LAST_MSG + 5 // x; y #define SYNERGY_MSG_FAKE_MOVE SYNERGY_HOOK_LAST_MSG + 6 -// delta; +// xDelta; yDelta #define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7 // POINT*; #define SYNERGY_MSG_CURSOR_POS SYNERGY_HOOK_LAST_MSG + 8 @@ -77,6 +78,8 @@ #define SYNERGY_MSG_SCREENSAVER SYNERGY_HOOK_LAST_MSG + 10 // dx; dy #define SYNERGY_MSG_FAKE_REL_MOVE SYNERGY_HOOK_LAST_MSG + 11 +// enable; +#define SYNERGY_MSG_FAKE_INPUT SYNERGY_HOOK_LAST_MSG + 12 // // CMSWindowsDesks @@ -106,6 +109,7 @@ CMSWindowsDesks::CMSWindowsDesks( m_cursor = createBlankCursor(); m_deskClass = createDeskWindowClass(m_isPrimary); m_keyLayout = GetKeyboardLayout(GetCurrentThreadId()); + resetOptions(); } CMSWindowsDesks::~CMSWindowsDesks() @@ -164,6 +168,23 @@ CMSWindowsDesks::leave(HKL keyLayout) sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)keyLayout, 0); } +void +CMSWindowsDesks::resetOptions() +{ + m_leaveForegroundOption = false; +} + +void +CMSWindowsDesks::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionWin32KeepForeground) { + m_leaveForegroundOption = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "%s the foreground window", m_leaveForegroundOption ? "Don\'t grab" : "Grab")); + } + } +} + void CMSWindowsDesks::updateKeys() { @@ -193,6 +214,18 @@ CMSWindowsDesks::installScreensaverHooks(bool install) } } +void +CMSWindowsDesks::fakeInputBegin() +{ + sendMessage(SYNERGY_MSG_FAKE_INPUT, 1, 0); +} + +void +CMSWindowsDesks::fakeInputEnd() +{ + sendMessage(SYNERGY_MSG_FAKE_INPUT, 0, 0); +} + void CMSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const { @@ -310,9 +343,9 @@ CMSWindowsDesks::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -CMSWindowsDesks::fakeMouseWheel(SInt32 delta) const +CMSWindowsDesks::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - sendMessage(SYNERGY_MSG_FAKE_WHEEL, delta, 0); + sendMessage(SYNERGY_MSG_FAKE_WHEEL, xDelta, yDelta); } void @@ -565,9 +598,21 @@ CMSWindowsDesks::deskEnter(CDesk* desk) SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW); - // this is here only because of the "ConsoleWindowClass" stuff in - // deskLeave. + // restore the foreground window + // XXX -- this raises the window to the top of the Z-order. we + // want it to stay wherever it was to properly support X-mouse + // (mouse over activation) but i've no idea how to do that. + // the obvious workaround of using SetWindowPos() to move it back + // after being raised doesn't work. + DWORD thisThread = + GetWindowThreadProcessId(desk->m_window, NULL); + DWORD thatThread = + GetWindowThreadProcessId(desk->m_foregroundWindow, NULL); + AttachThreadInput(thatThread, thisThread, TRUE); + SetForegroundWindow(desk->m_foregroundWindow); + AttachThreadInput(thatThread, thisThread, FALSE); EnableWindow(desk->m_window, desk->m_lowLevel ? FALSE : TRUE); + desk->m_foregroundWindow = NULL; } void @@ -575,9 +620,6 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) { ShowCursor(FALSE); if (m_isPrimary) { - // update key state - m_updateKeys->run(); - // map a window to hide the cursor and to use whatever keyboard // layout we choose rather than the keyboard layout of the last // active window. @@ -610,38 +652,26 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) SetActiveWindow(desk->m_window); } - // if the active window is a console then activate our window. - // we do this because for some reason our hook reports unshifted - // characters when the shift is down and a console window is - // active. interestingly we do see the shift key go down and up. + // if using low-level hooks then disable the foreground window + // so it can't mess up any of our keyboard events. the console + // program, for example, will cause characters to be reported as + // unshifted, regardless of the shift key state. interestingly + // we do see the shift key go down and up. + // // note that we must enable the window to activate it and we // need to disable the window on deskEnter. - // FIXME -- figure out the real problem here and solve it. else { - HWND foreground = GetForegroundWindow(); - if (foreground != NULL) { - char className[40]; - if (GetClassName(foreground, className, - sizeof(className) / sizeof(className[0])) && - strcmp(className, "ConsoleWindowClass") == 0) { - EnableWindow(desk->m_window, TRUE); - SetActiveWindow(desk->m_window); - - // force our window to the foreground. we can't - // simply call SetForegroundWindow() because that - // will only alert the user that the window wants - // to be the foreground as of windows 98/2000. we - // have to attach to the thread of the current - // foreground window then call it on our window - // and finally detach the threads. - DWORD thisThread = - GetWindowThreadProcessId(desk->m_window, NULL); - DWORD thatThread = - GetWindowThreadProcessId(foreground, NULL); - AttachThreadInput(thatThread, thisThread, TRUE); - SetForegroundWindow(desk->m_window); - AttachThreadInput(thatThread, thisThread, FALSE); - } + desk->m_foregroundWindow = getForegroundWindow(); + if (desk->m_foregroundWindow != NULL) { + EnableWindow(desk->m_window, TRUE); + SetActiveWindow(desk->m_window); + DWORD thisThread = + GetWindowThreadProcessId(desk->m_window, NULL); + DWORD thatThread = + GetWindowThreadProcessId(desk->m_foregroundWindow, NULL); + AttachThreadInput(thatThread, thisThread, TRUE); + SetForegroundWindow(desk->m_window); + AttachThreadInput(thatThread, thisThread, FALSE); } } @@ -671,9 +701,10 @@ CMSWindowsDesks::deskThread(void* vdesk) MSG msg; // use given desktop for this thread - CDesk* desk = reinterpret_cast(vdesk); - desk->m_threadID = GetCurrentThreadId(); - desk->m_window = NULL; + CDesk* desk = reinterpret_cast(vdesk); + desk->m_threadID = GetCurrentThreadId(); + desk->m_window = NULL; + desk->m_foregroundWindow = NULL; if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) { // create a message queue PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE); @@ -763,7 +794,10 @@ CMSWindowsDesks::deskThread(void* vdesk) break; case SYNERGY_MSG_FAKE_WHEEL: - mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0); + // XXX -- add support for x-axis scrolling + if (msg.lParam != 0) { + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.lParam, 0); + } break; case SYNERGY_MSG_CURSOR_POS: { @@ -787,6 +821,12 @@ CMSWindowsDesks::deskThread(void* vdesk) m_uninstallScreensaver(); } break; + + case SYNERGY_MSG_FAKE_INPUT: + keybd_event(SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY, + SYNERGY_HOOK_FAKE_INPUT_SCANCODE, + msg.wParam ? 0 : KEYEVENTF_KEYUP, 0); + break; } // notify that message was processed @@ -870,11 +910,12 @@ CMSWindowsDesks::checkDesk() // inaccessible desktop to an accessible one we have to // update the keyboard state. LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); + bool syncKeys = false; bool isAccessible = isDeskAccessible(desk); if (isDeskAccessible(m_activeDesk) != isAccessible) { if (isAccessible) { LOG((CLOG_DEBUG "desktop is now accessible")); - sendMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); + syncKeys = true; } else { LOG((CLOG_DEBUG "desktop is now inaccessible")); @@ -890,6 +931,11 @@ CMSWindowsDesks::checkDesk() if (!wasOnScreen) { sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); } + + // update keys if necessary + if (syncKeys) { + updateKeys(); + } } else if (name != m_activeDeskName) { // screen saver might have started @@ -972,3 +1018,16 @@ CMSWindowsDesks::getDesktopName(HDESK desk) return result; } } + +HWND +CMSWindowsDesks::getForegroundWindow() const +{ + // Ideally we'd return NULL as much as possible, only returning + // the actual foreground window when we know it's going to mess + // up our keyboard input. For now we'll just let the user + // decide. + if (m_leaveForegroundOption) { + return NULL; + } + return GetForegroundWindow(); +} diff --git a/lib/platform/CMSWindowsDesks.h b/lib/platform/CMSWindowsDesks.h index caa1bb7d..f0388f98 100644 --- a/lib/platform/CMSWindowsDesks.h +++ b/lib/platform/CMSWindowsDesks.h @@ -18,6 +18,7 @@ #include "CSynergyHook.h" #include "KeyTypes.h" #include "MouseTypes.h" +#include "OptionTypes.h" #include "CCondVar.h" #include "CMutex.h" #include "CString.h" @@ -92,6 +93,19 @@ public: */ void leave(HKL keyLayout); + //! Notify of options changes + /*! + Resets all options to their default values. + */ + void resetOptions(); + + //! Notify of options changes + /*! + Set options to given values. Ignores unknown options and doesn't + modify options that aren't given in \c options. + */ + void setOptions(const COptionsList& options); + //! Update the key state /*! Causes the key state to get updated to reflect the physical keyboard @@ -115,6 +129,18 @@ public: */ void installScreensaverHooks(bool install); + //! Start ignoring user input + /*! + Starts ignoring user input so we don't pick up our own synthesized events. + */ + void fakeInputBegin(); + + //! Stop ignoring user input + /*! + Undoes whatever \c fakeInputBegin() did. + */ + void fakeInputEnd(); + //@} //! @name accessors //@{ @@ -152,9 +178,9 @@ public: //! Fake mouse wheel /*! - Synthesize a mouse wheel event of amount \c delta. + Synthesize a mouse wheel event of amount \c delta in direction \c axis. */ - void fakeMouseWheel(SInt32 delta) const; + void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; //@} @@ -167,6 +193,7 @@ private: DWORD m_targetID; HDESK m_desk; HWND m_window; + HWND m_foregroundWindow; bool m_lowLevel; }; typedef std::map CDesks; @@ -198,6 +225,9 @@ private: void waitForDesk() const; void sendMessage(UINT, WPARAM, LPARAM) const; + // work around for messed up keyboard events from low-level hooks + HWND getForegroundWindow() const; + // desk API wrappers HDESK openInputDesktop(); void closeDesktop(HDESK); @@ -258,6 +288,9 @@ private: // keyboard stuff IJob* m_updateKeys; HKL m_keyLayout; + + // options + bool m_leaveForegroundOption; }; #endif diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index e65030ac..2baba7d0 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -18,6 +18,8 @@ #include "CFunctionJob.h" #include "CLog.h" #include "CStringUtil.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include "CArchMiscWindows.h" // extended mouse buttons @@ -26,698 +28,586 @@ #define VK_XBUTTON2 0x06 #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 -#if !defined(VK_SLEEP) -#define VK_SLEEP 0x5F -#endif - // // CMSWindowsKeyState // -const char* CMSWindowsKeyState::s_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_SLEEP", - "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" -}; - // map virtual keys to synergy key enumeration -const KeyID CMSWindowsKeyState::s_virtualKey[][2] = +const KeyID CMSWindowsKeyState::s_virtualKey[] = { - /* 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 */ { kKeyZenkaku, kKeyNone }, // VK_KANJI - /* 0x1a */ { kKeyNone, kKeyNone }, // undefined - /* 0x1b */ { kKeyEscape, kKeyNone }, // VK_ESCAPE - /* 0x1c */ { kKeyHenkan, 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 */ { kKeySleep, kKeyNone }, // VK_SLEEP - /* 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 + /* 0x000 */ { kKeyNone }, // reserved + /* 0x001 */ { kKeyNone }, // VK_LBUTTON + /* 0x002 */ { kKeyNone }, // VK_RBUTTON + /* 0x003 */ { kKeyNone }, // VK_CANCEL + /* 0x004 */ { kKeyNone }, // VK_MBUTTON + /* 0x005 */ { kKeyNone }, // VK_XBUTTON1 + /* 0x006 */ { kKeyNone }, // VK_XBUTTON2 + /* 0x007 */ { kKeyNone }, // undefined + /* 0x008 */ { kKeyBackSpace }, // VK_BACK + /* 0x009 */ { kKeyTab }, // VK_TAB + /* 0x00a */ { kKeyNone }, // undefined + /* 0x00b */ { kKeyNone }, // undefined + /* 0x00c */ { kKeyClear }, // VK_CLEAR + /* 0x00d */ { kKeyReturn }, // VK_RETURN + /* 0x00e */ { kKeyNone }, // undefined + /* 0x00f */ { kKeyNone }, // undefined + /* 0x010 */ { kKeyShift_L }, // VK_SHIFT + /* 0x011 */ { kKeyControl_L }, // VK_CONTROL + /* 0x012 */ { kKeyAlt_L }, // VK_MENU + /* 0x013 */ { kKeyPause }, // VK_PAUSE + /* 0x014 */ { kKeyCapsLock }, // VK_CAPITAL + /* 0x015 */ { kKeyNone }, // VK_KANA + /* 0x016 */ { kKeyNone }, // VK_HANGUL + /* 0x017 */ { kKeyNone }, // VK_JUNJA + /* 0x018 */ { kKeyNone }, // VK_FINAL + /* 0x019 */ { kKeyZenkaku }, // VK_KANJI + /* 0x01a */ { kKeyNone }, // undefined + /* 0x01b */ { kKeyEscape }, // VK_ESCAPE + /* 0x01c */ { kKeyHenkan }, // VK_CONVERT + /* 0x01d */ { kKeyNone }, // VK_NONCONVERT + /* 0x01e */ { kKeyNone }, // VK_ACCEPT + /* 0x01f */ { kKeyNone }, // VK_MODECHANGE + /* 0x020 */ { kKeyNone }, // VK_SPACE + /* 0x021 */ { kKeyKP_PageUp }, // VK_PRIOR + /* 0x022 */ { kKeyKP_PageDown },// VK_NEXT + /* 0x023 */ { kKeyKP_End }, // VK_END + /* 0x024 */ { kKeyKP_Home }, // VK_HOME + /* 0x025 */ { kKeyKP_Left }, // VK_LEFT + /* 0x026 */ { kKeyKP_Up }, // VK_UP + /* 0x027 */ { kKeyKP_Right }, // VK_RIGHT + /* 0x028 */ { kKeyKP_Down }, // VK_DOWN + /* 0x029 */ { kKeySelect }, // VK_SELECT + /* 0x02a */ { kKeyNone }, // VK_PRINT + /* 0x02b */ { kKeyExecute }, // VK_EXECUTE + /* 0x02c */ { kKeyPrint }, // VK_SNAPSHOT + /* 0x02d */ { kKeyKP_Insert }, // VK_INSERT + /* 0x02e */ { kKeyKP_Delete }, // VK_DELETE + /* 0x02f */ { kKeyHelp }, // VK_HELP + /* 0x030 */ { kKeyNone }, // VK_0 + /* 0x031 */ { kKeyNone }, // VK_1 + /* 0x032 */ { kKeyNone }, // VK_2 + /* 0x033 */ { kKeyNone }, // VK_3 + /* 0x034 */ { kKeyNone }, // VK_4 + /* 0x035 */ { kKeyNone }, // VK_5 + /* 0x036 */ { kKeyNone }, // VK_6 + /* 0x037 */ { kKeyNone }, // VK_7 + /* 0x038 */ { kKeyNone }, // VK_8 + /* 0x039 */ { kKeyNone }, // VK_9 + /* 0x03a */ { kKeyNone }, // undefined + /* 0x03b */ { kKeyNone }, // undefined + /* 0x03c */ { kKeyNone }, // undefined + /* 0x03d */ { kKeyNone }, // undefined + /* 0x03e */ { kKeyNone }, // undefined + /* 0x03f */ { kKeyNone }, // undefined + /* 0x040 */ { kKeyNone }, // undefined + /* 0x041 */ { kKeyNone }, // VK_A + /* 0x042 */ { kKeyNone }, // VK_B + /* 0x043 */ { kKeyNone }, // VK_C + /* 0x044 */ { kKeyNone }, // VK_D + /* 0x045 */ { kKeyNone }, // VK_E + /* 0x046 */ { kKeyNone }, // VK_F + /* 0x047 */ { kKeyNone }, // VK_G + /* 0x048 */ { kKeyNone }, // VK_H + /* 0x049 */ { kKeyNone }, // VK_I + /* 0x04a */ { kKeyNone }, // VK_J + /* 0x04b */ { kKeyNone }, // VK_K + /* 0x04c */ { kKeyNone }, // VK_L + /* 0x04d */ { kKeyNone }, // VK_M + /* 0x04e */ { kKeyNone }, // VK_N + /* 0x04f */ { kKeyNone }, // VK_O + /* 0x050 */ { kKeyNone }, // VK_P + /* 0x051 */ { kKeyNone }, // VK_Q + /* 0x052 */ { kKeyNone }, // VK_R + /* 0x053 */ { kKeyNone }, // VK_S + /* 0x054 */ { kKeyNone }, // VK_T + /* 0x055 */ { kKeyNone }, // VK_U + /* 0x056 */ { kKeyNone }, // VK_V + /* 0x057 */ { kKeyNone }, // VK_W + /* 0x058 */ { kKeyNone }, // VK_X + /* 0x059 */ { kKeyNone }, // VK_Y + /* 0x05a */ { kKeyNone }, // VK_Z + /* 0x05b */ { kKeySuper_L }, // VK_LWIN + /* 0x05c */ { kKeySuper_R }, // VK_RWIN + /* 0x05d */ { kKeyMenu }, // VK_APPS + /* 0x05e */ { kKeyNone }, // undefined + /* 0x05f */ { kKeySleep }, // VK_SLEEP + /* 0x060 */ { kKeyKP_0 }, // VK_NUMPAD0 + /* 0x061 */ { kKeyKP_1 }, // VK_NUMPAD1 + /* 0x062 */ { kKeyKP_2 }, // VK_NUMPAD2 + /* 0x063 */ { kKeyKP_3 }, // VK_NUMPAD3 + /* 0x064 */ { kKeyKP_4 }, // VK_NUMPAD4 + /* 0x065 */ { kKeyKP_5 }, // VK_NUMPAD5 + /* 0x066 */ { kKeyKP_6 }, // VK_NUMPAD6 + /* 0x067 */ { kKeyKP_7 }, // VK_NUMPAD7 + /* 0x068 */ { kKeyKP_8 }, // VK_NUMPAD8 + /* 0x069 */ { kKeyKP_9 }, // VK_NUMPAD9 + /* 0x06a */ { kKeyKP_Multiply },// VK_MULTIPLY + /* 0x06b */ { kKeyKP_Add }, // VK_ADD + /* 0x06c */ { kKeyKP_Separator },// VK_SEPARATOR + /* 0x06d */ { kKeyKP_Subtract },// VK_SUBTRACT + /* 0x06e */ { kKeyKP_Decimal }, // VK_DECIMAL + /* 0x06f */ { kKeyNone }, // VK_DIVIDE + /* 0x070 */ { kKeyF1 }, // VK_F1 + /* 0x071 */ { kKeyF2 }, // VK_F2 + /* 0x072 */ { kKeyF3 }, // VK_F3 + /* 0x073 */ { kKeyF4 }, // VK_F4 + /* 0x074 */ { kKeyF5 }, // VK_F5 + /* 0x075 */ { kKeyF6 }, // VK_F6 + /* 0x076 */ { kKeyF7 }, // VK_F7 + /* 0x077 */ { kKeyF8 }, // VK_F8 + /* 0x078 */ { kKeyF9 }, // VK_F9 + /* 0x079 */ { kKeyF10 }, // VK_F10 + /* 0x07a */ { kKeyF11 }, // VK_F11 + /* 0x07b */ { kKeyF12 }, // VK_F12 + /* 0x07c */ { kKeyF13 }, // VK_F13 + /* 0x07d */ { kKeyF14 }, // VK_F14 + /* 0x07e */ { kKeyF15 }, // VK_F15 + /* 0x07f */ { kKeyF16 }, // VK_F16 + /* 0x080 */ { kKeyF17 }, // VK_F17 + /* 0x081 */ { kKeyF18 }, // VK_F18 + /* 0x082 */ { kKeyF19 }, // VK_F19 + /* 0x083 */ { kKeyF20 }, // VK_F20 + /* 0x084 */ { kKeyF21 }, // VK_F21 + /* 0x085 */ { kKeyF22 }, // VK_F22 + /* 0x086 */ { kKeyF23 }, // VK_F23 + /* 0x087 */ { kKeyF24 }, // VK_F24 + /* 0x088 */ { kKeyNone }, // unassigned + /* 0x089 */ { kKeyNone }, // unassigned + /* 0x08a */ { kKeyNone }, // unassigned + /* 0x08b */ { kKeyNone }, // unassigned + /* 0x08c */ { kKeyNone }, // unassigned + /* 0x08d */ { kKeyNone }, // unassigned + /* 0x08e */ { kKeyNone }, // unassigned + /* 0x08f */ { kKeyNone }, // unassigned + /* 0x090 */ { kKeyNumLock }, // VK_NUMLOCK + /* 0x091 */ { kKeyScrollLock }, // VK_SCROLL + /* 0x092 */ { kKeyNone }, // unassigned + /* 0x093 */ { kKeyNone }, // unassigned + /* 0x094 */ { kKeyNone }, // unassigned + /* 0x095 */ { kKeyNone }, // unassigned + /* 0x096 */ { kKeyNone }, // unassigned + /* 0x097 */ { kKeyNone }, // unassigned + /* 0x098 */ { kKeyNone }, // unassigned + /* 0x099 */ { kKeyNone }, // unassigned + /* 0x09a */ { kKeyNone }, // unassigned + /* 0x09b */ { kKeyNone }, // unassigned + /* 0x09c */ { kKeyNone }, // unassigned + /* 0x09d */ { kKeyNone }, // unassigned + /* 0x09e */ { kKeyNone }, // unassigned + /* 0x09f */ { kKeyNone }, // unassigned + /* 0x0a0 */ { kKeyShift_L }, // VK_LSHIFT + /* 0x0a1 */ { kKeyShift_R }, // VK_RSHIFT + /* 0x0a2 */ { kKeyControl_L }, // VK_LCONTROL + /* 0x0a3 */ { kKeyControl_R }, // VK_RCONTROL + /* 0x0a4 */ { kKeyAlt_L }, // VK_LMENU + /* 0x0a5 */ { kKeyAlt_R }, // VK_RMENU + /* 0x0a6 */ { kKeyNone }, // VK_BROWSER_BACK + /* 0x0a7 */ { kKeyNone }, // VK_BROWSER_FORWARD + /* 0x0a8 */ { kKeyNone }, // VK_BROWSER_REFRESH + /* 0x0a9 */ { kKeyNone }, // VK_BROWSER_STOP + /* 0x0aa */ { kKeyNone }, // VK_BROWSER_SEARCH + /* 0x0ab */ { kKeyNone }, // VK_BROWSER_FAVORITES + /* 0x0ac */ { kKeyNone }, // VK_BROWSER_HOME + /* 0x0ad */ { kKeyNone }, // VK_VOLUME_MUTE + /* 0x0ae */ { kKeyNone }, // VK_VOLUME_DOWN + /* 0x0af */ { kKeyNone }, // VK_VOLUME_UP + /* 0x0b0 */ { kKeyNone }, // VK_MEDIA_NEXT_TRACK + /* 0x0b1 */ { kKeyNone }, // VK_MEDIA_PREV_TRACK + /* 0x0b2 */ { kKeyNone }, // VK_MEDIA_STOP + /* 0x0b3 */ { kKeyNone }, // VK_MEDIA_PLAY_PAUSE + /* 0x0b4 */ { kKeyNone }, // VK_LAUNCH_MAIL + /* 0x0b5 */ { kKeyNone }, // VK_LAUNCH_MEDIA_SELECT + /* 0x0b6 */ { kKeyNone }, // VK_LAUNCH_APP1 + /* 0x0b7 */ { kKeyNone }, // VK_LAUNCH_APP2 + /* 0x0b8 */ { kKeyNone }, // unassigned + /* 0x0b9 */ { kKeyNone }, // unassigned + /* 0x0ba */ { kKeyNone }, // OEM specific + /* 0x0bb */ { kKeyNone }, // OEM specific + /* 0x0bc */ { kKeyNone }, // OEM specific + /* 0x0bd */ { kKeyNone }, // OEM specific + /* 0x0be */ { kKeyNone }, // OEM specific + /* 0x0bf */ { kKeyNone }, // OEM specific + /* 0x0c0 */ { kKeyNone }, // OEM specific + /* 0x0c1 */ { kKeyNone }, // unassigned + /* 0x0c2 */ { kKeyNone }, // unassigned + /* 0x0c3 */ { kKeyNone }, // unassigned + /* 0x0c4 */ { kKeyNone }, // unassigned + /* 0x0c5 */ { kKeyNone }, // unassigned + /* 0x0c6 */ { kKeyNone }, // unassigned + /* 0x0c7 */ { kKeyNone }, // unassigned + /* 0x0c8 */ { kKeyNone }, // unassigned + /* 0x0c9 */ { kKeyNone }, // unassigned + /* 0x0ca */ { kKeyNone }, // unassigned + /* 0x0cb */ { kKeyNone }, // unassigned + /* 0x0cc */ { kKeyNone }, // unassigned + /* 0x0cd */ { kKeyNone }, // unassigned + /* 0x0ce */ { kKeyNone }, // unassigned + /* 0x0cf */ { kKeyNone }, // unassigned + /* 0x0d0 */ { kKeyNone }, // unassigned + /* 0x0d1 */ { kKeyNone }, // unassigned + /* 0x0d2 */ { kKeyNone }, // unassigned + /* 0x0d3 */ { kKeyNone }, // unassigned + /* 0x0d4 */ { kKeyNone }, // unassigned + /* 0x0d5 */ { kKeyNone }, // unassigned + /* 0x0d6 */ { kKeyNone }, // unassigned + /* 0x0d7 */ { kKeyNone }, // unassigned + /* 0x0d8 */ { kKeyNone }, // unassigned + /* 0x0d9 */ { kKeyNone }, // unassigned + /* 0x0da */ { kKeyNone }, // unassigned + /* 0x0db */ { kKeyNone }, // OEM specific + /* 0x0dc */ { kKeyNone }, // OEM specific + /* 0x0dd */ { kKeyNone }, // OEM specific + /* 0x0de */ { kKeyNone }, // OEM specific + /* 0x0df */ { kKeyNone }, // OEM specific + /* 0x0e0 */ { kKeyNone }, // OEM specific + /* 0x0e1 */ { kKeyNone }, // OEM specific + /* 0x0e2 */ { kKeyNone }, // OEM specific + /* 0x0e3 */ { kKeyNone }, // OEM specific + /* 0x0e4 */ { kKeyNone }, // OEM specific + /* 0x0e5 */ { kKeyNone }, // unassigned + /* 0x0e6 */ { kKeyNone }, // OEM specific + /* 0x0e7 */ { kKeyNone }, // unassigned + /* 0x0e8 */ { kKeyNone }, // unassigned + /* 0x0e9 */ { kKeyNone }, // OEM specific + /* 0x0ea */ { kKeyNone }, // OEM specific + /* 0x0eb */ { kKeyNone }, // OEM specific + /* 0x0ec */ { kKeyNone }, // OEM specific + /* 0x0ed */ { kKeyNone }, // OEM specific + /* 0x0ee */ { kKeyNone }, // OEM specific + /* 0x0ef */ { kKeyNone }, // OEM specific + /* 0x0f0 */ { kKeyNone }, // OEM specific + /* 0x0f1 */ { kKeyNone }, // OEM specific + /* 0x0f2 */ { kKeyNone }, // OEM specific + /* 0x0f3 */ { kKeyNone }, // OEM specific + /* 0x0f4 */ { kKeyNone }, // OEM specific + /* 0x0f5 */ { kKeyNone }, // OEM specific + /* 0x0f6 */ { kKeyNone }, // VK_ATTN + /* 0x0f7 */ { kKeyNone }, // VK_CRSEL + /* 0x0f8 */ { kKeyNone }, // VK_EXSEL + /* 0x0f9 */ { kKeyNone }, // VK_EREOF + /* 0x0fa */ { kKeyNone }, // VK_PLAY + /* 0x0fb */ { kKeyNone }, // VK_ZOOM + /* 0x0fc */ { kKeyNone }, // reserved + /* 0x0fd */ { kKeyNone }, // VK_PA1 + /* 0x0fe */ { kKeyNone }, // VK_OEM_CLEAR + /* 0x0ff */ { kKeyNone }, // reserved + + /* 0x100 */ { kKeyNone }, // reserved + /* 0x101 */ { kKeyNone }, // VK_LBUTTON + /* 0x102 */ { kKeyNone }, // VK_RBUTTON + /* 0x103 */ { kKeyBreak }, // VK_CANCEL + /* 0x104 */ { kKeyNone }, // VK_MBUTTON + /* 0x105 */ { kKeyNone }, // VK_XBUTTON1 + /* 0x106 */ { kKeyNone }, // VK_XBUTTON2 + /* 0x107 */ { kKeyNone }, // undefined + /* 0x108 */ { kKeyNone }, // VK_BACK + /* 0x109 */ { kKeyNone }, // VK_TAB + /* 0x10a */ { kKeyNone }, // undefined + /* 0x10b */ { kKeyNone }, // undefined + /* 0x10c */ { kKeyClear }, // VK_CLEAR + /* 0x10d */ { kKeyKP_Enter }, // VK_RETURN + /* 0x10e */ { kKeyNone }, // undefined + /* 0x10f */ { kKeyNone }, // undefined + /* 0x110 */ { kKeyShift_R }, // VK_SHIFT + /* 0x111 */ { kKeyControl_R }, // VK_CONTROL + /* 0x112 */ { kKeyAlt_R }, // VK_MENU + /* 0x113 */ { kKeyNone }, // VK_PAUSE + /* 0x114 */ { kKeyNone }, // VK_CAPITAL + /* 0x115 */ { kKeyNone }, // VK_KANA + /* 0x116 */ { kKeyNone }, // VK_HANGUL + /* 0x117 */ { kKeyNone }, // VK_JUNJA + /* 0x118 */ { kKeyNone }, // VK_FINAL + /* 0x119 */ { kKeyNone }, // VK_KANJI + /* 0x11a */ { kKeyNone }, // undefined + /* 0x11b */ { kKeyNone }, // VK_ESCAPE + /* 0x11c */ { kKeyNone }, // VK_CONVERT + /* 0x11d */ { kKeyNone }, // VK_NONCONVERT + /* 0x11e */ { kKeyNone }, // VK_ACCEPT + /* 0x11f */ { kKeyNone }, // VK_MODECHANGE + /* 0x120 */ { kKeyNone }, // VK_SPACE + /* 0x121 */ { kKeyPageUp }, // VK_PRIOR + /* 0x122 */ { kKeyPageDown }, // VK_NEXT + /* 0x123 */ { kKeyEnd }, // VK_END + /* 0x124 */ { kKeyHome }, // VK_HOME + /* 0x125 */ { kKeyLeft }, // VK_LEFT + /* 0x126 */ { kKeyUp }, // VK_UP + /* 0x127 */ { kKeyRight }, // VK_RIGHT + /* 0x128 */ { kKeyDown }, // VK_DOWN + /* 0x129 */ { kKeySelect }, // VK_SELECT + /* 0x12a */ { kKeyNone }, // VK_PRINT + /* 0x12b */ { kKeyExecute }, // VK_EXECUTE + /* 0x12c */ { kKeyPrint }, // VK_SNAPSHOT + /* 0x12d */ { kKeyInsert }, // VK_INSERT + /* 0x12e */ { kKeyDelete }, // VK_DELETE + /* 0x12f */ { kKeyHelp }, // VK_HELP + /* 0x130 */ { kKeyNone }, // VK_0 + /* 0x131 */ { kKeyNone }, // VK_1 + /* 0x132 */ { kKeyNone }, // VK_2 + /* 0x133 */ { kKeyNone }, // VK_3 + /* 0x134 */ { kKeyNone }, // VK_4 + /* 0x135 */ { kKeyNone }, // VK_5 + /* 0x136 */ { kKeyNone }, // VK_6 + /* 0x137 */ { kKeyNone }, // VK_7 + /* 0x138 */ { kKeyNone }, // VK_8 + /* 0x139 */ { kKeyNone }, // VK_9 + /* 0x13a */ { kKeyNone }, // undefined + /* 0x13b */ { kKeyNone }, // undefined + /* 0x13c */ { kKeyNone }, // undefined + /* 0x13d */ { kKeyNone }, // undefined + /* 0x13e */ { kKeyNone }, // undefined + /* 0x13f */ { kKeyNone }, // undefined + /* 0x140 */ { kKeyNone }, // undefined + /* 0x141 */ { kKeyNone }, // VK_A + /* 0x142 */ { kKeyNone }, // VK_B + /* 0x143 */ { kKeyNone }, // VK_C + /* 0x144 */ { kKeyNone }, // VK_D + /* 0x145 */ { kKeyNone }, // VK_E + /* 0x146 */ { kKeyNone }, // VK_F + /* 0x147 */ { kKeyNone }, // VK_G + /* 0x148 */ { kKeyNone }, // VK_H + /* 0x149 */ { kKeyNone }, // VK_I + /* 0x14a */ { kKeyNone }, // VK_J + /* 0x14b */ { kKeyNone }, // VK_K + /* 0x14c */ { kKeyNone }, // VK_L + /* 0x14d */ { kKeyNone }, // VK_M + /* 0x14e */ { kKeyNone }, // VK_N + /* 0x14f */ { kKeyNone }, // VK_O + /* 0x150 */ { kKeyNone }, // VK_P + /* 0x151 */ { kKeyNone }, // VK_Q + /* 0x152 */ { kKeyNone }, // VK_R + /* 0x153 */ { kKeyNone }, // VK_S + /* 0x154 */ { kKeyNone }, // VK_T + /* 0x155 */ { kKeyNone }, // VK_U + /* 0x156 */ { kKeyNone }, // VK_V + /* 0x157 */ { kKeyNone }, // VK_W + /* 0x158 */ { kKeyNone }, // VK_X + /* 0x159 */ { kKeyNone }, // VK_Y + /* 0x15a */ { kKeyNone }, // VK_Z + /* 0x15b */ { kKeySuper_L }, // VK_LWIN + /* 0x15c */ { kKeySuper_R }, // VK_RWIN + /* 0x15d */ { kKeyMenu }, // VK_APPS + /* 0x15e */ { kKeyNone }, // undefined + /* 0x15f */ { kKeyNone }, // VK_SLEEP + /* 0x160 */ { kKeyNone }, // VK_NUMPAD0 + /* 0x161 */ { kKeyNone }, // VK_NUMPAD1 + /* 0x162 */ { kKeyNone }, // VK_NUMPAD2 + /* 0x163 */ { kKeyNone }, // VK_NUMPAD3 + /* 0x164 */ { kKeyNone }, // VK_NUMPAD4 + /* 0x165 */ { kKeyNone }, // VK_NUMPAD5 + /* 0x166 */ { kKeyNone }, // VK_NUMPAD6 + /* 0x167 */ { kKeyNone }, // VK_NUMPAD7 + /* 0x168 */ { kKeyNone }, // VK_NUMPAD8 + /* 0x169 */ { kKeyNone }, // VK_NUMPAD9 + /* 0x16a */ { kKeyNone }, // VK_MULTIPLY + /* 0x16b */ { kKeyNone }, // VK_ADD + /* 0x16c */ { kKeyKP_Separator },// VK_SEPARATOR + /* 0x16d */ { kKeyNone }, // VK_SUBTRACT + /* 0x16e */ { kKeyNone }, // VK_DECIMAL + /* 0x16f */ { kKeyKP_Divide }, // VK_DIVIDE + /* 0x170 */ { kKeyNone }, // VK_F1 + /* 0x171 */ { kKeyNone }, // VK_F2 + /* 0x172 */ { kKeyNone }, // VK_F3 + /* 0x173 */ { kKeyNone }, // VK_F4 + /* 0x174 */ { kKeyNone }, // VK_F5 + /* 0x175 */ { kKeyNone }, // VK_F6 + /* 0x176 */ { kKeyNone }, // VK_F7 + /* 0x177 */ { kKeyNone }, // VK_F8 + /* 0x178 */ { kKeyNone }, // VK_F9 + /* 0x179 */ { kKeyNone }, // VK_F10 + /* 0x17a */ { kKeyNone }, // VK_F11 + /* 0x17b */ { kKeyNone }, // VK_F12 + /* 0x17c */ { kKeyF13 }, // VK_F13 + /* 0x17d */ { kKeyF14 }, // VK_F14 + /* 0x17e */ { kKeyF15 }, // VK_F15 + /* 0x17f */ { kKeyF16 }, // VK_F16 + /* 0x180 */ { kKeyF17 }, // VK_F17 + /* 0x181 */ { kKeyF18 }, // VK_F18 + /* 0x182 */ { kKeyF19 }, // VK_F19 + /* 0x183 */ { kKeyF20 }, // VK_F20 + /* 0x184 */ { kKeyF21 }, // VK_F21 + /* 0x185 */ { kKeyF22 }, // VK_F22 + /* 0x186 */ { kKeyF23 }, // VK_F23 + /* 0x187 */ { kKeyF24 }, // VK_F24 + /* 0x188 */ { kKeyNone }, // unassigned + /* 0x189 */ { kKeyNone }, // unassigned + /* 0x18a */ { kKeyNone }, // unassigned + /* 0x18b */ { kKeyNone }, // unassigned + /* 0x18c */ { kKeyNone }, // unassigned + /* 0x18d */ { kKeyNone }, // unassigned + /* 0x18e */ { kKeyNone }, // unassigned + /* 0x18f */ { kKeyNone }, // unassigned + /* 0x190 */ { kKeyNumLock }, // VK_NUMLOCK + /* 0x191 */ { kKeyNone }, // VK_SCROLL + /* 0x192 */ { kKeyNone }, // unassigned + /* 0x193 */ { kKeyNone }, // unassigned + /* 0x194 */ { kKeyNone }, // unassigned + /* 0x195 */ { kKeyNone }, // unassigned + /* 0x196 */ { kKeyNone }, // unassigned + /* 0x197 */ { kKeyNone }, // unassigned + /* 0x198 */ { kKeyNone }, // unassigned + /* 0x199 */ { kKeyNone }, // unassigned + /* 0x19a */ { kKeyNone }, // unassigned + /* 0x19b */ { kKeyNone }, // unassigned + /* 0x19c */ { kKeyNone }, // unassigned + /* 0x19d */ { kKeyNone }, // unassigned + /* 0x19e */ { kKeyNone }, // unassigned + /* 0x19f */ { kKeyNone }, // unassigned + /* 0x1a0 */ { kKeyShift_L }, // VK_LSHIFT + /* 0x1a1 */ { kKeyShift_R }, // VK_RSHIFT + /* 0x1a2 */ { kKeyControl_L }, // VK_LCONTROL + /* 0x1a3 */ { kKeyControl_R }, // VK_RCONTROL + /* 0x1a4 */ { kKeyAlt_L }, // VK_LMENU + /* 0x1a5 */ { kKeyAlt_R }, // VK_RMENU + /* 0x1a6 */ { kKeyWWWBack }, // VK_BROWSER_BACK + /* 0x1a7 */ { kKeyWWWForward }, // VK_BROWSER_FORWARD + /* 0x1a8 */ { kKeyWWWRefresh }, // VK_BROWSER_REFRESH + /* 0x1a9 */ { kKeyWWWStop }, // VK_BROWSER_STOP + /* 0x1aa */ { kKeyWWWSearch }, // VK_BROWSER_SEARCH + /* 0x1ab */ { kKeyWWWFavorites },// VK_BROWSER_FAVORITES + /* 0x1ac */ { kKeyWWWHome }, // VK_BROWSER_HOME + /* 0x1ad */ { kKeyAudioMute }, // VK_VOLUME_MUTE + /* 0x1ae */ { kKeyAudioDown }, // VK_VOLUME_DOWN + /* 0x1af */ { kKeyAudioUp }, // VK_VOLUME_UP + /* 0x1b0 */ { kKeyAudioNext }, // VK_MEDIA_NEXT_TRACK + /* 0x1b1 */ { kKeyAudioPrev }, // VK_MEDIA_PREV_TRACK + /* 0x1b2 */ { kKeyAudioStop }, // VK_MEDIA_STOP + /* 0x1b3 */ { kKeyAudioPlay }, // VK_MEDIA_PLAY_PAUSE + /* 0x1b4 */ { kKeyAppMail }, // VK_LAUNCH_MAIL + /* 0x1b5 */ { kKeyAppMedia }, // VK_LAUNCH_MEDIA_SELECT + /* 0x1b6 */ { kKeyAppUser1 }, // VK_LAUNCH_APP1 + /* 0x1b7 */ { kKeyAppUser2 }, // VK_LAUNCH_APP2 + /* 0x1b8 */ { kKeyNone }, // unassigned + /* 0x1b9 */ { kKeyNone }, // unassigned + /* 0x1ba */ { kKeyNone }, // OEM specific + /* 0x1bb */ { kKeyNone }, // OEM specific + /* 0x1bc */ { kKeyNone }, // OEM specific + /* 0x1bd */ { kKeyNone }, // OEM specific + /* 0x1be */ { kKeyNone }, // OEM specific + /* 0x1bf */ { kKeyNone }, // OEM specific + /* 0x1c0 */ { kKeyNone }, // OEM specific + /* 0x1c1 */ { kKeyNone }, // unassigned + /* 0x1c2 */ { kKeyNone }, // unassigned + /* 0x1c3 */ { kKeyNone }, // unassigned + /* 0x1c4 */ { kKeyNone }, // unassigned + /* 0x1c5 */ { kKeyNone }, // unassigned + /* 0x1c6 */ { kKeyNone }, // unassigned + /* 0x1c7 */ { kKeyNone }, // unassigned + /* 0x1c8 */ { kKeyNone }, // unassigned + /* 0x1c9 */ { kKeyNone }, // unassigned + /* 0x1ca */ { kKeyNone }, // unassigned + /* 0x1cb */ { kKeyNone }, // unassigned + /* 0x1cc */ { kKeyNone }, // unassigned + /* 0x1cd */ { kKeyNone }, // unassigned + /* 0x1ce */ { kKeyNone }, // unassigned + /* 0x1cf */ { kKeyNone }, // unassigned + /* 0x1d0 */ { kKeyNone }, // unassigned + /* 0x1d1 */ { kKeyNone }, // unassigned + /* 0x1d2 */ { kKeyNone }, // unassigned + /* 0x1d3 */ { kKeyNone }, // unassigned + /* 0x1d4 */ { kKeyNone }, // unassigned + /* 0x1d5 */ { kKeyNone }, // unassigned + /* 0x1d6 */ { kKeyNone }, // unassigned + /* 0x1d7 */ { kKeyNone }, // unassigned + /* 0x1d8 */ { kKeyNone }, // unassigned + /* 0x1d9 */ { kKeyNone }, // unassigned + /* 0x1da */ { kKeyNone }, // unassigned + /* 0x1db */ { kKeyNone }, // OEM specific + /* 0x1dc */ { kKeyNone }, // OEM specific + /* 0x1dd */ { kKeyNone }, // OEM specific + /* 0x1de */ { kKeyNone }, // OEM specific + /* 0x1df */ { kKeyNone }, // OEM specific + /* 0x1e0 */ { kKeyNone }, // OEM specific + /* 0x1e1 */ { kKeyNone }, // OEM specific + /* 0x1e2 */ { kKeyNone }, // OEM specific + /* 0x1e3 */ { kKeyNone }, // OEM specific + /* 0x1e4 */ { kKeyNone }, // OEM specific + /* 0x1e5 */ { kKeyNone }, // unassigned + /* 0x1e6 */ { kKeyNone }, // OEM specific + /* 0x1e7 */ { kKeyNone }, // unassigned + /* 0x1e8 */ { kKeyNone }, // unassigned + /* 0x1e9 */ { kKeyNone }, // OEM specific + /* 0x1ea */ { kKeyNone }, // OEM specific + /* 0x1eb */ { kKeyNone }, // OEM specific + /* 0x1ec */ { kKeyNone }, // OEM specific + /* 0x1ed */ { kKeyNone }, // OEM specific + /* 0x1ee */ { kKeyNone }, // OEM specific + /* 0x1ef */ { kKeyNone }, // OEM specific + /* 0x1f0 */ { kKeyNone }, // OEM specific + /* 0x1f1 */ { kKeyNone }, // OEM specific + /* 0x1f2 */ { kKeyNone }, // OEM specific + /* 0x1f3 */ { kKeyNone }, // OEM specific + /* 0x1f4 */ { kKeyNone }, // OEM specific + /* 0x1f5 */ { kKeyNone }, // OEM specific + /* 0x1f6 */ { kKeyNone }, // VK_ATTN + /* 0x1f7 */ { kKeyNone }, // VK_CRSEL + /* 0x1f8 */ { kKeyNone }, // VK_EXSEL + /* 0x1f9 */ { kKeyNone }, // VK_EREOF + /* 0x1fa */ { kKeyNone }, // VK_PLAY + /* 0x1fb */ { kKeyNone }, // VK_ZOOM + /* 0x1fc */ { kKeyNone }, // reserved + /* 0x1fd */ { kKeyNone }, // VK_PA1 + /* 0x1fe */ { kKeyNone }, // VK_OEM_CLEAR + /* 0x1ff */ { kKeyNone } // reserved }; -// map special KeyID keys to virtual key codes plus whether or not -// the key maps to an extended scan code -const UINT CMSWindowsKeyState::s_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, VK_SLEEP | 0x100u, - /* 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 | 0x100u, VK_BROWSER_FORWARD | 0x100u, - /* 0xa8 */ VK_BROWSER_REFRESH | 0x100u, VK_BROWSER_STOP | 0x100u, - /* 0xaa */ VK_BROWSER_SEARCH | 0x100u, VK_BROWSER_FAVORITES | 0x100u, - /* 0xac */ VK_BROWSER_HOME | 0x100u, VK_VOLUME_MUTE | 0x100u, - /* 0xae */ VK_VOLUME_DOWN | 0x100u, VK_VOLUME_UP | 0x100u, - /* 0xb0 */ VK_MEDIA_NEXT_TRACK | 0x100u, VK_MEDIA_PREV_TRACK | 0x100u, - /* 0xb2 */ VK_MEDIA_STOP | 0x100u, VK_MEDIA_PLAY_PAUSE | 0x100u, - /* 0xb4 */ VK_LAUNCH_MAIL | 0x100u, VK_LAUNCH_MEDIA_SELECT | 0x100u, - /* 0xb6 */ VK_LAUNCH_APP1 | 0x100u, VK_LAUNCH_APP2 | 0x100u, - /* 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 -}; -const UINT CMSWindowsKeyState::s_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 -}; -/* in g_mapEF00, 0xac is VK_DECIMAL not VK_SEPARATOR because win32 - * doesn't seem to use VK_SEPARATOR but instead maps VK_DECIMAL to - * the same meaning. */ -const UINT CMSWindowsKeyState::s_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, VK_CONVERT, 0, 0, 0, 0, - /* 0x28 */ 0, 0, VK_KANJI, 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 | 0x100u, VK_LEFT | 0x100u, - /* 0x52 */ VK_UP | 0x100u, VK_RIGHT | 0x100u, - /* 0x54 */ VK_DOWN | 0x100u, VK_PRIOR | 0x100u, - /* 0x56 */ VK_NEXT | 0x100u, VK_END | 0x100u, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT | 0x100u, - /* 0x64 */ 0, 0, 0, VK_APPS | 0x100u, - /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL | 0x100u, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK | 0x100u, - /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN | 0x100u, 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_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE | 0x100u, - /* 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 | 0x100u, VK_F14 | 0x100u, - /* 0xcc */ VK_F15 | 0x100u, VK_F16 | 0x100u, - /* 0xce */ VK_F17 | 0x100u, VK_F18 | 0x100u, - /* 0xd0 */ VK_F19 | 0x100u, VK_F20 | 0x100u, - /* 0xd2 */ VK_F21 | 0x100u, VK_F22 | 0x100u, - /* 0xd4 */ VK_F23 | 0x100u, VK_F24 | 0x100u, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT | 0x100u, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL | 0x100u, VK_CAPITAL, 0, 0, - /* 0xe8 */ 0, VK_LMENU, VK_RMENU | 0x100u, VK_LWIN | 0x100u, - /* 0xec */ VK_RWIN | 0x100u, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE | 0x100u +struct CWin32Modifiers { +public: + UINT m_vk; + KeyModifierMask m_mask; }; -CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks) : +static const CWin32Modifiers s_modifiers[] = +{ + { VK_SHIFT, KeyModifierShift }, + { VK_LSHIFT, KeyModifierShift }, + { VK_RSHIFT, KeyModifierShift }, + { VK_CONTROL, KeyModifierControl }, + { VK_LCONTROL, KeyModifierControl }, + { VK_RCONTROL, KeyModifierControl }, + { VK_MENU, KeyModifierAlt }, + { VK_LMENU, KeyModifierAlt }, + { VK_RMENU, KeyModifierAlt }, + { VK_LWIN, KeyModifierSuper }, + { VK_RWIN, KeyModifierSuper } +}; + +CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks, + void* eventTarget) : m_is95Family(CArchMiscWindows::isWindows95Family()), + m_eventTarget(eventTarget), m_desks(desks), - m_keyLayout(GetKeyboardLayout(0)) + m_keyLayout(GetKeyboardLayout(0)), + m_fixTimer(NULL), + m_lastDown(0), + m_useSavedModifiers(false), + m_savedModifiers(0), + m_originalSavedModifiers(0) { - // do nothing + // look up symbol that's available on winNT family but not win95 + HMODULE userModule = GetModuleHandle("user32.dll"); + m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx"); } CMSWindowsKeyState::~CMSWindowsKeyState() { - // do nothing + disable(); +} + +void +CMSWindowsKeyState::disable() +{ + if (m_fixTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); + EVENTQUEUE->deleteTimer(m_fixTimer); + m_fixTimer = NULL; + } + m_lastDown = 0; +} + +KeyButton +CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const +{ + return m_virtualKeyToButton[virtualKey & 0xffu]; } void @@ -726,25 +616,39 @@ CMSWindowsKeyState::setKeyLayout(HKL keyLayout) m_keyLayout = keyLayout; } -void -CMSWindowsKeyState::fixKey(void* target, UINT virtualKey) +bool +CMSWindowsKeyState::testAutoRepeat(bool press, bool isRepeat, KeyButton button) { - // check if virtualKey is up but we think it's down. if so then - // synthesize a key release for it. - // - // we use GetAsyncKeyState() to check the state of the keys even - // though we might not be in sync with that yet. - KeyButton button = m_virtKeyToScanCode[virtualKey]; - if (isKeyDown(button) && (GetAsyncKeyState(virtualKey) & 0x8000) == 0) { - // compute appropriate parameters for fake event - LPARAM lParam = 0xc0000000 | ((LPARAM)button << 16); + if (!isRepeat) { + isRepeat = (press && m_lastDown != 0 && button == m_lastDown); + } + if (press) { + m_lastDown = button; + } + else { + m_lastDown = 0; + } + return isRepeat; +} - // process as if it were a key up - KeyModifierMask mask; - KeyID key = mapKeyFromEvent(virtualKey, lParam, &mask); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); - setKeyDown(button, false); +void +CMSWindowsKeyState::saveModifiers() +{ + m_savedModifiers = getActiveModifiers(); + m_originalSavedModifiers = m_savedModifiers; +} + +void +CMSWindowsKeyState::useSavedModifiers(bool enable) +{ + if (enable != m_useSavedModifiers) { + m_useSavedModifiers = enable; + if (!m_useSavedModifiers) { + // transfer any modifier state changes to CKeyState's state + KeyModifierMask mask = m_originalSavedModifiers ^ m_savedModifiers; + getActiveModifiersRValue() = + (getActiveModifiers() & ~mask) | (m_savedModifiers & mask); + } } } @@ -752,19 +656,16 @@ KeyID CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut) const { - // 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 + static const KeyModifierMask s_controlAlt = + KeyModifierControl | KeyModifierAlt; - // extract character and virtual key + // extract character, virtual key, and if we didn't use AltGr char c = (char)((charAndVirtKey & 0xff00u) >> 8); UINT vkCode = (charAndVirtKey & 0xffu); + bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0); // handle some keys via table lookup - int extended = ((info >> 24) & 1); - KeyID id = s_virtualKey[vkCode][extended]; + KeyID id = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu)); // check if not in table; map character to key id if (id == kKeyNone && c != 0) { @@ -789,63 +690,51 @@ CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, } } - // set mask - KeyModifierMask activeMask = getActiveModifiers(); - bool needAltGr = false; - if (id != kKeyNone && 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 = VkKeyScanEx(c, m_keyLayout); - if (virtualKeyAndModifierState == 0xffff) { - // there is no mapping. assume AltGr. - LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); - needAltGr = true; - } - else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { - // we didn't get the key that was actually pressed - LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); - if ((activeMask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - needAltGr = true; - } - } - else { - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - if ((modifierState & 6) == 6) { - // key requires ctrl and alt == AltGr - needAltGr = true; - } - } - } - - // map modifier key + // set modifier mask if (maskOut != NULL) { - if (needAltGr) { - activeMask |= KeyModifierModeSwitch; - activeMask &= ~(KeyModifierControl | KeyModifierAlt); + KeyModifierMask active = getActiveModifiers(); + if (!noAltGr && (active & s_controlAlt) == s_controlAlt) { + // if !noAltGr then we're only interested in matching the + // key, not the AltGr. AltGr is down (i.e. control and alt + // are down) but we don't want the client to have to match + // that so we clear it. + active &= ~s_controlAlt; } - else { - activeMask &= ~KeyModifierModeSwitch; - } - *maskOut = activeMask; + *maskOut = active; } return id; } -KeyButton -CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const +bool +CMSWindowsKeyState::didGroupsChange() const { - return m_virtKeyToScanCode[virtualKey & 0xffu]; + GroupList groups; + return (getGroups(groups) && groups != m_groups); +} + +UINT +CMSWindowsKeyState::mapKeyToVirtualKey(KeyID key) const +{ + if (key == kKeyNone) { + return 0; + } + KeyToVKMap::const_iterator i = m_keyToVKMap.find(key); + if (i == m_keyToVKMap.end()) { + return 0; + } + else { + return i->second; + } +} + +void +CMSWindowsKeyState::onKey(KeyButton button, bool down, KeyModifierMask newState) +{ + // handle win32 brokenness and forward to superclass + fixKeys(); + CKeyState::onKey(button, down, newState); + fixKeys(); } void @@ -855,47 +744,8 @@ CMSWindowsKeyState::sendKeyEvent(void* target, SInt32 count, KeyButton button) { if (press || isAutoRepeat) { - // if AltGr is 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 they were after we simulate - // the key. - bool ctrlL = isKeyDown(m_virtKeyToScanCode[VK_LCONTROL]); - bool ctrlR = isKeyDown(m_virtKeyToScanCode[VK_RCONTROL]); - bool altL = isKeyDown(m_virtKeyToScanCode[VK_LMENU]); - bool altR = isKeyDown(m_virtKeyToScanCode[VK_RMENU]); - if ((mask & KeyModifierModeSwitch) != 0) { - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - CKeyState::sendKeyEvent(target, false, false, - kKeyControl_L, mask2, 1, - m_virtKeyToScanCode[VK_LCONTROL]); - } - if (ctrlR) { - CKeyState::sendKeyEvent(target, false, false, - kKeyControl_R, mask2, 1, - m_virtKeyToScanCode[VK_RCONTROL]); - } - if (altL) { - CKeyState::sendKeyEvent(target, false, false, - kKeyAlt_L, mask2, 1, - m_virtKeyToScanCode[VK_LMENU]); - } - if (altR) { - CKeyState::sendKeyEvent(target, false, false, - kKeyAlt_R, mask2, 1, - m_virtKeyToScanCode[VK_RMENU]); - } - } - // send key - if (press) { + if (press && !isAutoRepeat) { CKeyState::sendKeyEvent(target, true, false, key, mask, 1, button); if (count > 0) { @@ -906,38 +756,6 @@ CMSWindowsKeyState::sendKeyEvent(void* target, CKeyState::sendKeyEvent(target, true, true, key, mask, count, button); } - - // restore ctrl and alt state - if ((mask & KeyModifierModeSwitch) != 0) { - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - CKeyState::sendKeyEvent(target, true, false, - kKeyControl_L, mask2, 1, - m_virtKeyToScanCode[VK_LCONTROL]); - mask2 |= KeyModifierControl; - } - if (ctrlR) { - CKeyState::sendKeyEvent(target, true, false, - kKeyControl_R, mask2, 1, - m_virtKeyToScanCode[VK_RCONTROL]); - mask2 |= KeyModifierControl; - } - if (altL) { - CKeyState::sendKeyEvent(target, true, false, - kKeyAlt_L, mask2, 1, - m_virtKeyToScanCode[VK_LMENU]); - mask2 |= KeyModifierAlt; - } - if (altR) { - CKeyState::sendKeyEvent(target, true, false, - kKeyAlt_R, mask2, 1, - m_virtKeyToScanCode[VK_RMENU]); - mask2 |= KeyModifierAlt; - } - } } else { // do key up @@ -945,6 +763,20 @@ CMSWindowsKeyState::sendKeyEvent(void* target, } } +void +CMSWindowsKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button) +{ + CKeyState::fakeKeyDown(id, mask, button); +} + +void +CMSWindowsKeyState::fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + CKeyState::fakeKeyRepeat(id, mask, count, button); +} + bool CMSWindowsKeyState::fakeCtrlAltDel() { @@ -960,7 +792,7 @@ CMSWindowsKeyState::fakeCtrlAltDel() else { // simulate ctrl+alt+del fakeKeyDown(kKeyDelete, KeyModifierControl | KeyModifierAlt, - m_virtKeyToScanCode[VK_DELETE]); + virtualKeyToButton(VK_DELETE)); } return true; } @@ -985,762 +817,604 @@ CMSWindowsKeyState::ctrlAltDelThread(void*) } } -const char* -CMSWindowsKeyState::getKeyName(KeyButton button) const +KeyModifierMask +CMSWindowsKeyState::pollActiveModifiers() const { - char keyName[100]; - char keyName2[100]; - CMSWindowsKeyState* self = const_cast(this); - if (GetKeyNameText(button << 16, keyName, sizeof(keyName)) != 0) { - // get the extended name of the key if button is not extended - // or vice versa. if the names are different then report both. - button ^= 0x100u; - if (GetKeyNameText(button << 16, keyName2, sizeof(keyName2)) != 0 && - strcmp(keyName, keyName2) != 0) { - self->m_keyName = CStringUtil::print("%s or %s", keyName, keyName2); - } - else { - self->m_keyName = keyName; + KeyModifierMask state = 0; + + // get non-toggle modifiers from our own shadow key state + for (size_t i = 0; i < sizeof(s_modifiers) / sizeof(s_modifiers[0]); ++i) { + KeyButton button = virtualKeyToButton(s_modifiers[i].m_vk); + if (button != 0 && isKeyDown(button)) { + state |= s_modifiers[i].m_mask; } } - else if (m_scanCodeToVirtKey[button] != 0) { - self->m_keyName = s_vkToName[m_scanCodeToVirtKey[button]]; + + // we can get toggle modifiers from the system + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) { + state |= KeyModifierCapsLock; } - else { - self->m_keyName = CStringUtil::print("scan code 0x%03x", button); + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) { + state |= KeyModifierNumLock; } - return m_keyName.c_str(); + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) { + state |= KeyModifierScrollLock; + } + + return state; } -void -CMSWindowsKeyState::doUpdateKeys() +SInt32 +CMSWindowsKeyState::pollActiveGroup() const { - // clear scan code to/from virtual key mapping - memset(m_scanCodeToVirtKey, 0, sizeof(m_scanCodeToVirtKey)); - memset(m_scanCodeToVirtKeyNumLock, 0, sizeof(m_scanCodeToVirtKeyNumLock)); - memset(m_virtKeyToScanCode, 0, sizeof(m_virtKeyToScanCode)); - - // add modifiers. note that ModeSwitch is mapped to VK_RMENU and - // that it's mapped *before* the Alt modifier. we must map it so - // KeyModifierModeSwitch mask can be converted to keystrokes. it - // must be mapped before the Alt modifier so that the Alt modifier - // takes precedence when mapping keystrokes to modifier masks. - KeyButtons keys; - keys.push_back(mapVirtKeyToButton(VK_RMENU)); - addModifier(KeyModifierModeSwitch, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LSHIFT)); - keys.push_back(mapVirtKeyToButton(VK_RSHIFT)); - addModifier(KeyModifierShift, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LCONTROL)); - keys.push_back(mapVirtKeyToButton(VK_RCONTROL)); - addModifier(KeyModifierControl, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LMENU)); - keys.push_back(mapVirtKeyToButton(VK_RMENU)); - addModifier(KeyModifierAlt, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LWIN)); - keys.push_back(mapVirtKeyToButton(VK_RWIN)); - addModifier(KeyModifierSuper, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_CAPITAL)); - addModifier(KeyModifierCapsLock, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_NUMLOCK)); - addModifier(KeyModifierNumLock, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_SCROLL)); - addModifier(KeyModifierScrollLock, keys); - - BYTE keyState[256]; - GetKeyboardState(keyState); - for (UINT i = 1; i < 256; ++i) { - // skip mouse button virtual keys - switch (i) { - case VK_LBUTTON: - case VK_RBUTTON: - case VK_MBUTTON: - case VK_XBUTTON1: - case VK_XBUTTON2: - continue; - - default: - break; - } - - // map to a scancode and back to a virtual key - KeyButton button2; - KeyButton button = mapVirtKeyToButton(i, button2); - if (button == 0) { - continue; - } - - // okay, now we have the scan code for the virtual key. the - // numpad causes some confusion. buttons on the numpad are - // used for two virtual keys (one for num lock off and one - // for num lock on). keep a separate map for virtual keys - // used when num lock is on. - if ((i >= VK_NUMPAD0 && i <= VK_NUMPAD9) || - i == VK_SEPARATOR || i == VK_DECIMAL) { - m_scanCodeToVirtKeyNumLock[button] = i; - m_scanCodeToVirtKeyNumLock[button2] = i; - } - else { - m_scanCodeToVirtKey[button] = i; - m_scanCodeToVirtKey[button2] = i; - } - m_virtKeyToScanCode[i] = button; - - // if the virtual key is VK_DELETE then use the extended - // scan code. this is important for simulating ctrl+alt+del - // which only works with the extended key. - if (i == VK_DELETE) { - m_virtKeyToScanCode[i] |= 0x100u; - } - - // save the key state - if ((keyState[i] & 0x80) != 0) { - setKeyDown(button, true); - } - - // toggle state applies to all keys but we only want it for - // the modifier keys with corresponding lights. - if ((keyState[i] & 0x01) != 0) { - switch (i) { - case VK_CAPITAL: - setToggled(KeyModifierCapsLock); - break; - - case VK_NUMLOCK: - setToggled(KeyModifierNumLock); - break; - - case VK_SCROLL: - setToggled(KeyModifierScrollLock); - break; - } - } - } -} - -void -CMSWindowsKeyState::doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat) -{ - UINT vk = 0; - if ((getActiveModifiers() & KeyModifierNumLock) != 0) { - vk = m_scanCodeToVirtKeyNumLock[button]; - } - if (vk == 0) { - vk = m_scanCodeToVirtKey[button]; - } - m_desks->fakeKeyEvent(button, vk, press, isAutoRepeat); -} - -KeyButton -CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask mask, bool isAutoRepeat) const -{ - UINT extVirtualKey = 0; - - // check for special keys - if ((id & 0xfffff000u) == 0xe000u) { - if ((id & 0xff00u) == 0xe000u) { - extVirtualKey = s_mapE000[id & 0xffu]; - } - else if ((id & 0xff00) == 0xee00) { - extVirtualKey = s_mapEE00[id & 0xffu]; - } - else if ((id & 0xff00) == 0xef00) { - extVirtualKey = s_mapEF00[id & 0xffu]; - } - if (extVirtualKey == 0) { - LOG((CLOG_DEBUG2 "unknown special key")); - return 0; - } - } - - // special handling of VK_SNAPSHOT - if (extVirtualKey == VK_SNAPSHOT) { - // ignore key repeats on print screen - if (!isAutoRepeat) { - // active window (with alt) or fullscreen (without alt)? - BYTE scan = 0; - if ((mask & KeyModifierAlt) != 0) { - scan = 1; - } - - // send events - keybd_event(VK_SNAPSHOT, scan, 0, 0); - keybd_event(VK_SNAPSHOT, scan, KEYEVENTF_KEYUP, 0); - } - return 0; - } - - // handle other special keys - if (extVirtualKey != 0) { - // compute required modifiers - KeyModifierMask requiredMask = 0; - KeyModifierMask outMask = 0; - - // 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. - UINT virtualKey = (extVirtualKey & 0xffu); - if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { - requiredMask |= KeyModifierNumLock; - if ((getActiveModifiers() & KeyModifierNumLock) != 0) { - LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); - outMask |= KeyModifierNumLock; - } - } - - // check for left tab. that requires the shift key. - if (id == kKeyLeftTab) { - requiredMask |= KeyModifierShift; - outMask |= KeyModifierShift; - } - - // now generate the keystrokes and return the resulting modifier mask - KeyButton button = m_virtKeyToScanCode[virtualKey]; - if ((extVirtualKey & 0x100u) != 0) { - button |= 0x100u; - } - LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%03x mask 0x%04x", id, virtualKey, button, outMask)); - return mapToKeystrokes(keys, button, - outMask, requiredMask, isAutoRepeat); - } - // 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)); + // get keyboard layout for the thread + HKL hkl = GetKeyboardLayout(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 & 0x0000ffffu); - 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 0; - } - KeyButton button = mapCharacter(keys, multiByte[0], hkl, isAutoRepeat); - if (button != 0) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - if (isDeadChar(multiByte[0], hkl, false)) { - // 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_key = m_virtKeyToScanCode[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. - button = 0; - } - return button; - } - 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 0; - } - 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)); + // get group + GroupMap::const_iterator i = m_groupMap.find(hkl); + if (i == m_groupMap.end()) { + LOG((CLOG_DEBUG1 "can't find keyboard layout %08x", hkl)); return 0; } - // 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 0; - } - if (nChars == 2) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); - mapCharacter(keys, multiByte[1], hkl, isAutoRepeat); - } - - // process character - LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - return mapCharacter(keys, multiByte[0], hkl, isAutoRepeat); + return i->second; } -UINT -CMSWindowsKeyState::getCodePageFromLangID(LANGID langid) const +void +CMSWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) 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; + BYTE keyState[256]; + GetKeyboardState(keyState); + for (KeyButton i = 1; i < 256; ++i) { + if ((keyState[i] & 0x80) != 0) { + pressedKeys.insert(i); + } } - - // 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; } -KeyButton -CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey, - KeyButton& extended) const +void +CMSWindowsKeyState::getKeyMap(CKeyMap& keyMap) { - // this method does what MapVirtualKey(virtualKey, 0) should do. - // we have to explicitly set the extended key flag for some - // modifiers because the win32 API is inadequate. we also find - // the unextended and the extended scancodes for those virtual - // keys that have both except for VK_SHIFT, VK_CONTROL, and VK_MENU. - // - // the windows 95 family doesn't map the side distinguishing virtual - // keys. but we know that VK_CONTROL maps to VK_LCONTROL and - // that VK_RCONTROL is the same scan code | 0x100. similarly for - // VK_MENU. but VK_RSHIFT cannot be determined that way so we - // search for it. - extended = 0; - KeyButton button; - if (m_is95Family) { - UINT scancode; - switch (virtualKey) { - case VK_LSHIFT: - button = (KeyButton)MapVirtualKey(VK_SHIFT, 0); - break; + // update keyboard groups + if (getGroups(m_groups)) { + m_groupMap.clear(); + SInt32 numGroups = (SInt32)m_groups.size(); + for (SInt32 g = 0; g < numGroups; ++g) { + m_groupMap[m_groups[g]] = g; + } + } + HKL activeLayout = GetKeyboardLayout(0); - case VK_RSHIFT: - // we have to search - scancode = MapVirtualKey(VK_SHIFT, 0); - for (UINT i = 1; i < 256; ++i) { - if (i != scancode && MapVirtualKey(i, 1) == VK_SHIFT) { - return (KeyButton)(i); + // clear table + memset(m_virtualKeyToButton, 0, sizeof(m_virtualKeyToButton)); + m_keyToVKMap.clear(); + + CKeyMap::KeyItem item; + SInt32 numGroups = (SInt32)m_groups.size(); + for (SInt32 g = 0; g < numGroups; ++g) { + item.m_group = g; + ActivateKeyboardLayout(m_groups[g], 0); + + // clear tables + memset(m_buttonToVK, 0, sizeof(m_buttonToVK)); + memset(m_buttonToNumpadVK, 0, sizeof(m_buttonToNumpadVK)); + + // map buttons (scancodes) to virtual keys + for (KeyButton i = 1; i < 256; ++i) { + UINT vk = MapVirtualKey(i, 1); + if (vk == 0) { + // unmapped + continue; + } + + // deal with certain virtual keys specially + switch (vk) { + case VK_SHIFT: + if (MapVirtualKey(VK_RSHIFT, 0) == i) { + vk = VK_RSHIFT; + } + else { + vk = VK_LSHIFT; + } + break; + + case VK_CONTROL: + vk = VK_LCONTROL; + break; + + case VK_MENU: + vk = VK_LMENU; + break; + + case VK_NUMLOCK: + vk = VK_PAUSE; + break; + + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + case VK_DECIMAL: + // numpad keys are saved in their own table + m_buttonToNumpadVK[i] = vk; + continue; + + case VK_LWIN: + case VK_RWIN: + // add extended key only for these on 95 family + if (m_is95Family) { + m_buttonToVK[i | 0x100u] = vk; + continue; + } + break; + + case VK_RETURN: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_INSERT: + case VK_DELETE: + // also add extended key for these + m_buttonToVK[i | 0x100u] = vk; + break; + } + + if (m_buttonToVK[i] == 0) { + m_buttonToVK[i] = vk; + } + } + + // now map virtual keys to buttons. multiple virtual keys may map + // to a single button. if the virtual key matches the one in + // m_buttonToVK then we use the button as is. if not then it's + // either a numpad key and we use the button as is or it's an + // extended button. + for (UINT i = 1; i < 255; ++i) { + // skip virtual keys we don't want + switch (i) { + case VK_LBUTTON: + case VK_RBUTTON: + case VK_MBUTTON: + case VK_XBUTTON1: + case VK_XBUTTON2: + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + continue; + } + + // get the button + KeyButton button = static_cast(MapVirtualKey(i, 0)); + if (button == 0) { + continue; + } + + // deal with certain virtual keys specially + switch (i) { + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + case VK_DECIMAL: + m_buttonToNumpadVK[button] = i; + break; + + default: + // add extended key if virtual keys don't match + if (m_buttonToVK[button] != i) { + m_buttonToVK[button | 0x100u] = i; + } + break; + } + } + + // add alt+printscreen + if (m_buttonToVK[0x54u] == 0) { + m_buttonToVK[0x54u] = VK_SNAPSHOT; + } + + // set virtual key to button table + if (GetKeyboardLayout(0) == m_groups[g]) { + for (KeyButton i = 0; i < 512; ++i) { + if (m_buttonToVK[i] != 0) { + if (m_virtualKeyToButton[m_buttonToVK[i]] == 0) { + m_virtualKeyToButton[m_buttonToVK[i]] = i; + } + } + if (m_buttonToNumpadVK[i] != 0) { + if (m_virtualKeyToButton[m_buttonToNumpadVK[i]] == 0) { + m_virtualKeyToButton[m_buttonToNumpadVK[i]] = i; + } } } - return 0; + } - case VK_LCONTROL: - case VK_RCONTROL: - button = (KeyButton)MapVirtualKey(VK_CONTROL, 0); - break; + // add numpad keys + for (KeyButton i = 0; i < 512; ++i) { + if (m_buttonToNumpadVK[i] != 0) { + item.m_id = getKeyID(m_buttonToNumpadVK[i], i); + item.m_button = i; + item.m_required = KeyModifierNumLock; + item.m_sensitive = KeyModifierNumLock | KeyModifierShift; + item.m_generates = 0; + item.m_client = m_buttonToNumpadVK[i]; + addKeyEntry(keyMap, item); + } + } - case VK_LMENU: - case VK_RMENU: - button = (KeyButton)MapVirtualKey(VK_MENU, 0); - break; + // add other keys + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + for (KeyButton i = 0; i < 512; ++i) { + if (m_buttonToVK[i] != 0) { + // initialize item + item.m_id = getKeyID(m_buttonToVK[i], i); + item.m_button = i; + item.m_required = 0; + item.m_sensitive = 0; + item.m_client = m_buttonToVK[i]; - case VK_PAUSE: - // mapped to 0. i hope this works on all keyboards. - button = (KeyButton)0x45u; - break; + // get flags for modifier keys + CKeyMap::initModifierKey(item); - case VK_DIVIDE: - // mapped to 0. i hope this works on all keyboards. - button = (KeyButton)0x35u; - break; + if (item.m_id == 0) { + // translate virtual key to a character with and without + // shift, caps lock, and AltGr. + struct Modifier { + UINT m_vk1; + UINT m_vk2; + BYTE m_state; + KeyModifierMask m_mask; + }; + static const Modifier modifiers[] = { + { VK_SHIFT, VK_SHIFT, 0x80u, KeyModifierShift }, + { VK_CAPITAL, VK_CAPITAL, 0x01u, KeyModifierCapsLock }, + { VK_CONTROL, VK_MENU, 0x80u, KeyModifierControl | + KeyModifierAlt } + }; + static const size_t s_numModifiers = + sizeof(modifiers) / sizeof(modifiers[0]); + static const size_t s_numCombinations = 1 << s_numModifiers; + KeyID id[s_numCombinations]; - default: - button = (KeyButton)MapVirtualKey(virtualKey, 0); + bool anyFound = false; + KeyButton button = static_cast(i & 0xffu); + for (size_t j = 0; j < s_numCombinations; ++j) { + for (size_t k = 0; k < s_numModifiers; ++k) { + if ((j & (1 << k)) != 0) { + keys[modifiers[k].m_vk1] = modifiers[k].m_state; + keys[modifiers[k].m_vk2] = modifiers[k].m_state; + } + else { + keys[modifiers[k].m_vk1] = 0; + keys[modifiers[k].m_vk2] = 0; + } + } + id[j] = getIDForKey(item, button, + m_buttonToVK[i], keys, m_groups[g]); + if (id[j] != 0) { + anyFound = true; + } + } - // okay, now we have the scan code for the virtual key. windows - // may map different virtual keys to the same button. for example, - // windows 95/98/me maps virtual keys 220 and 226 to scan code 86 - // in the british english keyboard map. why? who knows. it - // doesn't make any sense since a button can't actually generate - // more than one virtual key. to avoid this stupidity, we map the - // button back to a virtual key to see if it matches the starting - // point. we don't do this for number pad keys since we expect - // each key to generate one of two virtual keys, depending on the - // state of NumLock, a state we can't pass to MapVirtualKey. - if ((virtualKey < VK_NUMPAD0 || virtualKey > VK_NUMPAD9) && - virtualKey != VK_SEPARATOR && virtualKey != VK_DECIMAL) { - if (button == 0 || MapVirtualKey(button, 1) != virtualKey) { - return 0; + if (anyFound) { + // determine what modifiers we're sensitive to. + // we're sensitive if the KeyID changes when the + // modifier does. + item.m_sensitive = 0; + for (size_t k = 0; k < s_numModifiers; ++k) { + for (size_t j = 0; j < s_numCombinations; ++j) { + if (id[j] != id[j ^ (1u << k)]) { + item.m_sensitive |= modifiers[k].m_mask; + break; + } + } + } + + // save each key. the map will automatically discard + // duplicates, like an unshift and shifted version of + // a key that's insensitive to shift. + for (size_t j = 0; j < s_numCombinations; ++j) { + item.m_id = id[j]; + item.m_required = 0; + for (size_t k = 0; k < s_numModifiers; ++k) { + if ((j & (1 << k)) != 0) { + item.m_required |= modifiers[k].m_mask; + } + } + addKeyEntry(keyMap, item); + } + } + } + else { + // found in table + switch (m_buttonToVK[i]) { + case VK_TAB: + // add kKeyLeftTab, too + item.m_id = kKeyLeftTab; + item.m_required |= KeyModifierShift; + item.m_sensitive |= KeyModifierShift; + addKeyEntry(keyMap, item); + item.m_id = kKeyTab; + item.m_required &= ~KeyModifierShift; + break; + + case VK_CANCEL: + item.m_required |= KeyModifierControl; + item.m_sensitive |= KeyModifierControl; + break; + + case VK_SNAPSHOT: + item.m_sensitive |= KeyModifierAlt; + if ((i & 0x100u) == 0) { + // non-extended snapshot key requires alt + item.m_required |= KeyModifierAlt; + } + break; + } + addKeyEntry(keyMap, item); } } + } + } + + // restore keyboard layout + ActivateKeyboardLayout(activeLayout, 0); +} + +void +CMSWindowsKeyState::fakeKey(const Keystroke& keystroke) +{ + switch (keystroke.m_type) { + case Keystroke::kButton: { + LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); + KeyButton button = keystroke.m_data.m_button.m_button; + + // windows doesn't send key ups for key repeats + if (keystroke.m_data.m_button.m_repeat && + !keystroke.m_data.m_button.m_press) { + LOG((CLOG_DEBUG1 " discard key repeat release")); break; } + + // get the virtual key for the button + UINT vk = keystroke.m_data.m_button.m_client; + + // special handling of VK_SNAPSHOT + if (vk == VK_SNAPSHOT) { + if ((getActiveModifiers() & KeyModifierAlt) != 0) { + // snapshot active window + button = 1; + } + else { + // snapshot full screen + button = 0; + } + } + + // synthesize event + m_desks->fakeKeyEvent(button, vk, + keystroke.m_data.m_button.m_press, + keystroke.m_data.m_button.m_repeat); + break; + } + + case Keystroke::kGroup: + // we don't restore the group. we'd like to but we can't be + // sure the restoring group change will be processed after the + // key events. + if (!keystroke.m_data.m_group.m_restore) { + if (keystroke.m_data.m_group.m_absolute) { + LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); + setWindowGroup(keystroke.m_data.m_group.m_group); + } + else { + LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); + setWindowGroup(getEffectiveGroup(pollActiveGroup(), + keystroke.m_data.m_group.m_group)); + } + } + break; + } +} + +KeyModifierMask& +CMSWindowsKeyState::getActiveModifiersRValue() +{ + if (m_useSavedModifiers) { + return m_savedModifiers; } else { - switch (virtualKey) { - case VK_PAUSE: - // mapped to 0. i hope this works on all keyboards. - button = (KeyButton)0x45u; - break; - - default: - button = (KeyButton)MapVirtualKey(virtualKey, 0); - break; - } + return CKeyState::getActiveModifiersRValue(); } - - // map extended keys - switch (virtualKey) { - case VK_RETURN: // Return/numpad Enter - case VK_PRIOR: // numpad PageUp/PageUp - case VK_NEXT: // numpad PageDown/PageDown - case VK_END: // numpad End/End - case VK_HOME: // numpad Home/Home - case VK_LEFT: // numpad Left/Left - case VK_UP: // numpad Up/Up - case VK_RIGHT: // numpad Right/Right - case VK_DOWN: // numpad Down/Down - case VK_INSERT: // numpad Insert/Insert - case VK_DELETE: // numpad Delete/Delete -// case VK_SELECT: -// case VK_EXECUTE: -// case VK_HELP: - extended = (KeyButton)(button | 0x100u); - break; - } - - // see if the win32 API can help us determine an extended key. - // if the remapped virtual key doesn't match the starting - // point then there's a really good chance that that virtual - // key is mapped to an extended key. however, this is not - // the case for modifiers that don't distinguish between left - // and right. - UINT virtualKey2 = MapVirtualKey(button, 3); - if (virtualKey2 != 0 && virtualKey2 != virtualKey) { - switch (virtualKey) { - case VK_SHIFT: - case VK_CONTROL: - case VK_MENU: - break; - - case VK_NUMPAD0: - case VK_NUMPAD1: - case VK_NUMPAD2: - case VK_NUMPAD3: - case VK_NUMPAD4: - case VK_NUMPAD5: - case VK_NUMPAD6: - case VK_NUMPAD7: - case VK_NUMPAD8: - case VK_NUMPAD9: - case VK_MULTIPLY: - case VK_ADD: - case VK_SEPARATOR: - case VK_SUBTRACT: - case VK_DECIMAL: - break; - - case VK_PAUSE: - break; - - default: - button |= 0x100u; - extended = 0; - break; - } - return button; - } - - // note other extended keys that the win32 API won't help us with. - // on the windows 95 family this is the only way to find extended - // keys since MapVirtualKey(N, 3) is unimplemented. - switch (virtualKey) { - case VK_CANCEL: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: -// case VK_SEPARATOR: - case VK_DIVIDE: - case VK_F13: - case VK_F14: - case VK_F15: - case VK_F16: - case VK_F17: - case VK_F18: - case VK_F19: - case VK_F20: - case VK_F21: - case VK_F22: - case VK_F23: - case VK_F24: - case VK_NUMLOCK: - case VK_RSHIFT: - case VK_RCONTROL: - case VK_RMENU: - case VK_BROWSER_BACK: - case VK_BROWSER_FORWARD: - case VK_BROWSER_REFRESH: - case VK_BROWSER_STOP: - case VK_BROWSER_SEARCH: - case VK_BROWSER_FAVORITES: - case VK_BROWSER_HOME: - case VK_VOLUME_MUTE: - case VK_VOLUME_DOWN: - case VK_VOLUME_UP: - case VK_MEDIA_NEXT_TRACK: - case VK_MEDIA_PREV_TRACK: - case VK_MEDIA_STOP: - case VK_MEDIA_PLAY_PAUSE: - case VK_LAUNCH_MAIL: - case VK_LAUNCH_MEDIA_SELECT: - case VK_LAUNCH_APP1: - case VK_LAUNCH_APP2: - button |= 0x100u; - extended = 0; - break; - } - - return button; -} - -KeyButton -CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey) const -{ - KeyButton dummy; - return mapVirtKeyToButton(virtualKey, dummy); -} - -KeyButton -CMSWindowsKeyState::mapCharacter(Keystrokes& keys, - char c, HKL hkl, bool isAutoRepeat) const -{ - KeyModifierMask activeMask = getActiveModifiers(); - - // 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 (virtualKey == 0xffu) { - LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); - return 0; - } - - // get the required modifier state - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - - // see what modifiers are needed. we only check for shift and - // AltGr. we must always match the desired shift state but only - // the desired AltGr state if AltGr is required. AltGr is actually - // ctrl + alt so we can't require that ctrl and alt not be pressed - // otherwise users couldn't do, say, ctrl+z. - // - // the space character (ascii 32) is special in that it's unaffected - // by shift and should match our stored shift state. - KeyModifierMask desiredMask = 0; - KeyModifierMask requiredMask = KeyModifierShift; - if (c == 32) { - desiredMask |= (activeMask & KeyModifierShift); - } - else if ((modifierState & 0x01u) == 1) { - desiredMask |= KeyModifierShift; - } - if ((modifierState & 0x06u) == 6) { - // add ctrl and alt, which must be matched. match alt via - // mode-switch, which uses the right alt key rather than - // the left. windows doesn't care which alt key so long - // as ctrl is also down but some apps do their own mapping - // and they do care. Emacs and PuTTY, for example. - desiredMask |= KeyModifierControl | KeyModifierModeSwitch; - requiredMask |= KeyModifierControl | KeyModifierModeSwitch; - } - - // 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 ((activeMask & 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(reinterpret_cast(uc)) != - CharUpper(reinterpret_cast(uc)) || - (MapVirtualKeyEx(virtualKey, 2, hkl) & 0x80000000lu) != 0) { - LOG((CLOG_DEBUG2 "flip shift")); - desiredMask ^= 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). - KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; - LOG((CLOG_DEBUG2 "character %d to virtual key %d scan code 0x%04x mask 0x%08x", (unsigned char)c, virtualKey, scanCode, desiredMask)); - return mapToKeystrokes(keys, scanCode, - desiredMask, requiredMask, isAutoRepeat); -} - -KeyButton -CMSWindowsKeyState::mapToKeystrokes(Keystrokes& keys, KeyButton button, - KeyModifierMask desiredMask, KeyModifierMask requiredMask, - bool isAutoRepeat) const -{ - // adjust the modifiers to match the desired modifiers - Keystrokes undo; - if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) { - LOG((CLOG_DEBUG2 "failed to adjust modifiers")); - keys.clear(); - return 0; - } - - // add the key event - Keystroke keystroke; - keystroke.m_key = button; - keystroke.m_press = true; - keystroke.m_repeat = isAutoRepeat; - keys.push_back(keystroke); - - // put undo keystrokes at end of keystrokes in reverse order - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - return button; } bool -CMSWindowsKeyState::adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const +CMSWindowsKeyState::getGroups(GroupList& groups) const { - // for each modifier in requiredMask make sure the current state - // of that modifier matches the bit in desiredMask. - for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) { - if ((mask & requiredMask) != 0) { - bool active = ((desiredMask & mask) != 0); - if (!mapModifier(keys, undo, mask, active)) { - return false; - } - requiredMask ^= mask; - } + // get keyboard layouts + UInt32 newNumLayouts = GetKeyboardLayoutList(0, NULL); + if (newNumLayouts == 0) { + LOG((CLOG_DEBUG1 "can't get keyboard layouts")); + return false; } + HKL* newLayouts = new HKL[newNumLayouts]; + newNumLayouts = GetKeyboardLayoutList(newNumLayouts, newLayouts); + if (newNumLayouts == 0) { + LOG((CLOG_DEBUG1 "can't get keyboard layouts")); + delete[] newLayouts; + return false; + } + + groups.clear(); + groups.insert(groups.end(), newLayouts, newLayouts + newNumLayouts); + delete[] newLayouts; return true; } -int -CMSWindowsKeyState::toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const +void +CMSWindowsKeyState::setWindowGroup(SInt32 group) { - // ignore bogus character - if (c == 0) { - return 0; - } + HWND targetWindow = GetForegroundWindow(); - // translate the character into its virtual key and its required - // modifier state. - SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + bool sysCharSet = true; + // XXX -- determine if m_groups[group] can be used with the system + // character set. - // get virtual key - BYTE virtualKey = LOBYTE(virtualKeyAndModifierState); - if (virtualKey == 0xffu) { - return 0; - } + PostMessage(targetWindow, WM_INPUTLANGCHANGEREQUEST, + sysCharSet ? 1 : 0, (LPARAM)m_groups[group]); - // get the required modifier state - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - - // set shift state required to generate key - BYTE keys[256]; - memset(keys, 0, sizeof(keys)); - if (modifierState & 0x01u) { - keys[VK_SHIFT] = 0x80u; - } - if (modifierState & 0x02u) { - keys[VK_CONTROL] = 0x80u; - } - if (modifierState & 0x04u) { - keys[VK_MENU] = 0x80u; - } - - // get the scan code for the key - UINT scanCode = MapVirtualKeyEx(virtualKey, 0, hkl); - - // discard characters if chars is NULL - WORD dummy; - if (chars == NULL) { - chars = &dummy; - } - - // put it back - return ToAsciiEx(virtualKey, scanCode, keys, chars, menu ? 1 : 0, hkl); + // XXX -- use a short delay to let the target window process the message + // before it sees the keyboard events. i'm not sure why this is + // necessary since the messages should arrive in order. if we don't + // delay, though, some of our keyboard events may disappear. + Sleep(100); } -bool -CMSWindowsKeyState::isDeadChar(TCHAR c, HKL hkl, bool menu) const +void +CMSWindowsKeyState::fixKeys() { - // first clear out ToAsciiEx()'s internal buffer by sending it - // a space. - WORD ascii; - int old = toAscii(' ', hkl, 0, &ascii); - - // now pass the character of interest - WORD dummy; - bool isDead = (toAscii(c, hkl, menu, &dummy) < 0); - - // clear out internal buffer again - toAscii(' ', hkl, 0, &dummy); - - // put old dead key back if there was one - if (old == 1 && ascii != ' ') { - toAscii(static_cast(ascii & 0xffu), hkl, menu, &dummy); + // fake key releases for the windows keys if we think they're + // down but they're really up. we have to do this because if the + // user presses and releases a windows key without pressing any + // other key while it's down then the system will eat the key + // release. if we don't detect that and synthesize the release + // then the client won't take the usual windows key release action + // (which on windows is to show the start menu). + // + // only check on the windows 95 family since the NT family reports + // the key releases as usual. + if (!m_is95Family) { + return; } - return isDead; + KeyButton leftButton = virtualKeyToButton(VK_LWIN); + KeyButton rightButton = virtualKeyToButton(VK_RWIN); + bool leftDown = isKeyDown(leftButton); + bool rightDown = isKeyDown(rightButton); + bool fix = (leftDown || rightDown); + if (fix) { + // check if either button is not really down + bool leftAsyncDown = ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0); + bool rightAsyncDown = ((GetAsyncKeyState(VK_RWIN) & 0x8000) != 0); + + if (leftAsyncDown != leftDown || rightAsyncDown != rightDown) { + KeyModifierMask state = getActiveModifiers(); + if (!leftAsyncDown && !rightAsyncDown) { + // no win keys are down so remove super modifier + state &= ~KeyModifierSuper; + } + + // report up events + if (leftDown && !leftAsyncDown) { + LOG((CLOG_DEBUG1 "event: fake key release left windows key (0x%03x)", leftButton)); + CKeyState::onKey(leftButton, false, state); + CKeyState::sendKeyEvent(m_eventTarget, false, false, + kKeySuper_L, state, 1, leftButton); + } + if (rightDown && !rightAsyncDown) { + LOG((CLOG_DEBUG1 "event: fake key release right windows key (0x%03x)", rightButton)); + CKeyState::onKey(rightButton, false, state); + CKeyState::sendKeyEvent(m_eventTarget, false, false, + kKeySuper_R, state, 1, rightButton); + } + } + } + + if (fix && m_fixTimer == NULL) { + // schedule check + m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, + new TMethodEventJob( + this, &CMSWindowsKeyState::handleFixKeys)); + } + else if (!fix && m_fixTimer != NULL) { + // remove scheduled check + EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); + EVENTQUEUE->deleteTimer(m_fixTimer); + m_fixTimer = NULL; + } +} + +void +CMSWindowsKeyState::handleFixKeys(const CEvent&, void*) +{ + fixKeys(); +} + +KeyID +CMSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button) +{ + if ((button & 0x100u) != 0) { + virtualKey += 0x100u; + } + return s_virtualKey[virtualKey]; +} + +KeyID +CMSWindowsKeyState::getIDForKey(CKeyMap::KeyItem& item, + KeyButton button, UINT virtualKey, + PBYTE keyState, HKL hkl) const +{ + int n; + KeyID id; + if (m_is95Family) { + // XXX -- how do we get characters not in Latin-1? + WORD ascii; + n = ToAsciiEx(virtualKey, button, keyState, &ascii, 0, hkl); + id = static_cast(ascii & 0xffu); + } + else { + WCHAR unicode[2]; + n = m_ToUnicodeEx(virtualKey, button, keyState, + unicode, sizeof(unicode) / sizeof(unicode[0]), + 0, hkl); + id = static_cast(unicode[0]); + } + switch (n) { + case -1: + return CKeyMap::getDeadKey(id); + + default: + case 0: + // unmapped + return kKeyNone; + + case 1: + return id; + + case 2: + // left over dead key in buffer; oops. + return getIDForKey(item, button, virtualKey, keyState, hkl); + } +} + +void +CMSWindowsKeyState::addKeyEntry(CKeyMap& keyMap, CKeyMap::KeyItem& item) +{ + keyMap.addKeyEntry(item); + if (item.m_group == 0) { + m_keyToVKMap[item.m_id] = static_cast(item.m_client); + } } diff --git a/lib/platform/CMSWindowsKeyState.h b/lib/platform/CMSWindowsKeyState.h index c2b7b149..d8320a58 100644 --- a/lib/platform/CMSWindowsKeyState.h +++ b/lib/platform/CMSWindowsKeyState.h @@ -17,9 +17,12 @@ #include "CKeyState.h" #include "CString.h" +#include "stdvector.h" #define WIN32_LEAN_AND_MEAN #include +class CEvent; +class CEventQueueTimer; class CMSWindowsDesks; //! Microsoft Windows key mapper @@ -28,24 +31,62 @@ This class maps KeyIDs to keystrokes. */ class CMSWindowsKeyState : public CKeyState { public: - CMSWindowsKeyState(CMSWindowsDesks* desks); + CMSWindowsKeyState(CMSWindowsDesks* desks, void* eventTarget); virtual ~CMSWindowsKeyState(); - //! @name accessors + //! @name manipulators //@{ + //! Handle screen disabling + /*! + Called when screen is disabled. This is needed to deal with platform + brokenness. + */ + void disable(); + //! Set the active keyboard layout /*! Uses \p keyLayout when querying the keyboard. */ void setKeyLayout(HKL keyLayout); - //! Check the named virtual key for release + //! Test and set autorepeat state /*! - If \p virtualKey isn't really pressed but we think it is then - update our state and post a key release event to \p eventTarget. + Returns true if the given button is autorepeating and updates internal + state. */ - void fixKey(void* eventTarget, UINT virtualKey); + bool testAutoRepeat(bool press, bool isRepeat, KeyButton); + + //! Remember modifier state + /*! + Records the current non-toggle modifier state. + */ + void saveModifiers(); + + //! Set effective modifier state + /*! + Temporarily sets the non-toggle modifier state to those saved by the + last call to \c saveModifiers if \p enable is \c true. Restores the + modifier state to the current modifier state if \p enable is \c false. + This is for synthesizing keystrokes on the primary screen when the + cursor is on a secondary screen. When on a secondary screen we capture + all non-toggle modifier state, track the state internally and do not + pass it on. So if Alt+F1 synthesizes Alt+X we need to synthesize + not just X but also Alt, despite the fact that our internal modifier + state indicates Alt is down, because local apps never saw the Alt down + event. + */ + void useSavedModifiers(bool enable); + + //@} + //! @name accessors + //@{ + + //! Map a virtual key to a button + /*! + Returns the button for the \p virtualKey. + */ + KeyButton virtualKeyToButton(UINT virtualKey) const; //! Map key event to a key /*! @@ -55,84 +96,121 @@ public: KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut) const; - //! Map a virtual key to a button + //! Check if keyboard groups have changed /*! - Returns the button for the \p virtualKey. + Returns true iff the number or order of the keyboard groups have + changed since the last call to updateKeys(). */ - KeyButton virtualKeyToButton(UINT virtualKey) const; + bool didGroupsChange() const; + + //! Map key to virtual key + /*! + Returns the virtual key for key \p key or 0 if there's no such virtual + key. + */ + UINT mapKeyToVirtualKey(KeyID key) const; + + //! Map virtual key and button to KeyID + /*! + Returns the KeyID for virtual key \p virtualKey and button \p button + (button should include the extended key bit), or kKeyNone if there is + no such key. + */ + static KeyID getKeyID(UINT virtualKey, KeyButton button); //@} // IKeyState overrides + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button); + virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button); + virtual bool fakeCtrlAltDel(); + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; + + // CKeyState overrides + virtual void onKey(KeyButton button, bool down, + KeyModifierMask newState); virtual void sendKeyEvent(void* target, bool press, bool isAutoRepeat, KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button); - virtual bool fakeCtrlAltDel(); - virtual const char* getKeyName(KeyButton) const; protected: - // IKeyState overrides - virtual void doUpdateKeys(); - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat); - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + // CKeyState overrides + virtual void getKeyMap(CKeyMap& keyMap); + virtual void fakeKey(const Keystroke& keystroke); + virtual KeyModifierMask& + getActiveModifiersRValue(); private: + typedef std::vector GroupList; + // send ctrl+alt+del hotkey event on NT family static void ctrlAltDelThread(void*); - // convert a language ID to a code page - UINT getCodePageFromLangID(LANGID langid) const; + bool getGroups(GroupList&) const; + void setWindowGroup(SInt32 group); - // map a virtual key to a button. this tries to deal with the - // broken win32 API as best it can. - KeyButton mapVirtKeyToButton(UINT virtualKey, - KeyButton& extended) const; + void fixKeys(); + void handleFixKeys(const CEvent&, void*); - // same as above and discard extended - KeyButton mapVirtKeyToButton(UINT virtualKey) const; + KeyID getIDForKey(CKeyMap::KeyItem& item, + KeyButton button, UINT virtualKey, + PBYTE keyState, HKL hkl) const; - // map character \c c given keyboard layout \c hkl to the keystrokes - // to generate it. - KeyButton mapCharacter(Keystrokes& keys, - char c, HKL hkl, bool isAutoRepeat) const; - - // map \c virtualKey to the keystrokes to generate it, along with - // keystrokes to update and restore the modifier state. - KeyButton mapToKeystrokes(Keystrokes& keys, KeyButton button, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask, - bool isAutoRepeat) const; - - // get keystrokes to get modifiers in a desired state - bool adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const; - - // pass character to ToAsciiEx(), returning what it returns - int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const; - - // return true iff \c c is a dead character - bool isDeadChar(TCHAR c, HKL hkl, bool menu) const; + void addKeyEntry(CKeyMap& keyMap, CKeyMap::KeyItem& item); private: + // not implemented + CMSWindowsKeyState(const CMSWindowsKeyState&); + CMSWindowsKeyState& operator=(const CMSWindowsKeyState&); + +private: + typedef std::map GroupMap; + typedef std::map KeyToVKMap; + bool m_is95Family; + void* m_eventTarget; CMSWindowsDesks* m_desks; HKL m_keyLayout; - CString m_keyName; - UINT m_scanCodeToVirtKey[512]; - UINT m_scanCodeToVirtKeyNumLock[512]; - KeyButton m_virtKeyToScanCode[256]; + UINT m_buttonToVK[512]; + UINT m_buttonToNumpadVK[512]; + KeyButton m_virtualKeyToButton[256]; + KeyToVKMap m_keyToVKMap; - static const char* s_vkToName[]; - static const KeyID s_virtualKey[][2]; - static const UINT s_mapE000[]; - static const UINT s_mapEE00[]; - static const UINT s_mapEF00[]; + // the timer used to check for fixing key state + CEventQueueTimer* m_fixTimer; + + // the groups (keyboard layouts) + GroupList m_groups; + GroupMap m_groupMap; + + // the last button that we generated a key down event for. this + // is zero if the last key event was a key up. we use this to + // synthesize key repeats since the low level keyboard hook can't + // tell us if an event is a key repeat. + KeyButton m_lastDown; + + // modifier tracking + bool m_useSavedModifiers; + KeyModifierMask m_savedModifiers; + KeyModifierMask m_originalSavedModifiers; + + // pointer to ToUnicodeEx. on win95 family this will be NULL. + typedef int (WINAPI *ToUnicodeEx_t)(UINT wVirtKey, + UINT wScanCode, + PBYTE lpKeyState, + LPWSTR pwszBuff, + int cchBuff, + UINT wFlags, + HKL dwhkl); + ToUnicodeEx_t m_ToUnicodeEx; + + static const KeyID s_virtualKey[]; }; #endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 2ad2cd57..66349ff4 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -19,6 +19,7 @@ #include "CMSWindowsKeyState.h" #include "CMSWindowsScreenSaver.h" #include "CClipboard.h" +#include "CKeyMap.h" #include "XScreen.h" #include "CLock.h" #include "CThread.h" @@ -54,8 +55,8 @@ #define WM_NCXBUTTONDOWN 0x00AB #define WM_NCXBUTTONUP 0x00AC #define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 +#define MOUSEEVENTF_XDOWN 0x0080 +#define MOUSEEVENTF_XUP 0x0100 #define XBUTTON1 0x0001 #define XBUTTON2 0x0002 #endif @@ -76,8 +77,7 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, - IJob* suspend, IJob* resume) : +CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : m_isPrimary(isPrimary), m_is95Family(CArchMiscWindows::isWindows95Family()), m_isOnScreen(m_isPrimary), @@ -90,8 +90,8 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_sequenceNumber(0), m_mark(0), m_markReceived(0), - m_keyLayout(NULL), m_fixTimer(NULL), + m_keyLayout(NULL), m_screensaver(NULL), m_screensaverNotify(false), m_screensaverActive(false), @@ -106,8 +106,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_setZone(NULL), m_setMode(NULL), m_keyState(NULL), - m_suspend(suspend), - m_resume(resume), m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0), m_showingMouse(false) { @@ -124,7 +122,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_hookLibrary, m_screensaver, new TMethodJob(this, &CMSWindowsScreen::updateKeysCB)); - m_keyState = new CMSWindowsKeyState(m_desks); + m_keyState = new CMSWindowsKeyState(m_desks, getEventTarget()); updateScreenShape(); m_class = createWindowClass(); m_window = createWindow(m_class, "Synergy"); @@ -139,8 +137,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, destroyWindow(m_window); destroyClass(m_class); closeHookLibrary(m_hookLibrary); - delete m_suspend; - delete m_resume; s_screen = NULL; throw; } @@ -167,8 +163,6 @@ CMSWindowsScreen::~CMSWindowsScreen() destroyWindow(m_window); destroyClass(m_class); closeHookLibrary(m_hookLibrary); - delete m_suspend; - delete m_resume; s_screen = NULL; } @@ -192,6 +186,12 @@ CMSWindowsScreen::enable() { assert(m_isOnScreen == m_isPrimary); + // we need to poll some things to fix them + m_fixTimer = EVENTQUEUE->newTimer(1.0, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, + new TMethodEventJob(this, + &CMSWindowsScreen::handleFixes)); + // install our clipboard snooper m_nextClipboardWindow = SetClipboardViewer(m_window); @@ -232,17 +232,20 @@ CMSWindowsScreen::disable() CArchMiscWindows::kDISPLAY); } - // uninstall fix key timer + // tell key state + m_keyState->disable(); + + // stop snooping the clipboard + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // uninstall fix timer if (m_fixTimer != NULL) { EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); EVENTQUEUE->deleteTimer(m_fixTimer); m_fixTimer = NULL; } - // stop snooping the clipboard - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - m_isOnScreen = m_isPrimary; forceShowCursor(); } @@ -292,6 +295,10 @@ CMSWindowsScreen::leave() // all messages prior to now are invalid nextMark(); + // remember the modifier state. this is the modifier state + // reflected in the internal keyboard state. + m_keyState->saveModifiers(); + // capture events m_setMode(kHOOK_RELAY_EVENTS); } @@ -388,13 +395,13 @@ CMSWindowsScreen::screensaver(bool activate) void CMSWindowsScreen::resetOptions() { - // no options + m_desks->resetOptions(); } void -CMSWindowsScreen::setOptions(const COptionsList&) +CMSWindowsScreen::setOptions(const COptionsList& options) { - // no options + m_desks->setOptions(options); } void @@ -467,6 +474,130 @@ CMSWindowsScreen::warpCursor(SInt32 x, SInt32 y) m_yCursor = y; } +UInt32 +CMSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) +{ + // only allow certain modifiers + if ((mask & ~(KeyModifierShift | KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) != 0) { + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // fail if no keys + if (key == kKeyNone && mask == 0) { + return 0; + } + + // convert to win32 + UINT modifiers = 0; + if ((mask & KeyModifierShift) != 0) { + modifiers |= MOD_SHIFT; + } + if ((mask & KeyModifierControl) != 0) { + modifiers |= MOD_CONTROL; + } + if ((mask & KeyModifierAlt) != 0) { + modifiers |= MOD_ALT; + } + if ((mask & KeyModifierSuper) != 0) { + modifiers |= MOD_WIN; + } + UINT vk = m_keyState->mapKeyToVirtualKey(key); + if (key != kKeyNone && vk == 0) { + // can't map key + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // choose hotkey id + UInt32 id; + if (!m_oldHotKeyIDs.empty()) { + id = m_oldHotKeyIDs.back(); + m_oldHotKeyIDs.pop_back(); + } + else { + id = m_hotKeys.size() + 1; + } + + // if this hot key has modifiers only then we'll handle it specially + bool err; + if (key == kKeyNone) { + // check if already registered + err = (m_hotKeyToIDMap.count(CHotKeyItem(vk, modifiers)) > 0); + } + else { + // register with OS + err = (RegisterHotKey(NULL, id, modifiers, vk) == 0); + } + + if (!err) { + m_hotKeys.insert(std::make_pair(id, CHotKeyItem(vk, modifiers))); + m_hotKeyToIDMap[CHotKeyItem(vk, modifiers)] = id; + } + else { + m_oldHotKeyIDs.push_back(id); + m_hotKeys.erase(id); + LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask)); + return 0; + } + + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id)); + return id; +} + +void +CMSWindowsScreen::unregisterHotKey(UInt32 id) +{ + // look up hotkey + HotKeyMap::iterator i = m_hotKeys.find(id); + if (i == m_hotKeys.end()) { + return; + } + + // unregister with OS + bool err; + if (i->second.getVirtualKey() != 0) { + err = !UnregisterHotKey(NULL, id); + } + else { + err = false; + } + if (err) { + LOG((CLOG_WARN "failed to unregister hotkey id=%d", id)); + } + else { + LOG((CLOG_DEBUG "unregistered hotkey id=%d", id)); + } + + // discard hot key from map and record old id for reuse + m_hotKeyToIDMap.erase(i->second); + m_hotKeys.erase(i); + m_oldHotKeyIDs.push_back(id); +} + +void +CMSWindowsScreen::fakeInputBegin() +{ + assert(m_isPrimary); + + if (!m_isOnScreen) { + m_keyState->useSavedModifiers(true); + } + m_desks->fakeInputBegin(); +} + +void +CMSWindowsScreen::fakeInputEnd() +{ + assert(m_isPrimary); + + m_desks->fakeInputEnd(); + if (!m_isOnScreen) { + m_keyState->useSavedModifiers(false); + } +} + SInt32 CMSWindowsScreen::getJumpZoneSize() const { @@ -521,9 +652,9 @@ CMSWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const +CMSWindowsScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - m_desks->fakeMouseWheel(delta); + m_desks->fakeMouseWheel(xDelta, yDelta); } void @@ -556,9 +687,9 @@ CMSWindowsScreen::fakeKeyUp(KeyButton button) } void -CMSWindowsScreen::fakeToggle(KeyModifierMask modifier) +CMSWindowsScreen::fakeAllKeysUp() { - CPlatformScreen::fakeToggle(modifier); + CPlatformScreen::fakeAllKeysUp(); updateForceShowCursor(); } @@ -776,7 +907,8 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, static_cast(lParam)); case SYNERGY_MSG_MOUSE_WHEEL: - return onMouseWheel(static_cast(wParam)); + // XXX -- support x-axis scrolling + return onMouseWheel(0, static_cast(wParam)); case SYNERGY_MSG_PRE_WARP: { @@ -800,6 +932,13 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, case SYNERGY_MSG_POST_WARP: LOG((CLOG_WARN "unmatched post warp")); return true; + + case WM_HOTKEY: + // we discard these messages. we'll catch the hot key in the + // regular key event handling, where we can detect both key + // press and release. we only register the hot key so no other + // app will act on the key combination. + break; } return false; @@ -827,8 +966,6 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, break; case WM_DRAWCLIPBOARD: - LOG((CLOG_DEBUG "clipboard was taken")); - // first pass on the message if (m_nextClipboardWindow != NULL) { SendMessage(m_nextClipboardWindow, msg, wParam, lParam); @@ -843,7 +980,6 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, 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, wParam, lParam)); SendMessage(m_nextClipboardWindow, msg, wParam, lParam); } return true; @@ -856,15 +992,15 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, case PBT_APMRESUMEAUTOMATIC: case PBT_APMRESUMECRITICAL: case PBT_APMRESUMESUSPEND: - if (m_resume != NULL) { - m_resume->run(); - } + EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(), + getEventTarget(), NULL, + CEvent::kDeliverImmediately)); break; case PBT_APMSUSPEND: - if (m_suspend != NULL) { - m_suspend->run(); - } + EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(), + getEventTarget(), NULL, + CEvent::kDeliverImmediately)); break; } *result = TRUE; @@ -894,62 +1030,70 @@ CMSWindowsScreen::onMark(UInt32 mark) bool CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) { - LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, lParam)); + static const KeyModifierMask s_ctrlAlt = + KeyModifierControl | KeyModifierAlt; - // fix up key state - fixKeys(); + LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, (wParam & 0x10000u) ? 1 : 0, lParam)); - // get key info - KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16); - bool down = ((lParam & 0xc0000000u) == 0x00000000u); - bool up = ((lParam & 0x80000000u) == 0x80000000u); - bool wasDown = isKeyDown(button); + // get event info + KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16); + bool down = ((lParam & 0x80000000u) == 0x00000000u); + bool wasDown = isKeyDown(button); + KeyModifierMask oldState = pollActiveModifiers(); - // the windows keys are a royal pain on the windows 95 family. - // the system eats the key up events if and only if the windows - // key wasn't combined with another key, i.e. it was tapped. - // fixKeys() and scheduleFixKeys() are all about synthesizing - // the missing key up. but even windows itself gets a little - // confused and sets bit 30 in lParam if you tap the windows - // key twice. that bit means the key was previously down and - // that makes some sense since the up event was missing. - // anyway, on the windows 95 family we forget about windows - // key repeats and treat anything that's not a key down as a - // key up. - if (m_is95Family && - ((wParam & 0xffu) == VK_LWIN || (wParam & 0xffu) == VK_RWIN)) { - down = !up; + // check for autorepeat + if (m_keyState->testAutoRepeat(down, (lParam & 0x40000000u) == 1, button)) { + lParam |= 0x40000000u; } - // update key state. ignore key repeats. - if (down) { - m_keyState->setKeyDown(button, true); - } - else if (up) { - m_keyState->setKeyDown(button, false); + // if the button is zero then guess what the button should be. + // these are badly synthesized key events and logitech software + // that maps mouse buttons to keys is known to do this. + // alternatively, we could just throw these events out. + if (button == 0) { + button = m_keyState->virtualKeyToButton(wParam & 0xffu); + if (button == 0) { + return true; + } + wasDown = isKeyDown(button); } - // schedule a timer if we need to fix keys later - scheduleFixKeys(); + // record keyboard state + m_keyState->onKey(button, down, oldState); - // 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. - KeyModifierMask mask = getActiveModifiers(); - bool ctrlAlt = ((mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)); - if (down && ctrlAlt && - isKeyDown(m_keyState->virtualKeyToButton(VK_DELETE))) { - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_LCONTROL), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_RCONTROL), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_LMENU), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_RMENU), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_DELETE), false); + // windows doesn't tell us the modifier key state on mouse or key + // events so we have to figure it out. most apps would use + // GetKeyState() or even GetAsyncKeyState() for that but we can't + // because our hook doesn't pass on key events for several modifiers. + // it can't otherwise the system would interpret them normally on + // the primary screen even when on a secondary screen. so tapping + // alt would activate menus and tapping the windows key would open + // the start menu. if you don't pass those events on in the hook + // then GetKeyState() understandably doesn't reflect the effect of + // the event. curiously, neither does GetAsyncKeyState(), which is + // surprising. + // + // so anyway, we have to track the modifier state ourselves for + // at least those modifiers we don't pass on. pollActiveModifiers() + // does that but we have to update the keyboard state before calling + // pollActiveModifiers() to get the right answer. but the only way + // to set the modifier state or to set the up/down state of a key + // is via onKey(). so we have to call onKey() twice. + KeyModifierMask state = pollActiveModifiers(); + m_keyState->onKey(button, down, state); + + // check for hot keys + if (oldState != state) { + // modifier key was pressed/released + if (onHotKey(0, lParam)) { + return true; + } + } + else { + // non-modifier was pressed/released + if (onHotKey(wParam, lParam)) { + return true; + } } // ignore message if posted prior to last mark change @@ -957,20 +1101,23 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // check for ctrl+alt+del. we do not want to pass that to the // client. the user can use ctrl+alt+pause to emulate it. UINT virtKey = (wParam & 0xffu); - if (virtKey == VK_DELETE && ctrlAlt) { + if (virtKey == VK_DELETE && (state & s_ctrlAlt) == s_ctrlAlt) { LOG((CLOG_DEBUG "discard ctrl+alt+del")); return true; } // check for ctrl+alt+del emulation - if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && ctrlAlt) { + if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && + (state & s_ctrlAlt) == s_ctrlAlt) { LOG((CLOG_DEBUG "emulate ctrl+alt+del")); // switch wParam and lParam to be as if VK_DELETE was - // pressed or released - wParam = VK_DELETE; + // pressed or released. when mapping the key we require that + // we not use AltGr (the 0x10000 flag in wParam) and we not + // use the keypad delete key (the 0x01000000 flag in lParam). + wParam = VK_DELETE | 0x00010000u; lParam &= 0xfe000000; - lParam |= m_keyState->virtualKeyToButton(wParam) << 16; - lParam |= 0x00000001; + lParam |= m_keyState->virtualKeyToButton(wParam & 0xffu) << 16; + lParam |= 0x01000001; } // process key @@ -978,28 +1125,28 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask); button = static_cast((lParam & 0x01ff0000u) >> 16); if (key != kKeyNone) { - // fix up key. if the key isn't down according to + // fix key up. 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 && !wasDown && up) { + if (m_is95Family && !wasDown && !down) { switch (virtKey) { + case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: - case VK_SHIFT: + case VK_CONTROL: case VK_LCONTROL: case VK_RCONTROL: - case VK_CONTROL: + case VK_MENU: case VK_LMENU: case VK_RMENU: - case VK_MENU: + case VK_LWIN: + case VK_RWIN: case VK_CAPITAL: case VK_NUMLOCK: case VK_SCROLL: - case VK_LWIN: - case VK_RWIN: break; default: @@ -1012,17 +1159,64 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // do it m_keyState->sendKeyEvent(getEventTarget(), ((lParam & 0x80000000u) == 0), - ((lParam & 0x40000000u) == 1), + ((lParam & 0x40000000u) != 0), key, mask, (SInt32)(lParam & 0xffff), button); } else { - LOG((CLOG_DEBUG2 "event: cannot map key")); + LOG((CLOG_DEBUG1 "cannot map key")); } } return true; } +bool +CMSWindowsScreen::onHotKey(WPARAM wParam, LPARAM lParam) +{ + // get the key info + KeyModifierMask state = getActiveModifiers(); + UINT virtKey = (wParam & 0xffu); + UINT modifiers = 0; + if ((state & KeyModifierShift) != 0) { + modifiers |= MOD_SHIFT; + } + if ((state & KeyModifierControl) != 0) { + modifiers |= MOD_CONTROL; + } + if ((state & KeyModifierAlt) != 0) { + modifiers |= MOD_ALT; + } + if ((state & KeyModifierSuper) != 0) { + modifiers |= MOD_WIN; + } + + // find the hot key id + HotKeyToIDMap::const_iterator i = + m_hotKeyToIDMap.find(CHotKeyItem(virtKey, modifiers)); + if (i == m_hotKeyToIDMap.end()) { + return false; + } + + // find what kind of event + CEvent::Type type; + if ((lParam & 0x80000000u) == 0u) { + if ((lParam & 0x40000000u) != 0u) { + // ignore key repeats but it counts as a hot key + return true; + } + type = getHotKeyDownEvent(); + } + else { + type = getHotKeyUpEvent(); + } + + // generate event + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(i->second))); + + return true; +} + bool CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) { @@ -1042,16 +1236,19 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) // ignore message if posted prior to last mark change if (!ignore()) { + KeyModifierMask mask = m_keyState->getActiveModifiers(); if (pressed) { LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonDownEvent(), + CButtonInfo::alloc(button, mask)); } } else { LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonUpEvent(), + CButtonInfo::alloc(button, mask)); } } } @@ -1109,12 +1306,12 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } bool -CMSWindowsScreen::onMouseWheel(SInt32 delta) +CMSWindowsScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta) { // ignore message if posted prior to last mark change if (!ignore()) { - LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); - sendEvent(getWheelEvent(), CWheelInfo::alloc(delta)); + LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(xDelta, yDelta)); } return true; } @@ -1197,7 +1394,6 @@ CMSWindowsScreen::onClipboardChange() // 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; @@ -1205,7 +1401,7 @@ CMSWindowsScreen::onClipboardChange() sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); } } - else { + else if (!m_ownClipboard) { LOG((CLOG_DEBUG "clipboard changed: synergy owned")); m_ownClipboard = true; } @@ -1282,6 +1478,34 @@ CMSWindowsScreen::updateScreenShape() m_desks->setShape(m_x, m_y, m_w, m_h, m_xCenter, m_yCenter, m_multimon); } +void +CMSWindowsScreen::handleFixes(const CEvent&, void*) +{ + // fix clipboard chain + fixClipboardViewer(); + + // update keys if keyboard layouts have changed + if (m_keyState->didGroupsChange()) { + updateKeys(); + } +} + +void +CMSWindowsScreen::fixClipboardViewer() +{ + // XXX -- disable this code for now. somehow it can cause an infinite + // recursion in the WM_DRAWCLIPBOARD handler. either we're sending + // the message to our own window or some window farther down the chain + // forwards the message to our window or a window farther up the chain. + // i'm not sure how that could happen. the m_nextClipboardWindow = NULL + // was not in the code that infinite loops and may fix the bug but i + // doubt it. + return; + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + m_nextClipboardWindow = SetClipboardViewer(m_window); +} + void CMSWindowsScreen::enableSpecialKeys(bool enable) const { @@ -1387,63 +1611,37 @@ CMSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const } } -void -CMSWindowsScreen::fixKeys() -{ - // fake key releases for the windows keys if we think they're - // down but they're really up. we have to do this because if the - // user presses and releases a windows key without pressing any - // other key while it's down then the system will eat the key - // release. if we don't detect that and synthesize the release - // then the client won't take the usual windows key release action - // (which on windows is to show the start menu). - // - // only check on the windows 95 family since the NT family reports - // the key releases as usual. - if (m_is95Family) { - m_keyState->fixKey(getEventTarget(), VK_LWIN); - m_keyState->fixKey(getEventTarget(), VK_RWIN); - - // check if we need the fix timer anymore - scheduleFixKeys(); - } -} - -void -CMSWindowsScreen::scheduleFixKeys() -{ - if (m_is95Family) { - // see if any keys that need fixing are down - bool fix = - (m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_LWIN)) || - m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_RWIN))); - - // start or stop fix timer - if (fix && m_fixTimer == NULL) { - m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, - new TMethodEventJob( - this, &CMSWindowsScreen::handleFixKeys)); - } - else if (!fix && m_fixTimer != NULL) { - EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); - EVENTQUEUE->deleteTimer(m_fixTimer); - m_fixTimer = NULL; - } - } -} - -void -CMSWindowsScreen::handleFixKeys(const CEvent&, void*) -{ - fixKeys(); -} - void CMSWindowsScreen::updateKeysCB(void*) { - m_keyState->updateKeys(); - updateButtons(); + // record which keys we think are down + bool down[IKeyState::kNumButtons]; + bool sendFixes = (isPrimary() && !m_isOnScreen); + if (sendFixes) { + for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { + down[i] = m_keyState->isKeyDown(i); + } + } + + // update layouts if necessary + if (m_keyState->didGroupsChange()) { + CPlatformScreen::updateKeyMap(); + } + + // now update the keyboard state + CPlatformScreen::updateKeyState(); + + // now see which keys we thought were down but now think are up. + // send key releases for these keys to the active client. + if (sendFixes) { + KeyModifierMask mask = pollActiveModifiers(); + for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { + if (down[i] && !m_keyState->isKeyDown(i)) { + m_keyState->sendKeyEvent(getEventTarget(), + false, false, kKeyNone, mask, 1, i); + } + } + } } void @@ -1512,3 +1710,28 @@ CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return result; } + + +// +// CMSWindowsScreen::CHotKeyItem +// + +CMSWindowsScreen::CHotKeyItem::CHotKeyItem(UINT keycode, UINT mask) : + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +UINT +CMSWindowsScreen::CHotKeyItem::getVirtualKey() const +{ + return m_keycode; +} + +bool +CMSWindowsScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const +{ + return (m_keycode < x.m_keycode || + (m_keycode == x.m_keycode && m_mask < x.m_mask)); +} diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 2b8b7366..eda3f554 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -28,12 +28,11 @@ class CMSWindowsDesks; class CMSWindowsKeyState; class CMSWindowsScreenSaver; class CThread; -class IJob; //! Implementation of IPlatformScreen for Microsoft Windows class CMSWindowsScreen : public CPlatformScreen { public: - CMSWindowsScreen(bool isPrimary, IJob* suspend, IJob* resume); + CMSWindowsScreen(bool isPrimary); virtual ~CMSWindowsScreen(); //! @name manipulators @@ -68,6 +67,11 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 registerHotKey(KeyID key, + KeyModifierMask mask); + virtual void unregisterHotKey(UInt32 id); + virtual void fakeInputBegin(); + virtual void fakeInputEnd(); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; @@ -76,7 +80,7 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; - virtual void fakeMouseWheel(SInt32 delta) const; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; // IKeyState overrides virtual void updateKeys(); @@ -85,7 +89,7 @@ public: virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); - virtual void fakeToggle(KeyModifierMask modifier); + virtual void fakeAllKeysUp(); // IPlatformScreen overrides virtual void enable(); @@ -139,9 +143,10 @@ private: // message handlers bool onMark(UInt32 mark); bool onKey(WPARAM, LPARAM); + bool onHotKey(WPARAM, LPARAM); bool onMouseButton(WPARAM, LPARAM); bool onMouseMove(SInt32 x, SInt32 y); - bool onMouseWheel(SInt32 delta); + bool onMouseWheel(SInt32 xDelta, SInt32 yDelta); bool onScreensaver(bool activated); bool onDisplayChange(); bool onClipboardChange(); @@ -158,6 +163,12 @@ private: // update screen size cache void updateScreenShape(); + // fix timer callback + void handleFixes(const CEvent&, void*); + + // fix the clipboard viewer chain + void fixClipboardViewer(); + // enable/disable special key combinations so we can catch/pass them void enableSpecialKeys(bool) const; @@ -167,16 +178,6 @@ private: // map a button event to a press (true) or release (false) bool mapPressFromEvent(WPARAM msg, LPARAM button) const; - // fix the key state, synthesizing fake key releases for keys - // that aren't down anymore. - void fixKeys(); - - // (un)schedule a later call to fixKeys - void scheduleFixKeys(); - - // event handler to fix the key state - void handleFixKeys(const CEvent&, void*); - // job to update the key state void updateKeysCB(void*); @@ -194,6 +195,22 @@ private: static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); private: + struct CHotKeyItem { + public: + CHotKeyItem(UINT vk, UINT modifiers); + + UINT getVirtualKey() const; + + bool operator<(const CHotKeyItem&) const; + + private: + UINT m_keycode; + UINT m_mask; + }; + typedef std::map HotKeyMap; + typedef std::vector HotKeyIDList; + typedef std::map HotKeyToIDMap; + static HINSTANCE s_instance; // true if screen is being used as a primary screen, false otherwise @@ -229,12 +246,12 @@ private: // the main loop's thread id DWORD m_threadID; + // timer for periodically checking stuff that requires polling + CEventQueueTimer* m_fixTimer; + // the keyboard layout to use when off primary screen HKL m_keyLayout; - // the timer used to check for fixing key state - CEventQueueTimer* m_fixTimer; - // screen saver stuff CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; @@ -260,13 +277,14 @@ private: // keyboard stuff CMSWindowsKeyState* m_keyState; + // hot key stuff + HotKeyMap m_hotKeys; + HotKeyIDList m_oldHotKeyIDs; + HotKeyToIDMap m_hotKeyToIDMap; + // map of button state bool m_buttons[1 + kButtonExtra0 + 1]; - // suspend/resume callbacks - IJob* m_suspend; - IJob* m_resume; - // the system shows the mouse cursor when an internal display count // is >= 0. this count is maintained per application but there's // apparently a system wide count added to the application's count. diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index b321fddb..529cd176 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -107,7 +107,7 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); if (process == NULL) { // didn't start - LOG((CLOG_DEBUG "can't open screen saver process")); + LOG((CLOG_DEBUG2 "can't open screen saver process")); return false; } @@ -123,7 +123,7 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) // 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")); + LOG((CLOG_DEBUG2 "can't open screen saver desktop")); return false; } @@ -441,7 +441,7 @@ void CMSWindowsScreenSaver::setSecure(bool secure, bool saveSecureAsInt) { HKEY hkey = - CArchMiscWindows::openKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure); + CArchMiscWindows::addKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure); if (hkey == NULL) { return; } diff --git a/lib/platform/COSXClipboard.cpp b/lib/platform/COSXClipboard.cpp index 6a244587..7d39eaad 100644 --- a/lib/platform/COSXClipboard.cpp +++ b/lib/platform/COSXClipboard.cpp @@ -42,15 +42,18 @@ COSXClipboard::empty() assert(m_scrap != NULL); OSStatus err = ClearScrap(&m_scrap); - // XXX -- check err? + if (err != noErr) { + LOG((CLOG_DEBUG "failed to grab clipboard")); + return false; + } + // we own the clipboard err = PutScrapFlavor( m_scrap, getOwnershipFlavor(), kScrapFlavorMaskNone, 0, - 0); - + 0); if (err != noErr) { LOG((CLOG_DEBUG "failed to grab clipboard")); return false; diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index 035a0468..5ae7f5d9 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -15,27 +15,7 @@ #include "COSXKeyState.h" #include "CLog.h" #include "CArch.h" -#include -struct CKCHRDeadKeyRecord { -public: - UInt8 m_tableIndex; - UInt8 m_virtualKey; - SInt16 m_numCompletions; - UInt8 m_completion[1][2]; -}; - -struct CKCHRDeadKeys { -public: - SInt16 m_numRecords; - CKCHRDeadKeyRecord m_records[1]; -}; - -struct CKeyEntry { -public: - KeyID m_keyID; - UInt32 m_virtualKey; -}; // Hardcoded virtual key table. Oddly, Apple doesn't document the // meaning of virtual key codes. The whole point of *virtual* key // codes is to make them hardware independent so these codes should @@ -47,48 +27,28 @@ public: // first instance of a virtual key code maps to the KeyID that we // want to generate for that code. The others are for mapping // different KeyIDs to a single key code. +static const UInt32 s_shiftVK = 56; +static const UInt32 s_controlVK = 59; +static const UInt32 s_altVK = 55; +static const UInt32 s_superVK = 58; +static const UInt32 s_capsLockVK = 57; +struct CKeyEntry { +public: + KeyID m_keyID; + UInt32 m_virtualKey; +}; static const CKeyEntry s_controlKeys[] = { - // TTY functions - { kKeyBackSpace, 51 }, - { kKeyTab, 48 }, - { kKeyLeftTab, 48 }, - { kKeyReturn, 36 }, - { kKeyLinefeed, 36 }, -// { kKeyClear, 0xFFFF }, /* no mapping on apple */ -// { kKeyPause, 0xFFFF }, /* no mapping on apple */ -// { kKeyScrollLock, 0xFFFF }, /* no mapping on apple */ -// { kKeySysReq, 0xFFFF }, /* no mapping on apple */ - { kKeyEscape, 53 }, - { kKeyDelete, 117 }, - - // cursor control - { kKeyHome, 115 }, - { kKeyBegin, 115 }, + // cursor keys. if we don't do this we'll may still get these from + // the keyboard resource but they may not correspond to the arrow + // keys. { kKeyLeft, 123 }, - { kKeyUp, 126 }, { kKeyRight, 124 }, + { kKeyUp, 126 }, { kKeyDown, 125 }, + { kKeyHome, 115 }, + { kKeyEnd, 119 }, { kKeyPageUp, 116 }, { kKeyPageDown, 121 }, - { kKeyEnd, 119 }, - - // numeric keypad - { kKeyKP_0, 82 }, - { kKeyKP_1, 83 }, - { kKeyKP_2, 84 }, - { kKeyKP_3, 85 }, - { kKeyKP_4, 86 }, - { kKeyKP_5, 87 }, - { kKeyKP_6, 88 }, - { kKeyKP_7, 89 }, - { kKeyKP_8, 91 }, - { kKeyKP_9, 92 }, - { kKeyKP_Enter, 76 }, - { kKeyKP_Decimal, 65 }, - { kKeyKP_Add, 69 }, - { kKeyKP_Subtract, 78 }, - { kKeyKP_Multiply, 67 }, - { kKeyKP_Divide, 75 }, // function keys { kKeyF1, 122 }, @@ -106,43 +66,44 @@ static const CKeyEntry s_controlKeys[] = { { kKeyF13, 105 }, { kKeyF14, 107 }, { kKeyF15, 113 }, + { kKeyF16, 106 }, - // misc keys - { kKeyHelp, 114 }, + // virtual key 110 is fn+enter and i have no idea what that's supposed + // to map to. also the enter key with numlock on is a modifier but i + // don't know which. - // modifier keys. i don't know how to make the mac properly - // interpret the right hand versions of modifier keys so they're - // currently mapped to the left hand version. + // modifier keys. OS X doesn't seem to support right handed versions + // of modifier keys so we map them to the left handed versions. + { kKeyShift_L, s_shiftVK }, + { kKeyShift_R, s_shiftVK }, // 60 + { kKeyControl_L, s_controlVK }, + { kKeyControl_R, s_controlVK }, // 62 + { kKeyAlt_L, s_altVK }, + { kKeyAlt_R, s_altVK }, + { kKeySuper_L, s_superVK }, + { kKeySuper_R, s_superVK }, // 61 + { kKeyMeta_L, s_superVK }, + { kKeyMeta_R, s_superVK }, // 61 + + // toggle modifiers // { kKeyNumLock, 71 }, - { kKeyShift_L, 56 }, - { kKeyShift_R, 56 /*60*/ }, - { kKeyControl_L, 59 }, - { kKeyControl_R, 59 /*62*/ }, - { kKeyAlt_L, 55 }, - { kKeyAlt_R, 55 }, - { kKeySuper_L, 58 }, - { kKeySuper_R, 58 /*61*/ }, - { kKeyMeta_L, 58 }, - { kKeyMeta_R, 58 /*61*/ }, - { kKeyCapsLock, 57 } + { kKeyCapsLock, s_capsLockVK } }; -// special key that synthesizes a delay. see doFakeKeyEvent() and -// mapKey(). -static const KeyButton kDelayKey = 510; - // // COSXKeyState // COSXKeyState::COSXKeyState() : - m_uchrFound(false) + m_deadKeyState(0) { - setHalfDuplexMask(0); - SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); - SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys); - setKeyboardLayout(keyboardLayoutID); + // build virtual key map + for (size_t i = 0; i < sizeof(s_controlKeys) / + sizeof(s_controlKeys[0]); ++i) { + m_virtualKeyMap[s_controlKeys[i].m_virtualKey] = + s_controlKeys[i].m_keyID; + } } COSXKeyState::~COSXKeyState() @@ -150,252 +111,37 @@ COSXKeyState::~COSXKeyState() // do nothing } -void -COSXKeyState::sendKeyEvent(void* target, - bool press, bool isAutoRepeat, - KeyID key, KeyModifierMask mask, - SInt32 count, KeyButton button) +KeyModifierMask +COSXKeyState::mapModifiersFromOSX(UInt32 mask) const { - if (press || isAutoRepeat) { - // send key - if (press) { - CKeyState::sendKeyEvent(target, true, false, - key, mask, 1, button); - if (count > 0) { - --count; - } - } - - if (count >= 1) { - CKeyState::sendKeyEvent(target, true, true, - key, mask, count, button); - } - + // convert + KeyModifierMask outMask = 0; + if ((mask & shiftKey) != 0) { + outMask |= KeyModifierShift; } - else { - // do key up - CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); + if ((mask & rightShiftKey) != 0) { + outMask |= KeyModifierShift; } -} - -void -COSXKeyState::setHalfDuplexMask(KeyModifierMask mask) -{ - CKeyState::setHalfDuplexMask(mask | KeyModifierCapsLock); -} - -bool -COSXKeyState::fakeCtrlAltDel() -{ - // pass keys through unchanged - return false; -} - -const char* -COSXKeyState::getKeyName(KeyButton button) const -{ - static char name[10]; - sprintf(name, "vk 0x%02x", button); - return name; -} - -void -COSXKeyState::doUpdateKeys() -{ - // save key mapping - m_keyMap.clear(); - m_uchrFound = false; - if (m_uchrResource != NULL) { - m_uchrFound = filluchrKeysMap(m_keyMap); + if ((mask & controlKey) != 0) { + outMask |= KeyModifierControl; } - if (!m_uchrFound && m_KCHRResource != NULL) { - fillKCHRKeysMap(m_keyMap); + if ((mask & rightControlKey) != 0) { + outMask |= KeyModifierControl; } - fillSpecialKeys(m_keyMap, m_virtualKeyMap); - - // add modifiers - KeyButtons keys; - addKeyButton(keys, kKeyShift_L); - addKeyButton(keys, kKeyShift_R); - addModifier(KeyModifierShift, keys); - keys.clear(); - addKeyButton(keys, kKeyControl_L); - addKeyButton(keys, kKeyControl_R); - addModifier(KeyModifierControl, keys); - keys.clear(); - addKeyButton(keys, kKeyAlt_L); - addKeyButton(keys, kKeyAlt_R); - addModifier(KeyModifierAlt, keys); - keys.clear(); - addKeyButton(keys, kKeySuper_L); - addKeyButton(keys, kKeySuper_R); - addModifier(KeyModifierSuper, keys); - keys.clear(); - addKeyButton(keys, kKeyCapsLock); - addModifier(KeyModifierCapsLock, keys); - keys.clear(); - addKeyButton(keys, kKeyNumLock); - addModifier(KeyModifierNumLock, keys); - keys.clear(); - - // FIXME -- get the current keyboard state. call setKeyDown() - // and setToggled() as appropriate. -} - -void -COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool) -{ - LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press)); - - // if it's the special delay key then just pause briefly - if (button == kDelayKey) { - ARCH->sleep(0.01); - return; + if ((mask & cmdKey) != 0) { + outMask |= KeyModifierAlt; + } + if ((mask & optionKey) != 0) { + outMask |= KeyModifierSuper; + } + if ((mask & rightOptionKey) != 0) { + outMask |= KeyModifierSuper; + } + if ((mask & alphaLock) != 0) { + outMask |= KeyModifierCapsLock; } - // let system figure out character for us - CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(button), press); -} - -KeyButton -COSXKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const -{ - // see if the keyboard layout has changed since we last checked it. - // reload the keyboard info if so. - const_cast(this)->checkKeyboardLayout(); - - // look up virtual key - CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); - if (keyIndex == m_keyMap.end()) { - return 0; - } - const CKeySequence& sequence = keyIndex->second; - if (sequence.empty()) { - return 0; - } - - // FIXME -- for both calls to addKeystrokes below we'd prefer to use - // a required mask that generates the same character but matches - // the desiredMask as closely as possible. - // FIXME -- would prefer to not restore the modifier keys after each - // dead key since it's unnecessary but we don't have a mechanism - // for tracking the modifier state without actually updating the - // internal keyboard state. we'd have to track the state to - // ensure we adjust the right modifiers for remaining dead keys - // and the final key. - - // add dead keys - for (size_t i = 0; i < sequence.size() - 1; ++i) { - // simulate press - KeyButton keyButton = - addKeystrokes(keys, sequence[i].m_button, - sequence[i].m_requiredState, - sequence[i].m_requiredMask, false); - - // simulate release - Keystroke keystroke; - keystroke.m_key = keyButton; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - - // pause briefly before sending the next key because some - // apps (e.g. TextEdit) seem to ignore dead keys that occur - // very shortly before the next key. why they'd do that i - // don't know. - keystroke.m_key = kDelayKey; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - - // add final key. - // if the desired mask includes Alt or Control then match the - // desired mask. this ensures that combinations like - // Command+Shift+S use the Command and Shift modifiers and - // those like Command+S do not use the shift modifier. do not - // do this if the key to synthesize is a modifier key, otherwise - // we'd apply modifiers to modifiers which breaks things (by - // say, putting a Control press and release around a Control - // press). - if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) != 0 && - id != kKeyShift_L && id != kKeyShift_R && - id != kKeyControl_L && id != kKeyControl_R && - id != kKeyAlt_L && id != kKeyAlt_R && - id != kKeySuper_L && id != kKeySuper_R && - id != kKeyMeta_L && id != kKeyMeta_R) { - return addKeystrokes(keys, sequence.back().m_button, - desiredMask, - KeyModifierShift | KeyModifierSuper | - KeyModifierAlt | KeyModifierControl | - KeyModifierCapsLock, - isAutoRepeat); - } - else { - return addKeystrokes(keys, sequence.back().m_button, - sequence.back().m_requiredState, - sequence.back().m_requiredMask, - isAutoRepeat); - } -} - -KeyButton -COSXKeyState::addKeystrokes(Keystrokes& keys, KeyButton keyButton, - KeyModifierMask desiredMask, KeyModifierMask requiredMask, - bool isAutoRepeat) const -{ - // adjust the modifiers - Keystrokes undo; - if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) { - LOG((CLOG_DEBUG2 "failed to adjust modifiers")); - return 0; - } - - // add the key event - Keystroke keystroke; - keystroke.m_key = keyButton; - if (!isAutoRepeat) { - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - else { - keystroke.m_press = false; - keystroke.m_repeat = true; - keys.push_back(keystroke); - keystroke.m_press = true; - keys.push_back(keystroke); - } - - // put undo keystrokes at end of keystrokes in reverse order - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - return keyButton; -} - -bool -COSXKeyState::adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const -{ - // for each modifier in requiredMask make sure the current state - // of that modifier matches the bit in desiredMask. - for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) { - if ((mask & requiredMask) != 0) { - bool active = ((desiredMask & mask) != 0); - if (!mapModifier(keys, undo, mask, active)) { - return false; - } - requiredMask ^= mask; - } - } - return true; + return outMask; } KeyButton @@ -407,7 +153,7 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, // map modifier key if (maskOut != NULL) { KeyModifierMask activeMask = getActiveModifiers(); - activeMask &= ~KeyModifierModeSwitch; + activeMask &= ~KeyModifierAltGr; *maskOut = activeMask; } @@ -434,15 +180,33 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, return mapVirtualKeyToKeyButton(vkCode); } - // check for character keys - if (m_uchrFound) { - // get the event modifiers and remove the command and control - // keys. - UInt32 modifiers; - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, - NULL, sizeof(modifiers), NULL, &modifiers); - modifiers = (modifiers & ~(cmdKey | controlKey | rightControlKey)) >> 8; + // get keyboard info + KeyboardLayoutRef keyboardLayout; + OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout); + if (status != noErr) { + return kKeyNone; + } + // get the event modifiers and remove the command and control + // keys. note if we used them though. + UInt32 modifiers; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(modifiers), NULL, &modifiers); + static const UInt32 s_commandModifiers = + cmdKey | controlKey | rightControlKey; + bool isCommand = ((modifiers & s_commandModifiers) != 0); + modifiers &= ~s_commandModifiers; + + // if we've used a command key then we want the glyph produced without + // the option key (i.e. the base glyph). + if (isCommand) { + modifiers &= ~optionKey; + } + + // translate via uchr resource + const void* resource; + if (KLGetKeyboardLayoutProperty(keyboardLayout, + kKLuchrData, &resource) == noErr) { // choose action UInt16 action; switch (eventKind) { @@ -461,10 +225,10 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, // translate key UniCharCount count; UniChar chars[2]; - OSStatus status = UCKeyTranslate(m_uchrResource, + OSStatus status = UCKeyTranslate((const UCKeyboardLayout*)resource, vkCode & 0xffu, action, - modifiers & 0xffu, - m_keyboardType, 0, &m_deadKeyState, + (modifiers >> 8) & 0xffu, + LMGetKbdType(), 0, &m_deadKeyState, sizeof(chars) / sizeof(chars[0]), &count, chars); // get the characters @@ -472,26 +236,24 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, if (count != 0 || m_deadKeyState == 0) { m_deadKeyState = 0; for (UniCharCount i = 0; i < count; ++i) { - ids.push_back(unicharToKeyID(chars[i])); + ids.push_back(CKeyResource::unicharToKeyID(chars[i])); } + adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); } + return 0; } } - else if (m_KCHRResource != NULL) { - // get the event modifiers and remove the command and control - // keys. - UInt32 modifiers; - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, - NULL, sizeof(modifiers), NULL, &modifiers); - modifiers &= ~(cmdKey | controlKey | rightControlKey); + // translate via KCHR resource + if (KLGetKeyboardLayoutProperty(keyboardLayout, + kKLKCHRData, &resource) == noErr) { // build keycode UInt16 keycode = static_cast((modifiers & 0xff00u) | (vkCode & 0x00ffu)); // translate key - UInt32 result = KeyTranslate(m_KCHRResource, keycode, &m_deadKeyState); + UInt32 result = KeyTranslate(resource, keycode, &m_deadKeyState); // get the characters UInt8 c1 = static_cast((result >> 16) & 0xffu); @@ -499,9 +261,10 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, if (c2 != 0) { m_deadKeyState = 0; if (c1 != 0) { - ids.push_back(charToKeyID(c1)); + ids.push_back(CKeyResource::getKeyID(c1)); } - ids.push_back(charToKeyID(c2)); + ids.push_back(CKeyResource::getKeyID(c2)); + adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); } } @@ -509,16 +272,307 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, return 0; } -void -COSXKeyState::addKeyButton(KeyButtons& keys, KeyID id) const +bool +COSXKeyState::fakeCtrlAltDel() { - CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); - if (keyIndex == m_keyMap.end()) { - return; - } - keys.push_back(keyIndex->second[0].m_button); + // pass keys through unchanged + return false; } +KeyModifierMask +COSXKeyState::pollActiveModifiers() const +{ + return mapModifiersFromOSX(GetCurrentKeyModifiers()); +} + +SInt32 +COSXKeyState::pollActiveGroup() const +{ + KeyboardLayoutRef keyboardLayout; + OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout); + if (status == noErr) { + GroupMap::const_iterator i = m_groupMap.find(keyboardLayout); + if (i != m_groupMap.end()) { + return i->second; + } + } + return 0; +} + +void +COSXKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const +{ + KeyMap km; + GetKeys(km); + const UInt8* m = reinterpret_cast(km); + for (UInt32 i = 0; i < 16; ++i) { + for (UInt32 j = 0; j < 8; ++j) { + if ((m[i] & (1u << j)) != 0) { + pressedKeys.insert(mapVirtualKeyToKeyButton(8 * i + j)); + } + } + } +} + +void +COSXKeyState::getKeyMap(CKeyMap& keyMap) +{ + // update keyboard groups + if (getGroups(m_groups)) { + m_groupMap.clear(); + SInt32 numGroups = (SInt32)m_groups.size(); + for (SInt32 g = 0; g < numGroups; ++g) { + m_groupMap[m_groups[g]] = g; + } + } + + UInt32 keyboardType = LMGetKbdType(); + for (SInt32 g = 0, n = (SInt32)m_groups.size(); g < n; ++g) { + // add special keys + getKeyMapForSpecialKeys(keyMap, g); + + // add regular keys + + // try uchr resource first + const void* resource; + if (KLGetKeyboardLayoutProperty(m_groups[g], + kKLuchrData, &resource) == noErr) { + CUCHRKeyResource uchr(resource, keyboardType); + if (uchr.isValid()) { + LOG((CLOG_DEBUG1 "using uchr resource for group %d", g)); + getKeyMap(keyMap, g, uchr); + continue; + } + } + + // try KCHR resource + if (KLGetKeyboardLayoutProperty(m_groups[g], + kKLKCHRData, &resource) == noErr) { + CKCHRKeyResource kchr(resource); + if (kchr.isValid()) { + LOG((CLOG_DEBUG1 "using KCHR resource for group %d", g)); + getKeyMap(keyMap, g, kchr); + continue; + } + } + + LOG((CLOG_DEBUG1 "no keyboard resource for group %d", g)); + } +} + +void +COSXKeyState::fakeKey(const Keystroke& keystroke) +{ + switch (keystroke.m_type) { + case Keystroke::kButton: + LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); + + // let system figure out character for us + CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey( + keystroke.m_data.m_button.m_button), + keystroke.m_data.m_button.m_press); + + // add a delay if client data isn't zero + if (keystroke.m_data.m_button.m_client) { + ARCH->sleep(0.01); + } + break; + + case Keystroke::kGroup: + if (keystroke.m_data.m_group.m_absolute) { + LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); + setGroup(keystroke.m_data.m_group.m_group); + } + else { + LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); + setGroup(getEffectiveGroup(pollActiveGroup(), + keystroke.m_data.m_group.m_group)); + } + break; + } +} + +void +COSXKeyState::getKeyMapForSpecialKeys(CKeyMap& keyMap, SInt32 group) const +{ + // special keys are insensitive to modifers and none are dead keys + CKeyMap::KeyItem item; + for (size_t i = 0; i < sizeof(s_controlKeys) / + sizeof(s_controlKeys[0]); ++i) { + const CKeyEntry& entry = s_controlKeys[i]; + item.m_id = entry.m_keyID; + item.m_group = group; + item.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey); + item.m_required = 0; + item.m_sensitive = 0; + item.m_dead = false; + item.m_client = 0; + CKeyMap::initModifierKey(item); + keyMap.addKeyEntry(item); + + if (item.m_lock) { + // all locking keys are half duplex on OS X + keyMap.addHalfDuplexButton(item.m_button); + } + } + + // note: we don't special case the number pad keys. querying the + // mac keyboard returns the non-keypad version of those keys but + // a CKeyState always provides a mapping from keypad keys to + // non-keypad keys so we'll be able to generate the characters + // anyway. +} + +bool +COSXKeyState::getKeyMap(CKeyMap& keyMap, + SInt32 group, const CKeyResource& r) const +{ + if (!r.isValid()) { + return false; + } + + // space for all possible modifier combinations + std::vector modifiers(r.getNumModifierCombinations()); + + // make space for the keys that any single button can synthesize + std::vector > buttonKeys(r.getNumTables()); + + // iterate over each button + CKeyMap::KeyItem item; + for (UInt32 i = 0; i < r.getNumButtons(); ++i) { + item.m_button = mapVirtualKeyToKeyButton(i); + + // the KeyIDs we've already handled + std::set keys; + + // convert the entry in each table for this button to a KeyID + for (UInt32 j = 0; j < r.getNumTables(); ++j) { + buttonKeys[j].first = r.getKey(j, i); + buttonKeys[j].second = CKeyMap::isDeadKey(buttonKeys[j].first); + } + + // iterate over each character table + for (UInt32 j = 0; j < r.getNumTables(); ++j) { + // get the KeyID for the button/table + KeyID id = buttonKeys[j].first; + if (id == kKeyNone) { + continue; + } + + // if we've already handled the KeyID in the table then + // move on to the next table + if (keys.count(id) > 0) { + continue; + } + keys.insert(id); + + // prepare item. the client state is 1 for dead keys. + item.m_id = id; + item.m_group = group; + item.m_dead = buttonKeys[j].second; + item.m_client = buttonKeys[j].second ? 1 : 0; + CKeyMap::initModifierKey(item); + if (item.m_lock) { + // all locking keys are half duplex on OS X + keyMap.addHalfDuplexButton(i); + } + + // collect the tables that map to the same KeyID. we know it + // can't be any earlier tables because of the check above. + std::set tables; + tables.insert(static_cast(j)); + for (UInt32 k = j + 1; k < r.getNumTables(); ++k) { + if (buttonKeys[k].first == id) { + tables.insert(static_cast(k)); + } + } + + // collect the modifier combinations that map to any of the + // tables we just collected + for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) { + modifiers[k] = (tables.count(r.getTableForModifier(k)) > 0); + } + + // figure out which modifiers the key is sensitive to. the + // key is insensitive to a modifier if for every modifier mask + // with the modifier bit unset in the modifiers we also find + // the same mask with the bit set. + // + // we ignore a few modifiers that we know aren't important + // for generating characters. in fact, we want to ignore any + // characters generated by the control key. we don't map + // those and instead expect the control modifier plus a key. + UInt32 sensitive = 0; + for (UInt32 k = 0; (1u << k) < + r.getNumModifierCombinations(); ++k) { + UInt32 bit = (1u << k); + if ((bit << 8) == cmdKey || + (bit << 8) == controlKey || + (bit << 8) == rightControlKey) { + continue; + } + for (UInt32 m = 0; m < r.getNumModifierCombinations(); ++m) { + if (modifiers[m] != modifiers[m ^ bit]) { + sensitive |= bit; + break; + } + } + } + + // find each required modifier mask. the key can be synthesized + // using any of the masks. + std::set required; + for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) { + if ((k & sensitive) == k && modifiers[k & sensitive]) { + required.insert(k); + } + } + + // now add a key entry for each key/required modifier pair. + item.m_sensitive = mapModifiersFromOSX(sensitive << 8); + for (std::set::iterator k = required.begin(); + k != required.end(); ++k) { + item.m_required = mapModifiersFromOSX(*k << 8); + keyMap.addKeyEntry(item); + } + } + } + + return true; +} + +bool +COSXKeyState::mapSynergyHotKeyToMac(KeyID key, KeyModifierMask mask, + UInt32 &macVirtualKey, UInt32 &macModifierMask) const +{ + // look up button for key + KeyButton button = getButton(key, pollActiveGroup()); + if (button == 0 && key != kKeyNone) { + return false; + } + macVirtualKey = mapKeyButtonToVirtualKey(button); + + // calculate modifier mask + macModifierMask = 0; + if ((mask & KeyModifierShift) != 0) { + macModifierMask |= shiftKey; + } + if ((mask & KeyModifierControl) != 0) { + macModifierMask |= controlKey; + } + if ((mask & KeyModifierAlt) != 0) { + macModifierMask |= cmdKey; + } + if ((mask & KeyModifierSuper) != 0) { + macModifierMask |= optionKey; + } + if ((mask & KeyModifierCapsLock) != 0) { + macModifierMask |= alphaLock; + } + + return true; +} + void COSXKeyState::handleModifierKeys(void* target, KeyModifierMask oldMask, KeyModifierMask newMask) @@ -528,399 +582,92 @@ COSXKeyState::handleModifierKeys(void* target, // synthesize changed modifier keys if ((changed & KeyModifierShift) != 0) { - handleModifierKey(target, kKeyShift_L, - (newMask & KeyModifierShift) != 0); + handleModifierKey(target, s_shiftVK, kKeyShift_L, + (newMask & KeyModifierShift) != 0, newMask); } if ((changed & KeyModifierControl) != 0) { - handleModifierKey(target, kKeyControl_L, - (newMask & KeyModifierControl) != 0); + handleModifierKey(target, s_controlVK, kKeyControl_L, + (newMask & KeyModifierControl) != 0, newMask); } if ((changed & KeyModifierAlt) != 0) { - handleModifierKey(target, kKeyAlt_L, - (newMask & KeyModifierAlt) != 0); + handleModifierKey(target, s_altVK, kKeyAlt_L, + (newMask & KeyModifierAlt) != 0, newMask); } if ((changed & KeyModifierSuper) != 0) { - handleModifierKey(target, kKeySuper_L, - (newMask & KeyModifierSuper) != 0); + handleModifierKey(target, s_superVK, kKeySuper_L, + (newMask & KeyModifierSuper) != 0, newMask); } if ((changed & KeyModifierCapsLock) != 0) { - handleModifierKey(target, kKeyCapsLock, - (newMask & KeyModifierCapsLock) != 0); + handleModifierKey(target, s_capsLockVK, kKeyCapsLock, + (newMask & KeyModifierCapsLock) != 0, newMask); } } void -COSXKeyState::handleModifierKey(void* target, KeyID id, bool down) +COSXKeyState::handleModifierKey(void* target, + UInt32 virtualKey, KeyID id, + bool down, KeyModifierMask newMask) { - CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); - if (keyIndex == m_keyMap.end()) { - return; + KeyButton button = mapVirtualKeyToKeyButton(virtualKey); + onKey(button, down, newMask); + sendKeyEvent(target, down, false, id, newMask, 0, button); +} + +bool +COSXKeyState::getGroups(GroupList& groups) const +{ + // get number of layouts + CFIndex n; + OSStatus status = KLGetKeyboardLayoutCount(&n); + if (status != noErr) { + LOG((CLOG_DEBUG1 "can't get keyboard layouts")); + return false; } - KeyButton button = keyIndex->second[0].m_button; - setKeyDown(button, down); - sendKeyEvent(target, down, false, id, getActiveModifiers(), 0, button); + + // get each layout + groups.clear(); + for (CFIndex i = 0; i < n; ++i) { + KeyboardLayoutRef keyboardLayout; + status = KLGetKeyboardLayoutAtIndex(i, &keyboardLayout); + if (status == noErr) { + groups.push_back(keyboardLayout); + } + } + return true; +} + +void +COSXKeyState::setGroup(SInt32 group) +{ + KLSetCurrentKeyboardLayout(m_groups[group]); } void COSXKeyState::checkKeyboardLayout() { - SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); - SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys); - UInt32 keyboardType = LMGetKbdType(); - if (keyboardLayoutID != m_keyboardLayoutID || - keyboardType != m_keyboardType) { - // layout changed - m_keyboardType = keyboardType; - setKeyboardLayout(keyboardLayoutID); - updateKeys(); + // XXX -- should call this when notified that groups have changed. + // if no notification for that then we should poll. + GroupList groups; + if (getGroups(groups) && groups != m_groups) { + updateKeyMap(); + updateKeyState(); } } void -COSXKeyState::setKeyboardLayout(SInt16 keyboardLayoutID) +COSXKeyState::adjustAltGrModifier(const CKeyIDs& ids, + KeyModifierMask* mask, bool isCommand) const { - m_keyboardLayoutID = keyboardLayoutID; - m_deadKeyState = 0; - m_KCHRHandle = GetResource('KCHR', m_keyboardLayoutID); - m_uchrHandle = GetResource('uchr', m_keyboardLayoutID); - m_KCHRResource = NULL; - m_uchrResource = NULL; - if (m_uchrHandle != NULL) { - m_uchrResource = reinterpret_cast(*m_uchrHandle); - } - if (m_KCHRHandle != NULL) { - m_KCHRResource = reinterpret_cast(*m_KCHRHandle); - } -} - -void -COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap, - CVirtualKeyMap& virtualKeyMap) const -{ - // FIXME -- would like to avoid hard coded tables - CKeyEventInfo info; - for (UInt32 i = 0; i < sizeof(s_controlKeys) / - sizeof(s_controlKeys[0]); ++i) { - const CKeyEntry& entry = s_controlKeys[i]; - KeyID keyID = entry.m_keyID; - info.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey); - info.m_requiredMask = 0; - info.m_requiredState = 0; - if (keyMap.count(keyID) == 0) { - keyMap[keyID].push_back(info); - } - if (virtualKeyMap.count(entry.m_virtualKey) == 0) { - virtualKeyMap[entry.m_virtualKey] = entry.m_keyID; - } - } -} - -bool -COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const -{ - assert(m_KCHRResource != NULL); - - CKCHRResource* r = m_KCHRResource; - - // build non-composed keys to virtual keys mapping - std::map vkMap; - for (SInt32 i = 0; i < r->m_numTables; ++i) { - // determine the modifier keys for table i - KeyModifierMask mask = - maskForTable(static_cast(i), r->m_tableSelectionIndex, - 256, static_cast(i)); - - // build the KeyID to virtual key map - for (SInt32 j = 0; j < 128; ++j) { - // get character - UInt8 c = r->m_characterTables[i][j]; - - // save key info - // FIXME -- should set only those bits in m_requiredMask that - // correspond to modifiers that are truly necessary to - // generate the character. this mostly works as-is, though. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton(j); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierCapsLock; - info.m_requiredState = mask; - - // save character to virtual key mapping - if (vkMap.count(c) == 0) { - vkMap[c] = info; - } - - // skip non-glyph character - if (c < 32 || c == 127) { - continue; - } - - // map character to KeyID - KeyID keyID = charToKeyID(c); - - // if we've seen this character already then do nothing - if (keyMap.count(keyID) != 0) { - continue; - } - - // save entry for character - keyMap[keyID].push_back(info); - } - } - - // build composed keys to virtual keys mapping - CKCHRDeadKeys* dkp = - reinterpret_cast(r->m_characterTables[r->m_numTables]); - CKCHRDeadKeyRecord* dkr = dkp->m_records; - for (SInt32 i = 0; i < dkp->m_numRecords; ++i) { - // determine the modifier keys for table i - KeyModifierMask mask = - maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex, - 256, dkr->m_tableIndex); - - // map each completion - for (SInt32 j = 0; j < dkr->m_numCompletions; ++j) { - // get character - UInt8 c = dkr->m_completion[j][1]; - - // skip non-glyph character - if (c < 32 || c == 127) { - continue; - } - - // map character to KeyID - KeyID keyID = charToKeyID(c); - - // if we've seen this character already then do nothing - if (keyMap.count(keyID) != 0) { - continue; - } - - // map keyID, first to the dead key then to uncomposed - // character. we must find a virtual key that maps to - // to the uncomposed character. - if (vkMap.count(dkr->m_completion[j][0]) != 0) { - CKeySequence& sequence = keyMap[keyID]; - - // save key info - // FIXME -- should set only those bits in m_requiredMask that - // correspond to modifiers that are truly necessary to - // generate the character. this mostly works as-is, though. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton( - dkr->m_virtualKey); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierAlt | KeyModifierControl | - KeyModifierCapsLock; - info.m_requiredState = mask; - sequence.push_back(info); - sequence.push_back(vkMap[dkr->m_completion[j][0]]); + if (!isCommand) { + for (CKeyIDs::const_iterator i = ids.begin(); i != ids.end(); ++i) { + KeyID id = *i; + if (id != kKeyNone && + ((id < 0xe000u || id > 0xefffu) || + (id >= kKeyKP_Equal && id <= kKeyKP_9))) { + *mask |= KeyModifierAltGr; + return; } } - - // next table. skip all the completions and the no match - // pair to get the next table. - dkr = reinterpret_cast( - dkr->m_completion[dkr->m_numCompletions + 1]); - } - - return true; -} - -bool -COSXKeyState::filluchrKeysMap(CKeyIDMap& keyMap) const -{ - assert(m_uchrResource != NULL); - - UCKeyboardLayout* r = m_uchrResource; - UInt8* base = reinterpret_cast(r); - - UCKeyLayoutFeatureInfo* fi = NULL; - if (r->keyLayoutFeatureInfoOffset != 0) { - fi = reinterpret_cast( - base + r->keyLayoutFeatureInfoOffset); - } - - // find the keyboard info for the current keyboard type - UCKeyboardTypeHeader* th = NULL; - for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { - if (m_keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && - m_keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { - th = r->keyboardTypeList + i; - break; - } - if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { - // found the default. use it unless we find a match. - th = r->keyboardTypeList + i; - } - } - if (th == NULL) { - // cannot find a suitable keyboard type - return false; - } - - // get tables for keyboard type - UCKeyModifiersToTableNum* m = - reinterpret_cast( - base + th->keyModifiersToTableNumOffset); - UCKeyToCharTableIndex* cti = - reinterpret_cast( - base + th->keyToCharTableIndexOffset); - UCKeySequenceDataIndex* sdi = - reinterpret_cast( - base + th->keySequenceDataIndexOffset); - - // build non-composed keys to virtual keys mapping - CDeadKeyMap dkMap; - for (UInt32 i = 0; i < cti->keyToCharTableCount; ++i) { - // determine the modifier keys for table i - KeyModifierMask mask = - maskForTable(static_cast(i), m->tableNum, - m->modifiersCount, m->defaultTableNum); - - // get the character table - UCKeyOutput* ct = - reinterpret_cast( - base + cti->keyToCharTableOffsets[i]); - - // build the KeyID to virtual key map - for (SInt32 j = 0; j < cti->keyToCharTableSize; ++j) { - // get character - UInt16 c = ct[j]; - - // ignore key sequence mappings - if ((c & 0xc000) == 0x8000) { - UInt16 index = (c & 0x3fff); - if (index < sdi->charSequenceCount && - sdi->charSequenceOffsets[index] != - sdi->charSequenceOffsets[index + 1]) { - continue; - } - - // not a sequence mapping. use character as-is. - } - - // just record dead key mappings - else if ((c & 0xc000) == 0x4000) { - UInt16 index = (c & 0x3fff); - if (dkMap.count(index) == 0) { - dkMap[index] = std::make_pair(j, mask); - } - continue; - } - - // skip non-glyph character - if (c < 32 || c == 127 || c == 0xfffe || c == 0xffff) { - continue; - } - - // map character to KeyID - KeyID keyID = unicharToKeyID(c); - - // if we've seen this character already then do nothing - if (keyMap.count(keyID) != 0) { - continue; - } - - // save entry for character - // FIXME -- should set only those bits in m_requiredMask that - // correspond to modifiers that are truly necessary to - // generate the character. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton(j); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierCapsLock; - info.m_requiredState = mask; - keyMap[keyID].push_back(info); - } - } - - // build composed keys to virtual keys mapping - UCKeyStateRecordsIndex* sri = NULL; - if (th->keyStateRecordsIndexOffset != NULL) { - sri = reinterpret_cast( - base + th->keyStateRecordsIndexOffset); - } - UCKeyStateTerminators* st = NULL; - if (th->keyStateTerminatorsOffset != NULL) { - st = reinterpret_cast( - base + th->keyStateTerminatorsOffset); - } - CKeySequence sequence; - mapDeadKeySequence(keyMap, sequence, 0, base, sri, st, dkMap); - - return true; -} - -void -COSXKeyState::mapDeadKeySequence(CKeyIDMap& keyMap, - CKeySequence& sequence, - UInt16 state, const UInt8* base, - const UCKeyStateRecordsIndex* sri, - const UCKeyStateTerminators* st, - CDeadKeyMap& dkMap) -{ - for (CDeadKeyMap::const_iterator i = dkMap.begin(); i != dkMap.end(); ++i) { - UInt16 index = i->first; - const UCKeyStateRecord* sr = - reinterpret_cast( - base + sri->keyStateRecordOffsets[index]); - const UCKeyStateEntryTerminal* kset = - reinterpret_cast( - sr->stateEntryData); - - UInt16 c = 0; - UInt16 nextState = 0; - if (state == 0) { - c = sr->stateZeroCharData; - nextState = sr->stateZeroNextState; - } - else if (sr->stateEntryFormat == kUCKeyStateEntryTerminalFormat) { - for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { - if (kset[j].curState == state) { - c = kset[j].charData; - break; - } - } - // XXX -- default state terminator not supported yet - } - else if (sr->stateEntryFormat == kUCKeyStateEntryRangeFormat) { - // XXX -- not supported yet - } - - // push character onto sequence. m_requiredMask should only - // have those modifiers that are truly necessary to generate - // the character. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton( - i->second.first); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierCapsLock; - info.m_requiredState = i->second.second; - sequence.push_back(info); - - if (nextState != 0) { - // next in dead key sequence - mapDeadKeySequence(keyMap, sequence, - nextState, base, sri, st, dkMap); - } - else if (c >= 32 && c != 127 && c != 0xfffe && c != 0xffff) { - // terminate sequence - KeyID keyID = unicharToKeyID(c); - - // if we've seen this character already then do nothing, - // otherwise save it in the key map. - if (keyMap.count(keyID) == 0) { - keyMap[keyID] = sequence; - } - } - - // pop character from sequence - sequence.pop_back(); } } @@ -937,123 +684,513 @@ COSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton) return static_cast(keyButton - KeyButtonOffset); } + +// +// COSXKeyState::CKeyResource +// + KeyID -COSXKeyState::charToKeyID(UInt8 c) +COSXKeyState::CKeyResource::getKeyID(UInt8 c) { if (c == 0) { return kKeyNone; } - else if (c >= 32 && c < 127) { + else if (c >= 32 && c < 127) { // ASCII - return static_cast(c); - } - else { - // create string with character - char str[2]; - str[0] = static_cast(c); - str[1] = 0; + return static_cast(c); + } + else { + // handle special keys + switch (c) { + case 0x01: + return kKeyHome; - // convert to unicode - CFStringRef cfString = - CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, - str, GetScriptManagerVariable(smKeyScript), - kCFAllocatorNull); + case 0x02: + return kKeyKP_Enter; - // convert to precomposed - CFMutableStringRef mcfString = - CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); - CFRelease(cfString); - CFStringNormalize(mcfString, kCFStringNormalizationFormC); + case 0x03: + return kKeyKP_Enter; - // check result - int unicodeLength = CFStringGetLength(mcfString); - if (unicodeLength == 0) { - return kKeyNone; - } - if (unicodeLength > 1) { + case 0x04: + return kKeyEnd; + + case 0x05: + return kKeyHelp; + + case 0x08: + return kKeyBackSpace; + + case 0x09: + return kKeyTab; + + case 0x0b: + return kKeyPageUp; + + case 0x0c: + return kKeyPageDown; + + case 0x0d: + return kKeyReturn; + + case 0x10: + // OS X maps all the function keys (F1, etc) to this one key. + // we can't determine the right key here so we have to do it + // some other way. + return kKeyNone; + + case 0x1b: + return kKeyEscape; + + case 0x1c: + return kKeyLeft; + + case 0x1d: + return kKeyRight; + + case 0x1e: + return kKeyUp; + + case 0x1f: + return kKeyDown; + + case 0x7f: + return kKeyDelete; + + case 0x06: + case 0x07: + case 0x0a: + case 0x0e: + case 0x0f: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + // discard other control characters + return kKeyNone; + + default: + // not special or unknown + break; + } + + // create string with character + char str[2]; + str[0] = static_cast(c); + str[1] = 0; + + // convert to unicode + CFStringRef cfString = + CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, + str, GetScriptManagerVariable(smKeyScript), + kCFAllocatorNull); + + // convert to precomposed + CFMutableStringRef mcfString = + CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); + CFRelease(cfString); + CFStringNormalize(mcfString, kCFStringNormalizationFormC); + + // check result + int unicodeLength = CFStringGetLength(mcfString); + if (unicodeLength == 0) { + CFRelease(mcfString); + return kKeyNone; + } + if (unicodeLength > 1) { // FIXME -- more than one character, we should handle this - return kKeyNone; - } + CFRelease(mcfString); + return kKeyNone; + } - // get unicode character - UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); - CFRelease(mcfString); + // get unicode character + UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); + CFRelease(mcfString); - // convert to KeyID - return static_cast(uc); - } + // convert to KeyID + return static_cast(uc); + } } KeyID -COSXKeyState::unicharToKeyID(UniChar c) +COSXKeyState::CKeyResource::unicharToKeyID(UniChar c) { - return static_cast(c); + switch (c) { + case 3: + return kKeyKP_Enter; + + case 8: + return kKeyBackSpace; + + case 9: + return kKeyTab; + + case 13: + return kKeyReturn; + + case 27: + return kKeyEscape; + + case 127: + return kKeyDelete; + + default: + if (c < 32) { + return kKeyNone; + } + return static_cast(c); + } } -KeyModifierMask -COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors, - UInt32 numEntries, UInt8 defaultIndex) + +// +// COSXKeyState::CKCHRKeyResource +// + +COSXKeyState::CKCHRKeyResource::CKCHRKeyResource(const void* resource) { - // this is a table of 0 to 255 sorted by the number of 1 bits then - // numerical order. - static const UInt8 s_indexTable[] = { -0, 1, 2, 4, 8, 16, 32, 64, 128, 3, 5, 6, 9, 10, 12, 17, -18, 20, 24, 33, 34, 36, 40, 48, 65, 66, 68, 72, 80, 96, 129, 130, -132, 136, 144, 160, 192, 7, 11, 13, 14, 19, 21, 22, 25, 26, 28, 35, -37, 38, 41, 42, 44, 49, 50, 52, 56, 67, 69, 70, 73, 74, 76, 81, -82, 84, 88, 97, 98, 100, 104, 112, 131, 133, 134, 137, 138, 140, 145, 146, -148, 152, 161, 162, 164, 168, 176, 193, 194, 196, 200, 208, 224, 15, 23, 27, -29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78, -83, 85, 86, 89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, -135, 139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166, 169, 170, 172, -177, 178, 180, 184, 195, 197, 198, 201, 202, 204, 209, 210, 212, 216, 225, 226, -228, 232, 240, 31, 47, 55, 59, 61, 62, 79, 87, 91, 93, 94, 103, 107, -109, 110, 115, 117, 118, 121, 122, 124, 143, 151, 155, 157, 158, 167, 171, 173, -174, 179, 181, 182, 185, 186, 188, 199, 203, 205, 206, 211, 213, 214, 217, 218, -220, 227, 229, 230, 233, 234, 236, 241, 242, 244, 248, 63, 95, 111, 119, 123, -125, 126, 159, 175, 183, 187, 189, 190, 207, 215, 219, 221, 222, 231, 235, 237, -238, 243, 245, 246, 249, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255 - }; + m_resource = reinterpret_cast(resource); +} - while (true) { - // find first entry in tableSelectors that maps to i. this is the - // one that uses the fewest modifier keys. - UInt8 maxIndex = static_cast(numEntries - 1); - for (UInt32 j = 0; j < 256; ++j) { - if (s_indexTable[j] <= maxIndex && - tableSelectors[s_indexTable[j]] == i) { - // convert our mask to a traditional mac modifier mask - // (which just means shifting it left 8 bits). - UInt16 macMask = (static_cast(s_indexTable[j]) << 8); +bool +COSXKeyState::CKCHRKeyResource::isValid() const +{ + return (m_resource != NULL); +} - // convert the mac modifier mask to our mask. - KeyModifierMask mask = 0; - if ((macMask & (shiftKey | rightShiftKey)) != 0) { - mask |= KeyModifierShift; - } - if ((macMask & (controlKey | rightControlKey)) != 0) { - mask |= KeyModifierControl; - } - if ((macMask & cmdKey) != 0) { - mask |= KeyModifierAlt; - } - if ((macMask & (optionKey | rightOptionKey)) != 0) { - mask |= KeyModifierSuper; - } - if ((macMask & alphaLock) != 0) { - mask |= KeyModifierCapsLock; - } - return mask; - } - } +UInt32 +COSXKeyState::CKCHRKeyResource::getNumModifierCombinations() const +{ + // only 32 (not 256) because the righthanded modifier bits are ignored + return 32; +} - // no match. try defaultIndex. - if (i == defaultIndex) { - break; +UInt32 +COSXKeyState::CKCHRKeyResource::getNumTables() const +{ + return m_resource->m_numTables; +} + +UInt32 +COSXKeyState::CKCHRKeyResource::getNumButtons() const +{ + return 128; +} + +UInt32 +COSXKeyState::CKCHRKeyResource::getTableForModifier(UInt32 mask) const +{ + assert(mask < getNumModifierCombinations()); + + return m_resource->m_tableSelectionIndex[mask]; +} + +KeyID +COSXKeyState::CKCHRKeyResource::getKey(UInt32 table, UInt32 button) const +{ + assert(table < getNumTables()); + assert(button < getNumButtons()); + + UInt8 c = m_resource->m_characterTables[table][button]; + if (c == 0) { + // could be a dead key + const CKCHRDeadKeys* dkp = + reinterpret_cast( + m_resource->m_characterTables[getNumTables()]); + const CKCHRDeadKeyRecord* dkr = dkp->m_records; + for (SInt16 i = 0; i < dkp->m_numRecords; ++i) { + if (dkr->m_tableIndex == table && dkr->m_virtualKey == button) { + // get the no completion entry + c = dkr->m_completion[dkr->m_numCompletions][1]; + return CKeyMap::getDeadKey(getKeyID(c)); + } + + // next table. skip all the completions and the no match + // pair to get the next table. + dkr = reinterpret_cast( + dkr->m_completion[dkr->m_numCompletions + 1]); } - i = defaultIndex; } - // should never get here. - return 0; + return getKeyID(c); +} + + +// +// COSXKeyState::CUCHRKeyResource +// + +COSXKeyState::CUCHRKeyResource::CUCHRKeyResource(const void* resource, + UInt32 keyboardType) : + m_m(NULL), + m_cti(NULL), + m_sdi(NULL), + m_sri(NULL), + m_st(NULL) +{ + m_resource = reinterpret_cast(resource); + if (m_resource == NULL) { + return; + } + + // find the keyboard info for the current keyboard type + const UCKeyboardTypeHeader* th = NULL; + const UCKeyboardLayout* r = m_resource; + for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { + if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && + keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { + th = r->keyboardTypeList + i; + break; + } + if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { + // found the default. use it unless we find a match. + th = r->keyboardTypeList + i; + } + } + if (th == NULL) { + // cannot find a suitable keyboard type + return; + } + + // get tables for keyboard type + const UInt8* base = reinterpret_cast(m_resource); + m_m = reinterpret_cast(base + + th->keyModifiersToTableNumOffset); + m_cti = reinterpret_cast(base + + th->keyToCharTableIndexOffset); + m_sdi = reinterpret_cast(base + + th->keySequenceDataIndexOffset); + if (th->keyStateRecordsIndexOffset != 0) { + m_sri = reinterpret_cast(base + + th->keyStateRecordsIndexOffset); + } + if (th->keyStateTerminatorsOffset != 0) { + m_st = reinterpret_cast(base + + th->keyStateTerminatorsOffset); + } + + // find the space key, but only if it can combine with dead keys. + // a dead key followed by a space yields the non-dead version of + // the dead key. + m_spaceOutput = 0xffffu; + UInt32 table = getTableForModifier(0); + for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) { + KeyID id = getKey(table, button); + if (id == 0x20) { + UCKeyOutput c = + reinterpret_cast(base + + m_cti->keyToCharTableOffsets[table])[button]; + if ((c & kUCKeyOutputTestForIndexMask) == + kUCKeyOutputStateIndexMask) { + m_spaceOutput = (c & kUCKeyOutputGetIndexMask); + break; + } + } + } +} + +bool +COSXKeyState::CUCHRKeyResource::isValid() const +{ + return (m_m != NULL); +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getNumModifierCombinations() const +{ + // only 32 (not 256) because the righthanded modifier bits are ignored + return 32; +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getNumTables() const +{ + return m_cti->keyToCharTableCount; +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getNumButtons() const +{ + return m_cti->keyToCharTableSize; +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getTableForModifier(UInt32 mask) const +{ + if (mask >= m_m->modifiersCount) { + return m_m->defaultTableNum; + } + else { + return m_m->tableNum[mask]; + } +} + +KeyID +COSXKeyState::CUCHRKeyResource::getKey(UInt32 table, UInt32 button) const +{ + assert(table < getNumTables()); + assert(button < getNumButtons()); + + const UInt8* base = reinterpret_cast(m_resource); + const UCKeyOutput c = reinterpret_cast(base + + m_cti->keyToCharTableOffsets[table])[button]; + + KeySequence keys; + switch (c & kUCKeyOutputTestForIndexMask) { + case kUCKeyOutputStateIndexMask: + if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) { + return kKeyNone; + } + break; + + case kUCKeyOutputSequenceIndexMask: + default: + if (!addSequence(keys, c)) { + return kKeyNone; + } + break; + } + + // XXX -- no support for multiple characters + if (keys.size() != 1) { + return kKeyNone; + } + + return keys.front(); +} + +bool +COSXKeyState::CUCHRKeyResource::getDeadKey( + KeySequence& keys, UInt16 index) const +{ + if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { + // XXX -- should we be using some other fallback? + return false; + } + + UInt16 state = 0; + if (!getKeyRecord(keys, index, state)) { + return false; + } + if (state == 0) { + // not a dead key + return true; + } + + // no dead keys if we couldn't find the space key + if (m_spaceOutput == 0xffffu) { + return false; + } + + // the dead key should not have put anything in the key list + if (!keys.empty()) { + return false; + } + + // get the character generated by pressing the space key after the + // dead key. if we're still in a compose state afterwards then we're + // confused so we bail. + if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) { + return false; + } + + // convert keys to their dead counterparts + for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) { + *i = CKeyMap::getDeadKey(*i); + } + + return true; +} + +bool +COSXKeyState::CUCHRKeyResource::getKeyRecord( + KeySequence& keys, UInt16 index, UInt16& state) const +{ + const UInt8* base = reinterpret_cast(m_resource); + const UCKeyStateRecord* sr = + reinterpret_cast(base + + m_sri->keyStateRecordOffsets[index]); + const UCKeyStateEntryTerminal* kset = + reinterpret_cast(sr->stateEntryData); + + UInt16 nextState = 0; + bool found = false; + if (state == 0) { + found = true; + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + else { + // we have a next entry + switch (sr->stateEntryFormat) { + case kUCKeyStateEntryTerminalFormat: + for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { + if (kset[j].curState == state) { + if (!addSequence(keys, kset[j].charData)) { + return false; + } + nextState = 0; + found = true; + break; + } + } + break; + + case kUCKeyStateEntryRangeFormat: + // XXX -- not supported yet + break; + + default: + // XXX -- unknown format + return false; + } + } + if (!found) { + // use a terminator + if (m_st != NULL && state < m_st->keyStateTerminatorCount) { + if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) { + return false; + } + } + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + + // next + state = nextState; + + return true; +} + +bool +COSXKeyState::CUCHRKeyResource::addSequence( + KeySequence& keys, UCKeyCharSeq c) const +{ + if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { + UInt16 index = (c & kUCKeyOutputGetIndexMask); + if (index < m_sdi->charSequenceCount && + m_sdi->charSequenceOffsets[index] != + m_sdi->charSequenceOffsets[index + 1]) { + // XXX -- sequences not supported yet + return false; + } + } + + if (c != 0xfffe && c != 0xffff) { + KeyID id = unicharToKeyID(c); + if (id != kKeyNone) { + keys.push_back(id); + } + } + + return true; } diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index 685a56b1..baf69713 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -32,6 +32,28 @@ public: COSXKeyState(); virtual ~COSXKeyState(); + //! @name modifiers + //@{ + + //! Handle modifier key change + /*! + Determines which modifier keys have changed and updates the modifier + state and sends key events as appropriate. + */ + void handleModifierKeys(void* target, + KeyModifierMask oldMask, KeyModifierMask newMask); + + //@} + //! @name accessors + //@{ + + //! Convert OS X modifier mask to synergy mask + /*! + Returns the synergy modifier mask corresponding to the OS X modifier + mask in \p mask. + */ + KeyModifierMask mapModifiersFromOSX(UInt32 mask) const; + //! Map key event to keys /*! Converts a key event into a sequence of KeyIDs and the shadow modifier @@ -43,80 +65,64 @@ public: KeyButton mapKeyFromEvent(CKeyIDs& ids, KeyModifierMask* maskOut, EventRef event) const; - //! Handle modifier key change + //! Map key and mask to native values /*! - Determines which modifier keys have changed and updates the modifier - state and sends key events as appropriate. + Calculates mac virtual key and mask for a key \p key and modifiers + \p mask. Returns \c true if the key can be mapped, \c false otherwise. */ - void handleModifierKeys(void* target, - KeyModifierMask oldMask, KeyModifierMask newMask); + bool mapSynergyHotKeyToMac(KeyID key, KeyModifierMask mask, + UInt32& macVirtualKey, + UInt32& macModifierMask) const; + + //@} // IKeyState overrides - virtual void setHalfDuplexMask(KeyModifierMask); virtual bool fakeCtrlAltDel(); - virtual const char* getKeyName(KeyButton) const; - virtual void sendKeyEvent(void* target, - bool press, bool isAutoRepeat, - KeyID key, KeyModifierMask mask, - SInt32 count, KeyButton button); + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; protected: - // IKeyState overrides - virtual void doUpdateKeys(); - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat); - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + // CKeyState overrides + virtual void getKeyMap(CKeyMap& keyMap); + virtual void fakeKey(const Keystroke& keystroke); private: - struct CKeyEventInfo { - public: - KeyButton m_button; - KeyModifierMask m_requiredMask; - KeyModifierMask m_requiredState; - }; - typedef std::vector CKeySequence; - typedef std::map CKeyIDMap; - typedef std::map CVirtualKeyMap; - typedef std::map > CDeadKeyMap; + class CKeyResource; + typedef std::vector GroupList; - KeyButton addKeystrokes(Keystrokes& keys, - KeyButton keyButton, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask, - bool isAutoRepeat) const; - bool adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const; - void addKeyButton(KeyButtons& keys, KeyID id) const; - void handleModifierKey(void* target, KeyID id, bool down); + // Add hard coded special keys to a CKeyMap. + void getKeyMapForSpecialKeys( + CKeyMap& keyMap, SInt32 group) const; - // Check if the keyboard layout has changed and call doUpdateKeys + // Convert keyboard resource to a key map + bool getKeyMap(CKeyMap& keyMap, + SInt32 group, const CKeyResource& r) const; + + // Get the available keyboard groups + bool getGroups(GroupList&) const; + + // Change active keyboard group to group + void setGroup(SInt32 group); + + // Check if the keyboard layout has changed and update keyboard state // if so. void checkKeyboardLayout(); - // Switch to a new keyboard layout. - void setKeyboardLayout(SInt16 keyboardLayoutID); + // Send an event for the given modifier key + void handleModifierKey(void* target, + UInt32 virtualKey, KeyID id, + bool down, KeyModifierMask newMask); - // Insert KeyID to key sequences for non-printing characters, like - // delete, home, up arrow, etc. and the virtual key to KeyID mapping. - void fillSpecialKeys(CKeyIDMap& keyMap, - CVirtualKeyMap& virtualKeyMap) const; - - // Convert the KCHR resource to a KeyID to key sequence map. the - // map maps each KeyID to the sequence of keys (with modifiers) - // that would have to be synthesized to generate the KeyID character. - // Returns false iff no KCHR resource was found. - bool fillKCHRKeysMap(CKeyIDMap& keyMap) const; - - // Convert the uchr resource to a KeyID to key sequence map. the - // map maps each KeyID to the sequence of keys (with modifiers) - // that would have to be synthesized to generate the KeyID character. - // Returns false iff no uchr resource was found or it couldn't be - // mapped. - bool filluchrKeysMap(CKeyIDMap& keyMap) const; + // Checks if any in \p ids is a glyph key and if \p isCommand is false. + // If so it adds the AltGr modifier to \p mask. This allows OS X + // servers to use the option key both as AltGr and as a modifier. If + // option is acting as AltGr (i.e. it generates a glyph and there are + // no command modifiers active) then we don't send the super modifier + // to clients because they'd try to match it as a command modifier. + void adjustAltGrModifier(const CKeyIDs& ids, + KeyModifierMask* mask, bool isCommand) const; // Maps an OS X virtual key id to a KeyButton. This simply remaps // the ids so we don't use KeyButton 0. @@ -126,28 +132,89 @@ private: // mapVirtualKeyToKeyButton. static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton); - // Convert a character in the current script to the equivalent KeyID. - static KeyID charToKeyID(UInt8); - - // Convert a unicode character to the equivalent KeyID. - static KeyID unicharToKeyID(UniChar); - - // Choose the modifier mask with the fewest modifiers for character - // mapping table i. The tableSelectors table has numEntries. If - // no mapping is found for i, try mapping defaultIndex. - static KeyModifierMask - maskForTable(UInt8 i, UInt8* tableSelectors, - UInt32 numEntries, UInt8 defaultIndex); - - // Save characters built from dead key sequences. - static void mapDeadKeySequence(CKeyIDMap& keyMap, - CKeySequence& sequence, - UInt16 state, const UInt8* base, - const UCKeyStateRecordsIndex* sri, - const UCKeyStateTerminators* st, - CDeadKeyMap& dkMap); - private: + class CKeyResource : public IInterface { + public: + virtual bool isValid() const = 0; + virtual UInt32 getNumModifierCombinations() const = 0; + virtual UInt32 getNumTables() const = 0; + virtual UInt32 getNumButtons() const = 0; + virtual UInt32 getTableForModifier(UInt32 mask) const = 0; + virtual KeyID getKey(UInt32 table, UInt32 button) const = 0; + + // Convert a character in the current script to the equivalent KeyID + static KeyID getKeyID(UInt8); + + // Convert a unicode character to the equivalent KeyID. + static KeyID unicharToKeyID(UniChar); + }; + + class CKCHRKeyResource : public CKeyResource { + public: + CKCHRKeyResource(const void*); + + // CKeyResource overrides + virtual bool isValid() const; + virtual UInt32 getNumModifierCombinations() const; + virtual UInt32 getNumTables() const; + virtual UInt32 getNumButtons() const; + virtual UInt32 getTableForModifier(UInt32 mask) const; + virtual KeyID getKey(UInt32 table, UInt32 button) const; + + private: + struct KCHRResource { + public: + SInt16 m_version; + UInt8 m_tableSelectionIndex[256]; + SInt16 m_numTables; + UInt8 m_characterTables[1][128]; + }; + struct CKCHRDeadKeyRecord { + public: + UInt8 m_tableIndex; + UInt8 m_virtualKey; + SInt16 m_numCompletions; + UInt8 m_completion[1][2]; + }; + struct CKCHRDeadKeys { + public: + SInt16 m_numRecords; + CKCHRDeadKeyRecord m_records[1]; + }; + + const KCHRResource* m_resource; + }; + + class CUCHRKeyResource : public CKeyResource { + public: + CUCHRKeyResource(const void*, UInt32 keyboardType); + + // CKeyResource overrides + virtual bool isValid() const; + virtual UInt32 getNumModifierCombinations() const; + virtual UInt32 getNumTables() const; + virtual UInt32 getNumButtons() const; + virtual UInt32 getTableForModifier(UInt32 mask) const; + virtual KeyID getKey(UInt32 table, UInt32 button) const; + + private: + typedef std::vector KeySequence; + + bool getDeadKey(KeySequence& keys, UInt16 index) const; + bool getKeyRecord(KeySequence& keys, + UInt16 index, UInt16& state) const; + bool addSequence(KeySequence& keys, UCKeyCharSeq c) const; + + private: + const UCKeyboardLayout* m_resource; + const UCKeyModifiersToTableNum* m_m; + const UCKeyToCharTableIndex* m_cti; + const UCKeySequenceDataIndex* m_sdi; + const UCKeyStateRecordsIndex* m_sri; + const UCKeyStateTerminators* m_st; + UInt16 m_spaceOutput; + }; + // OS X uses a physical key if 0 for the 'A' key. synergy reserves // KeyButton 0 so we offset all OS X physical key ids by this much // when used as a KeyButton and by minus this much to map a KeyButton @@ -156,25 +223,13 @@ private: KeyButtonOffset = 1 }; - // KCHR resource header - struct CKCHRResource { - public: - SInt16 m_version; - UInt8 m_tableSelectionIndex[256]; - SInt16 m_numTables; - UInt8 m_characterTables[1][128]; - }; + typedef std::map GroupMap; + typedef std::map CVirtualKeyMap; - SInt16 m_keyboardLayoutID; - UInt32 m_keyboardType; - mutable UInt32 m_deadKeyState; - Handle m_KCHRHandle; - Handle m_uchrHandle; - CKCHRResource* m_KCHRResource; - UCKeyboardLayout* m_uchrResource; - CKeyIDMap m_keyMap; CVirtualKeyMap m_virtualKeyMap; - bool m_uchrFound; + mutable UInt32 m_deadKeyState; + GroupList m_groups; + GroupMap m_groupMap; }; #endif diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index 8ce872f1..82574cf4 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -18,33 +18,74 @@ #include "COSXKeyState.h" #include "COSXScreenSaver.h" #include "CClipboard.h" +#include "CKeyMap.h" +#include "CCondVar.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" #include "CLog.h" #include "IEventQueue.h" #include "TMethodEventJob.h" +#include "TMethodJob.h" +#include "XArch.h" + +#include +#include + +// Set some enums for fast user switching if we're building with an SDK +// from before such support was added. +#if !defined(MAC_OS_X_VERSION_10_3) || \ + (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3) +enum { + kEventClassSystem = 'macs', + kEventSystemUserSessionActivated = 10, + kEventSystemUserSessionDeactivated = 11 +}; +#endif + +// This isn't in any Apple SDK that I know of as of yet. +enum { + kSynergyEventMouseScroll = 11, + kSynergyMouseScrollAxisX = 'saxx', + kSynergyMouseScrollAxisY = 'saxy' +}; // // COSXScreen // +bool COSXScreen::s_testedForGHOM = false; +bool COSXScreen::s_hasGHOM = false; +CEvent::Type COSXScreen::s_confirmSleepEvent = CEvent::kUnknown; + COSXScreen::COSXScreen(bool isPrimary) : m_isPrimary(isPrimary), m_isOnScreen(m_isPrimary), m_cursorPosValid(false), m_cursorHidden(false), + m_dragNumButtonsDown(0), + m_dragTimer(NULL), m_keyState(NULL), m_sequenceNumber(0), m_screensaver(NULL), m_screensaverNotify(false), m_ownClipboard(false), + m_clipboardTimer(NULL), m_hiddenWindow(NULL), m_userInputWindow(NULL), - m_displayManagerNotificationUPP(NULL) + m_displayManagerNotificationUPP(NULL), + m_switchEventHandlerRef(0), + m_pmMutex(new CMutex), + m_pmWatchThread(NULL), + m_pmThreadReady(new CCondVar(m_pmMutex, false)), + m_activeModifierHotKey(0), + m_activeModifierHotKeyMask(0) { try { - m_displayID = CGMainDisplayID(); + m_displayID = CGMainDisplayID(); updateScreenShape(); - m_screensaver = new COSXScreenSaver(); - m_keyState = new COSXKeyState(); + m_screensaver = new COSXScreenSaver(getEventTarget()); + m_keyState = new COSXKeyState(); if (m_isPrimary) { // 1x1 window (to minimze the back buffer allocated for this @@ -81,17 +122,47 @@ COSXScreen::COSXScreen(bool isPrimary) : SetWindowAlpha(m_userInputWindow, 0); } + // install display manager notification handler m_displayManagerNotificationUPP = NewDMExtendedNotificationUPP(displayManagerCallback); - OSStatus err = GetCurrentProcess(&m_PSN); - err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP, this, 0, &m_PSN); + + // install fast user switching event handler + EventTypeSpec switchEventTypes[2]; + switchEventTypes[0].eventClass = kEventClassSystem; + switchEventTypes[0].eventKind = kEventSystemUserSessionDeactivated; + switchEventTypes[1].eventClass = kEventClassSystem; + switchEventTypes[1].eventKind = kEventSystemUserSessionActivated; + EventHandlerUPP switchEventHandler = + NewEventHandlerUPP(userSwitchCallback); + InstallApplicationEventHandler(switchEventHandler, 2, switchEventTypes, + this, &m_switchEventHandlerRef); + DisposeEventHandlerUPP(switchEventHandler); + + // watch for requests to sleep + EVENTQUEUE->adoptHandler(COSXScreen::getConfirmSleepEvent(), + getEventTarget(), + new TMethodEventJob(this, + &COSXScreen::handleConfirmSleep)); + + // create thread for monitoring system power state. + LOG((CLOG_DEBUG "starting watchSystemPowerThread")); + m_pmWatchThread = new CThread(new TMethodJob + (this, &COSXScreen::watchSystemPowerThread)); } catch (...) { - DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, + EVENTQUEUE->removeHandler(COSXScreen::getConfirmSleepEvent(), + getEventTarget()); + if (m_switchEventHandlerRef != 0) { + RemoveEventHandler(m_switchEventHandlerRef); + } + if (m_displayManagerNotificationUPP != NULL) { + DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, NULL, &m_PSN, 0); + } + if (m_hiddenWindow) { ReleaseWindow(m_hiddenWindow); m_hiddenWindow = NULL; @@ -121,6 +192,33 @@ COSXScreen::~COSXScreen() EVENTQUEUE->adoptBuffer(NULL); EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + if (m_pmWatchThread) { + // make sure the thread has setup the runloop. + { + CLock lock(m_pmMutex); + while (!(bool)*m_pmThreadReady) { + m_pmThreadReady->wait(); + } + } + + // now exit the thread's runloop and wait for it to exit + LOG((CLOG_DEBUG "stopping watchSystemPowerThread")); + CFRunLoopStop(m_pmRunloop); + m_pmWatchThread->wait(); + delete m_pmWatchThread; + m_pmWatchThread = NULL; + } + delete m_pmThreadReady; + delete m_pmMutex; + + EVENTQUEUE->removeHandler(COSXScreen::getConfirmSleepEvent(), + getEventTarget()); + + RemoveEventHandler(m_switchEventHandlerRef); + + DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, + NULL, &m_PSN, 0); + if (m_hiddenWindow) { ReleaseWindow(m_hiddenWindow); m_hiddenWindow = NULL; @@ -131,9 +229,6 @@ COSXScreen::~COSXScreen() m_userInputWindow = NULL; } - DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, - NULL, &m_PSN, 0); - delete m_keyState; delete m_screensaver; } @@ -194,6 +289,18 @@ COSXScreen::warpCursor(SInt32 x, SInt32 y) m_cursorPosValid = true; } +void +COSXScreen::fakeInputBegin() +{ + // FIXME -- not implemented +} + +void +COSXScreen::fakeInputEnd() +{ + // FIXME -- not implemented +} + SInt32 COSXScreen::getJumpZoneSize() const { @@ -203,8 +310,7 @@ COSXScreen::getJumpZoneSize() const bool COSXScreen::isAnyMouseButtonDown() const { - // FIXME - return false; + return (GetCurrentButtonState() != 0); } void @@ -214,9 +320,137 @@ COSXScreen::getCursorCenter(SInt32& x, SInt32& y) const y = m_yCenter; } -void -COSXScreen::postMouseEvent(const CGPoint & pos) const +UInt32 +COSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) { + // get mac virtual key and modifier mask matching synergy key and mask + UInt32 macKey, macMask; + if (!m_keyState->mapSynergyHotKeyToMac(key, mask, macKey, macMask)) { + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // choose hotkey id + UInt32 id; + if (!m_oldHotKeyIDs.empty()) { + id = m_oldHotKeyIDs.back(); + m_oldHotKeyIDs.pop_back(); + } + else { + id = m_hotKeys.size() + 1; + } + + // if this hot key has modifiers only then we'll handle it specially + EventHotKeyRef ref = NULL; + bool okay; + if (key == kKeyNone) { + if (m_modifierHotKeys.count(mask) > 0) { + // already registered + okay = false; + } + else { + m_modifierHotKeys[mask] = id; + okay = true; + } + } + else { + EventHotKeyID hkid = { 'SNRG', (UInt32)id }; + OSStatus status = RegisterEventHotKey(macKey, macMask, hkid, + GetApplicationEventTarget(), 0, + &ref); + okay = (status == noErr); + m_hotKeyToIDMap[CHotKeyItem(macKey, macMask)] = id; + } + + if (!okay) { + m_oldHotKeyIDs.push_back(id); + m_hotKeyToIDMap.erase(CHotKeyItem(macKey, macMask)); + LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask)); + return 0; + } + + m_hotKeys.insert(std::make_pair(id, CHotKeyItem(ref, macKey, macMask))); + + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id)); + return id; +} + +void +COSXScreen::unregisterHotKey(UInt32 id) +{ + // look up hotkey + HotKeyMap::iterator i = m_hotKeys.find(id); + if (i == m_hotKeys.end()) { + return; + } + + // unregister with OS + bool okay; + if (i->second.getRef() != NULL) { + okay = (UnregisterEventHotKey(i->second.getRef()) == noErr); + } + else { + okay = false; + // XXX -- this is inefficient + for (ModifierHotKeyMap::iterator j = m_modifierHotKeys.begin(); + j != m_modifierHotKeys.end(); ++j) { + if (j->second == id) { + m_modifierHotKeys.erase(j); + okay = true; + break; + } + } + } + if (!okay) { + LOG((CLOG_WARN "failed to unregister hotkey id=%d", id)); + } + else { + LOG((CLOG_DEBUG "unregistered hotkey id=%d", id)); + } + + // discard hot key from map and record old id for reuse + m_hotKeyToIDMap.erase(i->second); + m_hotKeys.erase(i); + m_oldHotKeyIDs.push_back(id); + if (m_activeModifierHotKey == id) { + m_activeModifierHotKey = 0; + m_activeModifierHotKeyMask = 0; + } +} + +void +COSXScreen::postMouseEvent(CGPoint& pos) const +{ + // check if cursor position is valid on the client display configuration + // stkamp@users.sourceforge.net + CGDisplayCount displayCount = 0; + CGGetDisplaysWithPoint(pos, 0, NULL, &displayCount); + if (displayCount == 0) { + // cursor position invalid - clamp to bounds of last valid display. + // find the last valid display using the last cursor position. + displayCount = 0; + CGDirectDisplayID displayID; + CGGetDisplaysWithPoint(CGPointMake(m_xCursor, m_yCursor), 1, + &displayID, &displayCount); + if (displayCount != 0) { + CGRect displayRect = CGDisplayBounds(displayID); + if (pos.x < displayRect.origin.x) { + pos.x = displayRect.origin.x; + } + else if (pos.x > displayRect.origin.x + + displayRect.size.width - 1) { + pos.x = displayRect.origin.x + displayRect.size.width - 1; + } + if (pos.y < displayRect.origin.y) { + pos.y = displayRect.origin.y; + } + else if (pos.y > displayRect.origin.y + + displayRect.size.height - 1) { + pos.y = displayRect.origin.y + displayRect.size.height - 1; + } + } + } + // synthesize event. CGPostMouseEvent is a particularly good // example of a bad API. we have to shadow the mouse state to // use this API and if we want to support more buttons we have @@ -271,8 +505,8 @@ COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const postMouseEvent(pos); // save new cursor position - m_xCursor = x; - m_yCursor = y; + m_xCursor = static_cast(pos.x); + m_yCursor = static_cast(pos.y); m_cursorPosValid = true; } @@ -290,8 +524,10 @@ COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const // synthesize event CGPoint pos; - pos.x = oldPos.h + dx; - pos.y = oldPos.v + dy; + m_xCursor = static_cast(oldPos.h); + m_yCursor = static_cast(oldPos.v); + pos.x = oldPos.h + dx; + pos.y = oldPos.v + dy; postMouseEvent(pos); // we now assume we don't know the current cursor position @@ -299,51 +535,22 @@ COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -COSXScreen::fakeMouseWheel(SInt32 delta) const +COSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - // synergy uses a wheel step size of 120. the mac uses a step size of 1. - delta /= 120; - if (delta == 0) { - return; + if (xDelta != 0 || yDelta != 0) { + CGPostScrollWheelEvent(2, mapScrollWheelFromSynergy(yDelta), + -mapScrollWheelFromSynergy(xDelta)); } - - CFPropertyListRef pref = ::CFPreferencesCopyValue( - CFSTR("com.apple.scrollwheel.scaling") , - kCFPreferencesAnyApplication, - kCFPreferencesCurrentUser, - kCFPreferencesAnyHost); - - int32_t wheelIncr = 1; - - if (pref != NULL) { - CFTypeID id = CFGetTypeID(pref); - if (id == CFNumberGetTypeID()) { - CFNumberRef value = static_cast(pref); - - double scaling; - if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) { - wheelIncr = (int32_t)(8 * scaling); - if (wheelIncr == 0) { - wheelIncr = 1; - } - } - } - CFRelease(pref); - } - - // note that we ignore the magnitude of the delta. i think this is to - // avoid local wheel acceleration. - if (delta < 0) { - wheelIncr = -wheelIncr; - } - - CGPostScrollWheelEvent(1, wheelIncr); } void COSXScreen::enable() { - // FIXME -- install clipboard snooper (if we need one) + // watch the clipboard + m_clipboardTimer = EVENTQUEUE->newTimer(1.0, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_clipboardTimer, + new TMethodEventJob(this, + &COSXScreen::handleClipboardCheck)); if (m_isPrimary) { // FIXME -- start watching jump zones @@ -362,8 +569,6 @@ COSXScreen::enable() // FIXME -- prepare to show cursor if it moves } - - updateKeys(); } void @@ -382,7 +587,16 @@ COSXScreen::disable() // FIXME -- allow system to enter power saving mode } - // FIXME -- uninstall clipboard snooper (if we needed one) + // disable drag handling + m_dragNumButtonsDown = 0; + enableDragTimer(false); + + // uninstall clipboard timer + if (m_clipboardTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_clipboardTimer); + EVENTQUEUE->deleteTimer(m_clipboardTimer); + m_clipboardTimer = NULL; + } m_isOnScreen = m_isPrimary; } @@ -397,7 +611,10 @@ COSXScreen::enter() SetMouseCoalescingEnabled(true, NULL); - CGSetLocalEventsSuppressionInterval(HUGE_VAL); + CGSetLocalEventsSuppressionInterval(0.0); + + // enable global hotkeys + setGlobalHotKeysEnabled(true); } else { // show cursor @@ -410,6 +627,16 @@ COSXScreen::enter() for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { m_buttons[i] = false; } + + // avoid suppression of local hardware events + // stkamp@users.sourceforge.net + CGSetLocalEventsFilterDuringSupressionState( + kCGEventFilterMaskPermitAllEvents, + kCGEventSupressionStateSupressionInterval); + CGSetLocalEventsFilterDuringSupressionState( + (kCGEventFilterMaskPermitLocalKeyboardEvents | + kCGEventFilterMaskPermitSystemDefinedEvents), + kCGEventSupressionStateRemoteMouseDrag); } // now on screen @@ -419,12 +646,7 @@ COSXScreen::enter() bool COSXScreen::leave() { - // FIXME -- choose keyboard layout if per-process and activate it here - if (m_isPrimary) { - // update key and button state - updateKeys(); - // warp to center warpCursor(m_xCenter, m_yCenter); @@ -440,6 +662,9 @@ COSXScreen::leave() // to send it over to other machines. So disable it. SetMouseCoalescingEnabled(false, NULL); CGSetLocalEventsSuppressionInterval(0.0001); + + // disable global hotkeys + setGlobalHotKeysEnabled(false); } else { // hide cursor @@ -467,10 +692,11 @@ bool COSXScreen::setClipboard(ClipboardID, const IClipboard* src) { COSXClipboard dst; - m_ownClipboard = true; if (src != NULL) { // save clipboard data - return CClipboard::copy(&dst, src); + if (!CClipboard::copy(&dst, src)) { + return false; + } } else { // assert clipboard ownership @@ -479,25 +705,27 @@ COSXScreen::setClipboard(ClipboardID, const IClipboard* src) } dst.empty(); dst.close(); - return true; } + checkClipboards(); + return true; } void COSXScreen::checkClipboards() { - if (m_ownClipboard && !COSXClipboard::isOwnedBySynergy()) { - static ScrapRef sScrapbook = NULL; - ScrapRef currentScrap; - GetCurrentScrap(¤tScrap); - - if (sScrapbook != currentScrap) { + // check if clipboard ownership changed + if (!COSXClipboard::isOwnedBySynergy()) { + if (m_ownClipboard) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership")); m_ownClipboard = false; sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); - sScrapbook = currentScrap; } } + else if (!m_ownClipboard) { + LOG((CLOG_DEBUG "clipboard changed: synergy owned")); + m_ownClipboard = true; + } } void @@ -632,7 +860,8 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) sizeof(axis), NULL, &axis); - if (axis == kEventMouseWheelAxisY) { + if (axis == kEventMouseWheelAxisX || + axis == kEventMouseWheelAxisY) { GetEventParameter(*carbonEvent, kEventParamMouseWheelDelta, typeLongInteger, @@ -640,10 +869,49 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) sizeof(delta), NULL, &delta); - onMouseWheel(120 * (SInt32)delta); + if (axis == kEventMouseWheelAxisX) { + onMouseWheel(-mapScrollWheelToSynergy((SInt32)delta), 0); + } + else { + onMouseWheel(0, mapScrollWheelToSynergy((SInt32)delta)); + } } break; } + + case kSynergyEventMouseScroll: + { + OSStatus r; + long xScroll; + long yScroll; + + // get scroll amount + r = GetEventParameter(*carbonEvent, + kSynergyMouseScrollAxisX, + typeLongInteger, + NULL, + sizeof(xScroll), + NULL, + &xScroll); + if (r != noErr) { + xScroll = 0; + } + r = GetEventParameter(*carbonEvent, + kSynergyMouseScrollAxisY, + typeLongInteger, + NULL, + sizeof(yScroll), + NULL, + &yScroll); + if (r != noErr) { + yScroll = 0; + } + + if (xScroll != 0 || yScroll != 0) { + onMouseWheel(-mapScrollWheelToSynergy(xScroll), + mapScrollWheelToSynergy(yScroll)); + } + } } break; @@ -653,11 +921,15 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) case kEventRawKeyDown: case kEventRawKeyRepeat: case kEventRawKeyModifiersChanged: -// case kEventHotKeyPressed: -// case kEventHotKeyReleased: onKey(*carbonEvent); break; + + case kEventHotKeyPressed: + case kEventHotKeyReleased: + onHotKey(*carbonEvent); + break; } + break; case kEventClassWindow: @@ -681,7 +953,8 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) } break; - default: + default: + SendEventToEventTarget(*carbonEvent, GetEventDispatcherTarget()); break; } } @@ -734,7 +1007,7 @@ COSXScreen::onMouseMove(SInt32 mx, SInt32 my) } bool -COSXScreen::onMouseButton(bool pressed, UInt16 macButton) const +COSXScreen::onMouseButton(bool pressed, UInt16 macButton) { // Buttons 2 and 3 are inverted on the mac ButtonID button = mapMacButtonToSynergy(macButton); @@ -742,27 +1015,51 @@ COSXScreen::onMouseButton(bool pressed, UInt16 macButton) const if (pressed) { LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); + KeyModifierMask mask = m_keyState->getActiveModifiers(); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button, mask)); } } else { LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); + KeyModifierMask mask = m_keyState->getActiveModifiers(); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button, mask)); } } - + + // handle drags with any button other than button 1 or 2 + if (macButton > 2) { + if (pressed) { + // one more button + if (m_dragNumButtonsDown++ == 0) { + enableDragTimer(true); + } + } + else { + // one less button + if (--m_dragNumButtonsDown == 0) { + enableDragTimer(false); + } + } + } + return true; } bool -COSXScreen::onMouseWheel(SInt32 delta) const +COSXScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); - sendEvent(getWheelEvent(), CWheelInfo::alloc(delta)); + LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(xDelta, yDelta)); return true; } +void +COSXScreen::handleClipboardCheck(const CEvent&, void*) +{ + checkClipboards(); +} + pascal void COSXScreen::displayManagerCallback(void* inUserData, SInt16 inMessage, void*) { @@ -798,16 +1095,17 @@ COSXScreen::onDisplayChange() return true; } - -bool -COSXScreen::onKey(EventRef event) const +bool +COSXScreen::onKey(EventRef event) { UInt32 eventKind = GetEventKind(event); - // get the key - UInt32 virtualKey; + // get the key and active modifiers + UInt32 virtualKey, macMask; GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(virtualKey), NULL, &virtualKey); + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(macMask), NULL, &macMask); LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey)); // sadly, OS X doesn't report the virtualKey for modifier keys. @@ -816,11 +1114,67 @@ COSXScreen::onKey(EventRef event) const if (virtualKey == 0 && eventKind == kEventRawKeyModifiersChanged) { // get old and new modifier state KeyModifierMask oldMask = getActiveModifiers(); - KeyModifierMask newMask = mapMacModifiersToSynergy(event); + KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask); m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask); + + // if the current set of modifiers exactly matches a modifiers-only + // hot key then generate a hot key down event. + if (m_activeModifierHotKey == 0) { + if (m_modifierHotKeys.count(newMask) > 0) { + m_activeModifierHotKey = m_modifierHotKeys[newMask]; + m_activeModifierHotKeyMask = newMask; + EVENTQUEUE->addEvent(CEvent(getHotKeyDownEvent(), + getEventTarget(), + CHotKeyInfo::alloc(m_activeModifierHotKey))); + } + } + + // if a modifiers-only hot key is active and should no longer be + // then generate a hot key up event. + else if (m_activeModifierHotKey != 0) { + KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask); + if (mask != m_activeModifierHotKeyMask) { + EVENTQUEUE->addEvent(CEvent(getHotKeyUpEvent(), + getEventTarget(), + CHotKeyInfo::alloc(m_activeModifierHotKey))); + m_activeModifierHotKey = 0; + m_activeModifierHotKeyMask = 0; + } + } + return true; } + // check for hot key. when we're on a secondary screen we disable + // all hotkeys so we can capture the OS defined hot keys as regular + // keystrokes but that means we don't get our own hot keys either. + // so we check for a key/modifier match in our hot key map. + if (!m_isOnScreen) { + HotKeyToIDMap::const_iterator i = + m_hotKeyToIDMap.find(CHotKeyItem(virtualKey, macMask & 0xff00u)); + if (i != m_hotKeyToIDMap.end()) { + UInt32 id = i->second; + + // determine event type + CEvent::Type type; + UInt32 eventKind = GetEventKind(event); + if (eventKind == kEventRawKeyDown) { + type = getHotKeyDownEvent(); + } + else if (eventKind == kEventRawKeyUp) { + type = getHotKeyUpEvent(); + } + else { + return false; + } + + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(id))); + + return true; + } + } + // decode event type bool down = (eventKind == kEventRawKeyDown); bool up = (eventKind == kEventRawKeyUp); @@ -834,28 +1188,65 @@ COSXScreen::onKey(EventRef event) const return false; } + // check for AltGr in mask. if set we send neither the AltGr nor + // the super modifiers to clients then remove AltGr before passing + // the modifiers to onKey. + KeyModifierMask sendMask = (mask & ~KeyModifierAltGr); + if ((mask & KeyModifierAltGr) != 0) { + sendMask &= ~KeyModifierSuper; + } + mask &= ~KeyModifierAltGr; + // update button state if (down) { - m_keyState->setKeyDown(button, true); + m_keyState->onKey(button, true, mask); } else if (up) { - if (!isKeyDown(button)) { + if (!m_keyState->isKeyDown(button)) { // up event for a dead key. throw it away. return false; } - m_keyState->setKeyDown(button, false); + m_keyState->onKey(button, false, mask); } // send key events for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin(); i != keys.end(); ++i) { m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, - *i, mask, 1, button); + *i, sendMask, 1, button); } return true; } +bool +COSXScreen::onHotKey(EventRef event) const +{ + // get the hotkey id + EventHotKeyID hkid; + GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, + NULL, sizeof(EventHotKeyID), NULL, &hkid); + UInt32 id = hkid.id; + + // determine event type + CEvent::Type type; + UInt32 eventKind = GetEventKind(event); + if (eventKind == kEventHotKeyPressed) { + type = getHotKeyDownEvent(); + } + else if (eventKind == kEventHotKeyReleased) { + type = getHotKeyUpEvent(); + } + else { + return false; + } + + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(id))); + + return true; +} + ButtonID COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const { @@ -870,51 +1261,93 @@ COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const return kButtonMiddle; } - return kButtonNone; + return static_cast(macButton); } -KeyModifierMask -COSXScreen::mapMacModifiersToSynergy(EventRef event) const +SInt32 +COSXScreen::mapScrollWheelToSynergy(SInt32 x) const { - // get native bit mask - UInt32 macMask; - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, - NULL, sizeof(macMask), NULL, &macMask); + // return accelerated scrolling but not exponentially scaled as it is + // on the mac. + double d = (1.0 + getScrollSpeed()) * x / getScrollSpeedFactor(); + return static_cast(120.0 * d); +} - // convert - KeyModifierMask outMask = 0; - if ((macMask & shiftKey) != 0) { - outMask |= KeyModifierShift; - } - if ((macMask & rightShiftKey) != 0) { - outMask |= KeyModifierShift; - } - if ((macMask & controlKey) != 0) { - outMask |= KeyModifierControl; - } - if ((macMask & rightControlKey) != 0) { - outMask |= KeyModifierControl; - } - if ((macMask & cmdKey) != 0) { - outMask |= KeyModifierAlt; - } - if ((macMask & optionKey) != 0) { - outMask |= KeyModifierSuper; - } - if ((macMask & rightOptionKey) != 0) { - outMask |= KeyModifierSuper; - } - if ((macMask & alphaLock) != 0) { - outMask |= KeyModifierCapsLock; +SInt32 +COSXScreen::mapScrollWheelFromSynergy(SInt32 x) const +{ + // use server's acceleration with a little boost since other platforms + // take one wheel step as a larger step than the mac does. + return static_cast(3.0 * x / 120.0); +} + +double +COSXScreen::getScrollSpeed() const +{ + double scaling = 0.0; + + CFPropertyListRef pref = ::CFPreferencesCopyValue( + CFSTR("com.apple.scrollwheel.scaling") , + kCFPreferencesAnyApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + if (pref != NULL) { + CFTypeID id = CFGetTypeID(pref); + if (id == CFNumberGetTypeID()) { + CFNumberRef value = static_cast(pref); + if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) { + if (scaling < 0.0) { + scaling = 0.0; + } + } + } + CFRelease(pref); } - return outMask; + return scaling; +} + +double +COSXScreen::getScrollSpeedFactor() const +{ + return pow(10.0, getScrollSpeed()); +} + +void +COSXScreen::enableDragTimer(bool enable) +{ + if (enable && m_dragTimer == NULL) { + m_dragTimer = EVENTQUEUE->newTimer(0.01, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_dragTimer, + new TMethodEventJob(this, + &COSXScreen::handleDrag)); + GetMouse(&m_dragLastPoint); + } + else if (!enable && m_dragTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_dragTimer); + EVENTQUEUE->deleteTimer(m_dragTimer); + m_dragTimer = NULL; + } +} + +void +COSXScreen::handleDrag(const CEvent&, void*) +{ + Point p; + GetMouse(&p); + if (p.h != m_dragLastPoint.h || p.v != m_dragLastPoint.v) { + m_dragLastPoint = p; + onMouseMove((SInt32)p.h, (SInt32)p.v); + } } void COSXScreen::updateButtons() { - // FIXME -- get current button state into m_buttons[] + UInt32 buttons = GetCurrentButtonState(); + for (size_t i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + m_buttons[i] = ((buttons & (1u << i)) != 0); + } } IKeyState* @@ -977,3 +1410,290 @@ COSXScreen::updateScreenShape() LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays")); } + +#pragma mark - + +// +// FAST USER SWITCH NOTIFICATION SUPPORT +// +// COSXScreen::userSwitchCallback(void*) +// +// gets called if a fast user switch occurs +// + +pascal OSStatus +COSXScreen::userSwitchCallback(EventHandlerCallRef nextHandler, + EventRef theEvent, + void* inUserData) +{ + COSXScreen* screen = (COSXScreen*)inUserData; + UInt32 kind = GetEventKind(theEvent); + + if (kind == kEventSystemUserSessionDeactivated) { + LOG((CLOG_DEBUG "user session deactivated")); + EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(), + screen->getEventTarget())); + } + else if (kind == kEventSystemUserSessionActivated) { + LOG((CLOG_DEBUG "user session activated")); + EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(), + screen->getEventTarget())); + } + return (CallNextEventHandler(nextHandler, theEvent)); +} + +#pragma mark - + +// +// SLEEP/WAKEUP NOTIFICATION SUPPORT +// +// COSXScreen::watchSystemPowerThread(void*) +// +// main of thread monitoring system power (sleep/wakup) using a CFRunLoop +// + +void +COSXScreen::watchSystemPowerThread(void*) +{ + io_object_t notifier; + IONotificationPortRef notificationPortRef; + CFRunLoopSourceRef runloopSourceRef = 0; + + m_pmRunloop = CFRunLoopGetCurrent(); + + // install system power change callback + m_pmRootPort = IORegisterForSystemPower(this, ¬ificationPortRef, + powerChangeCallback, ¬ifier); + if (m_pmRootPort == 0) { + LOG((CLOG_WARN "IORegisterForSystemPower failed")); + } + else { + runloopSourceRef = + IONotificationPortGetRunLoopSource(notificationPortRef); + CFRunLoopAddSource(m_pmRunloop, runloopSourceRef, + kCFRunLoopCommonModes); + } + + // thread is ready + { + CLock lock(m_pmMutex); + *m_pmThreadReady = true; + m_pmThreadReady->signal(); + } + + // if we were unable to initialize then exit. we must do this after + // setting m_pmThreadReady to true otherwise the parent thread will + // block waiting for it. + if (m_pmRootPort == 0) { + return; + } + + // start the run loop + LOG((CLOG_DEBUG "started watchSystemPowerThread")); + CFRunLoopRun(); + + // cleanup + if (notificationPortRef) { + CFRunLoopRemoveSource(m_pmRunloop, + runloopSourceRef, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(runloopSourceRef); + CFRelease(runloopSourceRef); + } + + CLock lock(m_pmMutex); + IODeregisterForSystemPower(¬ifier); + m_pmRootPort = 0; + LOG((CLOG_DEBUG "stopped watchSystemPowerThread")); +} + +void +COSXScreen::powerChangeCallback(void* refcon, io_service_t service, + natural_t messageType, void* messageArg) +{ + ((COSXScreen*)refcon)->handlePowerChangeRequest(messageType, messageArg); +} + +void +COSXScreen::handlePowerChangeRequest(natural_t messageType, void* messageArg) +{ + // we've received a power change notification + switch (messageType) { + case kIOMessageSystemWillSleep: + // COSXScreen has to handle this in the main thread so we have to + // queue a confirm sleep event here. we actually don't allow the + // system to sleep until the event is handled. + EVENTQUEUE->addEvent(CEvent(COSXScreen::getConfirmSleepEvent(), + getEventTarget(), messageArg, + CEvent::kDontFreeData)); + return; + + case kIOMessageSystemHasPoweredOn: + LOG((CLOG_DEBUG "system wakeup")); + EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(), + getEventTarget())); + break; + + default: + break; + } + + CLock lock(m_pmMutex); + if (m_pmRootPort != 0) { + IOAllowPowerChange(m_pmRootPort, (long)messageArg); + } +} + +CEvent::Type +COSXScreen::getConfirmSleepEvent() +{ + return CEvent::registerTypeOnce(s_confirmSleepEvent, + "COSXScreen::confirmSleep"); +} + +void +COSXScreen::handleConfirmSleep(const CEvent& event, void*) +{ + long messageArg = (long)event.getData(); + if (messageArg != 0) { + CLock lock(m_pmMutex); + if (m_pmRootPort != 0) { + // deliver suspend event immediately. + EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(), + getEventTarget(), NULL, + CEvent::kDeliverImmediately)); + + LOG((CLOG_DEBUG "system will sleep")); + IOAllowPowerChange(m_pmRootPort, messageArg); + } + } +} + +#pragma mark - + +// +// GLOBAL HOTKEY OPERATING MODE SUPPORT (10.3) +// +// CoreGraphics private API (OSX 10.3) +// Source: http://ichiro.nnip.org/osx/Cocoa/GlobalHotkey.html +// +// We load the functions dynamically because they're not available in +// older SDKs. We don't use weak linking because we want users of +// older SDKs to build an app that works on newer systems and older +// SDKs will not provide the symbols. +// + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int CGSConnection; +typedef enum { + CGSGlobalHotKeyEnable = 0, + CGSGlobalHotKeyDisable = 1, +} CGSGlobalHotKeyOperatingMode; + +extern CGSConnection _CGSDefaultConnection(void) WEAK_IMPORT_ATTRIBUTE; +extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode) WEAK_IMPORT_ATTRIBUTE; +extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode) WEAK_IMPORT_ATTRIBUTE; + +typedef CGSConnection (*_CGSDefaultConnection_t)(void); +typedef CGError (*CGSGetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode); +typedef CGError (*CGSSetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode); + +static _CGSDefaultConnection_t s__CGSDefaultConnection; +static CGSGetGlobalHotKeyOperatingMode_t s_CGSGetGlobalHotKeyOperatingMode; +static CGSSetGlobalHotKeyOperatingMode_t s_CGSSetGlobalHotKeyOperatingMode; + +#ifdef __cplusplus +} +#endif + +#define LOOKUP(name_) \ + s_ ## name_ = NULL; \ + if (NSIsSymbolNameDefinedWithHint("_" #name_, "CoreGraphics")) { \ + s_ ## name_ = (name_ ## _t)NSAddressOfSymbol( \ + NSLookupAndBindSymbolWithHint( \ + "_" #name_, "CoreGraphics")); \ + } + +bool +COSXScreen::isGlobalHotKeyOperatingModeAvailable() +{ + if (!s_testedForGHOM) { + s_testedForGHOM = true; + LOOKUP(_CGSDefaultConnection); + LOOKUP(CGSGetGlobalHotKeyOperatingMode); + LOOKUP(CGSSetGlobalHotKeyOperatingMode); + s_hasGHOM = (s__CGSDefaultConnection != NULL && + s_CGSGetGlobalHotKeyOperatingMode != NULL && + s_CGSSetGlobalHotKeyOperatingMode != NULL); + } + return s_hasGHOM; +} + +void +COSXScreen::setGlobalHotKeysEnabled(bool enabled) +{ + if (isGlobalHotKeyOperatingModeAvailable()) { + CGSConnection conn = s__CGSDefaultConnection(); + + CGSGlobalHotKeyOperatingMode mode; + s_CGSGetGlobalHotKeyOperatingMode(conn, &mode); + + if (enabled && mode == CGSGlobalHotKeyDisable) { + s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyEnable); + } + else if (!enabled && mode == CGSGlobalHotKeyEnable) { + s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyDisable); + } + } +} + +bool +COSXScreen::getGlobalHotKeysEnabled() +{ + CGSGlobalHotKeyOperatingMode mode; + if (isGlobalHotKeyOperatingModeAvailable()) { + CGSConnection conn = s__CGSDefaultConnection(); + s_CGSGetGlobalHotKeyOperatingMode(conn, &mode); + } + else { + mode = CGSGlobalHotKeyEnable; + } + return (mode == CGSGlobalHotKeyEnable); +} + + +// +// COSXScreen::CHotKeyItem +// + +COSXScreen::CHotKeyItem::CHotKeyItem(UInt32 keycode, UInt32 mask) : + m_ref(NULL), + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +COSXScreen::CHotKeyItem::CHotKeyItem(EventHotKeyRef ref, + UInt32 keycode, UInt32 mask) : + m_ref(ref), + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +EventHotKeyRef +COSXScreen::CHotKeyItem::getRef() const +{ + return m_ref; +} + +bool +COSXScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const +{ + return (m_keycode < x.m_keycode || + (m_keycode == x.m_keycode && m_mask < x.m_mask)); +} diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h index 292c1a40..f280d802 100644 --- a/lib/platform/COSXScreen.h +++ b/lib/platform/COSXScreen.h @@ -16,9 +16,21 @@ #define COSXSCREEN_H #include "CPlatformScreen.h" +#include "stdmap.h" #include "stdvector.h" #include +#include +#include +#include +#include +#include + +template +class CCondVar; +class CEventQueueTimer; +class CMutex; +class CThread; class COSXKeyState; class COSXScreenSaver; @@ -38,6 +50,10 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + virtual void unregisterHotKey(UInt32 id); + virtual void fakeInputBegin(); + virtual void fakeInputEnd(); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; @@ -46,7 +62,7 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; - virtual void fakeMouseWheel(SInt32 delta) const; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; // IPlatformScreen overrides virtual void enable(); @@ -71,7 +87,7 @@ protected: private: void updateScreenShape(); - void postMouseEvent(const CGPoint &) const; + void postMouseEvent(CGPoint&) const; // convenience function to send events void sendEvent(CEvent::Type type, void* = NULL) const; @@ -82,25 +98,83 @@ private: // mouse button handler. pressed is true if this is a mousedown // event, false if it is a mouseup event. macButton is the index // of the button pressed using the mac button mapping. - bool onMouseButton(bool pressed, UInt16 macButton) const; - bool onMouseWheel(SInt32 delta) const; + bool onMouseButton(bool pressed, UInt16 macButton); + bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const; bool onDisplayChange(); - bool onKey(EventRef event) const; + bool onKey(EventRef event); + bool onHotKey(EventRef event) const; // map mac mouse button to synergy buttons ButtonID mapMacButtonToSynergy(UInt16) const; - // map mac modifier mask to synergy modifier mask - KeyModifierMask mapMacModifiersToSynergy(EventRef event) const; - - /// Resolution switch callback + // map mac scroll wheel value to a synergy scroll wheel value + SInt32 mapScrollWheelToSynergy(SInt32) const; + + // map synergy scroll wheel value to a mac scroll wheel value + SInt32 mapScrollWheelFromSynergy(SInt32) const; + + // get the current scroll wheel speed + double getScrollSpeed() const; + + // get the current scroll wheel speed + double getScrollSpeedFactor() const; + + // enable/disable drag handling for buttons 3 and up + void enableDragTimer(bool enable); + + // drag timer handler + void handleDrag(const CEvent&, void*); + + // clipboard check timer handler + void handleClipboardCheck(const CEvent&, void*); + + // Resolution switch callback static pascal void displayManagerCallback(void* inUserData, SInt16 inMessage, void* inNotifyData); - + // fast user switch callback + static pascal OSStatus + userSwitchCallback(EventHandlerCallRef nextHandler, + EventRef theEvent, void* inUserData); + + // sleep / wakeup support + void watchSystemPowerThread(void*); + static void testCanceled(CFRunLoopTimerRef timer, void*info); + static void powerChangeCallback(void* refcon, io_service_t service, + natural_t messageType, void* messageArgument); + void handlePowerChangeRequest(natural_t messageType, + void* messageArgument); + + static CEvent::Type getConfirmSleepEvent(); + void handleConfirmSleep(const CEvent& event, void*); + + // global hotkey operating mode + static bool isGlobalHotKeyOperatingModeAvailable(); + static void setGlobalHotKeysEnabled(bool enabled); + static bool getGlobalHotKeysEnabled(); + private: + struct CHotKeyItem { + public: + CHotKeyItem(UInt32, UInt32); + CHotKeyItem(EventHotKeyRef, UInt32, UInt32); + + EventHotKeyRef getRef() const; + + bool operator<(const CHotKeyItem&) const; + + private: + EventHotKeyRef m_ref; + UInt32 m_keycode; + UInt32 m_mask; + }; + typedef std::map HotKeyMap; + typedef std::vector HotKeyIDList; + typedef std::map ModifierHotKeyMap; + typedef std::map HotKeyToIDMap; + // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; @@ -120,6 +194,9 @@ private: mutable bool m_cursorPosValid; mutable boolean_t m_buttons[5]; bool m_cursorHidden; + SInt32 m_dragNumButtonsDown; + Point m_dragLastPoint; + CEventQueueTimer* m_dragTimer; // keyboard stuff COSXKeyState* m_keyState; @@ -133,17 +210,43 @@ private: // clipboard stuff bool m_ownClipboard; - + CEventQueueTimer* m_clipboardTimer; + // window object that gets user input events when the server // has focus. WindowRef m_hiddenWindow; // window object that gets user input events when the server // does not have focus. WindowRef m_userInputWindow; - + // display manager stuff (to get screen resolution switches). DMExtendedNotificationUPP m_displayManagerNotificationUPP; ProcessSerialNumber m_PSN; + + // fast user switching + EventHandlerRef m_switchEventHandlerRef; + + // sleep / wakeup + CMutex* m_pmMutex; + CThread* m_pmWatchThread; + CCondVar* m_pmThreadReady; + CFRunLoopRef m_pmRunloop; + io_connect_t m_pmRootPort; + + // hot key stuff + HotKeyMap m_hotKeys; + HotKeyIDList m_oldHotKeyIDs; + ModifierHotKeyMap m_modifierHotKeys; + UInt32 m_activeModifierHotKey; + KeyModifierMask m_activeModifierHotKeyMask; + HotKeyToIDMap m_hotKeyToIDMap; + + // global hotkey operating mode + static bool s_testedForGHOM; + static bool s_hasGHOM; + + // events + static CEvent::Type s_confirmSleepEvent; }; #endif diff --git a/lib/platform/COSXScreenSaver.cpp b/lib/platform/COSXScreenSaver.cpp index f67dc016..9cc6a678 100644 --- a/lib/platform/COSXScreenSaver.cpp +++ b/lib/platform/COSXScreenSaver.cpp @@ -12,45 +12,164 @@ * GNU General Public License for more details. */ -#include "COSXScreenSaver.h" -#include - -// FIXME -- implement this +#import "COSXScreenSaver.h" +#import "COSXScreenSaverUtil.h" +#import "CLog.h" +#import "IEventQueue.h" +#import "IPrimaryScreen.h" +#import // // COSXScreenSaver // -COSXScreenSaver::COSXScreenSaver() +COSXScreenSaver::COSXScreenSaver(void* eventTarget) : + m_eventTarget(eventTarget), + m_enabled(true) { + m_autoReleasePool = screenSaverUtilCreatePool(); + m_screenSaverController = screenSaverUtilCreateController(); + + // install launch/termination event handlers + EventTypeSpec launchEventTypes[2]; + launchEventTypes[0].eventClass = kEventClassApplication; + launchEventTypes[0].eventKind = kEventAppLaunched; + launchEventTypes[1].eventClass = kEventClassApplication; + launchEventTypes[1].eventKind = kEventAppTerminated; + + EventHandlerUPP launchTerminationEventHandler = + NewEventHandlerUPP(launchTerminationCallback); + InstallApplicationEventHandler(launchTerminationEventHandler, 2, + launchEventTypes, this, + &m_launchTerminationEventHandlerRef); + DisposeEventHandlerUPP(launchTerminationEventHandler); + + m_screenSaverPSN.highLongOfPSN = 0; + m_screenSaverPSN.lowLongOfPSN = 0; + + // test if screensaver is running and find process number + if (isActive()) { + ProcessInfoRec procInfo; + Str31 procName; // pascal string. first byte holds length. + memset(&procInfo, 0, sizeof(procInfo)); + procInfo.processName = procName; + procInfo.processInfoLength = sizeof(ProcessInfoRec); + + ProcessSerialNumber psn; + OSErr err = GetNextProcess(&psn); + while (err == 0) { + memset(procName, 0, sizeof(procName)); + err = GetProcessInformation(&psn, &procInfo); + if (err != 0) { + break; + } + if (strcmp("ScreenSaverEngine", (const char*)&procName[1]) == 0) { + m_screenSaverPSN = psn; + break; + } + err = GetNextProcess(&psn); + } + } } COSXScreenSaver::~COSXScreenSaver() { + RemoveEventHandler(m_launchTerminationEventHandlerRef); +// screenSaverUtilReleaseController(m_screenSaverController); + screenSaverUtilReleasePool(m_autoReleasePool); } void COSXScreenSaver::enable() { + m_enabled = true; + screenSaverUtilEnable(m_screenSaverController); } void COSXScreenSaver::disable() { + m_enabled = false; + screenSaverUtilDisable(m_screenSaverController); } void COSXScreenSaver::activate() { + screenSaverUtilActivate(m_screenSaverController); } void COSXScreenSaver::deactivate() { + screenSaverUtilDeactivate(m_screenSaverController, m_enabled); } bool COSXScreenSaver::isActive() const { - return false; + return (screenSaverUtilIsActive(m_screenSaverController) != 0); +} + +void +COSXScreenSaver::processLaunched(ProcessSerialNumber psn) +{ + CFStringRef processName; + OSStatus err = CopyProcessName(&psn, &processName); + + if (err == 0 && CFEqual(CFSTR("ScreenSaverEngine"), processName)) { + m_screenSaverPSN = psn; + LOG((CLOG_DEBUG1 "ScreenSaverEngine launched. Enabled=%d", m_enabled)); + if (m_enabled) { + EVENTQUEUE->addEvent( + CEvent(IPrimaryScreen::getScreensaverActivatedEvent(), + m_eventTarget)); + } + } +} + +void +COSXScreenSaver::processTerminated(ProcessSerialNumber psn) +{ + if (m_screenSaverPSN.highLongOfPSN == psn.highLongOfPSN && + m_screenSaverPSN.lowLongOfPSN == psn.lowLongOfPSN) { + LOG((CLOG_DEBUG1 "ScreenSaverEngine terminated. Enabled=%d", m_enabled)); + if (m_enabled) { + EVENTQUEUE->addEvent( + CEvent(IPrimaryScreen::getScreensaverDeactivatedEvent(), + m_eventTarget)); + } + + m_screenSaverPSN.highLongOfPSN = 0; + m_screenSaverPSN.lowLongOfPSN = 0; + } +} + +pascal OSStatus +COSXScreenSaver::launchTerminationCallback( + EventHandlerCallRef nextHandler, + EventRef theEvent, void* userData) +{ + OSStatus result; + ProcessSerialNumber psn; + EventParamType actualType; + UInt32 actualSize; + + result = GetEventParameter(theEvent, kEventParamProcessID, + typeProcessSerialNumber, &actualType, + sizeof(psn), &actualSize, &psn); + + if ((result == noErr) && + (actualSize > 0) && + (actualType == typeProcessSerialNumber)) { + COSXScreenSaver* screenSaver = (COSXScreenSaver*)userData; + UInt32 eventKind = GetEventKind(theEvent); + if (eventKind == kEventAppLaunched) { + screenSaver->processLaunched(psn); + } + else if (eventKind == kEventAppTerminated) { + screenSaver->processTerminated(psn); + } + } + return (CallNextEventHandler(nextHandler, theEvent)); } diff --git a/lib/platform/COSXScreenSaver.h b/lib/platform/COSXScreenSaver.h index a40caefa..40c4e742 100644 --- a/lib/platform/COSXScreenSaver.h +++ b/lib/platform/COSXScreenSaver.h @@ -16,11 +16,12 @@ #define COSXSCREENSAVER_H #include "IScreenSaver.h" +#include //! OSX screen saver implementation class COSXScreenSaver : public IScreenSaver { public: - COSXScreenSaver(); + COSXScreenSaver(void* eventTarget); virtual ~COSXScreenSaver(); // IScreenSaver overrides @@ -29,6 +30,25 @@ public: virtual void activate(); virtual void deactivate(); virtual bool isActive() const; + +private: + void processLaunched(ProcessSerialNumber psn); + void processTerminated(ProcessSerialNumber psn); + + static pascal OSStatus + launchTerminationCallback( + EventHandlerCallRef nextHandler, + EventRef theEvent, void* userData); + +private: + // the target for the events we generate + void* m_eventTarget; + + bool m_enabled; + void* m_screenSaverController; + void* m_autoReleasePool; + EventHandlerRef m_launchTerminationEventHandlerRef; + ProcessSerialNumber m_screenSaverPSN; }; #endif diff --git a/lib/platform/COSXScreenSaverUtil.h b/lib/platform/COSXScreenSaverUtil.h new file mode 100644 index 00000000..d4124ab3 --- /dev/null +++ b/lib/platform/COSXScreenSaverUtil.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 COSXSCREENSAVERUTIL_H +#define COSXSCREENSAVERUTIL_H + +#include "common.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +void* screenSaverUtilCreatePool(); +void screenSaverUtilReleasePool(void*); + +void* screenSaverUtilCreateController(); +void screenSaverUtilReleaseController(void*); +void screenSaverUtilEnable(void*); +void screenSaverUtilDisable(void*); +void screenSaverUtilActivate(void*); +void screenSaverUtilDeactivate(void*, int isEnabled); +int screenSaverUtilIsActive(void*); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/lib/platform/COSXScreenSaverUtil.m b/lib/platform/COSXScreenSaverUtil.m new file mode 100644 index 00000000..1376d1f2 --- /dev/null +++ b/lib/platform/COSXScreenSaverUtil.m @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#import "COSXScreenSaverUtil.h" +#import "OSXScreenSaverControl.h" +#import + +// +// screenSaverUtil functions +// +// Note: these helper functions exist only so we can avoid using ObjC++. +// autoconf/automake don't know about ObjC++ and I don't know how to +// teach them about it. +// + +void* +screenSaverUtilCreatePool() +{ + return [[NSAutoreleasePool alloc] init]; +} + +void +screenSaverUtilReleasePool(void* pool) +{ + [(NSAutoreleasePool*)pool release]; +} + +void* +screenSaverUtilCreateController() +{ + return [[ScreenSaverController controller] retain]; +} + +void +screenSaverUtilReleaseController(void* controller) +{ + [(ScreenSaverController*)controller release]; +} + +void +screenSaverUtilEnable(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; +} + +void +screenSaverUtilDisable(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:NO]; +} + +void +screenSaverUtilActivate(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; + [(ScreenSaverController*)controller screenSaverStartNow]; +} + +void +screenSaverUtilDeactivate(void* controller, int isEnabled) +{ + [(ScreenSaverController*)controller screenSaverStopNow]; + [(ScreenSaverController*)controller setScreenSaverCanRun:isEnabled]; +} + +int +screenSaverUtilIsActive(void* controller) +{ + return [(ScreenSaverController*)controller screenSaverIsRunning]; +} diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 279da06d..852e8bbc 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -59,8 +59,8 @@ typedef struct tagMOUSEHOOKSTRUCTWin2000 { #define WM_NCXBUTTONDOWN 0x00AB #define WM_NCXBUTTONUP 0x00AC #define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 +#define MOUSEEVENTF_XDOWN 0x0080 +#define MOUSEEVENTF_XUP 0x0100 #define XBUTTON1 0x0001 #define XBUTTON2 0x0002 #endif @@ -96,10 +96,10 @@ static SInt32 g_wScreen = 0; static SInt32 g_hScreen = 0; static WPARAM g_deadVirtKey = 0; static LPARAM g_deadLParam = 0; -static WPARAM g_oldDeadVirtKey = 0; static BYTE g_deadKeyState[256] = { 0 }; static DWORD g_hookThread = 0; static DWORD g_attachedThread = 0; +static bool g_fakeInput = false; #if defined(_MSC_VER) #pragma data_seg() @@ -161,192 +161,213 @@ attachThreadToForeground() #if !NO_GRAB_KEYBOARD static WPARAM -makeKeyMsg(UINT virtKey, char c) +makeKeyMsg(UINT virtKey, char c, bool noAltGr) { - return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), 0); + return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), noAltGr ? 1 : 0); } static void keyboardGetState(BYTE keys[256]) { - if (g_hookThread != 0) { - GetKeyboardState(keys); - } - else { - SHORT key; - for (int i = 0; i < 256; ++i) { - key = GetAsyncKeyState(i); - keys[i] = (BYTE)((key < 0) ? 0x80u : 0); - } - key = GetKeyState(VK_CAPITAL); - keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1)); + // we have to use GetAsyncKeyState() rather than GetKeyState() because + // we don't pass through most keys so the event synchronous state + // doesn't get updated. we do that because certain modifier keys have + // side effects, like alt and the windows key. + SHORT key; + for (int i = 0; i < 256; ++i) { + key = GetAsyncKeyState(i); + keys[i] = (BYTE)((key < 0) ? 0x80u : 0); } + key = GetKeyState(VK_CAPITAL); + keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1)); } static bool doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) { - // check for dead keys. we don't forward those to our window. - // instead we'll leave the key in the keyboard layout (a buffer - // internal to the system) for translation when the next key is - // pressed. note that some systems set bit 31 to indicate a - // dead key and others bit 15. nice. - UINT c = MapVirtualKey(wParam, 2); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x00000000, c); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | (c << 8) | 0x01000000, lParam); - if ((c & 0x80008000u) != 0) { - if ((lParam & 0x80000000u) == 0) { - if (g_deadVirtKey == 0) { - // dead key press, no dead key in the buffer - g_deadVirtKey = wParam; - g_deadLParam = lParam; - keyboardGetState(g_deadKeyState); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x02000000, lParam); - return false; - } - // second dead key press in a row so let it pass - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x03000000, lParam); - } - else if (wParam == g_oldDeadVirtKey) { - // dead key release for second dead key in a row. discard - // because we've already handled it. also take it out of - // the keyboard buffer. - g_oldDeadVirtKey = 0; - WORD c; - UINT scanCode = ((lParam & 0x00ff0000u) >> 16); - ToAscii(wParam, scanCode, g_deadKeyState, &c, 0); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x09000000, lParam); - return true; - } - else { - // dead key release - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x04000000, lParam); + // check for special events indicating if we should start or stop + // passing events through and not report them to the server. this + // is used to allow the server to synthesize events locally but + // not pick them up as user events. + if (wParam == SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY && + ((lParam >> 16) & 0xffu) == SYNERGY_HOOK_FAKE_INPUT_SCANCODE) { + // update flag + g_fakeInput = ((lParam & 0x80000000u) == 0); + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + 0xff000000u | wParam, lParam); + + // discard event + return true; + } + + // if we're expecting fake input then just pass the event through + // and do not forward to the server + if (g_fakeInput) { + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + 0xfe000000u | wParam, lParam); + return false; + } + + // VK_RSHIFT may be sent with an extended scan code but right shift + // is not an extended key so we reset that bit. + if (wParam == VK_RSHIFT) { + lParam &= ~0x01000000u; + } + + // tell server about event + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, wParam, lParam); + + // ignore dead key release + if (g_deadVirtKey == wParam && + (lParam & 0x80000000u) != 0) { + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x04000000, lParam); + return false; + } + + // we need the keyboard state for ToAscii() + BYTE keys[256]; + keyboardGetState(keys); + + // ToAscii() maps ctrl+letter to the corresponding control code + // and ctrl+backspace to delete. we don't want those translations + // so clear the control modifier state. however, if we want to + // simulate AltGr (which is ctrl+alt) then we must not clear it. + UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL]; + UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU]; + if ((control & 0x80) == 0 || (menu & 0x80) == 0) { + keys[VK_LCONTROL] = 0; + keys[VK_RCONTROL] = 0; + keys[VK_CONTROL] = 0; + } + else { + keys[VK_LCONTROL] = 0x80; + keys[VK_RCONTROL] = 0x80; + keys[VK_CONTROL] = 0x80; + keys[VK_LMENU] = 0x80; + keys[VK_RMENU] = 0x80; + keys[VK_MENU] = 0x80; + } + + // ToAscii() needs to know if a menu is active for some reason. + // we don't know and there doesn't appear to be any way to find + // out. so we'll just assume a menu is active if the menu key + // is down. + // FIXME -- figure out some way to check if a menu is active + UINT flags = 0; + if ((menu & 0x80) != 0) + flags |= 1; + + // if we're on the server screen then just pass numpad keys with alt + // key down as-is. we won't pick up the resulting character but the + // local app will. if on a client screen then grab keys as usual; + // if the client is a windows system it'll synthesize the expected + // character. if not then it'll probably just do nothing. + if (g_mode != kHOOK_RELAY_EVENTS) { + // we don't use virtual keys because we don't know what the + // state of the numlock key is. we'll hard code the scan codes + // instead. hopefully this works across all keyboards. + UINT sc = (lParam & 0x01ff0000u) >> 16; + if (menu && + (sc >= 0x47u && sc <= 0x52u && sc != 0x4au && sc != 0x4eu)) { return false; } } - // convert key to a character. this combines a saved dead key, - // if any, with this key. however, the dead key must remain in - // the keyboard layout for the application receiving this event - // so it can also convert the key to a character. we only do - // this on a key press. - WPARAM charAndVirtKey = (wParam & 0xffu); - if (c != 0) { - // we need the keyboard state for ToAscii() - BYTE keys[256]; - keyboardGetState(keys); - - // ToAscii() maps ctrl+letter to the corresponding control code - // and ctrl+backspace to delete. we don't want those translations - // so clear the control modifier state. however, if we want to - // simulate AltGr (which is ctrl+alt) then we must not clear it. - UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL]; - UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU]; - if ((control & 0x80) == 0 || (menu & 0x80) == 0) { - keys[VK_LCONTROL] = 0; - keys[VK_RCONTROL] = 0; - keys[VK_CONTROL] = 0; - } - else { - keys[VK_LCONTROL] = 0x80; - keys[VK_RCONTROL] = 0x80; - keys[VK_CONTROL] = 0x80; - keys[VK_LMENU] = 0x80; - keys[VK_RMENU] = 0x80; - keys[VK_MENU] = 0x80; - } - - // ToAscii() needs to know if a menu is active for some reason. - // we don't know and there doesn't appear to be any way to find - // out. so we'll just assume a menu is active if the menu key - // is down. - // FIXME -- figure out some way to check if a menu is active - UINT flags = 0; - if ((menu & 0x80) != 0) - flags |= 1; - - // map the key event to a character. this has the side - // effect of removing the dead key from the system's keyboard - // layout buffer. - WORD c = 0; - UINT scanCode = ((lParam & 0x00ff0000u) >> 16); - int n = ToAscii(wParam, scanCode, keys, &c, flags); - - // if mapping failed and ctrl and alt are pressed then try again - // with both not pressed. this handles the case where ctrl and - // alt are being used as individual modifiers rather than AltGr. - // we have to put the dead key back first, if there was one. - if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x05000000, lParam); - if (g_deadVirtKey != 0) { - ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, - g_deadKeyState, &c, flags); - } - keys[VK_LCONTROL] = 0; - keys[VK_RCONTROL] = 0; - keys[VK_CONTROL] = 0; - keys[VK_LMENU] = 0; - keys[VK_RMENU] = 0; - keys[VK_MENU] = 0; - n = ToAscii(wParam, scanCode, keys, &c, flags); - } + // map the key event to a character. we have to put the dead + // key back first and this has the side effect of removing it. + if (g_deadVirtKey != 0) { + WORD c = 0; + ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, &c, flags); + } + WORD c = 0; + UINT scanCode = ((lParam & 0x10ff0000u) >> 16); + int n = ToAscii(wParam, scanCode, keys, &c, flags); + // if mapping failed and ctrl and alt are pressed then try again + // with both not pressed. this handles the case where ctrl and + // alt are being used as individual modifiers rather than AltGr. + // we note that's the case in the message sent back to synergy + // because there's no simple way to deduce it after the fact. + // we have to put the dead key back first, if there was one. + bool noAltGr = false; + if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { + noAltGr = true; PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | (c << 8) | ((n & 0xff) << 16) | 0x06000000, + wParam | 0x05000000, lParam); + if (g_deadVirtKey != 0) { + ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, &c, flags); + } + BYTE keys2[256]; + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) { + keys2[i] = keys[i]; + } + keys2[VK_LCONTROL] = 0; + keys2[VK_RCONTROL] = 0; + keys2[VK_CONTROL] = 0; + keys2[VK_LMENU] = 0; + keys2[VK_RMENU] = 0; + keys2[VK_MENU] = 0; + n = ToAscii(wParam, scanCode, keys2, &c, flags); + } + + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | ((c & 0xff) << 8) | + ((n & 0xff) << 16) | 0x06000000, lParam); - switch (n) { - default: - // key is a dead key; we're not expecting this since we - // bailed out above for any dead key. - g_deadVirtKey = wParam; - g_deadLParam = lParam; - break; + WPARAM charAndVirtKey = 0; + bool clearDeadKey = false; + switch (n) { + default: + // key is a dead key + g_deadVirtKey = wParam; + g_deadLParam = lParam; + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) { + g_deadKeyState[i] = keys[i]; + } + break; - case 0: - // key doesn't map to a character. this can happen if - // non-character keys are pressed after a dead key. - break; + case 0: + // key doesn't map to a character. this can happen if + // non-character keys are pressed after a dead key. + charAndVirtKey = makeKeyMsg(wParam, (char)0, noAltGr); + break; - case 1: - // key maps to a character composed with dead key - charAndVirtKey = makeKeyMsg(wParam, (char)LOBYTE(c)); - break; + case 1: + // key maps to a character composed with dead key + charAndVirtKey = makeKeyMsg(wParam, (char)LOBYTE(c), noAltGr); + clearDeadKey = true; + break; - case 2: { - // previous dead key not composed. send a fake key press - // and release for the dead key to our window. - WPARAM deadCharAndVirtKey = - makeKeyMsg(g_deadVirtKey, (char)LOBYTE(c)); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, + case 2: { + // previous dead key not composed. send a fake key press + // and release for the dead key to our window. + WPARAM deadCharAndVirtKey = + makeKeyMsg(g_deadVirtKey, (char)LOBYTE(c), noAltGr); + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, deadCharAndVirtKey, g_deadLParam & 0x7fffffffu); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, deadCharAndVirtKey, g_deadLParam | 0x80000000u); - // use uncomposed character - charAndVirtKey = makeKeyMsg(wParam, (char)HIBYTE(c)); - break; - } - } + // use uncomposed character + charAndVirtKey = makeKeyMsg(wParam, (char)HIBYTE(c), noAltGr); + clearDeadKey = true; + break; + } + } - // put back the dead key, if any, for the application to use - if (g_deadVirtKey != 0) { - ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, + // put back the dead key, if any, for the application to use + if (g_deadVirtKey != 0) { + ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, g_deadKeyState, &c, flags); - for (int i = 0; i < 256; ++i) { - g_deadKeyState[i] = 0; - } - } + } - // clear out old dead key state + // clear out old dead key state + if (clearDeadKey) { g_deadVirtKey = 0; g_deadLParam = 0; } @@ -355,19 +376,12 @@ doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) // 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_DEBUG, - charAndVirtKey | 0x07000000, lParam); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam); - - // send fake key release if the user just pressed two dead keys - // in a row, otherwise we'll lose the release because we always - // return from the top of this function for all dead key releases. - if ((c & 0x80008000u) != 0) { - g_oldDeadVirtKey = wParam; + // XXX -- with hot keys for actions we may only need to do this when + // forwarding. + if (charAndVirtKey != 0) { PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x08000000, lParam); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, - charAndVirtKey, lParam | 0x80000000u); + charAndVirtKey | 0x07000000, lParam); + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam); } if (g_mode == kHOOK_RELAY_EVENTS) { @@ -381,32 +395,14 @@ doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) // lights may not stay synchronized. break; - case VK_SHIFT: - case VK_LSHIFT: - case VK_RSHIFT: - // pass the shift modifiers. if we don't do this - // we may not get the right dead key when caps lock - // is on. for example, on the french layout (with - // english keycaps) on caps lock then press shift + [ - // and q. instead of an A with ^ above it you get an - // A with dots above it. - break; - - case VK_CONTROL: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_MENU: - case VK_LMENU: - case VK_RMENU: case VK_HANGUL: - // pass the control and alt modifiers if using a low - // level hook, discard them if not. + // pass these modifiers if using a low level hook, discard + // them if not. if (g_hookThread == 0) { return true; } break; - default: // discard return true; @@ -531,7 +527,7 @@ static bool mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) { - attachThreadToForeground(); +// attachThreadToForeground(); return doMouseHookHandler(wParam, x, y, data); } @@ -804,7 +800,7 @@ init(DWORD threadID) // save thread id. we'll post messages to this thread's // message queue. - g_threadID = threadID; + g_threadID = threadID; // set defaults g_mode = kHOOK_DISABLE; @@ -847,6 +843,9 @@ install() g_deadVirtKey = 0; g_deadLParam = 0; + // reset fake input flag + g_fakeInput = false; + // check for mouse wheel support g_wheelSupport = getWheelSupport(); diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index b0c8e5d3..e8661815 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -38,6 +38,9 @@ #define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP #define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_DEBUG +#define SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY VK_CANCEL +#define SYNERGY_HOOK_FAKE_INPUT_SCANCODE 0 + extern "C" { enum EHookResult { diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 5bbe98c2..c380c9cc 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -503,11 +503,10 @@ CXWindowsClipboard::icccmFillCache() (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)); + CXWindowsUtil::appendAtomData(data, XA_STRING); } + CXWindowsUtil::convertAtomProperty(data); const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); LOG((CLOG_DEBUG " available targets: %s", CXWindowsUtil::atomsToString(m_display, targets, numTargets).c_str())); @@ -525,12 +524,18 @@ CXWindowsClipboard::icccmFillCache() // see if atom is in target list Atom target = None; + // XXX -- just ask for the converter's target to see if it's + // available rather than checking TARGETS. i've seen clipboard + // owners that don't report all the targets they support. + target = converter->getAtom(); + /* for (UInt32 i = 0; i < numTargets; ++i) { if (converter->getAtom() == targets[i]) { target = targets[i]; break; } } + */ if (target == None) { continue; } @@ -661,7 +666,7 @@ CXWindowsClipboard::motifOwnsClipboard() const reinterpret_cast(data.data()); if (data.size() >= sizeof(CMotifClipHeader) && header->m_id == kMotifClipHeader) { - if (header->m_selectionOwner == owner) { + if (static_cast(header->m_selectionOwner) == owner) { return true; } } @@ -835,12 +840,12 @@ CXWindowsClipboard::insertMultipleReply(Window requestor, } // fail if the requested targets isn't of the correct form - if (format != 32 || - target != m_atomAtomPair) { + if (format != 32 || target != m_atomAtomPair) { return false; } // data is a list of atom pairs: target, property + CXWindowsUtil::convertAtomProperty(data); const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); @@ -851,10 +856,7 @@ CXWindowsClipboard::insertMultipleReply(Window requestor, 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)); + CXWindowsUtil::replaceAtomData(data, i, None); changed = true; } } @@ -900,16 +902,18 @@ CXWindowsClipboard::insertReply(CReply* reply) if (newWindow) { // note errors while we adjust event masks bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); + { + 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; + // 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); + // 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) { @@ -1193,13 +1197,9 @@ 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)); + CXWindowsUtil::appendAtomData(data, m_atomTargets); + CXWindowsUtil::appendAtomData(data, m_atomMultiple); + CXWindowsUtil::appendAtomData(data, m_atomTimestamp); // add targets we can convert to for (ConverterList::const_iterator index = m_converters.begin(); @@ -1208,8 +1208,7 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const // skip formats we don't have if (m_added[converter->getFormat()]) { - atom = converter->getAtom(); - data.append(reinterpret_cast(&atom), sizeof(Atom)); + CXWindowsUtil::appendAtomData(data, converter->getAtom()); } } @@ -1222,9 +1221,8 @@ CXWindowsClipboard::getTimestampData(CString& data, int* format) const { assert(format != NULL); - assert(sizeof(m_timeOwned) == 4); checkCache(); - data.append(reinterpret_cast(&m_timeOwned), 4); + CXWindowsUtil::appendTimeData(data, m_timeOwned); *format = 32; return m_atomInteger; } @@ -1264,6 +1262,9 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, LOG((CLOG_DEBUG1 "request selection=%s, target=%s, window=%x", CXWindowsUtil::atomToString(display, selection).c_str(), CXWindowsUtil::atomToString(display, target).c_str(), m_requestor)); + m_atomNone = XInternAtom(display, "NONE", False); + m_atomIncr = XInternAtom(display, "INCR", False); + // save output pointers m_actualTarget = actualTarget; m_data = data; @@ -1361,7 +1362,8 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( case SelectionNotify: if (xevent->xselection.requestor == m_requestor) { // done if we can't convert - if (xevent->xselection.property == None) { + if (xevent->xselection.property == None || + xevent->xselection.property == m_atomNone) { m_done = true; return true; } @@ -1409,7 +1411,7 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( // 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 (target == m_atomIncr) { if (m_incr) { m_failed = true; m_error = true; diff --git a/lib/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h index 5fe126f8..ba3c7b62 100644 --- a/lib/platform/CXWindowsClipboard.h +++ b/lib/platform/CXWindowsClipboard.h @@ -155,6 +155,10 @@ private: bool m_failed; bool m_done; + // atoms needed for the protocol + Atom m_atomNone; // NONE, not None + Atom m_atomIncr; + // true iff we've received the selection notify bool m_reading; @@ -182,7 +186,7 @@ private: SInt32 m_pad2[4]; SInt32 m_numItems; SInt32 m_pad3[3]; - Window m_selectionOwner; + SInt32 m_selectionOwner; // a Window SInt32 m_pad4[2]; }; @@ -204,9 +208,9 @@ private: SInt32 m_pad1[6]; SInt32 m_length; SInt32 m_data; - Atom m_type; + SInt32 m_type; // an Atom SInt32 m_pad2[1]; - int m_deleted; + SInt32 m_deleted; SInt32 m_pad3[4]; }; diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index 8e303e2e..9f98ded4 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -16,6 +16,7 @@ #include "CXWindowsUtil.h" #include "CLog.h" #include "CStringUtil.h" +#include "stdmap.h" #if X_DISPLAY_MISSING # error X11 is required to build synergy #else @@ -24,156 +25,106 @@ # define XK_MISCELLANY # define XK_XKB_KEYS # include -// these should be in XF86keysym.h but there are several versions of -// that file floating around and not all have all symbols and none of -// them provide any form of versioning so we just define 'em here. -#define XF86XK_Standby 0x1008FF10 -#define XF86XK_AudioLowerVolume 0x1008FF11 -#define XF86XK_AudioMute 0x1008FF12 -#define XF86XK_AudioRaiseVolume 0x1008FF13 -#define XF86XK_AudioPlay 0x1008FF14 -#define XF86XK_AudioStop 0x1008FF15 -#define XF86XK_AudioPrev 0x1008FF16 -#define XF86XK_AudioNext 0x1008FF17 -#define XF86XK_HomePage 0x1008FF18 -#define XF86XK_Mail 0x1008FF19 -#define XF86XK_Start 0x1008FF1A -#define XF86XK_Search 0x1008FF1B -#define XF86XK_AudioRecord 0x1008FF1C -#define XF86XK_Calculator 0x1008FF1D -#define XF86XK_Memo 0x1008FF1E -#define XF86XK_ToDoList 0x1008FF1F -#define XF86XK_Calendar 0x1008FF20 -#define XF86XK_PowerDown 0x1008FF21 -#define XF86XK_ContrastAdjust 0x1008FF22 -#define XF86XK_RockerUp 0x1008FF23 -#define XF86XK_RockerDown 0x1008FF24 -#define XF86XK_RockerEnter 0x1008FF25 -#define XF86XK_Back 0x1008FF26 -#define XF86XK_Forward 0x1008FF27 -#define XF86XK_Stop 0x1008FF28 -#define XF86XK_Refresh 0x1008FF29 -#define XF86XK_PowerOff 0x1008FF2A -#define XF86XK_WakeUp 0x1008FF2B -#define XF86XK_Eject 0x1008FF2C -#define XF86XK_ScreenSaver 0x1008FF2D -#define XF86XK_WWW 0x1008FF2E -#define XF86XK_Sleep 0x1008FF2F -#define XF86XK_Favorites 0x1008FF30 -#define XF86XK_AudioPause 0x1008FF31 -#define XF86XK_AudioMedia 0x1008FF32 -#define XF86XK_MyComputer 0x1008FF33 -#define XF86XK_VendorHome 0x1008FF34 -#define XF86XK_LightBulb 0x1008FF35 -#define XF86XK_Shop 0x1008FF36 -#define XF86XK_History 0x1008FF37 -#define XF86XK_OpenURL 0x1008FF38 -#define XF86XK_AddFavorite 0x1008FF39 -#define XF86XK_HotLinks 0x1008FF3A -#define XF86XK_BrightnessAdjust 0x1008FF3B -#define XF86XK_Finance 0x1008FF3C -#define XF86XK_Community 0x1008FF3D -#define XF86XK_Launch0 0x1008FF40 -#define XF86XK_Launch1 0x1008FF41 -#define XF86XK_Launch2 0x1008FF42 -#define XF86XK_Launch3 0x1008FF43 -#define XF86XK_Launch4 0x1008FF44 -#define XF86XK_Launch5 0x1008FF45 -#define XF86XK_Launch6 0x1008FF46 -#define XF86XK_Launch7 0x1008FF47 -#define XF86XK_Launch8 0x1008FF48 -#define XF86XK_Launch9 0x1008FF49 -#define XF86XK_LaunchA 0x1008FF4A -#define XF86XK_LaunchB 0x1008FF4B -#define XF86XK_LaunchC 0x1008FF4C -#define XF86XK_LaunchD 0x1008FF4D -#define XF86XK_LaunchE 0x1008FF4E -#define XF86XK_LaunchF 0x1008FF4F +#if HAVE_XKB_EXTENSION +# include +#endif #endif -// map special KeyID keys to KeySyms -#if HAVE_X11_XF86KEYSYM_H -static const KeySym g_mapE000[] = -{ - /* 0x00 */ 0, XF86XK_Eject, 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 - -CXWindowsKeyState::CXWindowsKeyState(Display* display) : +CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) : m_display(display) { - // do nothing + XGetKeyboardControl(m_display, &m_keyboardState); +#if HAVE_XKB_EXTENSION + if (useXKB) { + m_xkb = XkbGetMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask | + XkbAllClientInfoMask, XkbUseCoreKbd); + } + else { + m_xkb = NULL; + } +#endif + setActiveGroup(kGroupPollAndSet); } CXWindowsKeyState::~CXWindowsKeyState() { - // do nothing +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbFreeKeyboard(m_xkb, 0, True); + } +#endif +} + +void +CXWindowsKeyState::setActiveGroup(SInt32 group) +{ + if (group == kGroupPollAndSet) { + m_group = -1; + m_group = pollActiveGroup(); + } + else if (group == kGroupPoll) { + m_group = -1; + } + else { + assert(group >= 0); + m_group = group; + } +} + +void +CXWindowsKeyState::setAutoRepeat(const XKeyboardState& state) +{ + m_keyboardState = state; } KeyModifierMask CXWindowsKeyState::mapModifiersFromX(unsigned int state) const { + UInt32 offset = 8 * getGroupFromState(state); 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; + for (int i = 0; i < 8; ++i) { + if ((state & (1u << i)) != 0) { + mask |= m_modifierFromX[offset + i]; + } + } return mask; } +bool +CXWindowsKeyState::mapModifiersToX(KeyModifierMask mask, + unsigned int& modifiers) const +{ + modifiers = 0; + + for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) { + KeyModifierMask bit = (1u << i); + if ((mask & bit) != 0) { + KeyModifierToXMask::const_iterator j = m_modifierToX.find(bit); + if (j == m_modifierToX.end()) { + return false; + } + else { + modifiers |= j->second; + } + } + } + + return true; +} + +void +CXWindowsKeyState::mapKeyToKeycodes(KeyID key, CKeycodeList& keycodes) const +{ + keycodes.clear(); + std::pair range = + m_keyCodeFromKey.equal_range(key); + for (KeyToKeyCodeMap::const_iterator i = range.first; + i != range.second; ++i) { + keycodes.push_back(i->second); + } +} + bool CXWindowsKeyState::fakeCtrlAltDel() { @@ -181,32 +132,9 @@ CXWindowsKeyState::fakeCtrlAltDel() return false; } -const char* -CXWindowsKeyState::getKeyName(KeyButton keycode) const +KeyModifierMask +CXWindowsKeyState::pollActiveModifiers() const { - KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0); - char* name = XKeysymToString(keysym); - if (name != NULL) { - return name; - } - else { - static char buffer[20]; - return strcpy(buffer, - CStringUtil::print("keycode %d", keycode).c_str()); - } -} - -void -CXWindowsKeyState::doUpdateKeys() -{ - // query which keys are pressed - char keys[32]; - XQueryKeymap(m_display, keys); - - // save the auto-repeat mask - XGetKeyboardControl(m_display, &m_keyControl); - - // query the pointer to get the keyboard state Window root = DefaultRootWindow(m_display), window; int xRoot, yRoot, xWindow, yWindow; unsigned int state; @@ -214,901 +142,685 @@ CXWindowsKeyState::doUpdateKeys() &xRoot, &yRoot, &xWindow, &yWindow, &state)) { state = 0; } - - // update mappings - updateKeysymMap(); - updateModifiers(); - - // transfer to our state - for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { - if ((keys[i] & 0x01) != 0) - setKeyDown(j + 0, true); - if ((keys[i] & 0x02) != 0) - setKeyDown(j + 1, true); - if ((keys[i] & 0x04) != 0) - setKeyDown(j + 2, true); - if ((keys[i] & 0x08) != 0) - setKeyDown(j + 3, true); - if ((keys[i] & 0x10) != 0) - setKeyDown(j + 4, true); - if ((keys[i] & 0x20) != 0) - setKeyDown(j + 5, true); - if ((keys[i] & 0x40) != 0) - setKeyDown(j + 6, true); - if ((keys[i] & 0x80) != 0) - setKeyDown(j + 7, true); - } - - // set toggle modifier states - if ((state & LockMask) != 0) - setToggled(KeyModifierCapsLock); - if ((state & m_numLockMask) != 0) - setToggled(KeyModifierNumLock); - if ((state & m_scrollLockMask) != 0) - setToggled(KeyModifierScrollLock); + return mapModifiersFromX(state); } -void -CXWindowsKeyState::doFakeKeyEvent(KeyButton keycode, bool press, bool) +SInt32 +CXWindowsKeyState::pollActiveGroup() const { - XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime); - XFlush(m_display); -} - -KeyButton -CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, bool isAutoRepeat) const -{ - // 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. - // - // desiredMask is the mask desired by the caller. 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. - - // convert KeyID to a KeySym - KeySym keysym = keyIDToKeySym(id, desiredMask); - if (keysym == NoSymbol) { - // unknown key - return 0; + if (m_group != -1) { + assert(m_group >= 0); + return m_group; } - // get the mapping for this keysym - KeySymIndex keyIndex = m_keysymMap.find(keysym); - if (keyIndex != m_keysymMap.end()) { - // the keysym is mapped to some keycode. create the keystrokes - // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); - } - - // 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. - if (!isAutoRepeat) { - KeyButton keycode = mapDecompositionToKeystrokes(keys, keysym, true); - if (keycode != 0) { - return keycode; - } - keycode = mapDecompositionToKeystrokes(keys, keysym, false); - if (keycode != 0) { - // no key is left synthetically down when using the compose key - // so return 0 even though we succeeded. - return 0; +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbStateRec state; + if (XkbGetState(m_display, XkbUseCoreKbd, &state)) { + return state.group; } } - - // if the keysym is caps lock sensitive then convert the case of - // the keysym and try again. - 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. create the keystrokes - // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); - } - } - +#endif return 0; } void -CXWindowsKeyState::updateKeysymMap() +CXWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const +{ + char keys[32]; + XQueryKeymap(m_display, keys); + for (UInt32 i = 0; i < 32; ++i) { + for (UInt32 j = 0; j < 8; ++j) { + if ((keys[i] & (1u << j)) != 0) { + pressedKeys.insert(8 * i + j); + } + } + } +} + +void +CXWindowsKeyState::getKeyMap(CKeyMap& keyMap) +{ + // get autorepeat info. we must use the global_auto_repeat told to + // us because it may have modified by synergy. + int oldGlobalAutoRepeat = m_keyboardState.global_auto_repeat; + XGetKeyboardControl(m_display, &m_keyboardState); + m_keyboardState.global_auto_repeat = oldGlobalAutoRepeat; + +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbGetUpdatedMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask | + XkbAllClientInfoMask, m_xkb); + updateKeysymMapXKB(keyMap); + } + else +#endif + { + updateKeysymMap(keyMap); + } +} + +void +CXWindowsKeyState::fakeKey(const Keystroke& keystroke) +{ + switch (keystroke.m_type) { + case Keystroke::kButton: + LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); + if (keystroke.m_data.m_button.m_repeat) { + int c = keystroke.m_data.m_button.m_button; + int i = (c >> 3); + int b = 1 << (c & 7); + if (m_keyboardState.global_auto_repeat == AutoRepeatModeOff || + (m_keyboardState.auto_repeats[i] & b) == 0) { + LOG((CLOG_DEBUG1 " discard autorepeat")); + break; + } + } + XTestFakeKeyEvent(m_display, keystroke.m_data.m_button.m_button, + keystroke.m_data.m_button.m_press ? True : False, + CurrentTime); + break; + + case Keystroke::kGroup: + if (keystroke.m_data.m_group.m_absolute) { + LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbLockGroup(m_display, XkbUseCoreKbd, + keystroke.m_data.m_group.m_group); + } + else +#endif + { + LOG((CLOG_DEBUG1 " ignored")); + } + } + else { + LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbLockGroup(m_display, XkbUseCoreKbd, + getEffectiveGroup(pollActiveGroup(), + keystroke.m_data.m_group.m_group)); + } + else +#endif + { + LOG((CLOG_DEBUG1 " ignored")); + } + } + break; + } + XFlush(m_display); +} + +void +CXWindowsKeyState::updateKeysymMap(CKeyMap& keyMap) { // there are up to 4 keysyms per keycode - static const unsigned int maxKeysyms = 4; + static const int maxKeysyms = 4; + + LOG((CLOG_DEBUG1 "non-XKB mapping")); + + // prepare map from X modifier to KeyModifierMask. certain bits + // are predefined. + m_modifierFromX.clear(); + m_modifierFromX.resize(8); + m_modifierFromX[ShiftMapIndex] = KeyModifierShift; + m_modifierFromX[LockMapIndex] = KeyModifierCapsLock; + m_modifierFromX[ControlMapIndex] = KeyModifierControl; + m_modifierToX.clear(); + m_modifierToX[KeyModifierShift] = ShiftMask; + m_modifierToX[KeyModifierCapsLock] = LockMask; + m_modifierToX[KeyModifierControl] = ControlMask; + + // prepare map from KeyID to KeyCode + m_keyCodeFromKey.clear(); // get the number of keycodes int minKeycode, maxKeycode; XDisplayKeycodes(m_display, &minKeycode, &maxKeycode); - const int numKeycodes = maxKeycode - minKeycode + 1; + int numKeycodes = maxKeycode - minKeycode + 1; // get the keyboard mapping for all keys int keysymsPerKeycode; - KeySym* keysyms = XGetKeyboardMapping(m_display, + KeySym* allKeysyms = XGetKeyboardMapping(m_display, minKeycode, numKeycodes, &keysymsPerKeycode); - // we only understand up to maxKeysyms keysyms per keycodes - unsigned int numKeysyms = keysymsPerKeycode; - if (numKeysyms > maxKeysyms) { - numKeysyms = maxKeysyms; + // it's more convenient to always have maxKeysyms KeySyms per key + { + KeySym* tmpKeysyms = new KeySym[maxKeysyms * numKeycodes]; + for (int i = 0; i < numKeycodes; ++i) { + for (int j = 0; j < maxKeysyms; ++j) { + if (j < keysymsPerKeycode) { + tmpKeysyms[maxKeysyms * i + j] = + allKeysyms[keysymsPerKeycode * i + j]; + } + else { + tmpKeysyms[maxKeysyms * i + j] = NoSymbol; + } + } + } + XFree(allKeysyms); + allKeysyms = tmpKeysyms; } - // 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; - } - } - - // get modifier map from server - XModifierKeymap* modifiers = XGetModifierMapping(m_display); - unsigned int keysPerModifier = modifiers->max_keypermod; - - // clear state - m_keysymMap.clear(); - m_modeSwitchKeysym = NoSymbol; - m_altMask = 0; - m_metaMask = 0; - m_superMask = 0; - m_modeSwitchMask = 0; - m_numLockMask = 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); - - // for each modifier keycode, get the index 0 keycode and add it to - // the keysym map. also collect all keycodes for each modifier. + // get the buttons assigned to modifiers. X11 does not predefine + // the meaning of any modifiers except shift, caps lock, and the + // control key. the meaning of a modifier bit (other than those) + // depends entirely on the KeySyms mapped to that bit. unfortunately + // you cannot map a bit back to the KeySym used to produce it. + // for example, let's say button 1 maps to Alt_L without shift and + // Meta_L with shift. now if mod1 is mapped to button 1 that could + // mean the user used Alt or Meta to turn on that modifier and there's + // no way to know which. it's also possible for one button to be + // mapped to multiple bits so both mod1 and mod2 could be generated + // by button 1. + // + // we're going to ignore any modifier for a button except the first. + // with the above example, that means we'll ignore the mod2 modifier + // bit unless it's also mapped to some other button. we're also + // going to ignore all KeySyms except the first modifier KeySym, + // which means button 1 above won't map to Meta, just Alt. + std::map modifierButtons; + XModifierKeymap* modifiers = XGetModifierMapping(m_display); for (unsigned int i = 0; i < 8; ++i) { - // no keycodes for this modifier yet - KeyModifierMask mask = 0; - KeyButtons modifierKeys; + const KeyCode* buttons = + modifiers->modifiermap + i * modifiers->max_keypermod; + for (int j = 0; j < modifiers->max_keypermod; ++j) { + modifierButtons.insert(std::make_pair(buttons[j], i)); + } + } + XFreeModifiermap(modifiers); + modifierButtons.erase(0); - // 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; + // Hack to deal with VMware. When a VMware client grabs input the + // player clears out the X modifier map for whatever reason. We're + // notified of the change and arrive here to discover that there + // are no modifiers at all. Since this prevents the modifiers from + // working in the VMware client we'll use the last known good set + // of modifiers when there are no modifiers. If there are modifiers + // we update the last known good set. + if (!modifierButtons.empty()) { + m_lastGoodNonXKBModifiers = modifierButtons; + } + else { + modifierButtons = m_lastGoodNonXKBModifiers; + } + + // add entries for each keycode + CKeyMap::KeyItem item; + for (int i = 0; i < numKeycodes; ++i) { + KeySym* keysyms = allKeysyms + maxKeysyms * i; + KeyCode keycode = static_cast(i + minKeycode); + item.m_button = static_cast(keycode); + item.m_client = 0; + + // determine modifier sensitivity + item.m_sensitive = 0; + + // if the keysyms in levels 2 or 3 exist and differ from levels + // 0 and 1 then the key is sensitive AltGr (Mode_switch) + if ((keysyms[2] != NoSymbol && keysyms[2] != keysyms[0]) || + (keysyms[3] != NoSymbol && keysyms[2] != keysyms[1])) { + item.m_sensitive |= KeyModifierAltGr; + } + + // check if the key is caps-lock sensitive. some systems only + // provide one keysym for keys sensitive to caps-lock. if we + // find that then fill in the missing keysym. + if (keysyms[0] != NoSymbol && keysyms[1] == NoSymbol && + keysyms[2] == NoSymbol && keysyms[3] == NoSymbol) { + KeySym lKeysym, uKeysym; + XConvertCase(keysyms[0], &lKeysym, &uKeysym); + if (lKeysym != uKeysym) { + keysyms[0] = lKeysym; + keysyms[1] = uKeysym; + item.m_sensitive |= KeyModifierCapsLock; } - - // get keysym and get/create key mapping - int keycodeIndex = keycode - minKeycode; - KeySym keysym = keysyms[keycodeIndex * - keysymsPerKeycode + 0]; - - // prefer XK_ISO_Level3_Shift over XK_Mode_switch. newer - // versions of X use the former only. we assume here that - // if you have XK_ISO_Level3_Shift mapped then that's what - // you want to use even if XK_Mode_switch is also mapped. - if (j == 0 && keysym == XK_Mode_switch) { - // sort modifiers->modifiermap for this modifier so - // that a keycode mapped to XK_ISO_Level3_Shift appears - // before those mapped to anything else. this one keycode - // is enough so long as it's first because mapModifier in - // CKeyState uses the first keycode in modifierKeys to - // generate the key event for this modifier. - KeyCode* keycodes = modifiers->modifiermap + - i * keysPerModifier + j; - for (unsigned int k = j + 1; k < keysPerModifier; ++k) { - KeySym keysym2 = keysyms[(keycodes[k] - minKeycode) * - keysymsPerKeycode + 0]; - if (keysym2 == XK_ISO_Level3_Shift) { - // found an XK_ISO_Level3_Shift. swap it with - // the keycode in index j. - keycodes[j] = keycodes[k]; - keycodes[k] = keycode; - - // now use the new first keycode - keycode = keycodes[j]; - keycodeIndex = keycode - minKeycode; - keysym = keysym2; - break; - } + } + else if (keysyms[0] != NoSymbol && keysyms[1] != NoSymbol) { + KeySym lKeysym, uKeysym; + XConvertCase(keysyms[0], &lKeysym, &uKeysym); + if (lKeysym != uKeysym && + lKeysym == keysyms[0] && + uKeysym == keysyms[1]) { + item.m_sensitive |= KeyModifierCapsLock; + } + else if (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol) { + XConvertCase(keysyms[2], &lKeysym, &uKeysym); + if (lKeysym != uKeysym && + lKeysym == keysyms[2] && + uKeysym == keysyms[3]) { + item.m_sensitive |= KeyModifierCapsLock; } } + } - // get modifier mask if we haven't yet. this has the side - // effect of setting the m_*Mask members. - if (mask == 0) { - mask = mapToModifierMask(i, keysym); - if (mask == 0) { + // key is sensitive to shift if keysyms in levels 0 and 1 or + // levels 2 and 3 don't match. it's also sensitive to shift + // if it's sensitive to caps-lock. + if ((item.m_sensitive & KeyModifierCapsLock) != 0) { + item.m_sensitive |= KeyModifierShift; + } + else if ((keysyms[0] != NoSymbol && keysyms[1] != NoSymbol && + keysyms[0] != keysyms[1]) || + (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol && + keysyms[2] != keysyms[3])) { + item.m_sensitive |= KeyModifierShift; + } + + // key is sensitive to numlock if any keysym on it is + if (IsKeypadKey(keysyms[0]) || IsPrivateKeypadKey(keysyms[0]) || + IsKeypadKey(keysyms[1]) || IsPrivateKeypadKey(keysyms[1]) || + IsKeypadKey(keysyms[2]) || IsPrivateKeypadKey(keysyms[2]) || + IsKeypadKey(keysyms[3]) || IsPrivateKeypadKey(keysyms[3])) { + item.m_sensitive |= KeyModifierNumLock; + } + + // do each keysym (shift level) + for (int j = 0; j < maxKeysyms; ++j) { + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[j]); + if (item.m_id == kKeyNone) { + if (j != 0 && modifierButtons.count(keycode) > 0) { + // pretend the modifier works in other shift levels + // because it probably does. + if (keysyms[1] == NoSymbol || j != 3) { + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[0]); + } + else { + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[1]); + } + } + if (item.m_id == kKeyNone) { continue; } } - // save keycode for modifier - modifierKeys.push_back(keycode); + // group is 0 for levels 0 and 1 and 1 for levels 2 and 3 + item.m_group = (j >= 2) ? 1 : 0; - // skip if we already have a keycode for this index - KeyMapping& mapping = m_keysymMap[keysym]; - if (mapping.m_keycode[0] != 0) { - continue; + // compute required modifiers + item.m_required = 0; + if ((j & 1) != 0) { + item.m_required |= KeyModifierShift; + } + if ((j & 2) != 0) { + item.m_required |= KeyModifierAltGr; } - // fill in keysym info - mapping.m_keycode[0] = keycode; - mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; - mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; - mapping.m_modifierMask = mask; - mapping.m_capsLockSensitive = false; - mapping.m_numLockSensitive = false; - } + item.m_generates = 0; + item.m_lock = false; + if (modifierButtons.count(keycode) > 0) { + // get flags for modifier keys + CKeyMap::initModifierKey(item); - // note this modifier - if (mask != 0) { - addModifier(mask, modifierKeys); + // add mapping from X (unless we already have) + if (item.m_generates != 0) { + unsigned int bit = modifierButtons[keycode]; + if (m_modifierFromX[bit] == 0) { + m_modifierFromX[bit] = item.m_generates; + m_modifierToX[item.m_generates] = (1u << bit); + } + } + } + + // add key + keyMap.addKeyEntry(item); + m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode)); + + // add other ways to synthesize the key + if ((j & 1) != 0) { + // add capslock version of key is sensitive to capslock + KeySym lKeysym, uKeysym; + XConvertCase(keysyms[j], &lKeysym, &uKeysym); + if (lKeysym != uKeysym && + lKeysym == keysyms[j - 1] && + uKeysym == keysyms[j]) { + item.m_required &= ~KeyModifierShift; + item.m_required |= KeyModifierCapsLock; + keyMap.addKeyEntry(item); + item.m_required |= KeyModifierShift; + item.m_required &= ~KeyModifierCapsLock; + } + + // add numlock version of key if sensitive to numlock + if (IsKeypadKey(keysyms[j]) || IsPrivateKeypadKey(keysyms[j])) { + item.m_required &= ~KeyModifierShift; + item.m_required |= KeyModifierNumLock; + keyMap.addKeyEntry(item); + item.m_required |= KeyModifierShift; + item.m_required &= ~KeyModifierNumLock; + } + } } } - // 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 = isNumLockSensitive(keysym); - mapping.m_capsLockSensitive = isCapsLockSensitive(keysym); - } - } - - // clean up - XFreeModifiermap(modifiers); - XFree(keysyms); -} - -KeyModifierMask -CXWindowsKeyState::mapToModifierMask(unsigned int i, KeySym keysym) -{ - // some modifier indices (0,1,2) are dedicated to particular uses, - // the rest depend on the keysyms bound. - switch (i) { - case 0: - return KeyModifierShift; - - case 1: - return KeyModifierCapsLock; - - case 2: - return KeyModifierControl; - - default: - switch (keysym) { - case XK_Shift_L: - case XK_Shift_R: - return KeyModifierShift; - - case XK_Control_L: - case XK_Control_R: - return KeyModifierControl; - - case XK_Alt_L: - case XK_Alt_R: - m_altMask = (1 << i); - return KeyModifierAlt; - - case XK_Meta_L: - case XK_Meta_R: - m_metaMask = (1 << i); - return KeyModifierMeta; - - case XK_Super_L: - case XK_Super_R: - m_superMask = (1 << i); - return KeyModifierSuper; - - case XK_Mode_switch: - case XK_ISO_Level3_Shift: - m_modeSwitchMask = (1 << i); - return KeyModifierModeSwitch; - - case XK_Caps_Lock: - return KeyModifierCapsLock; - - case XK_Num_Lock: - m_numLockMask = (1 << i); - return KeyModifierNumLock; - - case XK_Scroll_Lock: - m_scrollLockMask = (1 << i); - return KeyModifierScrollLock; - - default: - return 0; - } - } + delete[] allKeysyms; } +#if HAVE_XKB_EXTENSION void -CXWindowsKeyState::updateModifiers() +CXWindowsKeyState::updateKeysymMapXKB(CKeyMap& keyMap) { - struct CHandedModifiers { - KeySym m_left; - KeySym m_right; - }; - static const CHandedModifiers s_handedModifiers[] = { - { XK_Shift_L, XK_Shift_R }, - { XK_Control_L, XK_Control_R }, - { XK_Meta_L, XK_Meta_R }, - { XK_Alt_L, XK_Alt_R }, - { XK_Super_L, XK_Super_R }, - { XK_Hyper_L, XK_Hyper_R } - }; - struct CModifierBitInfo { - public: - KeySym CXWindowsKeyState::*m_keysym; - KeySym m_left; - KeySym m_right; - }; - static const CModifierBitInfo s_modifierBitTable[] = { - { &CXWindowsKeyState::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, - { &CXWindowsKeyState::m_modeSwitchKeysym, XK_ISO_Level3_Shift, NoSymbol }, + static const XkbKTMapEntryRec defMapEntry = { + True, // active + 0, // level + { + 0, // mods.mask + 0, // mods.real_mods + 0 // mods.vmods + } }; - // for any modifier with left/right versions that has one side but - // not the other mapped, map the missing side to the existing side. - // this will map, for example, Alt_R to Alt_L if Alt_L is mapped - // but Alt_R isn't. this is almost always what the user wants - // since the user almost never cares about the difference between - // Alt_L and Alt_R. - for (size_t i = 0; i < sizeof(s_handedModifiers) / - sizeof(s_handedModifiers[0]); ++i) { - KeySymIndex lIndex = m_keysymMap.find(s_handedModifiers[i].m_left); - KeySymIndex rIndex = m_keysymMap.find(s_handedModifiers[i].m_right); - if (lIndex == m_keysymMap.end() && rIndex != m_keysymMap.end()) { - m_keysymMap[s_handedModifiers[i].m_left] = rIndex->second; - } - else if (lIndex != m_keysymMap.end() && rIndex == m_keysymMap.end()) { - m_keysymMap[s_handedModifiers[i].m_right] = lIndex->second; + LOG((CLOG_DEBUG1 "XKB mapping")); + + // find the number of groups + int maxNumGroups = 0; + for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { + int numGroups = XkbKeyNumGroups(m_xkb, static_cast(i)); + if (numGroups > maxNumGroups) { + maxNumGroups = numGroups; } } - // choose the keysym to use for some modifiers. if a modifier has - // both left and right versions then (arbitrarily) prefer the left. - for (size_t i = 0; i < sizeof(s_modifierBitTable) / - sizeof(s_modifierBitTable[0]); ++i) { - const CModifierBitInfo& info = s_modifierBitTable[i]; + // prepare map from X modifier to KeyModifierMask + std::vector modifierLevel(maxNumGroups * 8, 4); + m_modifierFromX.clear(); + m_modifierFromX.resize(maxNumGroups * 8); + m_modifierToX.clear(); - // 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); - } + // prepare map from KeyID to KeyCode + m_keyCodeFromKey.clear(); - // save modifier info - if (keyIndex != m_keysymMap.end() && - keyIndex->second.m_modifierMask != 0) { - this->*(info.m_keysym) = keyIndex->first; - } + // Hack to deal with VMware. When a VMware client grabs input the + // player clears out the X modifier map for whatever reason. We're + // notified of the change and arrive here to discover that there + // are no modifiers at all. Since this prevents the modifiers from + // working in the VMware client we'll use the last known good set + // of modifiers when there are no modifiers. If there are modifiers + // we update the last known good set. + bool useLastGoodModifiers = !hasModifiersXKB(); + if (!useLastGoodModifiers) { + m_lastGoodXKBModifiers.clear(); } - // 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; + // check every button. on this pass we save all modifiers as native + // X modifier masks. + CKeyMap::KeyItem item; + for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { + KeyCode keycode = static_cast(i); + item.m_button = static_cast(keycode); + item.m_client = 0; - // 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; + // skip keys with no groups (they generate no symbols) + if (XkbKeyNumGroups(m_xkb, keycode) == 0) { + continue; + } + + // note half-duplex keys + const XkbBehavior& b = m_xkb->server->behaviors[keycode]; + if ((b.type & XkbKB_OpMask) == XkbKB_Lock) { + keyMap.addHalfDuplexButton(item.m_button); + } + + // iterate over all groups + for (int group = 0; group < maxNumGroups; ++group) { + item.m_group = group; + int eGroup = getEffectiveGroup(keycode, group); + + // get key info + XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, eGroup); + + // set modifiers the item is sensitive to + item.m_sensitive = type->mods.mask; + + // iterate over all shift levels for the button (including none) + for (int j = -1; j < type->map_count; ++j) { + const XkbKTMapEntryRec* mapEntry = + ((j == -1) ? &defMapEntry : type->map + j); + if (!mapEntry->active) { + continue; + } + int level = mapEntry->level; + + // set required modifiers for this item + item.m_required = mapEntry->mods.mask; + if ((item.m_required & LockMask) != 0 && + j != -1 && type->preserve != NULL && + (type->preserve[j].mask & LockMask) != 0) { + // sensitive caps lock and we preserve caps-lock. + // preserving caps-lock means we Xlib functions would + // yield the capitialized KeySym so we'll adjust the + // level accordingly. + if ((level ^ 1) < type->num_levels) { + level ^= 1; + } + } + + // get the keysym for this item + KeySym keysym = XkbKeySymEntry(m_xkb, keycode, level, eGroup); + + // check for group change actions, locking modifiers, and + // modifier masks. + item.m_lock = false; + bool isModifier = false; + UInt32 modifierMask = m_xkb->map->modmap[keycode]; + if (XkbKeyHasActions(m_xkb, keycode)) { + XkbAction* action = + XkbKeyActionEntry(m_xkb, keycode, level, eGroup); + if (action->type == XkbSA_SetMods || + action->type == XkbSA_LockMods) { + isModifier = true; + + // note toggles + item.m_lock = (action->type == XkbSA_LockMods); + + // maybe use action's mask + if ((action->mods.flags & XkbSA_UseModMapMods) == 0) { + modifierMask = action->mods.mask; + } + } + else if (action->type == XkbSA_SetGroup || + action->type == XkbSA_LatchGroup || + action->type == XkbSA_LockGroup) { + // ignore group change key + continue; + } + } + level = mapEntry->level; + + // VMware modifier hack + if (useLastGoodModifiers) { + XKBModifierMap::const_iterator k = + m_lastGoodXKBModifiers.find(eGroup * 256 + keycode); + if (k != m_lastGoodXKBModifiers.end()) { + // Use last known good modifier + isModifier = true; + level = k->second.m_level; + modifierMask = k->second.m_mask; + item.m_lock = k->second.m_lock; + } + } + else if (isModifier) { + // Save known good modifier + XKBModifierInfo& info = + m_lastGoodXKBModifiers[eGroup * 256 + keycode]; + info.m_level = level; + info.m_mask = modifierMask; + info.m_lock = item.m_lock; + } + + // record the modifier mask for this key. don't bother + // for keys that change the group. + item.m_generates = 0; + UInt32 modifierBit = + CXWindowsUtil::getModifierBitForKeySym(keysym); + if (isModifier && modifierBit != kKeyModifierBitNone) { + item.m_generates = (1u << modifierBit); + for (SInt32 j = 0; j < 8; ++j) { + // skip modifiers this key doesn't generate + if ((modifierMask & (1u << j)) == 0) { + continue; + } + + // skip keys that map to a modifier that we've + // already seen using fewer modifiers. that is + // if this key must combine with other modifiers + // and we know of a key that combines with fewer + // modifiers (or no modifiers) then prefer the + // other key. + if (level >= modifierLevel[8 * group + j]) { + continue; + } + modifierLevel[8 * group + j] = level; + + // save modifier + m_modifierFromX[8 * group + j] |= (1u << modifierBit); + m_modifierToX.insert(std::make_pair( + 1u << modifierBit, 1u << j)); + } + } + + // handle special cases of just one keysym for the keycode + if (type->num_levels == 1) { + // if there are upper- and lowercase versions of the + // keysym then add both. + KeySym lKeysym, uKeysym; + XConvertCase(keysym, &lKeysym, &uKeysym); + if (lKeysym != uKeysym) { + if (j != -1) { + continue; + } + + item.m_sensitive |= ShiftMask | LockMask; + + KeyID lKeyID = CXWindowsUtil::mapKeySymToKeyID(lKeysym); + KeyID uKeyID = CXWindowsUtil::mapKeySymToKeyID(uKeysym); + if (lKeyID == kKeyNone || uKeyID == kKeyNone) { + continue; + } + + item.m_id = lKeyID; + item.m_required = 0; + keyMap.addKeyEntry(item); + + item.m_id = uKeyID; + item.m_required = ShiftMask; + keyMap.addKeyEntry(item); + item.m_required = LockMask; + keyMap.addKeyEntry(item); + + if (group == 0) { + m_keyCodeFromKey.insert( + std::make_pair(lKeyID, keycode)); + m_keyCodeFromKey.insert( + std::make_pair(uKeyID, keycode)); + } + continue; + } + } + + // add entry + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysym); + keyMap.addKeyEntry(item); + if (group == 0) { + m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode)); + } } } } + + // change all modifier masks to synergy masks from X masks + keyMap.foreachKey(&CXWindowsKeyState::remapKeyModifiers, this); + + // allow composition across groups + keyMap.allowGroupSwitchDuringCompose(); } - -KeySym -CXWindowsKeyState::keyIDToKeySym(KeyID id, KeyModifierMask mask) const -{ - // convert id to keysym - KeySym keysym = NoSymbol; - if ((id & 0xfffff000) == 0xe000) { - // special character - switch (id & 0x0000ff00) { -#if 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 & KeyModifierShift) != 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; -} - -KeyButton -CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, - KeySymIndex keyIndex, bool isAutoRepeat, - bool pressAndRelease) const +void +CXWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group, + CKeyMap::KeyItem& item, void* vself) { - // keyIndex must be valid - assert(keyIndex != m_keysymMap.end()); - - KeyModifierMask currentMask = getActiveModifiers(); - - // 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 - KeyButton 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)); - - // if this for auto-repeat and this key does not auto-repeat - // then return 0. - if (isAutoRepeat && - (m_keyControl.auto_repeats[keycode >> 3] & - static_cast(1 << (keycode & 7))) == 0) { - LOG((CLOG_DEBUG2 "non-autorepeating")); - return 0; - } - - // 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. - KeyModifierMask desiredMask = currentMask; - if (keyIndex->second.m_modifierMask != KeyModifierShift) { - if (keyIndex->second.m_shiftSensitive[bestIndex]) { - if ((bestIndex & 1) != 0) { - desiredMask |= KeyModifierShift; - } - else { - desiredMask &= ~KeyModifierShift; - } - } - if (keyIndex->second.m_modifierMask != KeyModifierModeSwitch) { - if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { - if ((bestIndex & 2) != 0) { - desiredMask |= KeyModifierModeSwitch; - } - else { - desiredMask &= ~KeyModifierModeSwitch; - } - } - } - } - - // adjust the modifiers to match the desired modifiers - Keystrokes undo; - if (!adjustModifiers(keys, undo, desiredMask)) { - LOG((CLOG_DEBUG2 "failed to adjust modifiers")); - return 0; - } - - // add the key event - Keystroke keystroke; - keystroke.m_key = keycode; - if (pressAndRelease) { - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - } - else if (!isAutoRepeat) { - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - else { - keystroke.m_press = false; - keystroke.m_repeat = true; - keys.push_back(keystroke); - keystroke.m_press = true; - keys.push_back(keystroke); - } - - // put undo keystrokes at end of keystrokes in reverse order - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - return keycode; -} - -KeyButton -CXWindowsKeyState::mapDecompositionToKeystrokes( - Keystrokes& keys, KeySym keysym, bool usingDeadKeys) const -{ - // decompose the keysym - CXWindowsUtil::KeySyms decomposed; - if (usingDeadKeys) { - if (!CXWindowsUtil::decomposeKeySymWithDeadKeys(keysym, decomposed)) { - // no decomposition - return 0; - } - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using dead keys", keysym, decomposed.size())); - } - else { - if (!CXWindowsUtil::decomposeKeySymWithCompose(keysym, decomposed)) { - // no decomposition - return 0; - } - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using compose key", keysym, decomposed.size())); - } - size_t n = decomposed.size(); - if (n == 0) { - // nothing in the decomposition - return 0; - } - - // map to keystrokes - Keystrokes keystrokes; - KeyButton keycode = 0; - for (size_t i = 0; i < n; ++i) { - // lookup the key - keysym = decomposed[i]; - KeySymIndex keyIndex = m_keysymMap.find(keysym); - if (keyIndex == m_keysymMap.end()) { - // missing a required keysym - LOG((CLOG_DEBUG2 "can't map keysym %d: 0x%04x", i, keysym)); - return 0; - } - - // the keysym is mapped to some keycode. add press and - // release unless this is the last key and usingDeadKeys. - keycode = mapToKeystrokes(keystrokes, keyIndex, - false, (i + 1 < n || !usingDeadKeys)); - if (keycode == 0) { - return 0; - } - } - - // copy keystrokes - keys.insert(keys.end(), keystrokes.begin(), keystrokes.end()); - - return keycode; -} - -unsigned int -CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex, - KeyModifierMask /*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 an auto-repeat then we don't call this - // method since we just need to synthesize a key repeat on the - // same keycode that we pressed. - // FIXME -- 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; + CXWindowsKeyState* self = reinterpret_cast(vself); + item.m_required = + self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group)); + item.m_sensitive = + self->mapModifiersFromX(XkbBuildCoreState(item.m_sensitive, group)); } bool -CXWindowsKeyState::isShiftInverted(KeySymIndex keyIndex, - KeyModifierMask currentMask) const +CXWindowsKeyState::hasModifiersXKB() 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 ((currentMask & KeyModifierNumLock) != 0) { - return true; +#if HAVE_XKB_EXTENSION + // iterate over all keycodes + for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { + KeyCode keycode = static_cast(i); + if (XkbKeyHasActions(m_xkb, keycode)) { + // iterate over all groups + int numGroups = XkbKeyNumGroups(m_xkb, keycode); + for (int group = 0; group < numGroups; ++group) { + // iterate over all shift levels for the button (including none) + XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, group); + for (int j = -1; j < type->map_count; ++j) { + if (j != -1 && !type->map[j].active) { + continue; + } + int level = ((j == -1) ? 0 : type->map[j].level); + XkbAction* action = + XkbKeyActionEntry(m_xkb, keycode, level, group); + if (action->type == XkbSA_SetMods || + action->type == XkbSA_LockMods) { + 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 ((currentMask & KeyModifierCapsLock) != 0) { - return true; - } - } - +#endif return false; } -bool -CXWindowsKeyState::adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask) const +int +CXWindowsKeyState::getEffectiveGroup(KeyCode keycode, int group) const { - KeyModifierMask currentMask = getActiveModifiers(); + (void)keycode; +#if HAVE_XKB_EXTENSION + // get effective group for key + int numGroups = XkbKeyNumGroups(m_xkb, keycode); + if (group >= numGroups) { + unsigned char groupInfo = XkbKeyGroupInfo(m_xkb, keycode); + switch (XkbOutOfRangeGroupAction(groupInfo)) { + case XkbClampIntoRange: + group = numGroups - 1; + break; - // 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. - bool forceShift = false; - bool wantShift = ((desiredMask & KeyModifierShift) != 0); - bool wantModeSwitch = ((desiredMask & KeyModifierModeSwitch) != 0); - bool haveModeSwitch = ((currentMask & KeyModifierModeSwitch) != 0); - if (wantModeSwitch != haveModeSwitch) { - LOG((CLOG_DEBUG2 "fix mode switch")); - - // adjust shift if necessary (i.e. turn it off it's on and mode - // shift is sensitive to the shift key) - KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); - assert(modeSwitchIndex != m_keysymMap.end()); - if (modeSwitchIndex->second.m_shiftSensitive[0]) { - bool haveShift = ((currentMask & KeyModifierShift) != 0); - if (haveShift) { - // add shift keystrokes - LOG((CLOG_DEBUG2 "fix shift for mode switch")); - if (!mapModifier(keys, undo, KeyModifierShift, false, true)) { - return false; - } - - // our local concept of shift has flipped - currentMask ^= KeyModifierShift; - - // force shift to get turned on below if we had to turn - // off here and shift is desired. if we didn't force it - // then mapModifier would think shift is already down - // and ignore the request. - forceShift = wantShift; + case XkbRedirectIntoRange: + group = XkbOutOfRangeGroupNumber(groupInfo); + if (group >= numGroups) { + group = 0; } - } + break; - // add mode switch keystrokes - if (!mapModifier(keys, undo, KeyModifierModeSwitch, wantModeSwitch)) { - return false; + default: + // wrap + group %= numGroups; + break; } - currentMask ^= KeyModifierModeSwitch; } +#endif + return group; +} - // get shift set correctly - bool haveShift = ((currentMask & KeyModifierShift) != 0); - if (wantShift != haveShift) { - // add shift keystrokes - LOG((CLOG_DEBUG2 "fix shift")); - if (!mapModifier(keys, undo, KeyModifierShift, wantShift, forceShift)) { - return false; - } - currentMask ^= KeyModifierShift; +UInt32 +CXWindowsKeyState::getGroupFromState(unsigned int state) const +{ +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + return XkbGroupForCoreState(state); } - - return true; -} - -bool -CXWindowsKeyState::isNumLockSensitive(KeySym keysym) const -{ - return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); -} - -bool -CXWindowsKeyState::isCapsLockSensitive(KeySym keysym) const -{ - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - return (lKey != uKey); -} - - -// -// CXWindowsKeyState::KeyMapping -// - -CXWindowsKeyState::KeyMapping::KeyMapping() -{ - m_keycode[0] = 0; - m_keycode[1] = 0; - m_keycode[2] = 0; - m_keycode[3] = 0; +#endif + return 0; } diff --git a/lib/platform/CXWindowsKeyState.h b/lib/platform/CXWindowsKeyState.h index bb660d48..02ed4520 100644 --- a/lib/platform/CXWindowsKeyState.h +++ b/lib/platform/CXWindowsKeyState.h @@ -17,6 +17,7 @@ #include "CKeyState.h" #include "stdmap.h" +#include "stdvector.h" #if X_DISPLAY_MISSING # error X11 is required to build synergy #else @@ -26,6 +27,9 @@ # else # error The XTest extension is required to build synergy # endif +# if HAVE_XKB_EXTENSION +# include +# endif #endif //! X Windows key state @@ -34,9 +38,35 @@ A key state for X Windows. */ class CXWindowsKeyState : public CKeyState { public: - CXWindowsKeyState(Display*); + typedef std::vector CKeycodeList; + enum { + kGroupPoll = -1, + kGroupPollAndSet = -2 + }; + + CXWindowsKeyState(Display*, bool useXKB); ~CXWindowsKeyState(); + //! @name modifiers + //@{ + + //! Set active group + /*! + Sets the active group to \p group. This is the group returned by + \c pollActiveGroup(). If \p group is \c kGroupPoll then + \c pollActiveGroup() will really poll, but that's a slow operation + on X11. If \p group is \c kGroupPollAndSet then this will poll the + active group now and use it for future calls to \c pollActiveGroup(). + */ + void setActiveGroup(SInt32 group); + + //! Set the auto-repeat state + /*! + Sets the auto-repeat state. + */ + void setAutoRepeat(const XKeyboardState&); + + //@} //! @name accessors //@{ @@ -47,106 +77,79 @@ public: */ KeyModifierMask mapModifiersFromX(unsigned int state) const; + //! Convert synergy modifier mask to X mask + /*! + Converts the synergy modifier mask to the corresponding X modifier + mask. Returns \c true if successful and \c false if any modifier + could not be converted. + */ + bool mapModifiersToX(KeyModifierMask, unsigned int&) const; + + //! Convert synergy key to all corresponding X keycodes + /*! + Converts the synergy key \p key to all of the keycodes that map to + that key. + */ + void mapKeyToKeycodes(KeyID key, + CKeycodeList& keycodes) const; + //@} // IKeyState overrides virtual bool fakeCtrlAltDel(); - virtual const char* getKeyName(KeyButton) const; + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; protected: - // IKeyState overrides - virtual void doUpdateKeys(); - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat); - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + // CKeyState overrides + virtual void getKeyMap(CKeyMap& keyMap); + virtual void fakeKey(const Keystroke& keystroke); private: - class KeyMapping { + void updateKeysymMap(CKeyMap&); + void updateKeysymMapXKB(CKeyMap&); + bool hasModifiersXKB() const; + int getEffectiveGroup(KeyCode, int group) const; + UInt32 getGroupFromState(unsigned int state) const; + + static void remapKeyModifiers(KeyID, SInt32, + CKeyMap::KeyItem&, void*); + +private: + struct XKBModifierInfo { 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 - KeyModifierMask m_modifierMask; - - // whether keysym is sensitive to caps and num lock - bool m_numLockSensitive; - bool m_capsLockSensitive; + unsigned char m_level; + UInt32 m_mask; + bool m_lock; }; - typedef std::map KeySymMap; - typedef KeySymMap::const_iterator KeySymIndex; - // save the current keyboard mapping and note the modifiers - void updateKeysymMap(); + typedef std::vector KeyModifierMaskList; + typedef std::map KeyModifierToXMask; + typedef std::multimap KeyToKeyCodeMap; + typedef std::map NonXKBModifierMap; + typedef std::map XKBModifierMap; - // note interesting modifier KeySyms - void updateModifiers(); - - // map a modifier index and its KeySym to a modifier mask. also - // save the modifier mask in one of m_*Mask. - KeyModifierMask mapToModifierMask(unsigned int, KeySym); - - // convert a KeyID to a KeySym - KeySym keyIDToKeySym(KeyID id, KeyModifierMask mask) const; - - // map a KeySym into the keystrokes to produce it - KeyButton mapToKeystrokes(Keystrokes& keys, - KeySymIndex keyIndex, - bool isAutoRepeat, - bool pressAndRelease) const; - - // map a decomposition into keystrokes to produce it. returns the - // last key added to keys iff successful and 0 otherwise. - KeyButton mapDecompositionToKeystrokes(Keystrokes& keys, - KeySym keysym, bool usingDeadKeys) const; - - // choose the best set of modifiers to generate the KeySym - unsigned int findBestKeyIndex(KeySymIndex keyIndex, - KeyModifierMask currentMask) const; - - // returns true if the sense of shift is inverted for KeySym - bool isShiftInverted(KeySymIndex keyIndex, - KeyModifierMask currentMask) const; - - // returns the keystrokes to adjust the modifiers into the desired - // state the keystrokes to get back to the current state. - bool adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask) const; - - // returns true if keysym is sensitive to the NumLock state - bool isNumLockSensitive(KeySym keysym) const; - - // returns true if keysym is sensitive to the CapsLock state - bool isCapsLockSensitive(KeySym keysym) const; - -private: Display* m_display; +#if HAVE_XKB_EXTENSION + XkbDescPtr m_xkb; +#endif + SInt32 m_group; + XKBModifierMap m_lastGoodXKBModifiers; + NonXKBModifierMap m_lastGoodNonXKBModifiers; - // keysym to keycode mapping - KeySymMap m_keysymMap; + // X modifier (bit number) to synergy modifier (mask) mapping + KeyModifierMaskList m_modifierFromX; - // the keyboard control state the last time this screen was entered - XKeyboardState m_keyControl; + // synergy modifier (mask) to X modifier (mask) + KeyModifierToXMask m_modifierToX; - // modifier keysyms - KeySym m_modeSwitchKeysym; + // map KeyID to all keycodes that can synthesize that KeyID + KeyToKeyCodeMap m_keyCodeFromKey; - // 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_scrollLockMask; + // autorepeat state + XKeyboardState m_keyboardState; }; #endif diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index ad62425b..9c4ab134 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -19,6 +19,7 @@ #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" +#include "CKeyMap.h" #include "XScreen.h" #include "CLog.h" #include "CStopwatch.h" @@ -45,47 +46,12 @@ # include } # endif +# if HAVE_XKB_EXTENSION +# include +# endif #endif #include "CArch.h" -// 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, kKeyEject, 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 -}; - // // CXWindowsScreen @@ -123,7 +89,8 @@ CXWindowsScreen::CXWindowsScreen(const char* displayName, bool isPrimary) : m_sequenceNumber(0), m_screensaver(NULL), m_screensaverNotify(false), - m_xtestIsXineramaUnaware(true) + m_xtestIsXineramaUnaware(true), + m_xkb(false) { assert(s_screen == NULL); @@ -139,7 +106,7 @@ CXWindowsScreen::CXWindowsScreen(const char* displayName, bool isPrimary) : m_window = openWindow(); m_screensaver = new CXWindowsScreenSaver(m_display, m_window, getEventTarget()); - m_keyState = new CXWindowsKeyState(m_display); + m_keyState = new CXWindowsKeyState(m_display, m_xkb); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : "")); LOG((CLOG_DEBUG "window is 0x%08x", m_window)); } @@ -215,6 +182,7 @@ CXWindowsScreen::enable() XKeyboardState keyControl; XGetKeyboardControl(m_display, &keyControl); m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn); + m_keyState->setAutoRepeat(keyControl); // move hider window under the cursor center XMoveWindow(m_display, m_window, m_xCenter, m_yCenter); @@ -226,8 +194,6 @@ CXWindowsScreen::enable() // warp the mouse to the cursor center fakeMouseMove(m_xCenter, m_yCenter); } - - updateKeys(); } void @@ -256,10 +222,6 @@ CXWindowsScreen::enter() XUnsetICFocus(m_ic); } - // unmap the hider/grab window. this also ungrabs the mouse and - // keyboard if they're grabbed. - XUnmapWindow(m_display, m_window); - // set the input focus to what it had been when we took it if (m_lastFocus != None) { // the window may not exist anymore so ignore errors @@ -267,6 +229,10 @@ CXWindowsScreen::enter() XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime); } + // unmap the hider/grab window. this also ungrabs the mouse and + // keyboard if they're grabbed. + XUnmapWindow(m_display, m_window); + /* maybe call this if entering for the screensaver // set keyboard focus to root window. the screensaver should then // pick up key events for when the user enters a password to unlock. @@ -278,6 +244,7 @@ CXWindowsScreen::enter() XKeyboardState keyControl; XGetKeyboardControl(m_display, &keyControl); m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn); + m_keyState->setAutoRepeat(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 @@ -511,6 +478,245 @@ CXWindowsScreen::warpCursor(SInt32 x, SInt32 y) m_yCursor = y; } +UInt32 +CXWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) +{ + // only allow certain modifiers + if ((mask & ~(KeyModifierShift | KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) != 0) { + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // fail if no keys + if (key == kKeyNone && mask == 0) { + return 0; + } + + // convert to X + unsigned int modifiers; + if (!m_keyState->mapModifiersToX(mask, modifiers)) { + // can't map all modifiers + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + CXWindowsKeyState::CKeycodeList keycodes; + m_keyState->mapKeyToKeycodes(key, keycodes); + if (key != kKeyNone && keycodes.empty()) { + // can't map key + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // choose hotkey id + UInt32 id; + if (!m_oldHotKeyIDs.empty()) { + id = m_oldHotKeyIDs.back(); + m_oldHotKeyIDs.pop_back(); + } + else { + id = m_hotKeys.size() + 1; + } + HotKeyList& hotKeys = m_hotKeys[id]; + + // all modifier hotkey must be treated specially. for each modifier + // we need to grab the modifier key in combination with all the other + // requested modifiers. + bool err = false; + { + CXWindowsUtil::CErrorLock lock(m_display, &err); + if (key == kKeyNone) { + static const KeyModifierMask s_hotKeyModifiers[] = { + KeyModifierShift, + KeyModifierControl, + KeyModifierAlt, + KeyModifierMeta, + KeyModifierSuper + }; + + XModifierKeymap* modKeymap = XGetModifierMapping(m_display); + for (size_t j = 0; j < sizeof(s_hotKeyModifiers) / + sizeof(s_hotKeyModifiers[0]) && !err; ++j) { + // skip modifier if not in mask + if ((mask & s_hotKeyModifiers[j]) == 0) { + continue; + } + + // skip with error if we can't map remaining modifiers + unsigned int modifiers2; + KeyModifierMask mask2 = (mask & ~s_hotKeyModifiers[j]); + if (!m_keyState->mapModifiersToX(mask2, modifiers2)) { + err = true; + continue; + } + + // compute modifier index for modifier. there should be + // exactly one X modifier missing + int index; + switch (modifiers ^ modifiers2) { + case ShiftMask: + index = ShiftMapIndex; + break; + + case LockMask: + index = LockMapIndex; + break; + + case ControlMask: + index = ControlMapIndex; + break; + + case Mod1Mask: + index = Mod1MapIndex; + break; + + case Mod2Mask: + index = Mod2MapIndex; + break; + + case Mod3Mask: + index = Mod3MapIndex; + break; + + case Mod4Mask: + index = Mod4MapIndex; + break; + + case Mod5Mask: + index = Mod5MapIndex; + break; + + default: + err = true; + continue; + } + + // grab each key for the modifier + const KeyCode* modifiermap = + modKeymap->modifiermap + index * modKeymap->max_keypermod; + for (int k = 0; k < modKeymap->max_keypermod && !err; ++k) { + KeyCode code = modifiermap[k]; + if (modifiermap[k] != 0) { + XGrabKey(m_display, code, modifiers2, m_root, + False, GrabModeAsync, GrabModeAsync); + if (!err) { + hotKeys.push_back(std::make_pair(code, modifiers2)); + m_hotKeyToIDMap[CHotKeyItem(code, modifiers2)] = id; + } + } + } + } + XFreeModifiermap(modKeymap); + } + + // a non-modifier key must be insensitive to CapsLock, NumLock and + // ScrollLock, so we have to grab the key with every combination of + // those. + else { + // collect available toggle modifiers + unsigned int modifier; + unsigned int toggleModifiers[3]; + size_t numToggleModifiers = 0; + if (m_keyState->mapModifiersToX(KeyModifierCapsLock, modifier)) { + toggleModifiers[numToggleModifiers++] = modifier; + } + if (m_keyState->mapModifiersToX(KeyModifierNumLock, modifier)) { + toggleModifiers[numToggleModifiers++] = modifier; + } + if (m_keyState->mapModifiersToX(KeyModifierScrollLock, modifier)) { + toggleModifiers[numToggleModifiers++] = modifier; + } + + + for (CXWindowsKeyState::CKeycodeList::iterator j = keycodes.begin(); + j != keycodes.end() && !err; ++j) { + for (size_t i = 0; i < (1u << numToggleModifiers); ++i) { + // add toggle modifiers for index i + unsigned int tmpModifiers = modifiers; + if ((i & 1) != 0) { + tmpModifiers |= toggleModifiers[0]; + } + if ((i & 2) != 0) { + tmpModifiers |= toggleModifiers[1]; + } + if ((i & 4) != 0) { + tmpModifiers |= toggleModifiers[2]; + } + + // add grab + XGrabKey(m_display, *j, tmpModifiers, m_root, + False, GrabModeAsync, GrabModeAsync); + if (!err) { + hotKeys.push_back(std::make_pair(*j, tmpModifiers)); + m_hotKeyToIDMap[CHotKeyItem(*j, tmpModifiers)] = id; + } + } + } + } + } + + if (err) { + // if any failed then unregister any we did get + for (HotKeyList::iterator j = hotKeys.begin(); + j != hotKeys.end(); ++j) { + XUngrabKey(m_display, j->first, j->second, m_root); + m_hotKeyToIDMap.erase(CHotKeyItem(j->first, j->second)); + } + + m_oldHotKeyIDs.push_back(id); + m_hotKeys.erase(id); + LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask)); + return 0; + } + + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id)); + return id; +} + +void +CXWindowsScreen::unregisterHotKey(UInt32 id) +{ + // look up hotkey + HotKeyMap::iterator i = m_hotKeys.find(id); + if (i == m_hotKeys.end()) { + return; + } + + // unregister with OS + bool err = false; + { + CXWindowsUtil::CErrorLock lock(m_display, &err); + HotKeyList& hotKeys = i->second; + for (HotKeyList::iterator j = hotKeys.begin(); + j != hotKeys.end(); ++j) { + XUngrabKey(m_display, j->first, j->second, m_root); + m_hotKeyToIDMap.erase(CHotKeyItem(j->first, j->second)); + } + } + if (err) { + LOG((CLOG_WARN "failed to unregister hotkey id=%d", id)); + } + else { + LOG((CLOG_DEBUG "unregistered hotkey id=%d", id)); + } + + // discard hot key from map and record old id for reuse + m_hotKeys.erase(i); + m_oldHotKeyIDs.push_back(id); +} + +void +CXWindowsScreen::fakeInputBegin() +{ + // FIXME -- not implemented +} + +void +CXWindowsScreen::fakeInputEnd() +{ + // FIXME -- not implemented +} + SInt32 CXWindowsScreen::getJumpZoneSize() const { @@ -578,17 +784,22 @@ CXWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -CXWindowsScreen::fakeMouseWheel(SInt32 delta) const +CXWindowsScreen::fakeMouseWheel(SInt32, SInt32 yDelta) const { + // XXX -- support x-axis scrolling + if (yDelta == 0) { + return; + } + // choose button depending on rotation direction const unsigned int xButton = mapButtonToX(static_cast( - (delta >= 0) ? -1 : -2)); + (yDelta >= 0) ? -1 : -2)); if (xButton == 0) { // If we get here, then the XServer does not support the scroll // wheel buttons, so send PageUp/PageDown keystrokes instead. // Patch by Tom Chadwick. KeyCode keycode = 0; - if (delta >= 0) { + if (yDelta >= 0) { keycode = XKeysymToKeycode(m_display, XK_Page_Up); } else { @@ -602,12 +813,12 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const } // now use absolute value of delta - if (delta < 0) { - delta = -delta; + if (yDelta < 0) { + yDelta = -yDelta; } // send as many clicks as necessary - for (; delta >= 120; delta -= 120) { + for (; yDelta >= 120; yDelta -= 120) { XTestFakeButtonEvent(m_display, xButton, True, CurrentTime); XTestFakeButtonEvent(m_display, xButton, False, CurrentTime); } @@ -615,7 +826,7 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const } Display* -CXWindowsScreen::openDisplay(const char* displayName) const +CXWindowsScreen::openDisplay(const char* displayName) { // get the DISPLAY if (displayName == NULL) { @@ -643,6 +854,25 @@ CXWindowsScreen::openDisplay(const char* displayName) const } } +#if HAVE_XKB_EXTENSION + { + m_xkb = false; + int major = XkbMajorVersion, minor = XkbMinorVersion; + if (XkbLibraryVersion(&major, &minor)) { + int opcode, firstError; + if (XkbQueryExtension(display, &opcode, &m_xkbEventBase, + &firstError, &major, &minor)) { + m_xkb = true; + XkbSelectEvents(display, XkbUseCoreKbd, + XkbMapNotifyMask, XkbMapNotifyMask); + XkbSelectEventDetails(display, XkbUseCoreKbd, + XkbStateNotifyMask, + XkbGroupStateMask, XkbGroupStateMask); + } + } + } +#endif + return display; } @@ -864,10 +1094,22 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) } if (xevent->type == KeyPress || xevent->type == KeyRelease) { - if (!isRepeat) { - m_keyState->setKeyDown(xevent->xkey.keycode, - xevent->type == KeyPress); + if (xevent->xkey.window == m_root) { + // this is a hot key + onHotKey(xevent->xkey, isRepeat); + return; } + else if (!m_isOnScreen) { + // this might be a hot key + if (onHotKey(xevent->xkey, isRepeat)) { + return; + } + } + + bool down = (isRepeat || xevent->type == KeyPress); + KeyModifierMask state = + m_keyState->mapModifiersFromX(xevent->xkey.state); + m_keyState->onKey(xevent->xkey.keycode, down, state); } } @@ -924,19 +1166,7 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) break; case MappingNotify: - 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; - } - } - - // keyboard mapping changed - XRefreshKeyboardMapping(&xevent->xmapping); - m_keyState->updateKeys(); + refreshKeyboard(xevent); break; case LeaveNotify: @@ -1036,6 +1266,21 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) return; default: +#if HAVE_XKB_EXTENSION + if (m_xkb && xevent->type == m_xkbEventBase) { + XkbEvent* xkbEvent = reinterpret_cast(xevent); + switch (xkbEvent->any.xkb_type) { + case XkbMapNotify: + refreshKeyboard(xevent); + return; + + case XkbStateNotify: + LOG((CLOG_INFO "group change: %d", xkbEvent->state.group)); + m_keyState->setActiveGroup((SInt32)xkbEvent->state.group); + return; + } + } +#endif break; } } @@ -1116,13 +1361,44 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat) } } +bool +CXWindowsScreen::onHotKey(XKeyEvent& xkey, bool isRepeat) +{ + // find the hot key id + HotKeyToIDMap::const_iterator i = + m_hotKeyToIDMap.find(CHotKeyItem(xkey.keycode, xkey.state)); + if (i == m_hotKeyToIDMap.end()) { + return false; + } + + // find what kind of event + CEvent::Type type; + if (xkey.type == KeyPress) { + type = getHotKeyDownEvent(); + } + else if (xkey.type == KeyRelease) { + type = getHotKeyUpEvent(); + } + else { + return false; + } + + // generate event (ignore key repeats) + if (!isRepeat) { + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(i->second))); + } + return true; +} + void CXWindowsScreen::onMousePress(const XButtonEvent& xbutton) { LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button)); - const ButtonID button = mapButtonFromX(&xbutton); + ButtonID button = mapButtonFromX(&xbutton); + KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button, mask)); } } @@ -1130,18 +1406,20 @@ void CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton) { LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button)); - const ButtonID button = mapButtonFromX(&xbutton); + ButtonID button = mapButtonFromX(&xbutton); + KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button, mask)); } else if (xbutton.button == 4) { // wheel forward (away from user) - sendEvent(getWheelEvent(), CWheelInfo::alloc(120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(0, 120)); } else if (xbutton.button == 5) { // wheel backward (toward user) - sendEvent(getWheelEvent(), CWheelInfo::alloc(-120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(0, -120)); } + // XXX -- support x-axis scrolling } void @@ -1409,41 +1687,7 @@ CXWindowsScreen::mapKeyFromX(XKeyEvent* event) const } // 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; - } - if (keysym == XK_ISO_Level3_Shift) { - // treat ISO_Level3_Shift as ModeSwitch - return kKeyModeSwitch; - } - 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; - } - } + return CXWindowsUtil::mapKeySymToKeyID(keysym); } ButtonID @@ -1608,3 +1852,50 @@ CXWindowsScreen::grabMouseAndKeyboard() LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); return true; } + +void +CXWindowsScreen::refreshKeyboard(XEvent* event) +{ + if (XPending(m_display) > 0) { + XEvent tmpEvent; + XPeekEvent(m_display, &tmpEvent); + if (tmpEvent.type == MappingNotify) { + // discard this event since another follows. + // we tend to get a bunch of these in a row. + return; + } + } + + // keyboard mapping changed +#if HAVE_XKB_EXTENSION + if (m_xkb && event->type == m_xkbEventBase) { + XkbRefreshKeyboardMapping((XkbMapNotifyEvent*)event); + } + else +#else + { + XRefreshKeyboardMapping(&event->xmapping); + } +#endif + m_keyState->updateKeyMap(); + m_keyState->updateKeyState(); +} + + +// +// CXWindowsScreen::CHotKeyItem +// + +CXWindowsScreen::CHotKeyItem::CHotKeyItem(int keycode, unsigned int mask) : + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +bool +CXWindowsScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const +{ + return (m_keycode < x.m_keycode || + (m_keycode == x.m_keycode && m_mask < x.m_mask)); +} diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index cec6c6fa..99c45576 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -49,6 +49,10 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + virtual void unregisterHotKey(UInt32 id); + virtual void fakeInputBegin(); + virtual void fakeInputEnd(); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; @@ -57,7 +61,7 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; - virtual void fakeMouseWheel(SInt32 delta) const; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; // IPlatformScreen overrides virtual void enable(); @@ -112,7 +116,7 @@ private: KeyCode m_keycode; }; - Display* openDisplay(const char* displayName) const; + Display* openDisplay(const char* displayName); void saveShape(); Window openWindow() const; void openIM(); @@ -120,6 +124,7 @@ private: bool grabMouseAndKeyboard(); void onKeyPress(XKeyEvent&); void onKeyRelease(XKeyEvent&, bool isRepeat); + bool onHotKey(XKeyEvent&, bool isRepeat); void onMousePress(const XButtonEvent&); void onMouseRelease(const XButtonEvent&); void onMouseMove(const XMotionEvent&); @@ -133,10 +138,27 @@ private: void warpCursorNoFlush(SInt32 x, SInt32 y); + void refreshKeyboard(XEvent*); + static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: + struct CHotKeyItem { + public: + CHotKeyItem(int, unsigned int); + + bool operator<(const CHotKeyItem&) const; + + private: + int m_keycode; + unsigned int m_mask; + }; typedef std::set CFilteredKeycodes; + typedef std::vector > HotKeyList; + typedef std::map HotKeyMap; + typedef std::vector HotKeyIDList; + typedef std::map HotKeyToIDMap; + // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; @@ -158,6 +180,11 @@ private: // keyboard stuff CXWindowsKeyState* m_keyState; + // hot key stuff + HotKeyMap m_hotKeys; + HotKeyIDList m_oldHotKeyIDs; + HotKeyToIDMap m_hotKeyToIDMap; + // input focus stuff Window m_lastFocus; int m_lastFocusRevert; @@ -190,6 +217,10 @@ private: bool m_xtestIsXineramaUnaware; bool m_xinerama; + // XKB extension stuff + bool m_xkb; + int m_xkbEventBase; + // pointer to (singleton) screen. this is only needed by // ioErrorHandler(). static CXWindowsScreen* s_screen; diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index 6c8efbde..65af34e1 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -62,7 +62,8 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( m_dpms(false), m_disabled(false), m_suppressDisable(false), - m_disableTimer(NULL) + m_disableTimer(NULL), + m_disablePos(0) { // get atoms m_atomScreenSaver = XInternAtom(m_display, @@ -87,18 +88,18 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( #endif // watch top-level windows for changes + bool error = false; { - 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; - } + } + if (error) { + LOG((CLOG_DEBUG "didn't set root event mask")); + m_rootEventMask = 0; } // get the built-in settings @@ -346,9 +347,11 @@ CXWindowsScreenSaver::setXScreenSaver(Window window) // see if xscreensaver is active bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); XWindowAttributes attr; - XGetWindowAttributes(m_display, m_xscreensaver, &attr); + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + XGetWindowAttributes(m_display, m_xscreensaver, &attr); + } setXScreenSaverActive(!error && attr.map_state != IsUnmapped); // save current DPMS state; xscreensaver may have changed it. @@ -418,8 +421,10 @@ CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) 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); + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + } if (error) { findXScreenSaver(); } @@ -465,18 +470,23 @@ CXWindowsScreenSaver::clearWatchForXScreenSaver() void CXWindowsScreenSaver::addWatchXScreenSaver(Window window) { - bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); - // get window attributes + bool error = false; XWindowAttributes attr; - XGetWindowAttributes(m_display, window, &attr); + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + 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, + error = false; + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + 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)); @@ -509,9 +519,9 @@ CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*) event.xmotion.root = DefaultRootWindow(m_display); event.xmotion.subwindow = None; event.xmotion.time = CurrentTime; - event.xmotion.x = 0; + event.xmotion.x = m_disablePos; event.xmotion.y = 0; - event.xmotion.x_root = 0; + event.xmotion.x_root = m_disablePos; event.xmotion.y_root = 0; event.xmotion.state = 0; event.xmotion.is_hint = NotifyNormal; @@ -519,6 +529,8 @@ CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*) CXWindowsUtil::CErrorLock lock(m_display); XSendEvent(m_display, m_xscreensaver, False, 0, &event); + + m_disablePos = 20 - m_disablePos; } } diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index 193e1e7b..991b20e1 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -154,6 +154,11 @@ private: // the disable timer (NULL if not installed) CEventQueueTimer* m_disableTimer; + + // fake mouse motion position for suppressing the screen saver. + // xscreensaver since 2.21 requires the mouse to move more than 10 + // pixels to be considered significant. + SInt32 m_disablePos; }; #endif diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 440be0ed..9478d655 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -13,19 +13,46 @@ */ #include "CXWindowsUtil.h" +#include "KeyTypes.h" #include "CThread.h" #include "CLog.h" #include "CStringUtil.h" #include -#define XK_MISCELLANY -#define XK_XKB_KEYS +#define XK_APL +#define XK_ARABIC +#define XK_ARMENIAN +#define XK_CAUCASUS +#define XK_CURRENCY +#define XK_CYRILLIC +#define XK_GEORGIAN +#define XK_GREEK +#define XK_HEBREW +#define XK_KATAKANA +#define XK_KOREAN #define XK_LATIN1 #define XK_LATIN2 #define XK_LATIN3 #define XK_LATIN4 #define XK_LATIN8 #define XK_LATIN9 -#include +#define XK_MISCELLANY +#define XK_PUBLISHING +#define XK_SPECIAL +#define XK_TECHNICAL +#define XK_THAI +#define XK_VIETNAMESE +#define XK_XKB_KEYS +#include + +#if !defined(XK_OE) +#define XK_OE 0x13bc +#endif +#if !defined(XK_oe) +#define XK_oe 0x13bd +#endif +#if !defined(XK_Ydiaeresis) +#define XK_Ydiaeresis 0x13be +#endif /* * This table maps keysym values into the corresponding ISO 10646 @@ -47,1094 +74,1189 @@ 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 */ +{ XK_Aogonek, 0x0104 }, /* LATIN CAPITAL LETTER A WITH OGONEK */ +{ XK_breve, 0x02d8 }, /* BREVE */ +{ XK_Lstroke, 0x0141 }, /* LATIN CAPITAL LETTER L WITH STROKE */ +{ XK_Lcaron, 0x013d }, /* LATIN CAPITAL LETTER L WITH CARON */ +{ XK_Sacute, 0x015a }, /* LATIN CAPITAL LETTER S WITH ACUTE */ +{ XK_Scaron, 0x0160 }, /* LATIN CAPITAL LETTER S WITH CARON */ +{ XK_Scedilla, 0x015e }, /* LATIN CAPITAL LETTER S WITH CEDILLA */ +{ XK_Tcaron, 0x0164 }, /* LATIN CAPITAL LETTER T WITH CARON */ +{ XK_Zacute, 0x0179 }, /* LATIN CAPITAL LETTER Z WITH ACUTE */ +{ XK_Zcaron, 0x017d }, /* LATIN CAPITAL LETTER Z WITH CARON */ +{ XK_Zabovedot, 0x017b }, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ +{ XK_aogonek, 0x0105 }, /* LATIN SMALL LETTER A WITH OGONEK */ +{ XK_ogonek, 0x02db }, /* OGONEK */ +{ XK_lstroke, 0x0142 }, /* LATIN SMALL LETTER L WITH STROKE */ +{ XK_lcaron, 0x013e }, /* LATIN SMALL LETTER L WITH CARON */ +{ XK_sacute, 0x015b }, /* LATIN SMALL LETTER S WITH ACUTE */ +{ XK_caron, 0x02c7 }, /* CARON */ +{ XK_scaron, 0x0161 }, /* LATIN SMALL LETTER S WITH CARON */ +{ XK_scedilla, 0x015f }, /* LATIN SMALL LETTER S WITH CEDILLA */ +{ XK_tcaron, 0x0165 }, /* LATIN SMALL LETTER T WITH CARON */ +{ XK_zacute, 0x017a }, /* LATIN SMALL LETTER Z WITH ACUTE */ +{ XK_doubleacute, 0x02dd }, /* DOUBLE ACUTE ACCENT */ +{ XK_zcaron, 0x017e }, /* LATIN SMALL LETTER Z WITH CARON */ +{ XK_zabovedot, 0x017c }, /* LATIN SMALL LETTER Z WITH DOT ABOVE */ +{ XK_Racute, 0x0154 }, /* LATIN CAPITAL LETTER R WITH ACUTE */ +{ XK_Abreve, 0x0102 }, /* LATIN CAPITAL LETTER A WITH BREVE */ +{ XK_Lacute, 0x0139 }, /* LATIN CAPITAL LETTER L WITH ACUTE */ +{ XK_Cacute, 0x0106 }, /* LATIN CAPITAL LETTER C WITH ACUTE */ +{ XK_Ccaron, 0x010c }, /* LATIN CAPITAL LETTER C WITH CARON */ +{ XK_Eogonek, 0x0118 }, /* LATIN CAPITAL LETTER E WITH OGONEK */ +{ XK_Ecaron, 0x011a }, /* LATIN CAPITAL LETTER E WITH CARON */ +{ XK_Dcaron, 0x010e }, /* LATIN CAPITAL LETTER D WITH CARON */ +{ XK_Dstroke, 0x0110 }, /* LATIN CAPITAL LETTER D WITH STROKE */ +{ XK_Nacute, 0x0143 }, /* LATIN CAPITAL LETTER N WITH ACUTE */ +{ XK_Ncaron, 0x0147 }, /* LATIN CAPITAL LETTER N WITH CARON */ +{ XK_Odoubleacute, 0x0150 }, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ +{ XK_Rcaron, 0x0158 }, /* LATIN CAPITAL LETTER R WITH CARON */ +{ XK_Uring, 0x016e }, /* LATIN CAPITAL LETTER U WITH RING ABOVE */ +{ XK_Udoubleacute, 0x0170 }, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ +{ XK_Tcedilla, 0x0162 }, /* LATIN CAPITAL LETTER T WITH CEDILLA */ +{ XK_racute, 0x0155 }, /* LATIN SMALL LETTER R WITH ACUTE */ +{ XK_abreve, 0x0103 }, /* LATIN SMALL LETTER A WITH BREVE */ +{ XK_lacute, 0x013a }, /* LATIN SMALL LETTER L WITH ACUTE */ +{ XK_cacute, 0x0107 }, /* LATIN SMALL LETTER C WITH ACUTE */ +{ XK_ccaron, 0x010d }, /* LATIN SMALL LETTER C WITH CARON */ +{ XK_eogonek, 0x0119 }, /* LATIN SMALL LETTER E WITH OGONEK */ +{ XK_ecaron, 0x011b }, /* LATIN SMALL LETTER E WITH CARON */ +{ XK_dcaron, 0x010f }, /* LATIN SMALL LETTER D WITH CARON */ +{ XK_dstroke, 0x0111 }, /* LATIN SMALL LETTER D WITH STROKE */ +{ XK_nacute, 0x0144 }, /* LATIN SMALL LETTER N WITH ACUTE */ +{ XK_ncaron, 0x0148 }, /* LATIN SMALL LETTER N WITH CARON */ +{ XK_odoubleacute, 0x0151 }, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */ +{ XK_rcaron, 0x0159 }, /* LATIN SMALL LETTER R WITH CARON */ +{ XK_uring, 0x016f }, /* LATIN SMALL LETTER U WITH RING ABOVE */ +{ XK_udoubleacute, 0x0171 }, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */ +{ XK_tcedilla, 0x0163 }, /* LATIN SMALL LETTER T WITH CEDILLA */ +{ XK_abovedot, 0x02d9 }, /* DOT ABOVE */ +{ XK_Hstroke, 0x0126 }, /* LATIN CAPITAL LETTER H WITH STROKE */ +{ XK_Hcircumflex, 0x0124 }, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ +{ XK_Iabovedot, 0x0130 }, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */ +{ XK_Gbreve, 0x011e }, /* LATIN CAPITAL LETTER G WITH BREVE */ +{ XK_Jcircumflex, 0x0134 }, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ +{ XK_hstroke, 0x0127 }, /* LATIN SMALL LETTER H WITH STROKE */ +{ XK_hcircumflex, 0x0125 }, /* LATIN SMALL LETTER H WITH CIRCUMFLEX */ +{ XK_idotless, 0x0131 }, /* LATIN SMALL LETTER DOTLESS I */ +{ XK_gbreve, 0x011f }, /* LATIN SMALL LETTER G WITH BREVE */ +{ XK_jcircumflex, 0x0135 }, /* LATIN SMALL LETTER J WITH CIRCUMFLEX */ +{ XK_Cabovedot, 0x010a }, /* LATIN CAPITAL LETTER C WITH DOT ABOVE */ +{ XK_Ccircumflex, 0x0108 }, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ +{ XK_Gabovedot, 0x0120 }, /* LATIN CAPITAL LETTER G WITH DOT ABOVE */ +{ XK_Gcircumflex, 0x011c }, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ +{ XK_Ubreve, 0x016c }, /* LATIN CAPITAL LETTER U WITH BREVE */ +{ XK_Scircumflex, 0x015c }, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ +{ XK_cabovedot, 0x010b }, /* LATIN SMALL LETTER C WITH DOT ABOVE */ +{ XK_ccircumflex, 0x0109 }, /* LATIN SMALL LETTER C WITH CIRCUMFLEX */ +{ XK_gabovedot, 0x0121 }, /* LATIN SMALL LETTER G WITH DOT ABOVE */ +{ XK_gcircumflex, 0x011d }, /* LATIN SMALL LETTER G WITH CIRCUMFLEX */ +{ XK_ubreve, 0x016d }, /* LATIN SMALL LETTER U WITH BREVE */ +{ XK_scircumflex, 0x015d }, /* LATIN SMALL LETTER S WITH CIRCUMFLEX */ +{ XK_kra, 0x0138 }, /* LATIN SMALL LETTER KRA */ +{ XK_Rcedilla, 0x0156 }, /* LATIN CAPITAL LETTER R WITH CEDILLA */ +{ XK_Itilde, 0x0128 }, /* LATIN CAPITAL LETTER I WITH TILDE */ +{ XK_Lcedilla, 0x013b }, /* LATIN CAPITAL LETTER L WITH CEDILLA */ +{ XK_Emacron, 0x0112 }, /* LATIN CAPITAL LETTER E WITH MACRON */ +{ XK_Gcedilla, 0x0122 }, /* LATIN CAPITAL LETTER G WITH CEDILLA */ +{ XK_Tslash, 0x0166 }, /* LATIN CAPITAL LETTER T WITH STROKE */ +{ XK_rcedilla, 0x0157 }, /* LATIN SMALL LETTER R WITH CEDILLA */ +{ XK_itilde, 0x0129 }, /* LATIN SMALL LETTER I WITH TILDE */ +{ XK_lcedilla, 0x013c }, /* LATIN SMALL LETTER L WITH CEDILLA */ +{ XK_emacron, 0x0113 }, /* LATIN SMALL LETTER E WITH MACRON */ +{ XK_gcedilla, 0x0123 }, /* LATIN SMALL LETTER G WITH CEDILLA */ +{ XK_tslash, 0x0167 }, /* LATIN SMALL LETTER T WITH STROKE */ +{ XK_ENG, 0x014a }, /* LATIN CAPITAL LETTER ENG */ +{ XK_eng, 0x014b }, /* LATIN SMALL LETTER ENG */ +{ XK_Amacron, 0x0100 }, /* LATIN CAPITAL LETTER A WITH MACRON */ +{ XK_Iogonek, 0x012e }, /* LATIN CAPITAL LETTER I WITH OGONEK */ +{ XK_Eabovedot, 0x0116 }, /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ +{ XK_Imacron, 0x012a }, /* LATIN CAPITAL LETTER I WITH MACRON */ +{ XK_Ncedilla, 0x0145 }, /* LATIN CAPITAL LETTER N WITH CEDILLA */ +{ XK_Omacron, 0x014c }, /* LATIN CAPITAL LETTER O WITH MACRON */ +{ XK_Kcedilla, 0x0136 }, /* LATIN CAPITAL LETTER K WITH CEDILLA */ +{ XK_Uogonek, 0x0172 }, /* LATIN CAPITAL LETTER U WITH OGONEK */ +{ XK_Utilde, 0x0168 }, /* LATIN CAPITAL LETTER U WITH TILDE */ +{ XK_Umacron, 0x016a }, /* LATIN CAPITAL LETTER U WITH MACRON */ +{ XK_amacron, 0x0101 }, /* LATIN SMALL LETTER A WITH MACRON */ +{ XK_iogonek, 0x012f }, /* LATIN SMALL LETTER I WITH OGONEK */ +{ XK_eabovedot, 0x0117 }, /* LATIN SMALL LETTER E WITH DOT ABOVE */ +{ XK_imacron, 0x012b }, /* LATIN SMALL LETTER I WITH MACRON */ +{ XK_ncedilla, 0x0146 }, /* LATIN SMALL LETTER N WITH CEDILLA */ +{ XK_omacron, 0x014d }, /* LATIN SMALL LETTER O WITH MACRON */ +{ XK_kcedilla, 0x0137 }, /* LATIN SMALL LETTER K WITH CEDILLA */ +{ XK_uogonek, 0x0173 }, /* LATIN SMALL LETTER U WITH OGONEK */ +{ XK_utilde, 0x0169 }, /* LATIN SMALL LETTER U WITH TILDE */ +{ XK_umacron, 0x016b }, /* LATIN SMALL LETTER U WITH MACRON */ +{ XK_Babovedot, 0x1e02 }, /* LATIN CAPITAL LETTER B WITH DOT ABOVE */ +{ XK_babovedot, 0x1e03 }, /* LATIN SMALL LETTER B WITH DOT ABOVE */ +{ XK_Dabovedot, 0x1e0a }, /* LATIN CAPITAL LETTER D WITH DOT ABOVE */ +{ XK_Wgrave, 0x1e80 }, /* LATIN CAPITAL LETTER W WITH GRAVE */ +{ XK_Wacute, 0x1e82 }, /* LATIN CAPITAL LETTER W WITH ACUTE */ +{ XK_dabovedot, 0x1e0b }, /* LATIN SMALL LETTER D WITH DOT ABOVE */ +{ XK_Ygrave, 0x1ef2 }, /* LATIN CAPITAL LETTER Y WITH GRAVE */ +{ XK_Fabovedot, 0x1e1e }, /* LATIN CAPITAL LETTER F WITH DOT ABOVE */ +{ XK_fabovedot, 0x1e1f }, /* LATIN SMALL LETTER F WITH DOT ABOVE */ +{ XK_Mabovedot, 0x1e40 }, /* LATIN CAPITAL LETTER M WITH DOT ABOVE */ +{ XK_mabovedot, 0x1e41 }, /* LATIN SMALL LETTER M WITH DOT ABOVE */ +{ XK_Pabovedot, 0x1e56 }, /* LATIN CAPITAL LETTER P WITH DOT ABOVE */ +{ XK_wgrave, 0x1e81 }, /* LATIN SMALL LETTER W WITH GRAVE */ +{ XK_pabovedot, 0x1e57 }, /* LATIN SMALL LETTER P WITH DOT ABOVE */ +{ XK_wacute, 0x1e83 }, /* LATIN SMALL LETTER W WITH ACUTE */ +{ XK_Sabovedot, 0x1e60 }, /* LATIN CAPITAL LETTER S WITH DOT ABOVE */ +{ XK_ygrave, 0x1ef3 }, /* LATIN SMALL LETTER Y WITH GRAVE */ +{ XK_Wdiaeresis, 0x1e84 }, /* LATIN CAPITAL LETTER W WITH DIAERESIS */ +{ XK_wdiaeresis, 0x1e85 }, /* LATIN SMALL LETTER W WITH DIAERESIS */ +{ XK_sabovedot, 0x1e61 }, /* LATIN SMALL LETTER S WITH DOT ABOVE */ +{ XK_Wcircumflex, 0x0174 }, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ +{ XK_Tabovedot, 0x1e6a }, /* LATIN CAPITAL LETTER T WITH DOT ABOVE */ +{ XK_Ycircumflex, 0x0176 }, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ +{ XK_wcircumflex, 0x0175 }, /* LATIN SMALL LETTER W WITH CIRCUMFLEX */ +{ XK_tabovedot, 0x1e6b }, /* LATIN SMALL LETTER T WITH DOT ABOVE */ +{ XK_ycircumflex, 0x0177 }, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX */ +{ XK_overline, 0x203e }, /* OVERLINE */ +{ XK_kana_fullstop, 0x3002 }, /* IDEOGRAPHIC FULL STOP */ +{ XK_kana_openingbracket, 0x300c }, /* LEFT CORNER BRACKET */ +{ XK_kana_closingbracket, 0x300d }, /* RIGHT CORNER BRACKET */ +{ XK_kana_comma, 0x3001 }, /* IDEOGRAPHIC COMMA */ +{ XK_kana_conjunctive, 0x30fb }, /* KATAKANA MIDDLE DOT */ +{ XK_kana_WO, 0x30f2 }, /* KATAKANA LETTER WO */ +{ XK_kana_a, 0x30a1 }, /* KATAKANA LETTER SMALL A */ +{ XK_kana_i, 0x30a3 }, /* KATAKANA LETTER SMALL I */ +{ XK_kana_u, 0x30a5 }, /* KATAKANA LETTER SMALL U */ +{ XK_kana_e, 0x30a7 }, /* KATAKANA LETTER SMALL E */ +{ XK_kana_o, 0x30a9 }, /* KATAKANA LETTER SMALL O */ +{ XK_kana_ya, 0x30e3 }, /* KATAKANA LETTER SMALL YA */ +{ XK_kana_yu, 0x30e5 }, /* KATAKANA LETTER SMALL YU */ +{ XK_kana_yo, 0x30e7 }, /* KATAKANA LETTER SMALL YO */ +{ XK_kana_tsu, 0x30c3 }, /* KATAKANA LETTER SMALL TU */ +{ XK_prolongedsound, 0x30fc }, /* KATAKANA-HIRAGANA PROLONGED SOUND MARK */ +{ XK_kana_A, 0x30a2 }, /* KATAKANA LETTER A */ +{ XK_kana_I, 0x30a4 }, /* KATAKANA LETTER I */ +{ XK_kana_U, 0x30a6 }, /* KATAKANA LETTER U */ +{ XK_kana_E, 0x30a8 }, /* KATAKANA LETTER E */ +{ XK_kana_O, 0x30aa }, /* KATAKANA LETTER O */ +{ XK_kana_KA, 0x30ab }, /* KATAKANA LETTER KA */ +{ XK_kana_KI, 0x30ad }, /* KATAKANA LETTER KI */ +{ XK_kana_KU, 0x30af }, /* KATAKANA LETTER KU */ +{ XK_kana_KE, 0x30b1 }, /* KATAKANA LETTER KE */ +{ XK_kana_KO, 0x30b3 }, /* KATAKANA LETTER KO */ +{ XK_kana_SA, 0x30b5 }, /* KATAKANA LETTER SA */ +{ XK_kana_SHI, 0x30b7 }, /* KATAKANA LETTER SI */ +{ XK_kana_SU, 0x30b9 }, /* KATAKANA LETTER SU */ +{ XK_kana_SE, 0x30bb }, /* KATAKANA LETTER SE */ +{ XK_kana_SO, 0x30bd }, /* KATAKANA LETTER SO */ +{ XK_kana_TA, 0x30bf }, /* KATAKANA LETTER TA */ +{ XK_kana_CHI, 0x30c1 }, /* KATAKANA LETTER TI */ +{ XK_kana_TSU, 0x30c4 }, /* KATAKANA LETTER TU */ +{ XK_kana_TE, 0x30c6 }, /* KATAKANA LETTER TE */ +{ XK_kana_TO, 0x30c8 }, /* KATAKANA LETTER TO */ +{ XK_kana_NA, 0x30ca }, /* KATAKANA LETTER NA */ +{ XK_kana_NI, 0x30cb }, /* KATAKANA LETTER NI */ +{ XK_kana_NU, 0x30cc }, /* KATAKANA LETTER NU */ +{ XK_kana_NE, 0x30cd }, /* KATAKANA LETTER NE */ +{ XK_kana_NO, 0x30ce }, /* KATAKANA LETTER NO */ +{ XK_kana_HA, 0x30cf }, /* KATAKANA LETTER HA */ +{ XK_kana_HI, 0x30d2 }, /* KATAKANA LETTER HI */ +{ XK_kana_FU, 0x30d5 }, /* KATAKANA LETTER HU */ +{ XK_kana_HE, 0x30d8 }, /* KATAKANA LETTER HE */ +{ XK_kana_HO, 0x30db }, /* KATAKANA LETTER HO */ +{ XK_kana_MA, 0x30de }, /* KATAKANA LETTER MA */ +{ XK_kana_MI, 0x30df }, /* KATAKANA LETTER MI */ +{ XK_kana_MU, 0x30e0 }, /* KATAKANA LETTER MU */ +{ XK_kana_ME, 0x30e1 }, /* KATAKANA LETTER ME */ +{ XK_kana_MO, 0x30e2 }, /* KATAKANA LETTER MO */ +{ XK_kana_YA, 0x30e4 }, /* KATAKANA LETTER YA */ +{ XK_kana_YU, 0x30e6 }, /* KATAKANA LETTER YU */ +{ XK_kana_YO, 0x30e8 }, /* KATAKANA LETTER YO */ +{ XK_kana_RA, 0x30e9 }, /* KATAKANA LETTER RA */ +{ XK_kana_RI, 0x30ea }, /* KATAKANA LETTER RI */ +{ XK_kana_RU, 0x30eb }, /* KATAKANA LETTER RU */ +{ XK_kana_RE, 0x30ec }, /* KATAKANA LETTER RE */ +{ XK_kana_RO, 0x30ed }, /* KATAKANA LETTER RO */ +{ XK_kana_WA, 0x30ef }, /* KATAKANA LETTER WA */ +{ XK_kana_N, 0x30f3 }, /* KATAKANA LETTER N */ +{ XK_voicedsound, 0x309b }, /* KATAKANA-HIRAGANA VOICED SOUND MARK */ +{ XK_semivoicedsound, 0x309c }, /* KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +{ XK_Farsi_0, 0x06f0 }, /* EXTENDED ARABIC-INDIC DIGIT 0 */ +{ XK_Farsi_1, 0x06f1 }, /* EXTENDED ARABIC-INDIC DIGIT 1 */ +{ XK_Farsi_2, 0x06f2 }, /* EXTENDED ARABIC-INDIC DIGIT 2 */ +{ XK_Farsi_3, 0x06f3 }, /* EXTENDED ARABIC-INDIC DIGIT 3 */ +{ XK_Farsi_4, 0x06f4 }, /* EXTENDED ARABIC-INDIC DIGIT 4 */ +{ XK_Farsi_5, 0x06f5 }, /* EXTENDED ARABIC-INDIC DIGIT 5 */ +{ XK_Farsi_6, 0x06f6 }, /* EXTENDED ARABIC-INDIC DIGIT 6 */ +{ XK_Farsi_7, 0x06f7 }, /* EXTENDED ARABIC-INDIC DIGIT 7 */ +{ XK_Farsi_8, 0x06f8 }, /* EXTENDED ARABIC-INDIC DIGIT 8 */ +{ XK_Farsi_9, 0x06f9 }, /* EXTENDED ARABIC-INDIC DIGIT 9 */ +{ XK_Arabic_percent, 0x066a }, /* ARABIC PERCENT */ +{ XK_Arabic_superscript_alef, 0x0670 }, /* ARABIC LETTER SUPERSCRIPT ALEF */ +{ XK_Arabic_tteh, 0x0679 }, /* ARABIC LETTER TTEH */ +{ XK_Arabic_peh, 0x067e }, /* ARABIC LETTER PEH */ +{ XK_Arabic_tcheh, 0x0686 }, /* ARABIC LETTER TCHEH */ +{ XK_Arabic_ddal, 0x0688 }, /* ARABIC LETTER DDAL */ +{ XK_Arabic_rreh, 0x0691 }, /* ARABIC LETTER RREH */ +{ XK_Arabic_comma, 0x060c }, /* ARABIC COMMA */ +{ XK_Arabic_fullstop, 0x06d4 }, /* ARABIC FULLSTOP */ +{ XK_Arabic_semicolon, 0x061b }, /* ARABIC SEMICOLON */ +{ XK_Arabic_0, 0x0660 }, /* ARABIC 0 */ +{ XK_Arabic_1, 0x0661 }, /* ARABIC 1 */ +{ XK_Arabic_2, 0x0662 }, /* ARABIC 2 */ +{ XK_Arabic_3, 0x0663 }, /* ARABIC 3 */ +{ XK_Arabic_4, 0x0664 }, /* ARABIC 4 */ +{ XK_Arabic_5, 0x0665 }, /* ARABIC 5 */ +{ XK_Arabic_6, 0x0666 }, /* ARABIC 6 */ +{ XK_Arabic_7, 0x0667 }, /* ARABIC 7 */ +{ XK_Arabic_8, 0x0668 }, /* ARABIC 8 */ +{ XK_Arabic_9, 0x0669 }, /* ARABIC 9 */ +{ XK_Arabic_question_mark, 0x061f }, /* ARABIC QUESTION MARK */ +{ XK_Arabic_hamza, 0x0621 }, /* ARABIC LETTER HAMZA */ +{ XK_Arabic_maddaonalef, 0x0622 }, /* ARABIC LETTER ALEF WITH MADDA ABOVE */ +{ XK_Arabic_hamzaonalef, 0x0623 }, /* ARABIC LETTER ALEF WITH HAMZA ABOVE */ +{ XK_Arabic_hamzaonwaw, 0x0624 }, /* ARABIC LETTER WAW WITH HAMZA ABOVE */ +{ XK_Arabic_hamzaunderalef, 0x0625 }, /* ARABIC LETTER ALEF WITH HAMZA BELOW */ +{ XK_Arabic_hamzaonyeh, 0x0626 }, /* ARABIC LETTER YEH WITH HAMZA ABOVE */ +{ XK_Arabic_alef, 0x0627 }, /* ARABIC LETTER ALEF */ +{ XK_Arabic_beh, 0x0628 }, /* ARABIC LETTER BEH */ +{ XK_Arabic_tehmarbuta, 0x0629 }, /* ARABIC LETTER TEH MARBUTA */ +{ XK_Arabic_teh, 0x062a }, /* ARABIC LETTER TEH */ +{ XK_Arabic_theh, 0x062b }, /* ARABIC LETTER THEH */ +{ XK_Arabic_jeem, 0x062c }, /* ARABIC LETTER JEEM */ +{ XK_Arabic_hah, 0x062d }, /* ARABIC LETTER HAH */ +{ XK_Arabic_khah, 0x062e }, /* ARABIC LETTER KHAH */ +{ XK_Arabic_dal, 0x062f }, /* ARABIC LETTER DAL */ +{ XK_Arabic_thal, 0x0630 }, /* ARABIC LETTER THAL */ +{ XK_Arabic_ra, 0x0631 }, /* ARABIC LETTER REH */ +{ XK_Arabic_zain, 0x0632 }, /* ARABIC LETTER ZAIN */ +{ XK_Arabic_seen, 0x0633 }, /* ARABIC LETTER SEEN */ +{ XK_Arabic_sheen, 0x0634 }, /* ARABIC LETTER SHEEN */ +{ XK_Arabic_sad, 0x0635 }, /* ARABIC LETTER SAD */ +{ XK_Arabic_dad, 0x0636 }, /* ARABIC LETTER DAD */ +{ XK_Arabic_tah, 0x0637 }, /* ARABIC LETTER TAH */ +{ XK_Arabic_zah, 0x0638 }, /* ARABIC LETTER ZAH */ +{ XK_Arabic_ain, 0x0639 }, /* ARABIC LETTER AIN */ +{ XK_Arabic_ghain, 0x063a }, /* ARABIC LETTER GHAIN */ +{ XK_Arabic_tatweel, 0x0640 }, /* ARABIC TATWEEL */ +{ XK_Arabic_feh, 0x0641 }, /* ARABIC LETTER FEH */ +{ XK_Arabic_qaf, 0x0642 }, /* ARABIC LETTER QAF */ +{ XK_Arabic_kaf, 0x0643 }, /* ARABIC LETTER KAF */ +{ XK_Arabic_lam, 0x0644 }, /* ARABIC LETTER LAM */ +{ XK_Arabic_meem, 0x0645 }, /* ARABIC LETTER MEEM */ +{ XK_Arabic_noon, 0x0646 }, /* ARABIC LETTER NOON */ +{ XK_Arabic_ha, 0x0647 }, /* ARABIC LETTER HEH */ +{ XK_Arabic_waw, 0x0648 }, /* ARABIC LETTER WAW */ +{ XK_Arabic_alefmaksura, 0x0649 }, /* ARABIC LETTER ALEF MAKSURA */ +{ XK_Arabic_yeh, 0x064a }, /* ARABIC LETTER YEH */ +{ XK_Arabic_fathatan, 0x064b }, /* ARABIC FATHATAN */ +{ XK_Arabic_dammatan, 0x064c }, /* ARABIC DAMMATAN */ +{ XK_Arabic_kasratan, 0x064d }, /* ARABIC KASRATAN */ +{ XK_Arabic_fatha, 0x064e }, /* ARABIC FATHA */ +{ XK_Arabic_damma, 0x064f }, /* ARABIC DAMMA */ +{ XK_Arabic_kasra, 0x0650 }, /* ARABIC KASRA */ +{ XK_Arabic_shadda, 0x0651 }, /* ARABIC SHADDA */ +{ XK_Arabic_sukun, 0x0652 }, /* ARABIC SUKUN */ +{ XK_Arabic_madda_above, 0x0653 }, /* ARABIC MADDA ABOVE */ +{ XK_Arabic_hamza_above, 0x0654 }, /* ARABIC HAMZA ABOVE */ +{ XK_Arabic_hamza_below, 0x0655 }, /* ARABIC HAMZA BELOW */ +{ XK_Arabic_jeh, 0x0698 }, /* ARABIC LETTER JEH */ +{ XK_Arabic_veh, 0x06a4 }, /* ARABIC LETTER VEH */ +{ XK_Arabic_keheh, 0x06a9 }, /* ARABIC LETTER KEHEH */ +{ XK_Arabic_gaf, 0x06af }, /* ARABIC LETTER GAF */ +{ XK_Arabic_noon_ghunna, 0x06ba }, /* ARABIC LETTER NOON GHUNNA */ +{ XK_Arabic_heh_doachashmee, 0x06be }, /* ARABIC LETTER HEH DOACHASHMEE */ +{ XK_Arabic_farsi_yeh, 0x06cc }, /* ARABIC LETTER FARSI YEH */ +{ XK_Arabic_yeh_baree, 0x06d2 }, /* ARABIC LETTER YEH BAREE */ +{ XK_Arabic_heh_goal, 0x06c1 }, /* ARABIC LETTER HEH GOAL */ +{ XK_Serbian_dje, 0x0452 }, /* CYRILLIC SMALL LETTER DJE */ +{ XK_Macedonia_gje, 0x0453 }, /* CYRILLIC SMALL LETTER GJE */ +{ XK_Cyrillic_io, 0x0451 }, /* CYRILLIC SMALL LETTER IO */ +{ XK_Ukrainian_ie, 0x0454 }, /* CYRILLIC SMALL LETTER UKRAINIAN IE */ +{ XK_Macedonia_dse, 0x0455 }, /* CYRILLIC SMALL LETTER DZE */ +{ XK_Ukrainian_i, 0x0456 }, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ XK_Ukrainian_yi, 0x0457 }, /* CYRILLIC SMALL LETTER YI */ +{ XK_Cyrillic_je, 0x0458 }, /* CYRILLIC SMALL LETTER JE */ +{ XK_Cyrillic_lje, 0x0459 }, /* CYRILLIC SMALL LETTER LJE */ +{ XK_Cyrillic_nje, 0x045a }, /* CYRILLIC SMALL LETTER NJE */ +{ XK_Serbian_tshe, 0x045b }, /* CYRILLIC SMALL LETTER TSHE */ +{ XK_Macedonia_kje, 0x045c }, /* CYRILLIC SMALL LETTER KJE */ +{ XK_Ukrainian_ghe_with_upturn, 0x0491 }, /* CYRILLIC SMALL LETTER GHE WITH UPTURN */ +{ XK_Byelorussian_shortu, 0x045e }, /* CYRILLIC SMALL LETTER SHORT U */ +{ XK_Cyrillic_dzhe, 0x045f }, /* CYRILLIC SMALL LETTER DZHE */ +{ XK_numerosign, 0x2116 }, /* NUMERO SIGN */ +{ XK_Serbian_DJE, 0x0402 }, /* CYRILLIC CAPITAL LETTER DJE */ +{ XK_Macedonia_GJE, 0x0403 }, /* CYRILLIC CAPITAL LETTER GJE */ +{ XK_Cyrillic_IO, 0x0401 }, /* CYRILLIC CAPITAL LETTER IO */ +{ XK_Ukrainian_IE, 0x0404 }, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */ +{ XK_Macedonia_DSE, 0x0405 }, /* CYRILLIC CAPITAL LETTER DZE */ +{ XK_Ukrainian_I, 0x0406 }, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ XK_Ukrainian_YI, 0x0407 }, /* CYRILLIC CAPITAL LETTER YI */ +{ XK_Cyrillic_JE, 0x0408 }, /* CYRILLIC CAPITAL LETTER JE */ +{ XK_Cyrillic_LJE, 0x0409 }, /* CYRILLIC CAPITAL LETTER LJE */ +{ XK_Cyrillic_NJE, 0x040a }, /* CYRILLIC CAPITAL LETTER NJE */ +{ XK_Serbian_TSHE, 0x040b }, /* CYRILLIC CAPITAL LETTER TSHE */ +{ XK_Macedonia_KJE, 0x040c }, /* CYRILLIC CAPITAL LETTER KJE */ +{ XK_Ukrainian_GHE_WITH_UPTURN, 0x0490 }, /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ +{ XK_Byelorussian_SHORTU, 0x040e }, /* CYRILLIC CAPITAL LETTER SHORT U */ +{ XK_Cyrillic_DZHE, 0x040f }, /* CYRILLIC CAPITAL LETTER DZHE */ +{ XK_Cyrillic_yu, 0x044e }, /* CYRILLIC SMALL LETTER YU */ +{ XK_Cyrillic_a, 0x0430 }, /* CYRILLIC SMALL LETTER A */ +{ XK_Cyrillic_be, 0x0431 }, /* CYRILLIC SMALL LETTER BE */ +{ XK_Cyrillic_tse, 0x0446 }, /* CYRILLIC SMALL LETTER TSE */ +{ XK_Cyrillic_de, 0x0434 }, /* CYRILLIC SMALL LETTER DE */ +{ XK_Cyrillic_ie, 0x0435 }, /* CYRILLIC SMALL LETTER IE */ +{ XK_Cyrillic_ef, 0x0444 }, /* CYRILLIC SMALL LETTER EF */ +{ XK_Cyrillic_ghe, 0x0433 }, /* CYRILLIC SMALL LETTER GHE */ +{ XK_Cyrillic_ha, 0x0445 }, /* CYRILLIC SMALL LETTER HA */ +{ XK_Cyrillic_i, 0x0438 }, /* CYRILLIC SMALL LETTER I */ +{ XK_Cyrillic_shorti, 0x0439 }, /* CYRILLIC SMALL LETTER SHORT I */ +{ XK_Cyrillic_ka, 0x043a }, /* CYRILLIC SMALL LETTER KA */ +{ XK_Cyrillic_el, 0x043b }, /* CYRILLIC SMALL LETTER EL */ +{ XK_Cyrillic_em, 0x043c }, /* CYRILLIC SMALL LETTER EM */ +{ XK_Cyrillic_en, 0x043d }, /* CYRILLIC SMALL LETTER EN */ +{ XK_Cyrillic_o, 0x043e }, /* CYRILLIC SMALL LETTER O */ +{ XK_Cyrillic_pe, 0x043f }, /* CYRILLIC SMALL LETTER PE */ +{ XK_Cyrillic_ya, 0x044f }, /* CYRILLIC SMALL LETTER YA */ +{ XK_Cyrillic_er, 0x0440 }, /* CYRILLIC SMALL LETTER ER */ +{ XK_Cyrillic_es, 0x0441 }, /* CYRILLIC SMALL LETTER ES */ +{ XK_Cyrillic_te, 0x0442 }, /* CYRILLIC SMALL LETTER TE */ +{ XK_Cyrillic_u, 0x0443 }, /* CYRILLIC SMALL LETTER U */ +{ XK_Cyrillic_zhe, 0x0436 }, /* CYRILLIC SMALL LETTER ZHE */ +{ XK_Cyrillic_ve, 0x0432 }, /* CYRILLIC SMALL LETTER VE */ +{ XK_Cyrillic_softsign, 0x044c }, /* CYRILLIC SMALL LETTER SOFT SIGN */ +{ XK_Cyrillic_yeru, 0x044b }, /* CYRILLIC SMALL LETTER YERU */ +{ XK_Cyrillic_ze, 0x0437 }, /* CYRILLIC SMALL LETTER ZE */ +{ XK_Cyrillic_sha, 0x0448 }, /* CYRILLIC SMALL LETTER SHA */ +{ XK_Cyrillic_e, 0x044d }, /* CYRILLIC SMALL LETTER E */ +{ XK_Cyrillic_shcha, 0x0449 }, /* CYRILLIC SMALL LETTER SHCHA */ +{ XK_Cyrillic_che, 0x0447 }, /* CYRILLIC SMALL LETTER CHE */ +{ XK_Cyrillic_hardsign, 0x044a }, /* CYRILLIC SMALL LETTER HARD SIGN */ +{ XK_Cyrillic_YU, 0x042e }, /* CYRILLIC CAPITAL LETTER YU */ +{ XK_Cyrillic_A, 0x0410 }, /* CYRILLIC CAPITAL LETTER A */ +{ XK_Cyrillic_BE, 0x0411 }, /* CYRILLIC CAPITAL LETTER BE */ +{ XK_Cyrillic_TSE, 0x0426 }, /* CYRILLIC CAPITAL LETTER TSE */ +{ XK_Cyrillic_DE, 0x0414 }, /* CYRILLIC CAPITAL LETTER DE */ +{ XK_Cyrillic_IE, 0x0415 }, /* CYRILLIC CAPITAL LETTER IE */ +{ XK_Cyrillic_EF, 0x0424 }, /* CYRILLIC CAPITAL LETTER EF */ +{ XK_Cyrillic_GHE, 0x0413 }, /* CYRILLIC CAPITAL LETTER GHE */ +{ XK_Cyrillic_HA, 0x0425 }, /* CYRILLIC CAPITAL LETTER HA */ +{ XK_Cyrillic_I, 0x0418 }, /* CYRILLIC CAPITAL LETTER I */ +{ XK_Cyrillic_SHORTI, 0x0419 }, /* CYRILLIC CAPITAL LETTER SHORT I */ +{ XK_Cyrillic_KA, 0x041a }, /* CYRILLIC CAPITAL LETTER KA */ +{ XK_Cyrillic_EL, 0x041b }, /* CYRILLIC CAPITAL LETTER EL */ +{ XK_Cyrillic_EM, 0x041c }, /* CYRILLIC CAPITAL LETTER EM */ +{ XK_Cyrillic_EN, 0x041d }, /* CYRILLIC CAPITAL LETTER EN */ +{ XK_Cyrillic_O, 0x041e }, /* CYRILLIC CAPITAL LETTER O */ +{ XK_Cyrillic_PE, 0x041f }, /* CYRILLIC CAPITAL LETTER PE */ +{ XK_Cyrillic_YA, 0x042f }, /* CYRILLIC CAPITAL LETTER YA */ +{ XK_Cyrillic_ER, 0x0420 }, /* CYRILLIC CAPITAL LETTER ER */ +{ XK_Cyrillic_ES, 0x0421 }, /* CYRILLIC CAPITAL LETTER ES */ +{ XK_Cyrillic_TE, 0x0422 }, /* CYRILLIC CAPITAL LETTER TE */ +{ XK_Cyrillic_U, 0x0423 }, /* CYRILLIC CAPITAL LETTER U */ +{ XK_Cyrillic_ZHE, 0x0416 }, /* CYRILLIC CAPITAL LETTER ZHE */ +{ XK_Cyrillic_VE, 0x0412 }, /* CYRILLIC CAPITAL LETTER VE */ +{ XK_Cyrillic_SOFTSIGN, 0x042c }, /* CYRILLIC CAPITAL LETTER SOFT SIGN */ +{ XK_Cyrillic_YERU, 0x042b }, /* CYRILLIC CAPITAL LETTER YERU */ +{ XK_Cyrillic_ZE, 0x0417 }, /* CYRILLIC CAPITAL LETTER ZE */ +{ XK_Cyrillic_SHA, 0x0428 }, /* CYRILLIC CAPITAL LETTER SHA */ +{ XK_Cyrillic_E, 0x042d }, /* CYRILLIC CAPITAL LETTER E */ +{ XK_Cyrillic_SHCHA, 0x0429 }, /* CYRILLIC CAPITAL LETTER SHCHA */ +{ XK_Cyrillic_CHE, 0x0427 }, /* CYRILLIC CAPITAL LETTER CHE */ +{ XK_Cyrillic_HARDSIGN, 0x042a }, /* CYRILLIC CAPITAL LETTER HARD SIGN */ +{ XK_Greek_ALPHAaccent, 0x0386 }, /* GREEK CAPITAL LETTER ALPHA WITH TONOS */ +{ XK_Greek_EPSILONaccent, 0x0388 }, /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ +{ XK_Greek_ETAaccent, 0x0389 }, /* GREEK CAPITAL LETTER ETA WITH TONOS */ +{ XK_Greek_IOTAaccent, 0x038a }, /* GREEK CAPITAL LETTER IOTA WITH TONOS */ +{ XK_Greek_IOTAdiaeresis, 0x03aa }, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ +{ XK_Greek_OMICRONaccent, 0x038c }, /* GREEK CAPITAL LETTER OMICRON WITH TONOS */ +{ XK_Greek_UPSILONaccent, 0x038e }, /* GREEK CAPITAL LETTER UPSILON WITH TONOS */ +{ XK_Greek_UPSILONdieresis, 0x03ab }, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ +{ XK_Greek_OMEGAaccent, 0x038f }, /* GREEK CAPITAL LETTER OMEGA WITH TONOS */ +{ XK_Greek_accentdieresis, 0x0385 }, /* GREEK DIALYTIKA TONOS */ +{ XK_Greek_horizbar, 0x2015 }, /* HORIZONTAL BAR */ +{ XK_Greek_alphaaccent, 0x03ac }, /* GREEK SMALL LETTER ALPHA WITH TONOS */ +{ XK_Greek_epsilonaccent, 0x03ad }, /* GREEK SMALL LETTER EPSILON WITH TONOS */ +{ XK_Greek_etaaccent, 0x03ae }, /* GREEK SMALL LETTER ETA WITH TONOS */ +{ XK_Greek_iotaaccent, 0x03af }, /* GREEK SMALL LETTER IOTA WITH TONOS */ +{ XK_Greek_iotadieresis, 0x03ca }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA */ +{ XK_Greek_iotaaccentdieresis, 0x0390 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ +{ XK_Greek_omicronaccent, 0x03cc }, /* GREEK SMALL LETTER OMICRON WITH TONOS */ +{ XK_Greek_upsilonaccent, 0x03cd }, /* GREEK SMALL LETTER UPSILON WITH TONOS */ +{ XK_Greek_upsilondieresis, 0x03cb }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ +{ XK_Greek_upsilonaccentdieresis, 0x03b0 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ +{ XK_Greek_omegaaccent, 0x03ce }, /* GREEK SMALL LETTER OMEGA WITH TONOS */ +{ XK_Greek_ALPHA, 0x0391 }, /* GREEK CAPITAL LETTER ALPHA */ +{ XK_Greek_BETA, 0x0392 }, /* GREEK CAPITAL LETTER BETA */ +{ XK_Greek_GAMMA, 0x0393 }, /* GREEK CAPITAL LETTER GAMMA */ +{ XK_Greek_DELTA, 0x0394 }, /* GREEK CAPITAL LETTER DELTA */ +{ XK_Greek_EPSILON, 0x0395 }, /* GREEK CAPITAL LETTER EPSILON */ +{ XK_Greek_ZETA, 0x0396 }, /* GREEK CAPITAL LETTER ZETA */ +{ XK_Greek_ETA, 0x0397 }, /* GREEK CAPITAL LETTER ETA */ +{ XK_Greek_THETA, 0x0398 }, /* GREEK CAPITAL LETTER THETA */ +{ XK_Greek_IOTA, 0x0399 }, /* GREEK CAPITAL LETTER IOTA */ +{ XK_Greek_KAPPA, 0x039a }, /* GREEK CAPITAL LETTER KAPPA */ +{ XK_Greek_LAMBDA, 0x039b }, /* GREEK CAPITAL LETTER LAMDA */ +{ XK_Greek_MU, 0x039c }, /* GREEK CAPITAL LETTER MU */ +{ XK_Greek_NU, 0x039d }, /* GREEK CAPITAL LETTER NU */ +{ XK_Greek_XI, 0x039e }, /* GREEK CAPITAL LETTER XI */ +{ XK_Greek_OMICRON, 0x039f }, /* GREEK CAPITAL LETTER OMICRON */ +{ XK_Greek_PI, 0x03a0 }, /* GREEK CAPITAL LETTER PI */ +{ XK_Greek_RHO, 0x03a1 }, /* GREEK CAPITAL LETTER RHO */ +{ XK_Greek_SIGMA, 0x03a3 }, /* GREEK CAPITAL LETTER SIGMA */ +{ XK_Greek_TAU, 0x03a4 }, /* GREEK CAPITAL LETTER TAU */ +{ XK_Greek_UPSILON, 0x03a5 }, /* GREEK CAPITAL LETTER UPSILON */ +{ XK_Greek_PHI, 0x03a6 }, /* GREEK CAPITAL LETTER PHI */ +{ XK_Greek_CHI, 0x03a7 }, /* GREEK CAPITAL LETTER CHI */ +{ XK_Greek_PSI, 0x03a8 }, /* GREEK CAPITAL LETTER PSI */ +{ XK_Greek_OMEGA, 0x03a9 }, /* GREEK CAPITAL LETTER OMEGA */ +{ XK_Greek_alpha, 0x03b1 }, /* GREEK SMALL LETTER ALPHA */ +{ XK_Greek_beta, 0x03b2 }, /* GREEK SMALL LETTER BETA */ +{ XK_Greek_gamma, 0x03b3 }, /* GREEK SMALL LETTER GAMMA */ +{ XK_Greek_delta, 0x03b4 }, /* GREEK SMALL LETTER DELTA */ +{ XK_Greek_epsilon, 0x03b5 }, /* GREEK SMALL LETTER EPSILON */ +{ XK_Greek_zeta, 0x03b6 }, /* GREEK SMALL LETTER ZETA */ +{ XK_Greek_eta, 0x03b7 }, /* GREEK SMALL LETTER ETA */ +{ XK_Greek_theta, 0x03b8 }, /* GREEK SMALL LETTER THETA */ +{ XK_Greek_iota, 0x03b9 }, /* GREEK SMALL LETTER IOTA */ +{ XK_Greek_kappa, 0x03ba }, /* GREEK SMALL LETTER KAPPA */ +{ XK_Greek_lambda, 0x03bb }, /* GREEK SMALL LETTER LAMDA */ +{ XK_Greek_mu, 0x03bc }, /* GREEK SMALL LETTER MU */ +{ XK_Greek_nu, 0x03bd }, /* GREEK SMALL LETTER NU */ +{ XK_Greek_xi, 0x03be }, /* GREEK SMALL LETTER XI */ +{ XK_Greek_omicron, 0x03bf }, /* GREEK SMALL LETTER OMICRON */ +{ XK_Greek_pi, 0x03c0 }, /* GREEK SMALL LETTER PI */ +{ XK_Greek_rho, 0x03c1 }, /* GREEK SMALL LETTER RHO */ +{ XK_Greek_sigma, 0x03c3 }, /* GREEK SMALL LETTER SIGMA */ +{ XK_Greek_finalsmallsigma, 0x03c2 }, /* GREEK SMALL LETTER FINAL SIGMA */ +{ XK_Greek_tau, 0x03c4 }, /* GREEK SMALL LETTER TAU */ +{ XK_Greek_upsilon, 0x03c5 }, /* GREEK SMALL LETTER UPSILON */ +{ XK_Greek_phi, 0x03c6 }, /* GREEK SMALL LETTER PHI */ +{ XK_Greek_chi, 0x03c7 }, /* GREEK SMALL LETTER CHI */ +{ XK_Greek_psi, 0x03c8 }, /* GREEK SMALL LETTER PSI */ +{ XK_Greek_omega, 0x03c9 }, /* GREEK SMALL LETTER OMEGA */ +{ XK_leftradical, 0x23b7 }, /* ??? */ +{ XK_topleftradical, 0x250c }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ XK_horizconnector, 0x2500 }, /* BOX DRAWINGS LIGHT HORIZONTAL */ +{ XK_topintegral, 0x2320 }, /* TOP HALF INTEGRAL */ +{ XK_botintegral, 0x2321 }, /* BOTTOM HALF INTEGRAL */ +{ XK_vertconnector, 0x2502 }, /* BOX DRAWINGS LIGHT VERTICAL */ +{ XK_topleftsqbracket, 0x23a1 }, /* ??? */ +{ XK_botleftsqbracket, 0x23a3 }, /* ??? */ +{ XK_toprightsqbracket, 0x23a4 }, /* ??? */ +{ XK_botrightsqbracket, 0x23a6 }, /* ??? */ +{ XK_topleftparens, 0x239b }, /* ??? */ +{ XK_botleftparens, 0x239d }, /* ??? */ +{ XK_toprightparens, 0x239e }, /* ??? */ +{ XK_botrightparens, 0x23a0 }, /* ??? */ +{ XK_leftmiddlecurlybrace, 0x23a8 }, /* ??? */ +{ XK_rightmiddlecurlybrace, 0x23ac }, /* ??? */ +{ XK_lessthanequal, 0x2264 }, /* LESS-THAN OR EQUAL TO */ +{ XK_notequal, 0x2260 }, /* NOT EQUAL TO */ +{ XK_greaterthanequal, 0x2265 }, /* GREATER-THAN OR EQUAL TO */ +{ XK_integral, 0x222b }, /* INTEGRAL */ +{ XK_therefore, 0x2234 }, /* THEREFORE */ +{ XK_variation, 0x221d }, /* PROPORTIONAL TO */ +{ XK_infinity, 0x221e }, /* INFINITY */ +{ XK_nabla, 0x2207 }, /* NABLA */ +{ XK_approximate, 0x223c }, /* TILDE OPERATOR */ +{ XK_similarequal, 0x2243 }, /* ASYMPTOTICALLY EQUAL TO */ +{ XK_ifonlyif, 0x21d4 }, /* LEFT RIGHT DOUBLE ARROW */ +{ XK_implies, 0x21d2 }, /* RIGHTWARDS DOUBLE ARROW */ +{ XK_identical, 0x2261 }, /* IDENTICAL TO */ +{ XK_radical, 0x221a }, /* SQUARE ROOT */ +{ XK_includedin, 0x2282 }, /* SUBSET OF */ +{ XK_includes, 0x2283 }, /* SUPERSET OF */ +{ XK_intersection, 0x2229 }, /* INTERSECTION */ +{ XK_union, 0x222a }, /* UNION */ +{ XK_logicaland, 0x2227 }, /* LOGICAL AND */ +{ XK_logicalor, 0x2228 }, /* LOGICAL OR */ +{ XK_partialderivative, 0x2202 }, /* PARTIAL DIFFERENTIAL */ +{ XK_function, 0x0192 }, /* LATIN SMALL LETTER F WITH HOOK */ +{ XK_leftarrow, 0x2190 }, /* LEFTWARDS ARROW */ +{ XK_uparrow, 0x2191 }, /* UPWARDS ARROW */ +{ XK_rightarrow, 0x2192 }, /* RIGHTWARDS ARROW */ +{ XK_downarrow, 0x2193 }, /* DOWNWARDS ARROW */ +/*{ XK_blank, ??? }, */ +{ XK_soliddiamond, 0x25c6 }, /* BLACK DIAMOND */ +{ XK_checkerboard, 0x2592 }, /* MEDIUM SHADE */ +{ XK_ht, 0x2409 }, /* SYMBOL FOR HORIZONTAL TABULATION */ +{ XK_ff, 0x240c }, /* SYMBOL FOR FORM FEED */ +{ XK_cr, 0x240d }, /* SYMBOL FOR CARRIAGE RETURN */ +{ XK_lf, 0x240a }, /* SYMBOL FOR LINE FEED */ +{ XK_nl, 0x2424 }, /* SYMBOL FOR NEWLINE */ +{ XK_vt, 0x240b }, /* SYMBOL FOR VERTICAL TABULATION */ +{ XK_lowrightcorner, 0x2518 }, /* BOX DRAWINGS LIGHT UP AND LEFT */ +{ XK_uprightcorner, 0x2510 }, /* BOX DRAWINGS LIGHT DOWN AND LEFT */ +{ XK_upleftcorner, 0x250c }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ XK_lowleftcorner, 0x2514 }, /* BOX DRAWINGS LIGHT UP AND RIGHT */ +{ XK_crossinglines, 0x253c }, /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ +{ XK_horizlinescan1, 0x23ba }, /* HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ +{ XK_horizlinescan3, 0x23bb }, /* HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ +{ XK_horizlinescan5, 0x2500 }, /* BOX DRAWINGS LIGHT HORIZONTAL */ +{ XK_horizlinescan7, 0x23bc }, /* HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ +{ XK_horizlinescan9, 0x23bd }, /* HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ +{ XK_leftt, 0x251c }, /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +{ XK_rightt, 0x2524 }, /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */ +{ XK_bott, 0x2534 }, /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */ +{ XK_topt, 0x252c }, /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ +{ XK_vertbar, 0x2502 }, /* BOX DRAWINGS LIGHT VERTICAL */ +{ XK_emspace, 0x2003 }, /* EM SPACE */ +{ XK_enspace, 0x2002 }, /* EN SPACE */ +{ XK_em3space, 0x2004 }, /* THREE-PER-EM SPACE */ +{ XK_em4space, 0x2005 }, /* FOUR-PER-EM SPACE */ +{ XK_digitspace, 0x2007 }, /* FIGURE SPACE */ +{ XK_punctspace, 0x2008 }, /* PUNCTUATION SPACE */ +{ XK_thinspace, 0x2009 }, /* THIN SPACE */ +{ XK_hairspace, 0x200a }, /* HAIR SPACE */ +{ XK_emdash, 0x2014 }, /* EM DASH */ +{ XK_endash, 0x2013 }, /* EN DASH */ +/*{ XK_signifblank, ??? }, */ +{ XK_ellipsis, 0x2026 }, /* HORIZONTAL ELLIPSIS */ +{ XK_doubbaselinedot, 0x2025 }, /* TWO DOT LEADER */ +{ XK_onethird, 0x2153 }, /* VULGAR FRACTION ONE THIRD */ +{ XK_twothirds, 0x2154 }, /* VULGAR FRACTION TWO THIRDS */ +{ XK_onefifth, 0x2155 }, /* VULGAR FRACTION ONE FIFTH */ +{ XK_twofifths, 0x2156 }, /* VULGAR FRACTION TWO FIFTHS */ +{ XK_threefifths, 0x2157 }, /* VULGAR FRACTION THREE FIFTHS */ +{ XK_fourfifths, 0x2158 }, /* VULGAR FRACTION FOUR FIFTHS */ +{ XK_onesixth, 0x2159 }, /* VULGAR FRACTION ONE SIXTH */ +{ XK_fivesixths, 0x215a }, /* VULGAR FRACTION FIVE SIXTHS */ +{ XK_careof, 0x2105 }, /* CARE OF */ +{ XK_figdash, 0x2012 }, /* FIGURE DASH */ +{ XK_leftanglebracket, 0x2329 }, /* LEFT-POINTING ANGLE BRACKET */ +/*{ XK_decimalpoint, ??? }, */ +{ XK_rightanglebracket, 0x232a }, /* RIGHT-POINTING ANGLE BRACKET */ +/*{ XK_marker, ??? }, */ +{ XK_oneeighth, 0x215b }, /* VULGAR FRACTION ONE EIGHTH */ +{ XK_threeeighths, 0x215c }, /* VULGAR FRACTION THREE EIGHTHS */ +{ XK_fiveeighths, 0x215d }, /* VULGAR FRACTION FIVE EIGHTHS */ +{ XK_seveneighths, 0x215e }, /* VULGAR FRACTION SEVEN EIGHTHS */ +{ XK_trademark, 0x2122 }, /* TRADE MARK SIGN */ +{ XK_signaturemark, 0x2613 }, /* SALTIRE */ +/*{ XK_trademarkincircle, ??? }, */ +{ XK_leftopentriangle, 0x25c1 }, /* WHITE LEFT-POINTING TRIANGLE */ +{ XK_rightopentriangle, 0x25b7 }, /* WHITE RIGHT-POINTING TRIANGLE */ +{ XK_emopencircle, 0x25cb }, /* WHITE CIRCLE */ +{ XK_emopenrectangle, 0x25af }, /* WHITE VERTICAL RECTANGLE */ +{ XK_leftsinglequotemark, 0x2018 }, /* LEFT SINGLE QUOTATION MARK */ +{ XK_rightsinglequotemark, 0x2019 }, /* RIGHT SINGLE QUOTATION MARK */ +{ XK_leftdoublequotemark, 0x201c }, /* LEFT DOUBLE QUOTATION MARK */ +{ XK_rightdoublequotemark, 0x201d }, /* RIGHT DOUBLE QUOTATION MARK */ +{ XK_prescription, 0x211e }, /* PRESCRIPTION TAKE */ +{ XK_minutes, 0x2032 }, /* PRIME */ +{ XK_seconds, 0x2033 }, /* DOUBLE PRIME */ +{ XK_latincross, 0x271d }, /* LATIN CROSS */ +/*{ XK_hexagram, ??? }, */ +{ XK_filledrectbullet, 0x25ac }, /* BLACK RECTANGLE */ +{ XK_filledlefttribullet, 0x25c0 }, /* BLACK LEFT-POINTING TRIANGLE */ +{ XK_filledrighttribullet, 0x25b6 }, /* BLACK RIGHT-POINTING TRIANGLE */ +{ XK_emfilledcircle, 0x25cf }, /* BLACK CIRCLE */ +{ XK_emfilledrect, 0x25ae }, /* BLACK VERTICAL RECTANGLE */ +{ XK_enopencircbullet, 0x25e6 }, /* WHITE BULLET */ +{ XK_enopensquarebullet, 0x25ab }, /* WHITE SMALL SQUARE */ +{ XK_openrectbullet, 0x25ad }, /* WHITE RECTANGLE */ +{ XK_opentribulletup, 0x25b3 }, /* WHITE UP-POINTING TRIANGLE */ +{ XK_opentribulletdown, 0x25bd }, /* WHITE DOWN-POINTING TRIANGLE */ +{ XK_openstar, 0x2606 }, /* WHITE STAR */ +{ XK_enfilledcircbullet, 0x2022 }, /* BULLET */ +{ XK_enfilledsqbullet, 0x25aa }, /* BLACK SMALL SQUARE */ +{ XK_filledtribulletup, 0x25b2 }, /* BLACK UP-POINTING TRIANGLE */ +{ XK_filledtribulletdown, 0x25bc }, /* BLACK DOWN-POINTING TRIANGLE */ +{ XK_leftpointer, 0x261c }, /* WHITE LEFT POINTING INDEX */ +{ XK_rightpointer, 0x261e }, /* WHITE RIGHT POINTING INDEX */ +{ XK_club, 0x2663 }, /* BLACK CLUB SUIT */ +{ XK_diamond, 0x2666 }, /* BLACK DIAMOND SUIT */ +{ XK_heart, 0x2665 }, /* BLACK HEART SUIT */ +{ XK_maltesecross, 0x2720 }, /* MALTESE CROSS */ +{ XK_dagger, 0x2020 }, /* DAGGER */ +{ XK_doubledagger, 0x2021 }, /* DOUBLE DAGGER */ +{ XK_checkmark, 0x2713 }, /* CHECK MARK */ +{ XK_ballotcross, 0x2717 }, /* BALLOT X */ +{ XK_musicalsharp, 0x266f }, /* MUSIC SHARP SIGN */ +{ XK_musicalflat, 0x266d }, /* MUSIC FLAT SIGN */ +{ XK_malesymbol, 0x2642 }, /* MALE SIGN */ +{ XK_femalesymbol, 0x2640 }, /* FEMALE SIGN */ +{ XK_telephone, 0x260e }, /* BLACK TELEPHONE */ +{ XK_telephonerecorder, 0x2315 }, /* TELEPHONE RECORDER */ +{ XK_phonographcopyright, 0x2117 }, /* SOUND RECORDING COPYRIGHT */ +{ XK_caret, 0x2038 }, /* CARET */ +{ XK_singlelowquotemark, 0x201a }, /* SINGLE LOW-9 QUOTATION MARK */ +{ XK_doublelowquotemark, 0x201e }, /* DOUBLE LOW-9 QUOTATION MARK */ +/*{ XK_cursor, ??? }, */ +{ XK_leftcaret, 0x003c }, /* LESS-THAN SIGN */ +{ XK_rightcaret, 0x003e }, /* GREATER-THAN SIGN */ +{ XK_downcaret, 0x2228 }, /* LOGICAL OR */ +{ XK_upcaret, 0x2227 }, /* LOGICAL AND */ +{ XK_overbar, 0x00af }, /* MACRON */ +{ XK_downtack, 0x22a5 }, /* UP TACK */ +{ XK_upshoe, 0x2229 }, /* INTERSECTION */ +{ XK_downstile, 0x230a }, /* LEFT FLOOR */ +{ XK_underbar, 0x005f }, /* LOW LINE */ +{ XK_jot, 0x2218 }, /* RING OPERATOR */ +{ XK_quad, 0x2395 }, /* APL FUNCTIONAL SYMBOL QUAD */ +{ XK_uptack, 0x22a4 }, /* DOWN TACK */ +{ XK_circle, 0x25cb }, /* WHITE CIRCLE */ +{ XK_upstile, 0x2308 }, /* LEFT CEILING */ +{ XK_downshoe, 0x222a }, /* UNION */ +{ XK_rightshoe, 0x2283 }, /* SUPERSET OF */ +{ XK_leftshoe, 0x2282 }, /* SUBSET OF */ +{ XK_lefttack, 0x22a2 }, /* RIGHT TACK */ +{ XK_righttack, 0x22a3 }, /* LEFT TACK */ +{ XK_hebrew_doublelowline, 0x2017 }, /* DOUBLE LOW LINE */ +{ XK_hebrew_aleph, 0x05d0 }, /* HEBREW LETTER ALEF */ +{ XK_hebrew_bet, 0x05d1 }, /* HEBREW LETTER BET */ +{ XK_hebrew_gimel, 0x05d2 }, /* HEBREW LETTER GIMEL */ +{ XK_hebrew_dalet, 0x05d3 }, /* HEBREW LETTER DALET */ +{ XK_hebrew_he, 0x05d4 }, /* HEBREW LETTER HE */ +{ XK_hebrew_waw, 0x05d5 }, /* HEBREW LETTER VAV */ +{ XK_hebrew_zain, 0x05d6 }, /* HEBREW LETTER ZAYIN */ +{ XK_hebrew_chet, 0x05d7 }, /* HEBREW LETTER HET */ +{ XK_hebrew_tet, 0x05d8 }, /* HEBREW LETTER TET */ +{ XK_hebrew_yod, 0x05d9 }, /* HEBREW LETTER YOD */ +{ XK_hebrew_finalkaph, 0x05da }, /* HEBREW LETTER FINAL KAF */ +{ XK_hebrew_kaph, 0x05db }, /* HEBREW LETTER KAF */ +{ XK_hebrew_lamed, 0x05dc }, /* HEBREW LETTER LAMED */ +{ XK_hebrew_finalmem, 0x05dd }, /* HEBREW LETTER FINAL MEM */ +{ XK_hebrew_mem, 0x05de }, /* HEBREW LETTER MEM */ +{ XK_hebrew_finalnun, 0x05df }, /* HEBREW LETTER FINAL NUN */ +{ XK_hebrew_nun, 0x05e0 }, /* HEBREW LETTER NUN */ +{ XK_hebrew_samech, 0x05e1 }, /* HEBREW LETTER SAMEKH */ +{ XK_hebrew_ayin, 0x05e2 }, /* HEBREW LETTER AYIN */ +{ XK_hebrew_finalpe, 0x05e3 }, /* HEBREW LETTER FINAL PE */ +{ XK_hebrew_pe, 0x05e4 }, /* HEBREW LETTER PE */ +{ XK_hebrew_finalzade, 0x05e5 }, /* HEBREW LETTER FINAL TSADI */ +{ XK_hebrew_zade, 0x05e6 }, /* HEBREW LETTER TSADI */ +{ XK_hebrew_qoph, 0x05e7 }, /* HEBREW LETTER QOF */ +{ XK_hebrew_resh, 0x05e8 }, /* HEBREW LETTER RESH */ +{ XK_hebrew_shin, 0x05e9 }, /* HEBREW LETTER SHIN */ +{ XK_hebrew_taw, 0x05ea }, /* HEBREW LETTER TAV */ +{ XK_Thai_kokai, 0x0e01 }, /* THAI CHARACTER KO KAI */ +{ XK_Thai_khokhai, 0x0e02 }, /* THAI CHARACTER KHO KHAI */ +{ XK_Thai_khokhuat, 0x0e03 }, /* THAI CHARACTER KHO KHUAT */ +{ XK_Thai_khokhwai, 0x0e04 }, /* THAI CHARACTER KHO KHWAI */ +{ XK_Thai_khokhon, 0x0e05 }, /* THAI CHARACTER KHO KHON */ +{ XK_Thai_khorakhang, 0x0e06 }, /* THAI CHARACTER KHO RAKHANG */ +{ XK_Thai_ngongu, 0x0e07 }, /* THAI CHARACTER NGO NGU */ +{ XK_Thai_chochan, 0x0e08 }, /* THAI CHARACTER CHO CHAN */ +{ XK_Thai_choching, 0x0e09 }, /* THAI CHARACTER CHO CHING */ +{ XK_Thai_chochang, 0x0e0a }, /* THAI CHARACTER CHO CHANG */ +{ XK_Thai_soso, 0x0e0b }, /* THAI CHARACTER SO SO */ +{ XK_Thai_chochoe, 0x0e0c }, /* THAI CHARACTER CHO CHOE */ +{ XK_Thai_yoying, 0x0e0d }, /* THAI CHARACTER YO YING */ +{ XK_Thai_dochada, 0x0e0e }, /* THAI CHARACTER DO CHADA */ +{ XK_Thai_topatak, 0x0e0f }, /* THAI CHARACTER TO PATAK */ +{ XK_Thai_thothan, 0x0e10 }, /* THAI CHARACTER THO THAN */ +{ XK_Thai_thonangmontho, 0x0e11 }, /* THAI CHARACTER THO NANGMONTHO */ +{ XK_Thai_thophuthao, 0x0e12 }, /* THAI CHARACTER THO PHUTHAO */ +{ XK_Thai_nonen, 0x0e13 }, /* THAI CHARACTER NO NEN */ +{ XK_Thai_dodek, 0x0e14 }, /* THAI CHARACTER DO DEK */ +{ XK_Thai_totao, 0x0e15 }, /* THAI CHARACTER TO TAO */ +{ XK_Thai_thothung, 0x0e16 }, /* THAI CHARACTER THO THUNG */ +{ XK_Thai_thothahan, 0x0e17 }, /* THAI CHARACTER THO THAHAN */ +{ XK_Thai_thothong, 0x0e18 }, /* THAI CHARACTER THO THONG */ +{ XK_Thai_nonu, 0x0e19 }, /* THAI CHARACTER NO NU */ +{ XK_Thai_bobaimai, 0x0e1a }, /* THAI CHARACTER BO BAIMAI */ +{ XK_Thai_popla, 0x0e1b }, /* THAI CHARACTER PO PLA */ +{ XK_Thai_phophung, 0x0e1c }, /* THAI CHARACTER PHO PHUNG */ +{ XK_Thai_fofa, 0x0e1d }, /* THAI CHARACTER FO FA */ +{ XK_Thai_phophan, 0x0e1e }, /* THAI CHARACTER PHO PHAN */ +{ XK_Thai_fofan, 0x0e1f }, /* THAI CHARACTER FO FAN */ +{ XK_Thai_phosamphao, 0x0e20 }, /* THAI CHARACTER PHO SAMPHAO */ +{ XK_Thai_moma, 0x0e21 }, /* THAI CHARACTER MO MA */ +{ XK_Thai_yoyak, 0x0e22 }, /* THAI CHARACTER YO YAK */ +{ XK_Thai_rorua, 0x0e23 }, /* THAI CHARACTER RO RUA */ +{ XK_Thai_ru, 0x0e24 }, /* THAI CHARACTER RU */ +{ XK_Thai_loling, 0x0e25 }, /* THAI CHARACTER LO LING */ +{ XK_Thai_lu, 0x0e26 }, /* THAI CHARACTER LU */ +{ XK_Thai_wowaen, 0x0e27 }, /* THAI CHARACTER WO WAEN */ +{ XK_Thai_sosala, 0x0e28 }, /* THAI CHARACTER SO SALA */ +{ XK_Thai_sorusi, 0x0e29 }, /* THAI CHARACTER SO RUSI */ +{ XK_Thai_sosua, 0x0e2a }, /* THAI CHARACTER SO SUA */ +{ XK_Thai_hohip, 0x0e2b }, /* THAI CHARACTER HO HIP */ +{ XK_Thai_lochula, 0x0e2c }, /* THAI CHARACTER LO CHULA */ +{ XK_Thai_oang, 0x0e2d }, /* THAI CHARACTER O ANG */ +{ XK_Thai_honokhuk, 0x0e2e }, /* THAI CHARACTER HO NOKHUK */ +{ XK_Thai_paiyannoi, 0x0e2f }, /* THAI CHARACTER PAIYANNOI */ +{ XK_Thai_saraa, 0x0e30 }, /* THAI CHARACTER SARA A */ +{ XK_Thai_maihanakat, 0x0e31 }, /* THAI CHARACTER MAI HAN-AKAT */ +{ XK_Thai_saraaa, 0x0e32 }, /* THAI CHARACTER SARA AA */ +{ XK_Thai_saraam, 0x0e33 }, /* THAI CHARACTER SARA AM */ +{ XK_Thai_sarai, 0x0e34 }, /* THAI CHARACTER SARA I */ +{ XK_Thai_saraii, 0x0e35 }, /* THAI CHARACTER SARA II */ +{ XK_Thai_saraue, 0x0e36 }, /* THAI CHARACTER SARA UE */ +{ XK_Thai_sarauee, 0x0e37 }, /* THAI CHARACTER SARA UEE */ +{ XK_Thai_sarau, 0x0e38 }, /* THAI CHARACTER SARA U */ +{ XK_Thai_sarauu, 0x0e39 }, /* THAI CHARACTER SARA UU */ +{ XK_Thai_phinthu, 0x0e3a }, /* THAI CHARACTER PHINTHU */ +/*{ XK_Thai_maihanakat_maitho, ??? }, */ +{ XK_Thai_baht, 0x0e3f }, /* THAI CURRENCY SYMBOL BAHT */ +{ XK_Thai_sarae, 0x0e40 }, /* THAI CHARACTER SARA E */ +{ XK_Thai_saraae, 0x0e41 }, /* THAI CHARACTER SARA AE */ +{ XK_Thai_sarao, 0x0e42 }, /* THAI CHARACTER SARA O */ +{ XK_Thai_saraaimaimuan, 0x0e43 }, /* THAI CHARACTER SARA AI MAIMUAN */ +{ XK_Thai_saraaimaimalai, 0x0e44 }, /* THAI CHARACTER SARA AI MAIMALAI */ +{ XK_Thai_lakkhangyao, 0x0e45 }, /* THAI CHARACTER LAKKHANGYAO */ +{ XK_Thai_maiyamok, 0x0e46 }, /* THAI CHARACTER MAIYAMOK */ +{ XK_Thai_maitaikhu, 0x0e47 }, /* THAI CHARACTER MAITAIKHU */ +{ XK_Thai_maiek, 0x0e48 }, /* THAI CHARACTER MAI EK */ +{ XK_Thai_maitho, 0x0e49 }, /* THAI CHARACTER MAI THO */ +{ XK_Thai_maitri, 0x0e4a }, /* THAI CHARACTER MAI TRI */ +{ XK_Thai_maichattawa, 0x0e4b }, /* THAI CHARACTER MAI CHATTAWA */ +{ XK_Thai_thanthakhat, 0x0e4c }, /* THAI CHARACTER THANTHAKHAT */ +{ XK_Thai_nikhahit, 0x0e4d }, /* THAI CHARACTER NIKHAHIT */ +{ XK_Thai_leksun, 0x0e50 }, /* THAI DIGIT ZERO */ +{ XK_Thai_leknung, 0x0e51 }, /* THAI DIGIT ONE */ +{ XK_Thai_leksong, 0x0e52 }, /* THAI DIGIT TWO */ +{ XK_Thai_leksam, 0x0e53 }, /* THAI DIGIT THREE */ +{ XK_Thai_leksi, 0x0e54 }, /* THAI DIGIT FOUR */ +{ XK_Thai_lekha, 0x0e55 }, /* THAI DIGIT FIVE */ +{ XK_Thai_lekhok, 0x0e56 }, /* THAI DIGIT SIX */ +{ XK_Thai_lekchet, 0x0e57 }, /* THAI DIGIT SEVEN */ +{ XK_Thai_lekpaet, 0x0e58 }, /* THAI DIGIT EIGHT */ +{ XK_Thai_lekkao, 0x0e59 }, /* THAI DIGIT NINE */ +{ XK_Hangul_Kiyeog, 0x3131 }, /* HANGUL LETTER KIYEOK */ +{ XK_Hangul_SsangKiyeog, 0x3132 }, /* HANGUL LETTER SSANGKIYEOK */ +{ XK_Hangul_KiyeogSios, 0x3133 }, /* HANGUL LETTER KIYEOK-SIOS */ +{ XK_Hangul_Nieun, 0x3134 }, /* HANGUL LETTER NIEUN */ +{ XK_Hangul_NieunJieuj, 0x3135 }, /* HANGUL LETTER NIEUN-CIEUC */ +{ XK_Hangul_NieunHieuh, 0x3136 }, /* HANGUL LETTER NIEUN-HIEUH */ +{ XK_Hangul_Dikeud, 0x3137 }, /* HANGUL LETTER TIKEUT */ +{ XK_Hangul_SsangDikeud, 0x3138 }, /* HANGUL LETTER SSANGTIKEUT */ +{ XK_Hangul_Rieul, 0x3139 }, /* HANGUL LETTER RIEUL */ +{ XK_Hangul_RieulKiyeog, 0x313a }, /* HANGUL LETTER RIEUL-KIYEOK */ +{ XK_Hangul_RieulMieum, 0x313b }, /* HANGUL LETTER RIEUL-MIEUM */ +{ XK_Hangul_RieulPieub, 0x313c }, /* HANGUL LETTER RIEUL-PIEUP */ +{ XK_Hangul_RieulSios, 0x313d }, /* HANGUL LETTER RIEUL-SIOS */ +{ XK_Hangul_RieulTieut, 0x313e }, /* HANGUL LETTER RIEUL-THIEUTH */ +{ XK_Hangul_RieulPhieuf, 0x313f }, /* HANGUL LETTER RIEUL-PHIEUPH */ +{ XK_Hangul_RieulHieuh, 0x3140 }, /* HANGUL LETTER RIEUL-HIEUH */ +{ XK_Hangul_Mieum, 0x3141 }, /* HANGUL LETTER MIEUM */ +{ XK_Hangul_Pieub, 0x3142 }, /* HANGUL LETTER PIEUP */ +{ XK_Hangul_SsangPieub, 0x3143 }, /* HANGUL LETTER SSANGPIEUP */ +{ XK_Hangul_PieubSios, 0x3144 }, /* HANGUL LETTER PIEUP-SIOS */ +{ XK_Hangul_Sios, 0x3145 }, /* HANGUL LETTER SIOS */ +{ XK_Hangul_SsangSios, 0x3146 }, /* HANGUL LETTER SSANGSIOS */ +{ XK_Hangul_Ieung, 0x3147 }, /* HANGUL LETTER IEUNG */ +{ XK_Hangul_Jieuj, 0x3148 }, /* HANGUL LETTER CIEUC */ +{ XK_Hangul_SsangJieuj, 0x3149 }, /* HANGUL LETTER SSANGCIEUC */ +{ XK_Hangul_Cieuc, 0x314a }, /* HANGUL LETTER CHIEUCH */ +{ XK_Hangul_Khieuq, 0x314b }, /* HANGUL LETTER KHIEUKH */ +{ XK_Hangul_Tieut, 0x314c }, /* HANGUL LETTER THIEUTH */ +{ XK_Hangul_Phieuf, 0x314d }, /* HANGUL LETTER PHIEUPH */ +{ XK_Hangul_Hieuh, 0x314e }, /* HANGUL LETTER HIEUH */ +{ XK_Hangul_A, 0x314f }, /* HANGUL LETTER A */ +{ XK_Hangul_AE, 0x3150 }, /* HANGUL LETTER AE */ +{ XK_Hangul_YA, 0x3151 }, /* HANGUL LETTER YA */ +{ XK_Hangul_YAE, 0x3152 }, /* HANGUL LETTER YAE */ +{ XK_Hangul_EO, 0x3153 }, /* HANGUL LETTER EO */ +{ XK_Hangul_E, 0x3154 }, /* HANGUL LETTER E */ +{ XK_Hangul_YEO, 0x3155 }, /* HANGUL LETTER YEO */ +{ XK_Hangul_YE, 0x3156 }, /* HANGUL LETTER YE */ +{ XK_Hangul_O, 0x3157 }, /* HANGUL LETTER O */ +{ XK_Hangul_WA, 0x3158 }, /* HANGUL LETTER WA */ +{ XK_Hangul_WAE, 0x3159 }, /* HANGUL LETTER WAE */ +{ XK_Hangul_OE, 0x315a }, /* HANGUL LETTER OE */ +{ XK_Hangul_YO, 0x315b }, /* HANGUL LETTER YO */ +{ XK_Hangul_U, 0x315c }, /* HANGUL LETTER U */ +{ XK_Hangul_WEO, 0x315d }, /* HANGUL LETTER WEO */ +{ XK_Hangul_WE, 0x315e }, /* HANGUL LETTER WE */ +{ XK_Hangul_WI, 0x315f }, /* HANGUL LETTER WI */ +{ XK_Hangul_YU, 0x3160 }, /* HANGUL LETTER YU */ +{ XK_Hangul_EU, 0x3161 }, /* HANGUL LETTER EU */ +{ XK_Hangul_YI, 0x3162 }, /* HANGUL LETTER YI */ +{ XK_Hangul_I, 0x3163 }, /* HANGUL LETTER I */ +{ XK_Hangul_J_Kiyeog, 0x11a8 }, /* HANGUL JONGSEONG KIYEOK */ +{ XK_Hangul_J_SsangKiyeog, 0x11a9 }, /* HANGUL JONGSEONG SSANGKIYEOK */ +{ XK_Hangul_J_KiyeogSios, 0x11aa }, /* HANGUL JONGSEONG KIYEOK-SIOS */ +{ XK_Hangul_J_Nieun, 0x11ab }, /* HANGUL JONGSEONG NIEUN */ +{ XK_Hangul_J_NieunJieuj, 0x11ac }, /* HANGUL JONGSEONG NIEUN-CIEUC */ +{ XK_Hangul_J_NieunHieuh, 0x11ad }, /* HANGUL JONGSEONG NIEUN-HIEUH */ +{ XK_Hangul_J_Dikeud, 0x11ae }, /* HANGUL JONGSEONG TIKEUT */ +{ XK_Hangul_J_Rieul, 0x11af }, /* HANGUL JONGSEONG RIEUL */ +{ XK_Hangul_J_RieulKiyeog, 0x11b0 }, /* HANGUL JONGSEONG RIEUL-KIYEOK */ +{ XK_Hangul_J_RieulMieum, 0x11b1 }, /* HANGUL JONGSEONG RIEUL-MIEUM */ +{ XK_Hangul_J_RieulPieub, 0x11b2 }, /* HANGUL JONGSEONG RIEUL-PIEUP */ +{ XK_Hangul_J_RieulSios, 0x11b3 }, /* HANGUL JONGSEONG RIEUL-SIOS */ +{ XK_Hangul_J_RieulTieut, 0x11b4 }, /* HANGUL JONGSEONG RIEUL-THIEUTH */ +{ XK_Hangul_J_RieulPhieuf, 0x11b5 }, /* HANGUL JONGSEONG RIEUL-PHIEUPH */ +{ XK_Hangul_J_RieulHieuh, 0x11b6 }, /* HANGUL JONGSEONG RIEUL-HIEUH */ +{ XK_Hangul_J_Mieum, 0x11b7 }, /* HANGUL JONGSEONG MIEUM */ +{ XK_Hangul_J_Pieub, 0x11b8 }, /* HANGUL JONGSEONG PIEUP */ +{ XK_Hangul_J_PieubSios, 0x11b9 }, /* HANGUL JONGSEONG PIEUP-SIOS */ +{ XK_Hangul_J_Sios, 0x11ba }, /* HANGUL JONGSEONG SIOS */ +{ XK_Hangul_J_SsangSios, 0x11bb }, /* HANGUL JONGSEONG SSANGSIOS */ +{ XK_Hangul_J_Ieung, 0x11bc }, /* HANGUL JONGSEONG IEUNG */ +{ XK_Hangul_J_Jieuj, 0x11bd }, /* HANGUL JONGSEONG CIEUC */ +{ XK_Hangul_J_Cieuc, 0x11be }, /* HANGUL JONGSEONG CHIEUCH */ +{ XK_Hangul_J_Khieuq, 0x11bf }, /* HANGUL JONGSEONG KHIEUKH */ +{ XK_Hangul_J_Tieut, 0x11c0 }, /* HANGUL JONGSEONG THIEUTH */ +{ XK_Hangul_J_Phieuf, 0x11c1 }, /* HANGUL JONGSEONG PHIEUPH */ +{ XK_Hangul_J_Hieuh, 0x11c2 }, /* HANGUL JONGSEONG HIEUH */ +{ XK_Hangul_RieulYeorinHieuh, 0x316d }, /* HANGUL LETTER RIEUL-YEORINHIEUH */ +{ XK_Hangul_SunkyeongeumMieum, 0x3171 }, /* HANGUL LETTER KAPYEOUNMIEUM */ +{ XK_Hangul_SunkyeongeumPieub, 0x3178 }, /* HANGUL LETTER KAPYEOUNPIEUP */ +{ XK_Hangul_PanSios, 0x317f }, /* HANGUL LETTER PANSIOS */ +{ XK_Hangul_KkogjiDalrinIeung, 0x3181 }, /* HANGUL LETTER YESIEUNG */ +{ XK_Hangul_SunkyeongeumPhieuf, 0x3184 }, /* HANGUL LETTER KAPYEOUNPHIEUPH */ +{ XK_Hangul_YeorinHieuh, 0x3186 }, /* HANGUL LETTER YEORINHIEUH */ +{ XK_Hangul_AraeA, 0x318d }, /* HANGUL LETTER ARAEA */ +{ XK_Hangul_AraeAE, 0x318e }, /* HANGUL LETTER ARAEAE */ +{ XK_Hangul_J_PanSios, 0x11eb }, /* HANGUL JONGSEONG PANSIOS */ +{ XK_Hangul_J_KkogjiDalrinIeung, 0x11f0 }, /* HANGUL JONGSEONG YESIEUNG */ +{ XK_Hangul_J_YeorinHieuh, 0x11f9 }, /* HANGUL JONGSEONG YEORINHIEUH */ +{ XK_Korean_Won, 0x20a9 }, /* WON SIGN */ +{ XK_OE, 0x0152 }, /* LATIN CAPITAL LIGATURE OE */ +{ XK_oe, 0x0153 }, /* LATIN SMALL LIGATURE OE */ +{ XK_Ydiaeresis, 0x0178 }, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ +{ XK_EuroSign, 0x20ac }, /* EURO SIGN */ + +/* combining dead keys */ +{ XK_dead_abovedot, 0x0307 }, /* COMBINING DOT ABOVE */ +{ XK_dead_abovering, 0x030a }, /* COMBINING RING ABOVE */ +{ XK_dead_acute, 0x0301 }, /* COMBINING ACUTE ACCENT */ +{ XK_dead_breve, 0x0306 }, /* COMBINING BREVE */ +{ XK_dead_caron, 0x030c }, /* COMBINING CARON */ +{ XK_dead_cedilla, 0x0327 }, /* COMBINING CEDILLA */ +{ XK_dead_circumflex, 0x0302 }, /* COMBINING CIRCUMFLEX ACCENT */ +{ XK_dead_diaeresis, 0x0308 }, /* COMBINING DIAERESIS */ +{ XK_dead_doubleacute, 0x030b }, /* COMBINING DOUBLE ACUTE ACCENT */ +{ XK_dead_grave, 0x0300 }, /* COMBINING GRAVE ACCENT */ +{ XK_dead_macron, 0x0304 }, /* COMBINING MACRON */ +{ XK_dead_ogonek, 0x0328 }, /* COMBINING OGONEK */ +{ XK_dead_tilde, 0x0303 } /* COMBINING TILDE */ }; +/* XXX -- map these too +XK_Cyrillic_GHE_bar +XK_Cyrillic_ZHE_descender +XK_Cyrillic_KA_descender +XK_Cyrillic_KA_vertstroke +XK_Cyrillic_EN_descender +XK_Cyrillic_U_straight +XK_Cyrillic_U_straight_bar +XK_Cyrillic_HA_descender +XK_Cyrillic_CHE_descender +XK_Cyrillic_CHE_vertstroke +XK_Cyrillic_SHHA +XK_Cyrillic_SCHWA +XK_Cyrillic_I_macron +XK_Cyrillic_O_bar +XK_Cyrillic_U_macron +XK_Cyrillic_ghe_bar +XK_Cyrillic_zhe_descender +XK_Cyrillic_ka_descender +XK_Cyrillic_ka_vertstroke +XK_Cyrillic_en_descender +XK_Cyrillic_u_straight +XK_Cyrillic_u_straight_bar +XK_Cyrillic_ha_descender +XK_Cyrillic_che_descender +XK_Cyrillic_che_vertstroke +XK_Cyrillic_shha +XK_Cyrillic_schwa +XK_Cyrillic_i_macron +XK_Cyrillic_o_bar +XK_Cyrillic_u_macron -static const KeySym s_rawDeadDecomposeTable[] = { - // 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, +XK_Armenian_eternity +XK_Armenian_ligature_ew +XK_Armenian_full_stop +XK_Armenian_verjaket +XK_Armenian_parenright +XK_Armenian_parenleft +XK_Armenian_guillemotright +XK_Armenian_guillemotleft +XK_Armenian_em_dash +XK_Armenian_dot +XK_Armenian_mijaket +XK_Armenian_but +XK_Armenian_separation_mark +XK_Armenian_comma +XK_Armenian_en_dash +XK_Armenian_hyphen +XK_Armenian_yentamna +XK_Armenian_ellipsis +XK_Armenian_amanak +XK_Armenian_exclam +XK_Armenian_accent +XK_Armenian_shesht +XK_Armenian_paruyk +XK_Armenian_question +XK_Armenian_AYB +XK_Armenian_ayb +XK_Armenian_BEN +XK_Armenian_ben +XK_Armenian_GIM +XK_Armenian_gim +XK_Armenian_DA +XK_Armenian_da +XK_Armenian_YECH +XK_Armenian_yech +XK_Armenian_ZA +XK_Armenian_za +XK_Armenian_E +XK_Armenian_e +XK_Armenian_AT +XK_Armenian_at +XK_Armenian_TO +XK_Armenian_to +XK_Armenian_ZHE +XK_Armenian_zhe +XK_Armenian_INI +XK_Armenian_ini +XK_Armenian_LYUN +XK_Armenian_lyun +XK_Armenian_KHE +XK_Armenian_khe +XK_Armenian_TSA +XK_Armenian_tsa +XK_Armenian_KEN +XK_Armenian_ken +XK_Armenian_HO +XK_Armenian_ho +XK_Armenian_DZA +XK_Armenian_dza +XK_Armenian_GHAT +XK_Armenian_ghat +XK_Armenian_TCHE +XK_Armenian_tche +XK_Armenian_MEN +XK_Armenian_men +XK_Armenian_HI +XK_Armenian_hi +XK_Armenian_NU +XK_Armenian_nu +XK_Armenian_SHA +XK_Armenian_sha +XK_Armenian_VO +XK_Armenian_vo +XK_Armenian_CHA +XK_Armenian_cha +XK_Armenian_PE +XK_Armenian_pe +XK_Armenian_JE +XK_Armenian_je +XK_Armenian_RA +XK_Armenian_ra +XK_Armenian_SE +XK_Armenian_se +XK_Armenian_VEV +XK_Armenian_vev +XK_Armenian_TYUN +XK_Armenian_tyun +XK_Armenian_RE +XK_Armenian_re +XK_Armenian_TSO +XK_Armenian_tso +XK_Armenian_VYUN +XK_Armenian_vyun +XK_Armenian_PYUR +XK_Armenian_pyur +XK_Armenian_KE +XK_Armenian_ke +XK_Armenian_O +XK_Armenian_o +XK_Armenian_FE +XK_Armenian_fe +XK_Armenian_apostrophe +XK_Armenian_section_sign - // 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, +XK_Georgian_an +XK_Georgian_ban +XK_Georgian_gan +XK_Georgian_don +XK_Georgian_en +XK_Georgian_vin +XK_Georgian_zen +XK_Georgian_tan +XK_Georgian_in +XK_Georgian_kan +XK_Georgian_las +XK_Georgian_man +XK_Georgian_nar +XK_Georgian_on +XK_Georgian_par +XK_Georgian_zhar +XK_Georgian_rae +XK_Georgian_san +XK_Georgian_tar +XK_Georgian_un +XK_Georgian_phar +XK_Georgian_khar +XK_Georgian_ghan +XK_Georgian_qar +XK_Georgian_shin +XK_Georgian_chin +XK_Georgian_can +XK_Georgian_jil +XK_Georgian_cil +XK_Georgian_char +XK_Georgian_xan +XK_Georgian_jhan +XK_Georgian_hae +XK_Georgian_he +XK_Georgian_hie +XK_Georgian_we +XK_Georgian_har +XK_Georgian_hoe +XK_Georgian_fi - // 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, +XK_Ccedillaabovedot +XK_Xabovedot +XK_Qabovedot +XK_Ibreve +XK_IE +XK_UO +XK_Zstroke +XK_Gcaron +XK_Obarred +XK_ccedillaabovedot +XK_xabovedot +XK_Ocaron +XK_qabovedot +XK_ibreve +XK_ie +XK_uo +XK_zstroke +XK_gcaron +XK_ocaron +XK_obarred +XK_SCHWA +XK_Lbelowdot +XK_Lstrokebelowdot +XK_Gtilde +XK_lbelowdot +XK_lstrokebelowdot +XK_gtilde +XK_schwa - // 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, +XK_Abelowdot +XK_abelowdot +XK_Ahook +XK_ahook +XK_Acircumflexacute +XK_acircumflexacute +XK_Acircumflexgrave +XK_acircumflexgrave +XK_Acircumflexhook +XK_acircumflexhook +XK_Acircumflextilde +XK_acircumflextilde +XK_Acircumflexbelowdot +XK_acircumflexbelowdot +XK_Abreveacute +XK_abreveacute +XK_Abrevegrave +XK_abrevegrave +XK_Abrevehook +XK_abrevehook +XK_Abrevetilde +XK_abrevetilde +XK_Abrevebelowdot +XK_abrevebelowdot +XK_Ebelowdot +XK_ebelowdot +XK_Ehook +XK_ehook +XK_Etilde +XK_etilde +XK_Ecircumflexacute +XK_ecircumflexacute +XK_Ecircumflexgrave +XK_ecircumflexgrave +XK_Ecircumflexhook +XK_ecircumflexhook +XK_Ecircumflextilde +XK_ecircumflextilde +XK_Ecircumflexbelowdot +XK_ecircumflexbelowdot +XK_Ihook +XK_ihook +XK_Ibelowdot +XK_ibelowdot +XK_Obelowdot +XK_obelowdot +XK_Ohook +XK_ohook +XK_Ocircumflexacute +XK_ocircumflexacute +XK_Ocircumflexgrave +XK_ocircumflexgrave +XK_Ocircumflexhook +XK_ocircumflexhook +XK_Ocircumflextilde +XK_ocircumflextilde +XK_Ocircumflexbelowdot +XK_ocircumflexbelowdot +XK_Ohornacute +XK_ohornacute +XK_Ohorngrave +XK_ohorngrave +XK_Ohornhook +XK_ohornhook +XK_Ohorntilde +XK_ohorntilde +XK_Ohornbelowdot +XK_ohornbelowdot +XK_Ubelowdot +XK_ubelowdot +XK_Uhook +XK_uhook +XK_Uhornacute +XK_uhornacute +XK_Uhorngrave +XK_uhorngrave +XK_Uhornhook +XK_uhornhook +XK_Uhorntilde +XK_uhorntilde +XK_Uhornbelowdot +XK_uhornbelowdot +XK_Ybelowdot +XK_ybelowdot +XK_Yhook +XK_yhook +XK_Ytilde +XK_ytilde +XK_Ohorn +XK_ohorn +XK_Uhorn +XK_uhorn +*/ - // 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) -#if defined(XK_Babovedot) - 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, -#endif - - // Latin-9 (ISO 8859-15) -#if defined(XK_Ydiaeresis) - XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, -#endif - - // end of table - 0 -}; - -static const KeySym s_rawComposedDecomposeTable[] = { - XK_AE, XK_Multi_key, XK_A, XK_E, 0, - XK_Aacute, XK_Multi_key, XK_A, XK_apostrophe, 0, - XK_Acircumflex, XK_Multi_key, XK_A, XK_asciicircum, 0, - XK_Adiaeresis, XK_Multi_key, XK_A, XK_quotedbl, 0, - XK_Agrave, XK_Multi_key, XK_A, XK_grave, 0, - XK_Aring, XK_Multi_key, XK_A, XK_asterisk, 0, - XK_Atilde, XK_Multi_key, XK_A, XK_asciitilde, 0, - XK_Ccedilla, XK_Multi_key, XK_C, XK_comma, 0, - XK_ETH, XK_Multi_key, XK_D, XK_minus, 0, - XK_Eacute, XK_Multi_key, XK_E, XK_apostrophe, 0, - XK_Ecircumflex, XK_Multi_key, XK_E, XK_asciicircum, 0, - XK_Ediaeresis, XK_Multi_key, XK_E, XK_quotedbl, 0, - XK_Egrave, XK_Multi_key, XK_E, XK_grave, 0, - XK_Iacute, XK_Multi_key, XK_I, XK_apostrophe, 0, - XK_Icircumflex, XK_Multi_key, XK_I, XK_asciicircum, 0, - XK_Idiaeresis, XK_Multi_key, XK_I, XK_quotedbl, 0, - XK_Igrave, XK_Multi_key, XK_I, XK_grave, 0, - XK_Ntilde, XK_Multi_key, XK_N, XK_asciitilde, 0, - XK_Oacute, XK_Multi_key, XK_O, XK_apostrophe, 0, - XK_Ocircumflex, XK_Multi_key, XK_O, XK_asciicircum, 0, - XK_Odiaeresis, XK_Multi_key, XK_O, XK_quotedbl, 0, - XK_Ograve, XK_Multi_key, XK_O, XK_grave, 0, - XK_Ooblique, XK_Multi_key, XK_O, XK_slash, 0, - XK_Otilde, XK_Multi_key, XK_O, XK_asciitilde, 0, - XK_THORN, XK_Multi_key, XK_T, XK_H, 0, - XK_Uacute, XK_Multi_key, XK_U, XK_apostrophe, 0, - XK_Ucircumflex, XK_Multi_key, XK_U, XK_asciicircum, 0, - XK_Udiaeresis, XK_Multi_key, XK_U, XK_quotedbl, 0, - XK_Ugrave, XK_Multi_key, XK_U, XK_grave, 0, - XK_Yacute, XK_Multi_key, XK_Y, XK_apostrophe, 0, - XK_aacute, XK_Multi_key, XK_a, XK_apostrophe, 0, - XK_acircumflex, XK_Multi_key, XK_a, XK_asciicircum, 0, - XK_acute, XK_Multi_key, XK_apostrophe, XK_apostrophe, 0, - XK_adiaeresis, XK_Multi_key, XK_a, XK_quotedbl, 0, - XK_ae, XK_Multi_key, XK_a, XK_e, 0, - XK_agrave, XK_Multi_key, XK_a, XK_grave, 0, - XK_aring, XK_Multi_key, XK_a, XK_asterisk, 0, - XK_at, XK_Multi_key, XK_A, XK_T, 0, - XK_atilde, XK_Multi_key, XK_a, XK_asciitilde, 0, - XK_backslash, XK_Multi_key, XK_slash, XK_slash, 0, - XK_bar, XK_Multi_key, XK_L, XK_V, 0, - XK_braceleft, XK_Multi_key, XK_parenleft, XK_minus, 0, - XK_braceright, XK_Multi_key, XK_parenright, XK_minus, 0, - XK_bracketleft, XK_Multi_key, XK_parenleft, XK_parenleft, 0, - XK_bracketright, XK_Multi_key, XK_parenright, XK_parenright, 0, - XK_brokenbar, XK_Multi_key, XK_B, XK_V, 0, - XK_ccedilla, XK_Multi_key, XK_c, XK_comma, 0, - XK_cedilla, XK_Multi_key, XK_comma, XK_comma, 0, - XK_cent, XK_Multi_key, XK_c, XK_slash, 0, - XK_copyright, XK_Multi_key, XK_parenleft, XK_c, 0, - XK_currency, XK_Multi_key, XK_o, XK_x, 0, - XK_degree, XK_Multi_key, XK_0, XK_asciicircum, 0, - XK_diaeresis, XK_Multi_key, XK_quotedbl, XK_quotedbl, 0, - XK_division, XK_Multi_key, XK_colon, XK_minus, 0, - XK_eacute, XK_Multi_key, XK_e, XK_apostrophe, 0, - XK_ecircumflex, XK_Multi_key, XK_e, XK_asciicircum, 0, - XK_ediaeresis, XK_Multi_key, XK_e, XK_quotedbl, 0, - XK_egrave, XK_Multi_key, XK_e, XK_grave, 0, - XK_eth, XK_Multi_key, XK_d, XK_minus, 0, - XK_exclamdown, XK_Multi_key, XK_exclam, XK_exclam, 0, - XK_guillemotleft, XK_Multi_key, XK_less, XK_less, 0, - XK_guillemotright, XK_Multi_key, XK_greater, XK_greater, 0, - XK_numbersign, XK_Multi_key, XK_plus, XK_plus, 0, - XK_hyphen, XK_Multi_key, XK_minus, XK_minus, 0, - XK_iacute, XK_Multi_key, XK_i, XK_apostrophe, 0, - XK_icircumflex, XK_Multi_key, XK_i, XK_asciicircum, 0, - XK_idiaeresis, XK_Multi_key, XK_i, XK_quotedbl, 0, - XK_igrave, XK_Multi_key, XK_i, XK_grave, 0, - XK_macron, XK_Multi_key, XK_minus, XK_asciicircum, 0, - XK_masculine, XK_Multi_key, XK_o, XK_underscore, 0, - XK_mu, XK_Multi_key, XK_u, XK_slash, 0, - XK_multiply, XK_Multi_key, XK_x, XK_x, 0, - XK_nobreakspace, XK_Multi_key, XK_space, XK_space, 0, - XK_notsign, XK_Multi_key, XK_comma, XK_minus, 0, - XK_ntilde, XK_Multi_key, XK_n, XK_asciitilde, 0, - XK_oacute, XK_Multi_key, XK_o, XK_apostrophe, 0, - XK_ocircumflex, XK_Multi_key, XK_o, XK_asciicircum, 0, - XK_odiaeresis, XK_Multi_key, XK_o, XK_quotedbl, 0, - XK_ograve, XK_Multi_key, XK_o, XK_grave, 0, - XK_onehalf, XK_Multi_key, XK_1, XK_2, 0, - XK_onequarter, XK_Multi_key, XK_1, XK_4, 0, - XK_onesuperior, XK_Multi_key, XK_1, XK_asciicircum, 0, - XK_ordfeminine, XK_Multi_key, XK_a, XK_underscore, 0, - XK_oslash, XK_Multi_key, XK_o, XK_slash, 0, - XK_otilde, XK_Multi_key, XK_o, XK_asciitilde, 0, - XK_paragraph, XK_Multi_key, XK_p, XK_exclam, 0, - XK_periodcentered, XK_Multi_key, XK_period, XK_period, 0, - XK_plusminus, XK_Multi_key, XK_plus, XK_minus, 0, - XK_questiondown, XK_Multi_key, XK_question, XK_question, 0, - XK_registered, XK_Multi_key, XK_parenleft, XK_r, 0, - XK_section, XK_Multi_key, XK_s, XK_o, 0, - XK_ssharp, XK_Multi_key, XK_s, XK_s, 0, - XK_sterling, XK_Multi_key, XK_L, XK_minus, 0, - XK_thorn, XK_Multi_key, XK_t, XK_h, 0, - XK_threequarters, XK_Multi_key, XK_3, XK_4, 0, - XK_threesuperior, XK_Multi_key, XK_3, XK_asciicircum, 0, - XK_twosuperior, XK_Multi_key, XK_2, XK_asciicircum, 0, - XK_uacute, XK_Multi_key, XK_u, XK_apostrophe, 0, - XK_ucircumflex, XK_Multi_key, XK_u, XK_asciicircum, 0, - XK_udiaeresis, XK_Multi_key, XK_u, XK_quotedbl, 0, - XK_ugrave, XK_Multi_key, XK_u, XK_grave, 0, - XK_yacute, XK_Multi_key, XK_y, XK_apostrophe, 0, - XK_ydiaeresis, XK_Multi_key, XK_y, XK_quotedbl, 0, - XK_yen, XK_Multi_key, XK_y, XK_equal, 0, - - // end of table - 0 +// map "Internet" keys to KeyIDs +static const KeySym s_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, kKeyEject, 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 }; @@ -1143,9 +1265,6 @@ static const KeySym s_rawComposedDecomposeTable[] = { // CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; -CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; -CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_deadKeyDecomposedKeySyms; -CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_composeDecomposedKeySyms; bool CXWindowsUtil::getWindowProperty(Display* display, Window window, @@ -1314,82 +1433,177 @@ CXWindowsUtil::getCurrentTime(Display* display, Window window) return xevent.xproperty.time; } +KeyID +CXWindowsUtil::mapKeySymToKeyID(KeySym k) +{ + initKeyMaps(); + + switch (k & 0xffffff00) { + case 0x0000: + // Latin-1 + return static_cast(k); + + case 0xfe00: + // ISO 9995 Function and Modifier Keys + switch (k) { + case XK_ISO_Left_Tab: + return kKeyLeftTab; + + case XK_ISO_Level3_Shift: + return kKeyAltGr; + + case XK_ISO_Next_Group: + return kKeyNextGroup; + + case XK_ISO_Prev_Group: + return kKeyPrevGroup; + + case XK_dead_grave: + return kKeyDeadGrave; + + case XK_dead_acute: + return kKeyDeadAcute; + + case XK_dead_circumflex: + return kKeyDeadCircumflex; + + case XK_dead_tilde: + return kKeyDeadTilde; + + case XK_dead_macron: + return kKeyDeadMacron; + + case XK_dead_breve: + return kKeyDeadBreve; + + case XK_dead_abovedot: + return kKeyDeadAbovedot; + + case XK_dead_diaeresis: + return kKeyDeadDiaeresis; + + case XK_dead_abovering: + return kKeyDeadAbovering; + + case XK_dead_doubleacute: + return kKeyDeadDoubleacute; + + case XK_dead_caron: + return kKeyDeadCaron; + + case XK_dead_cedilla: + return kKeyDeadCedilla; + + case XK_dead_ogonek: + return kKeyDeadOgonek; + + default: + return kKeyNone; + } + + case 0xff00: + // MISCELLANY + return static_cast(k - 0xff00 + 0xef00); + + case 0x1008ff00: + // "Internet" keys + return s_map1008FF[k & 0xff]; + + default: { + // lookup character in table + CKeySymMap::const_iterator index = s_keySymToUCS4.find(k); + if (index != s_keySymToUCS4.end()) { + return static_cast(index->second); + } + + // unknown character + return kKeyNone; + } + } +} + UInt32 -CXWindowsUtil::mapKeySymToUCS4(KeySym k) +CXWindowsUtil::getModifierBitForKeySym(KeySym keysym) { - initKeyMaps(); + switch (keysym) { + case XK_Shift_L: + case XK_Shift_R: + return kKeyModifierBitShift; - CKeySymMap::const_iterator index = s_keySymToUCS4.find(k); - if (index != s_keySymToUCS4.end()) { - return index->second; - } - else { - return 0x0000ffff; - } -} + case XK_Control_L: + case XK_Control_R: + return kKeyModifierBitControl; -KeySym -CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) -{ - initKeyMaps(); + case XK_Alt_L: + case XK_Alt_R: + return kKeyModifierBitAlt; - CUCS4Map::const_iterator index = s_UCS4ToKeySym.find(c); - if (index != s_UCS4ToKeySym.end()) { - return index->second; - } - else { - return NoSymbol; - } -} + case XK_Meta_L: + case XK_Meta_R: + return kKeyModifierBitMeta; -bool -CXWindowsUtil::decomposeKeySymWithDeadKeys(KeySym keysym, KeySyms& decomposed) -{ - // 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. - initKeyMaps(); - CKeySymsMap::const_iterator i = s_deadKeyDecomposedKeySyms.find(keysym); - if (i != s_deadKeyDecomposedKeySyms.end()) { - decomposed = i->second; - return true; - } - return false; -} + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + return kKeyModifierBitSuper; -bool -CXWindowsUtil::decomposeKeySymWithCompose(KeySym keysym, KeySyms& decomposed) -{ - // 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. - initKeyMaps(); - CKeySymsMap::const_iterator i = s_composeDecomposedKeySyms.find(keysym); - if (i != s_composeDecomposedKeySyms.end()) { - decomposed = i->second; - return true; + case XK_Mode_switch: + case XK_ISO_Level3_Shift: + return kKeyModifierBitAltGr; + + case XK_Caps_Lock: + return kKeyModifierBitCapsLock; + + case XK_Num_Lock: + return kKeyModifierBitNumLock; + + case XK_Scroll_Lock: + return kKeyModifierBitScrollLock; + + default: + return kKeyModifierBitNone; } - return false; } CString CXWindowsUtil::atomToString(Display* display, Atom atom) { + if (atom == 0) { + return "None"; + } + + bool error = false; + CXWindowsUtil::CErrorLock lock(display, &error); char* name = XGetAtomName(display, atom); - CString msg = CStringUtil::print("%s (%d)", name, (int)atom); - XFree(name); - return msg; + if (error) { + return CStringUtil::print(" (%d)", (int)atom); + } + else { + CString msg = CStringUtil::print("%s (%d)", name, (int)atom); + XFree(name); + return msg; + } } CString CXWindowsUtil::atomsToString(Display* display, const Atom* atom, UInt32 num) { char** names = new char*[num]; + bool error = false; + CXWindowsUtil::CErrorLock lock(display, &error); XGetAtomNames(display, const_cast(atom), (int)num, names); CString msg; - for (UInt32 i = 0; i < num; ++i) { - msg += CStringUtil::print("%s (%d), ", names[i], (int)atom[i]); - XFree(names[i]); + if (error) { + for (UInt32 i = 0; i < num; ++i) { + msg += CStringUtil::print(" (%d), ", (int)atom[i]); + } + } + else { + for (UInt32 i = 0; i < num; ++i) { + msg += CStringUtil::print("%s (%d), ", names[i], (int)atom[i]); + XFree(names[i]); + } } delete[] names; if (msg.size() > 2) { @@ -1398,6 +1612,41 @@ CXWindowsUtil::atomsToString(Display* display, const Atom* atom, UInt32 num) return msg; } +void +CXWindowsUtil::convertAtomProperty(CString& data) +{ + // as best i can tell, 64-bit systems don't pack Atoms into properties + // as 32-bit numbers but rather as the 64-bit numbers they are. that + // seems wrong but we have to cope. sometimes we'll get a list of + // atoms that's 8*n+4 bytes long, missing the trailing 4 bytes which + // should all be 0. since we're going to reference the Atoms as + // 64-bit numbers we have to ensure the last number is a full 64 bits. + if (sizeof(Atom) != 4 && ((data.size() / 4) & 1) != 0) { + UInt32 zero = 0; + data.append(reinterpret_cast(&zero), sizeof(zero)); + } +} + +void +CXWindowsUtil::appendAtomData(CString& data, Atom atom) +{ + data.append(reinterpret_cast(&atom), sizeof(Atom)); +} + +void +CXWindowsUtil::replaceAtomData(CString& data, UInt32 index, Atom atom) +{ + data.replace(index * sizeof(Atom), sizeof(Atom), + reinterpret_cast(&atom), + sizeof(Atom)); +} + +void +CXWindowsUtil::appendTimeData(CString& data, Time time) +{ + data.append(reinterpret_cast(&time), sizeof(Time)); +} + Bool CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { @@ -1412,38 +1661,9 @@ CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) 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 s_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; - } - } - - // fill decomposed key table if not filled yet - if (s_deadKeyDecomposedKeySyms.empty()) { - for (const KeySym* scan = s_rawDeadDecomposeTable; *scan != 0; ++scan) { - // add an entry for this keysym - KeySyms& entry = s_deadKeyDecomposedKeySyms[*scan]; - - // add the decomposed keysyms for the keysym - while (*++scan != 0) { - entry.push_back(*scan); - } - } - } - if (s_composeDecomposedKeySyms.empty()) { - for (const KeySym* scan = - s_rawComposedDecomposeTable; *scan != 0; ++scan) { - // add an entry for this keysym - KeySyms& entry = s_composeDecomposedKeySyms[*scan]; - - // add the decomposed keysyms for the keysym - while (*++scan != 0) { - entry.push_back(*scan); - } } } } diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index 13e9313e..a9049ef6 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -59,37 +59,19 @@ public: */ static Time getCurrentTime(Display*, Window); - //! Convert KeySym to UCS-4 + //! Convert KeySym to KeyID /*! - Converts a KeySym to the equivalent UCS-4 character. Returns - 0x0000ffff if the KeySym cannot be mapped. + Converts a KeySym to the equivalent KeyID. Returns kKeyNone if the + KeySym cannot be mapped. */ - static UInt32 mapKeySymToUCS4(KeySym); + static UInt32 mapKeySymToKeyID(KeySym); - //! Convert UCS-4 to KeySym + //! Convert KeySym to corresponding KeyModifierMask /*! - Converts a UCS-4 character to the equivalent KeySym. Returns - NoSymbol (0) if the character cannot be mapped. + Converts a KeySym to the corresponding KeyModifierMask, or 0 if the + KeySym is not a modifier. */ - static KeySym mapUCS4ToKeySym(UInt32); - - //! Decompose a KeySym using dead keys - /*! - Decomposes \c keysym into its component keysyms. All but the last - decomposed KeySym are dead keys. Returns true iff the decomposition - was successful. - */ - static bool decomposeKeySymWithDeadKeys(KeySym keysym, - KeySyms& decomposed); - - //! Decompose a KeySym using the compose key - /*! - Decomposes \c keysym into its component keysyms. The first key is - Multi_key and the rest are normal (i.e. not dead) keys. Returns - true iff the decomposition was successful. - */ - static bool decomposeKeySymWithCompose(KeySym keysym, - KeySyms& decomposed); + static UInt32 getModifierBitForKeySym(KeySym keysym); //! Convert Atom to its string /*! @@ -105,6 +87,35 @@ public: static CString atomsToString(Display* display, const Atom* atom, UInt32 num); + //! Prepare a property of atoms for use + /*! + 64-bit systems may need to modify a property's data if it's a + list of Atoms before using it. + */ + static void convertAtomProperty(CString& data); + + //! Append an Atom to property data + /*! + Converts \p atom to a 32-bit on-the-wire format and appends it to + \p data. + */ + static void appendAtomData(CString& data, Atom atom); + + //! Replace an Atom in property data + /*! + Converts \p atom to a 32-bit on-the-wire format and replaces the atom + at index \p index in \p data. + */ + static void replaceAtomData(CString& data, + UInt32 index, Atom atom); + + //! Append an Time to property data + /*! + Converts \p time to a 32-bit on-the-wire format and appends it to + \p data. + */ + static void appendTimeData(CString& data, Time time); + //! X11 error handler /*! This class sets an X error handler in the c'tor and restores the @@ -167,13 +178,8 @@ private: private: typedef std::map CKeySymMap; - typedef std::map CUCS4Map; - typedef std::map CKeySymsMap; static CKeySymMap s_keySymToUCS4; - static CUCS4Map s_UCS4ToKeySym; - static CKeySymsMap s_deadKeyDecomposedKeySyms; - static CKeySymsMap s_composeDecomposedKeySyms; }; #endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 116d6566..8c9a10e0 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -78,6 +78,7 @@ CARBON_SOURCE_FILES = \ COSXKeyState.cpp \ COSXScreen.cpp \ COSXScreenSaver.cpp \ + COSXScreenSaverUtil.m \ COSXClipboard.h \ COSXClipboardAnyTextConverter.h \ COSXClipboardTextConverter.h \ @@ -86,6 +87,8 @@ CARBON_SOURCE_FILES = \ COSXKeyState.h \ COSXScreen.h \ COSXScreenSaver.h \ + COSXScreenSaverUtil.h \ + OSXScreenSaverControl.h \ $(NULL) EXTRA_DIST = \ diff --git a/lib/platform/OSXScreenSaverControl.h b/lib/platform/OSXScreenSaverControl.h new file mode 100644 index 00000000..75aecb17 --- /dev/null +++ b/lib/platform/OSXScreenSaverControl.h @@ -0,0 +1,36 @@ +// ScreenSaver.framework private API +// Class dumping by Alex Harper http://www.ragingmenace.com/ + +#import + +@protocol ScreenSaverControl +- (double)screenSaverTimeRemaining; +- (void)restartForUser:fp12; +- (void)screenSaverStopNow; +- (void)screenSaverStartNow; +- (void)setScreenSaverCanRun:(char)fp12; +- (BOOL)screenSaverCanRun; +- (BOOL)screenSaverIsRunning; +@end + + +@interface ScreenSaverController:NSObject + ++ controller; ++ monitor; ++ daemonConnectionName; ++ daemonPath; ++ enginePath; +- init; +- (void)dealloc; +- (void)_connectionClosed:fp12; +- (BOOL)screenSaverIsRunning; +- (BOOL)screenSaverCanRun; +- (void)setScreenSaverCanRun:(char)fp12; +- (void)screenSaverStartNow; +- (void)screenSaverStopNow; +- (void)restartForUser:fp12; +- (double)screenSaverTimeRemaining; + +@end + diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index caab922e..1333f52d 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\platform.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\platform.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\platform.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\platform.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index 905c5aa6..df014508 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # 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 /Fd..\..\gen\build\synrgyhk.pdb +# ADD CPP /nologo /MT /W4 /GR /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"..\..\gen\build\synrgyhk.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # 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 /Fd..\..\gen\debug\synrgyhk.pdb +# ADD CPP /nologo /MTd /W4 /Gm /GR /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"..\..\gen\debug\synrgyhk.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/lib/server/CBaseClientProxy.cpp b/lib/server/CBaseClientProxy.cpp new file mode 100644 index 00000000..0f049e35 --- /dev/null +++ b/lib/server/CBaseClientProxy.cpp @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 "CBaseClientProxy.h" + +// +// CBaseClientProxy +// + +CBaseClientProxy::CBaseClientProxy(const CString& name) : + m_name(name), + m_x(0), + m_y(0) +{ + // do nothing +} + +CBaseClientProxy::~CBaseClientProxy() +{ + // do nothing +} + +void +CBaseClientProxy::setJumpCursorPos(SInt32 x, SInt32 y) +{ + m_x = x; + m_y = y; +} + +void +CBaseClientProxy::getJumpCursorPos(SInt32& x, SInt32& y) const +{ + x = m_x; + y = m_y; +} + +CString +CBaseClientProxy::getName() const +{ + return m_name; +} diff --git a/lib/server/CBaseClientProxy.h b/lib/server/CBaseClientProxy.h new file mode 100644 index 00000000..e9cceca4 --- /dev/null +++ b/lib/server/CBaseClientProxy.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 CBASECLIENTPROXY_H +#define CBASECLIENTPROXY_H + +#include "IClient.h" +#include "CString.h" + +//! Generic proxy for client or primary +class CBaseClientProxy : public IClient { +public: + /*! + \c name is the name of the client. + */ + CBaseClientProxy(const CString& name); + ~CBaseClientProxy(); + + //! @name manipulators + //@{ + + //! Save cursor position + /*! + Save the position of the cursor when jumping from client. + */ + void setJumpCursorPos(SInt32 x, SInt32 y); + + //@} + //! @name accessors + //@{ + + //! Get cursor position + /*! + Get the position of the cursor when last jumping from client. + */ + void getJumpCursorPos(SInt32& x, SInt32& y) const; + + //@} + + // IScreen + virtual void* getEventTarget() const = 0; + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + // IClient overrides + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver) = 0; + virtual bool leave() = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 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 mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0; + virtual void screensaver(bool activate) = 0; + virtual void resetOptions() = 0; + virtual void setOptions(const COptionsList& options) = 0; + virtual CString getName() const; + +private: + CString m_name; + SInt32 m_x, m_y; +}; + +#endif diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp index 99772cd9..715126d5 100644 --- a/lib/server/CClientProxy.cpp +++ b/lib/server/CClientProxy.cpp @@ -26,7 +26,7 @@ CEvent::Type CClientProxy::s_disconnectedEvent = CEvent::kUnknown; CEvent::Type CClientProxy::s_clipboardChangedEvent= CEvent::kUnknown; CClientProxy::CClientProxy(const CString& name, IStream* stream) : - m_name(name), + CBaseClientProxy(name), m_stream(stream) { // do nothing @@ -53,12 +53,6 @@ CClientProxy::getStream() const return m_stream; } -CString -CClientProxy::getName() const -{ - return m_name; -} - CEvent::Type CClientProxy::getReadyEvent() { diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index b8e38fa8..51ad014b 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -15,14 +15,14 @@ #ifndef CCLIENTPROXY_H #define CCLIENTPROXY_H -#include "IClient.h" +#include "CBaseClientProxy.h" #include "CEvent.h" #include "CString.h" class IStream; //! Generic proxy for client -class CClientProxy : public IClient { +class CClientProxy : public CBaseClientProxy { public: /*! \c name is the name of the client. @@ -97,14 +97,12 @@ public: virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; - virtual void mouseWheel(SInt32 delta) = 0; + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0; virtual void screensaver(bool activate) = 0; virtual void resetOptions() = 0; virtual void setOptions(const COptionsList& options) = 0; - virtual CString getName() const; private: - CString m_name; IStream* m_stream; static CEvent::Type s_readyEvent; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 3d2bd7e0..d4e06bd2 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -27,7 +27,6 @@ CClientProxy1_0::CClientProxy1_0(const CString& name, IStream* stream) : CClientProxy(name, stream), - m_heartbeatAlarm(kHeartRate * kHeartBeatsUntilDeath), m_heartbeatTimer(NULL), m_parser(&CClientProxy1_0::parseHandshakeMessage) { @@ -52,6 +51,8 @@ CClientProxy1_0::CClientProxy1_0(const CString& name, IStream* stream) : new TMethodEventJob(this, &CClientProxy1_0::handleFlatline, NULL)); + setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath); + LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); CProtocolUtil::writef(getStream(), kMsgQInfo); } @@ -104,6 +105,26 @@ CClientProxy1_0::removeHeartbeatTimer() } } +void +CClientProxy1_0::resetHeartbeatTimer() +{ + // reset the alarm + removeHeartbeatTimer(); + addHeartbeatTimer(); +} + +void +CClientProxy1_0::resetHeartbeatRate() +{ + setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath); +} + +void +CClientProxy1_0::setHeartbeatRate(double, double alarm) +{ + m_heartbeatAlarm = alarm; +} + void CClientProxy1_0::handleData(const CEvent&, void*) { @@ -121,7 +142,7 @@ CClientProxy1_0::handleData(const CEvent&, void*) // parse message LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); if (!(this->*m_parser)(code)) { - LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); + LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); disconnect(); return; } @@ -131,8 +152,7 @@ CClientProxy1_0::handleData(const CEvent&, void*) } // restart heartbeat timer - removeHeartbeatTimer(); - addHeartbeatTimer(); + resetHeartbeatTimer(); } bool @@ -148,6 +168,7 @@ CClientProxy1_0::parseHandshakeMessage(const UInt8* code) m_parser = &CClientProxy1_0::parseMessage; if (recvInfo()) { EVENTQUEUE->addEvent(CEvent(getReadyEvent(), getEventTarget())); + addHeartbeatTimer(); return true; } } @@ -325,10 +346,11 @@ CClientProxy1_0::mouseRelativeMove(SInt32, SInt32) } void -CClientProxy1_0::mouseWheel(SInt32 delta) +CClientProxy1_0::mouseWheel(SInt32, SInt32 yDelta) { - LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); - CProtocolUtil::writef(getStream(), kMsgDMouseWheel, delta); + // clients prior to 1.3 only support the y axis + LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), yDelta)); + CProtocolUtil::writef(getStream(), kMsgDMouseWheel1_0, yDelta); } void @@ -345,7 +367,7 @@ CClientProxy1_0::resetOptions() CProtocolUtil::writef(getStream(), kMsgCResetOptions); // reset heart rate and death - m_heartbeatAlarm = kHeartRate * kHeartBeatsUntilDeath; + resetHeartbeatRate(); removeHeartbeatTimer(); addHeartbeatTimer(); } @@ -363,7 +385,7 @@ CClientProxy1_0::setOptions(const COptionsList& options) if (rate <= 0.0) { rate = -1.0; } - m_heartbeatAlarm = rate * kHeartBeatsUntilDeath; + setHeartbeatRate(rate, rate * kHeartBeatsUntilDeath); removeHeartbeatTimer(); addHeartbeatTimer(); } diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 4f752085..9ebfc18d 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -50,7 +50,7 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); - virtual void mouseWheel(SInt32 delta); + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); @@ -59,11 +59,15 @@ protected: virtual bool parseHandshakeMessage(const UInt8* code); virtual bool parseMessage(const UInt8* code); + virtual void resetHeartbeatRate(); + virtual void setHeartbeatRate(double rate, double alarm); + virtual void resetHeartbeatTimer(); + virtual void addHeartbeatTimer(); + virtual void removeHeartbeatTimer(); + private: void disconnect(); void removeHandlers(); - void addHeartbeatTimer(); - void removeHeartbeatTimer(); void handleData(const CEvent&, void*); void handleDisconnect(const CEvent&, void*); diff --git a/lib/server/CClientProxy1_3.cpp b/lib/server/CClientProxy1_3.cpp new file mode 100644 index 00000000..f68b550e --- /dev/null +++ b/lib/server/CClientProxy1_3.cpp @@ -0,0 +1,114 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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_3.h" +#include "CProtocolUtil.h" +#include "CLog.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" + +// +// CClientProxy1_3 +// + +CClientProxy1_3::CClientProxy1_3(const CString& name, IStream* stream) : + CClientProxy1_2(name, stream), + m_keepAliveRate(kKeepAliveRate), + m_keepAliveTimer(NULL) +{ + setHeartbeatRate(kKeepAliveRate, kKeepAliveRate * kKeepAlivesUntilDeath); +} + +CClientProxy1_3::~CClientProxy1_3() +{ + // cannot do this in superclass or our override wouldn't get called + removeHeartbeatTimer(); +} + +void +CClientProxy1_3::mouseWheel(SInt32 xDelta, SInt32 yDelta) +{ + LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d,%+d", getName().c_str(), xDelta, yDelta)); + CProtocolUtil::writef(getStream(), kMsgDMouseWheel, xDelta, yDelta); +} + +bool +CClientProxy1_3::parseMessage(const UInt8* code) +{ + // process message + if (memcmp(code, kMsgCKeepAlive, 4) == 0) { + // reset alarm + resetHeartbeatTimer(); + return true; + } + else { + return CClientProxy1_2::parseMessage(code); + } +} + +void +CClientProxy1_3::resetHeartbeatRate() +{ + setHeartbeatRate(kKeepAliveRate, kKeepAliveRate * kKeepAlivesUntilDeath); +} + +void +CClientProxy1_3::setHeartbeatRate(double rate, double) +{ + m_keepAliveRate = rate; + CClientProxy1_2::setHeartbeatRate(rate, rate * kKeepAlivesUntilDeath); +} + +void +CClientProxy1_3::resetHeartbeatTimer() +{ + // reset the alarm but not the keep alive timer + CClientProxy1_2::removeHeartbeatTimer(); + CClientProxy1_2::addHeartbeatTimer(); +} + +void +CClientProxy1_3::addHeartbeatTimer() +{ + // create and install a timer to periodically send keep alives + if (m_keepAliveRate > 0.0) { + m_keepAliveTimer = EVENTQUEUE->newTimer(m_keepAliveRate, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_keepAliveTimer, + new TMethodEventJob(this, + &CClientProxy1_3::handleKeepAlive, NULL)); + } + + // superclass does the alarm + CClientProxy1_2::addHeartbeatTimer(); +} + +void +CClientProxy1_3::removeHeartbeatTimer() +{ + // remove the timer that sends keep alives periodically + if (m_keepAliveTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_keepAliveTimer); + EVENTQUEUE->deleteTimer(m_keepAliveTimer); + m_keepAliveTimer = NULL; + } + + // superclass does the alarm + CClientProxy1_2::removeHeartbeatTimer(); +} + +void +CClientProxy1_3::handleKeepAlive(const CEvent&, void*) +{ + CProtocolUtil::writef(getStream(), kMsgCKeepAlive); +} diff --git a/lib/server/CClientProxy1_3.h b/lib/server/CClientProxy1_3.h new file mode 100644 index 00000000..681094b6 --- /dev/null +++ b/lib/server/CClientProxy1_3.h @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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_3_H +#define CCLIENTPROXY1_3_H + +#include "CClientProxy1_2.h" + +//! Proxy for client implementing protocol version 1.3 +class CClientProxy1_3 : public CClientProxy1_2 { +public: + CClientProxy1_3(const CString& name, IStream* adoptedStream); + ~CClientProxy1_3(); + + // IClient overrides + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); + +protected: + // CClientProxy overrides + virtual bool parseMessage(const UInt8* code); + virtual void resetHeartbeatRate(); + virtual void setHeartbeatRate(double rate, double alarm); + virtual void resetHeartbeatTimer(); + virtual void addHeartbeatTimer(); + virtual void removeHeartbeatTimer(); + +private: + void handleKeepAlive(const CEvent&, void*); + + +private: + double m_keepAliveRate; + CEventQueueTimer* m_keepAliveTimer; +}; + +#endif diff --git a/lib/server/CClientProxyUnknown.cpp b/lib/server/CClientProxyUnknown.cpp index 806f23b8..51a25efa 100644 --- a/lib/server/CClientProxyUnknown.cpp +++ b/lib/server/CClientProxyUnknown.cpp @@ -16,6 +16,7 @@ #include "CClientProxy1_0.h" #include "CClientProxy1_1.h" #include "CClientProxy1_2.h" +#include "CClientProxy1_3.h" #include "ProtocolTypes.h" #include "CProtocolUtil.h" #include "XSynergy.h" @@ -219,6 +220,10 @@ CClientProxyUnknown::handleData(const CEvent&, void*) case 2: m_proxy = new CClientProxy1_2(name, m_stream); break; + + case 3: + m_proxy = new CClientProxy1_3(name, m_stream); + break; } } diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 360379fb..a502fe78 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -13,6 +13,8 @@ */ #include "CConfig.h" +#include "CServer.h" +#include "CKeyMap.h" #include "KeyTypes.h" #include "XSocket.h" #include "stdistream.h" @@ -23,7 +25,7 @@ // CConfig // -CConfig::CConfig() +CConfig::CConfig() : m_hasLockToScreenAction(false) { // do nothing } @@ -78,13 +80,9 @@ CConfig::renameScreen(const CString& oldName, m_nameToCanonicalName.insert(std::make_pair(newName, newName)); // update connections + CName oldNameObj(this, oldName); 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; - } - } + index->second.rename(oldNameObj, newName); } // update alias targets @@ -115,13 +113,9 @@ CConfig::removeScreen(const CString& name) m_map.erase(index); // disconnect + CName nameObj(this, name); 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(); - } - } + index->second.remove(nameObj); } // remove aliases (and canonical name) @@ -182,6 +176,28 @@ CConfig::removeAlias(const CString& alias) return true; } +bool +CConfig::removeAliases(const CString& canonical) +{ + // must be a canonical name + if (m_map.find(canonical) == m_map.end()) { + return false; + } + + // find and removing matching aliases + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ) { + if (index->second == canonical && index->first != canonical) { + m_nameToCanonicalName.erase(index++); + } + else { + ++index; + } + } + + return true; +} + void CConfig::removeAllAliases() { @@ -198,7 +214,10 @@ CConfig::removeAllAliases() bool CConfig::connect(const CString& srcName, - EDirection srcSide, const CString& dstName) + EDirection srcSide, + float srcStart, float srcEnd, + const CString& dstName, + float dstStart, float dstEnd) { assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); @@ -208,12 +227,10 @@ CConfig::connect(const CString& srcName, 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; + // add link + CCellEdge srcEdge(srcSide, CInterval(srcStart, srcEnd)); + CCellEdge dstEdge(dstName, srcSide, CInterval(dstStart, dstEnd)); + return index->second.add(srcEdge, dstEdge); } bool @@ -228,7 +245,24 @@ CConfig::disconnect(const CString& srcName, EDirection srcSide) } // disconnect side - index->second.m_neighbor[srcSide - kFirstDirection].erase(); + index->second.remove(srcSide); + + return true; +} + +bool +CConfig::disconnect(const CString& srcName, EDirection srcSide, float position) +{ + 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.remove(srcSide, position); return true; } @@ -399,7 +433,8 @@ CConfig::isScreen(const CString& name) const bool CConfig::isCanonicalName(const CString& name) const { - return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); + return (!name.empty() && + CStringUtil::CaselessCmp::equal(getCanonicalName(name), name)); } CString @@ -415,7 +450,8 @@ CConfig::getCanonicalName(const CString& name) const } CString -CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const +CConfig::getNeighbor(const CString& srcName, EDirection srcSide, + float position, float* positionOut) const { assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); @@ -425,9 +461,59 @@ CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const return CString(); } - // return connection - return getCanonicalName(index->second.m_neighbor[ - srcSide - kFirstDirection]); + // find edge + const CCellEdge* srcEdge, *dstEdge; + if (!index->second.getLink(srcSide, position, srcEdge, dstEdge)) { + // no neighbor + return ""; + } + else { + // compute position on neighbor + if (positionOut != NULL) { + *positionOut = + dstEdge->inverseTransform(srcEdge->transform(position)); + } + + // return neighbor's name + return getCanonicalName(dstEdge->getName()); + } +} + +bool +CConfig::hasNeighbor(const CString& srcName, EDirection srcSide) const +{ + return hasNeighbor(srcName, srcSide, 0.0f, 1.0f); +} + +bool +CConfig::hasNeighbor(const CString& srcName, EDirection srcSide, + float start, float end) const +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + if (index == m_map.end()) { + return false; + } + + return index->second.overlaps(CCellEdge(srcSide, CInterval(start, end))); +} + +CConfig::link_const_iterator +CConfig::beginNeighbor(const CString& srcName) const +{ + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + assert(index != m_map.end()); + return index->second.begin(); +} + +CConfig::link_const_iterator +CConfig::endNeighbor(const CString& srcName) const +{ + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + assert(index != m_map.end()); + return index->second.end(); } const CNetworkAddress& @@ -455,6 +541,12 @@ CConfig::getOptions(const CString& name) const return options; } +bool +CConfig::hasLockToScreenAction() const +{ + return m_hasLockToScreenAction; +} + bool CConfig::operator==(const CConfig& x) const { @@ -481,18 +573,10 @@ CConfig::operator==(const CConfig& x) const return false; } - // compare options - if (index1->second.m_options != index2->second.m_options) { + // compare cells + if (index1->second != index2->second) { 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(), @@ -505,6 +589,11 @@ CConfig::operator==(const CConfig& x) const } } + // compare input filters + if (m_inputFilter != x.m_inputFilter) { + return false; + } + return true; } @@ -514,92 +603,649 @@ CConfig::operator!=(const CConfig& x) const return !operator==(x); } +void +CConfig::read(CConfigReadContext& context) +{ + CConfig tmp; + while (context) { + tmp.readSection(context); + } + *this = tmp; +} + const char* CConfig::dirName(EDirection dir) { - static const char* s_name[] = { "left", "right", "top", "bottom" }; + static const char* s_name[] = { "left", "right", "up", "down" }; assert(dir >= kFirstDirection && dir <= kLastDirection); return s_name[dir - kFirstDirection]; } -bool -CConfig::readLine(std::istream& s, CString& line) +CInputFilter* +CConfig::getInputFilter() { - 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; + return &m_inputFilter; } -OptionValue -CConfig::parseBoolean(const CString& arg) +CString +CConfig::formatInterval(const CInterval& x) { - if (CStringUtil::CaselessCmp::equal(arg, "true")) { - return static_cast(true); + if (x.first == 0.0f && x.second == 1.0f) { + return ""; } - if (CStringUtil::CaselessCmp::equal(arg, "false")) { - return static_cast(false); - } - throw XConfigRead("invalid argument"); + return CStringUtil::print("(%d,%d)", (int)(x.first * 100.0f + 0.5f), + (int)(x.second * 100.0f + 0.5f)); } -OptionValue -CConfig::parseInt(const CString& arg) +void +CConfig::readSection(CConfigReadContext& s) { - const char* s = arg.c_str(); - char* end; - long tmp = strtol(s, &end, 10); - if (*end != '\0') { - // invalid characters - throw XConfigRead("invalid argument"); + 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 (!s.readLine(line)) { + // no more sections + return; } - OptionValue value = static_cast(tmp); - if (value != tmp) { - // out of range - throw XConfigRead("argument out of range"); + + // should be a section header + if (line.find(s_section) != 0) { + throw XConfigRead(s, "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(s, "section name is missing"); + } + CString name = line.substr(i); + i = name.find_first_of(" \t"); + if (i != CString::npos) { + throw XConfigRead(s, "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(s, "unknown section name \"%{1}\"", name); } - return value; } -OptionValue -CConfig::parseModifierKey(const CString& arg) +void +CConfig::readSectionOptions(CConfigReadContext& s) { - if (CStringUtil::CaselessCmp::equal(arg, "shift")) { - return static_cast(kKeyModifierIDShift); + CString line; + while (s.readLine(line)) { + // check for end of section + if (line == "end") { + return; + } + + // parse argument: `nameAndArgs = [values][;[values]]' + // nameAndArgs := [(arg[,...])] + // values := valueAndArgs[,valueAndArgs]... + // valueAndArgs := [(arg[,...])] + CString::size_type i = 0; + CString name, value; + CConfigReadContext::ArgList nameArgs, valueArgs; + s.parseNameWithArgs("name", line, "=", i, name, nameArgs); + ++i; + s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs); + + bool handled = true; + if (name == "address") { + try { + m_synergyAddress = CNetworkAddress(value, kDefaultPort); + m_synergyAddress.resolve(); + } + catch (XSocketAddress& e) { + throw XConfigRead(s, + CString("invalid address argument ") + e.what()); + } + } + else if (name == "heartbeat") { + addOption("", kOptionHeartbeat, s.parseInt(value)); + } + else if (name == "switchCorners") { + addOption("", kOptionScreenSwitchCorners, s.parseCorners(value)); + } + else if (name == "switchCornerSize") { + addOption("", kOptionScreenSwitchCornerSize, s.parseInt(value)); + } + else if (name == "switchDelay") { + addOption("", kOptionScreenSwitchDelay, s.parseInt(value)); + } + else if (name == "switchDoubleTap") { + addOption("", kOptionScreenSwitchTwoTap, s.parseInt(value)); + } + else if (name == "screenSaverSync") { + addOption("", kOptionScreenSaverSync, s.parseBoolean(value)); + } + else if (name == "relativeMouseMoves") { + addOption("", kOptionRelativeMouseMoves, s.parseBoolean(value)); + } + else if (name == "win32KeepForeground") { + addOption("", kOptionWin32KeepForeground, s.parseBoolean(value)); + } + else { + handled = false; + } + + if (handled) { + // make sure handled options aren't followed by more values + if (i < line.size() && (line[i] == ',' || line[i] == ';')) { + throw XConfigRead(s, "to many arguments to %s", name.c_str()); + } + } + else { + // make filter rule + CInputFilter::CRule rule(parseCondition(s, name, nameArgs)); + + // save first action (if any) + if (!value.empty() || line[i] != ';') { + parseAction(s, value, valueArgs, rule, true); + } + + // get remaining activate actions + while (i < line.length() && line[i] != ';') { + ++i; + s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs); + parseAction(s, value, valueArgs, rule, true); + } + + // get deactivate actions + if (i < line.length() && line[i] == ';') { + // allow trailing ';' + i = line.find_first_not_of(" \t", i + 1); + if (i == CString::npos) { + i = line.length(); + } + else { + --i; + } + + // get actions + while (i < line.length()) { + ++i; + s.parseNameWithArgs("value", line, ",\n", + i, value, valueArgs); + parseAction(s, value, valueArgs, rule, false); + } + } + + // add rule + m_inputFilter.addFilterRule(rule); + } } - if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) { - return static_cast(kKeyModifierIDControl); + throw XConfigRead(s, "unexpected end of options section"); +} + +void +CConfig::readSectionScreens(CConfigReadContext& s) +{ + CString line; + CString screen; + while (s.readLine(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(s, "invalid screen name \"%{1}\"", screen); + } + + // add the screen to the configuration + if (!addScreen(screen)) { + throw XConfigRead(s, "duplicate screen name \"%{1}\"", screen); + } + } + else if (screen.empty()) { + throw XConfigRead(s, "argument before first screen"); + } + else { + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead(s, "missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead(s, "missing ="); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead(s, "missing ="); + } + 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, + s.parseBoolean(value)); + } + else if (name == "halfDuplexNumLock") { + addOption(screen, kOptionHalfDuplexNumLock, + s.parseBoolean(value)); + } + else if (name == "halfDuplexScrollLock") { + addOption(screen, kOptionHalfDuplexScrollLock, + s.parseBoolean(value)); + } + else if (name == "shift") { + addOption(screen, kOptionModifierMapForShift, + s.parseModifierKey(value)); + } + else if (name == "ctrl") { + addOption(screen, kOptionModifierMapForControl, + s.parseModifierKey(value)); + } + else if (name == "alt") { + addOption(screen, kOptionModifierMapForAlt, + s.parseModifierKey(value)); + } + else if (name == "meta") { + addOption(screen, kOptionModifierMapForMeta, + s.parseModifierKey(value)); + } + else if (name == "super") { + addOption(screen, kOptionModifierMapForSuper, + s.parseModifierKey(value)); + } + else if (name == "xtestIsXineramaUnaware") { + addOption(screen, kOptionXTestXineramaUnaware, + s.parseBoolean(value)); + } + else if (name == "switchCorners") { + addOption(screen, kOptionScreenSwitchCorners, + s.parseCorners(value)); + } + else if (name == "switchCornerSize") { + addOption(screen, kOptionScreenSwitchCornerSize, + s.parseInt(value)); + } + else { + // unknown argument + throw XConfigRead(s, "unknown argument \"%{1}\"", name); + } + } } - if (CStringUtil::CaselessCmp::equal(arg, "alt")) { - return static_cast(kKeyModifierIDAlt); + throw XConfigRead(s, "unexpected end of screens section"); +} + +void +CConfig::readSectionLinks(CConfigReadContext& s) +{ + CString line; + CString screen; + while (s.readLine(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(s, "unknown screen name \"%{1}\"", screen); + } + if (!isCanonicalName(screen)) { + throw XConfigRead(s, "cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead(s, "argument before first screen"); + } + else { + // parse argument: `[(,)]=[(,)]' + // the stuff in brackets is optional. interval values must be + // in the range [0,100] and start < end. if not given the + // interval is taken to be (0,100). + CString::size_type i = 0; + CString side, dstScreen, srcArgString, dstArgString; + CConfigReadContext::ArgList srcArgs, dstArgs; + s.parseNameWithArgs("link", line, "=", i, side, srcArgs); + ++i; + s.parseNameWithArgs("screen", line, "", i, dstScreen, dstArgs); + CInterval srcInterval(s.parseInterval(srcArgs)); + CInterval dstInterval(s.parseInterval(dstArgs)); + + // handle argument + EDirection dir; + if (side == "left") { + dir = kLeft; + } + else if (side == "right") { + dir = kRight; + } + else if (side == "up") { + dir = kTop; + } + else if (side == "down") { + dir = kBottom; + } + else { + // unknown argument + throw XConfigRead(s, "unknown side \"%{1}\" in link", side); + } + if (!isScreen(dstScreen)) { + throw XConfigRead(s, "unknown screen name \"%{1}\"", dstScreen); + } + if (!connect(screen, dir, + srcInterval.first, srcInterval.second, + dstScreen, + dstInterval.first, dstInterval.second)) { + throw XConfigRead(s, "overlapping range"); + } + } } - if (CStringUtil::CaselessCmp::equal(arg, "meta")) { - return static_cast(kKeyModifierIDMeta); + throw XConfigRead(s, "unexpected end of links section"); +} + +void +CConfig::readSectionAliases(CConfigReadContext& s) +{ + CString line; + CString screen; + while (s.readLine(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(s, "unknown screen name \"%{1}\"", screen); + } + if (!isCanonicalName(screen)) { + throw XConfigRead(s, "cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead(s, "argument before first screen"); + } + else { + // verify validity of screen name + if (!isValidScreenName(line)) { + throw XConfigRead(s, "invalid screen alias \"%{1}\"", line); + } + + // add alias + if (!addAlias(screen, line)) { + throw XConfigRead(s, "alias \"%{1}\" is already used", line); + } + } } - if (CStringUtil::CaselessCmp::equal(arg, "super")) { - return static_cast(kKeyModifierIDSuper); + throw XConfigRead(s, "unexpected end of aliases section"); +} + + +CInputFilter::CCondition* +CConfig::parseCondition(CConfigReadContext& s, + const CString& name, const std::vector& args) +{ + if (name == "keystroke") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for condition: keystroke(modifiers+key)"); + } + + IPlatformScreen::CKeyInfo* keyInfo = s.parseKeystroke(args[0]); + + return new CInputFilter::CKeystrokeCondition(keyInfo); } - if (CStringUtil::CaselessCmp::equal(arg, "none")) { - return static_cast(kKeyModifierIDNull); + + if (name == "mousebutton") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for condition: mousebutton(modifiers+button)"); + } + + IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]); + + return new CInputFilter::CMouseButtonCondition(mouseInfo); + } + + if (name == "connect") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for condition: connect([screen])"); + } + + CString screen = args[0]; + if (isScreen(screen)) { + screen = getCanonicalName(screen); + } + else if (!screen.empty()) { + throw XConfigRead(s, "unknown screen name \"%{1}\" in connect", screen); + } + + return new CInputFilter::CScreenConnectedCondition(screen); + } + + throw XConfigRead(s, "unknown argument \"%{1}\"", name); +} + +void +CConfig::parseAction(CConfigReadContext& s, + const CString& name, const std::vector& args, + CInputFilter::CRule& rule, bool activate) +{ + CInputFilter::CAction* action; + + if (name == "keystroke" || name == "keyDown" || name == "keyUp") { + if (args.size() < 1 || args.size() > 2) { + throw XConfigRead(s, "syntax for action: keystroke(modifiers+key[,screens])"); + } + + IPlatformScreen::CKeyInfo* keyInfo; + if (args.size() == 1) { + keyInfo = s.parseKeystroke(args[0]); + } + else { + std::set screens; + parseScreens(s, args[1], screens); + keyInfo = s.parseKeystroke(args[0], screens); + } + + if (name == "keystroke") { + IPlatformScreen::CKeyInfo* keyInfo2 = + IKeyState::CKeyInfo::alloc(*keyInfo); + action = new CInputFilter::CKeystrokeAction(keyInfo2, true); + rule.adoptAction(action, true); + action = new CInputFilter::CKeystrokeAction(keyInfo, false); + activate = false; + } + else if (name == "keyDown") { + action = new CInputFilter::CKeystrokeAction(keyInfo, true); + } + else { + action = new CInputFilter::CKeystrokeAction(keyInfo, false); + } + } + + else if (name == "mousebutton" || + name == "mouseDown" || name == "mouseUp") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: mousebutton(modifiers+button)"); + } + + IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]); + + if (name == "mousebutton") { + IPlatformScreen::CButtonInfo* mouseInfo2 = + IPlatformScreen::CButtonInfo::alloc(*mouseInfo); + action = new CInputFilter::CMouseButtonAction(mouseInfo2, true); + rule.adoptAction(action, true); + action = new CInputFilter::CMouseButtonAction(mouseInfo, false); + activate = false; + } + else if (name == "mouseDown") { + action = new CInputFilter::CMouseButtonAction(mouseInfo, true); + } + else { + action = new CInputFilter::CMouseButtonAction(mouseInfo, false); + } + } + +/* XXX -- not supported + else if (name == "modifier") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: modifier(modifiers)"); + } + + KeyModifierMask mask = s.parseModifier(args[0]); + + action = new CInputFilter::CModifierAction(mask, ~mask); + } +*/ + + else if (name == "switchToScreen") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: switchToScreen(name)"); + } + + CString screen = args[0]; + if (isScreen(screen)) { + screen = getCanonicalName(screen); + } + else if (!screen.empty()) { + throw XConfigRead(s, "unknown screen name in switchToScreen"); + } + + action = new CInputFilter::CSwitchToScreenAction(screen); + } + + else if (name == "switchInDirection") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: switchInDirection()"); + } + + EDirection direction; + if (args[0] == "left") { + direction = kLeft; + } + else if (args[0] == "right") { + direction = kRight; + } + else if (args[0] == "up") { + direction = kTop; + } + else if (args[0] == "down") { + direction = kBottom; + } + else { + throw XConfigRead(s, "unknown direction \"%{1}\" in switchToScreen", args[0]); + } + + action = new CInputFilter::CSwitchInDirectionAction(direction); + } + + else if (name == "lockCursorToScreen") { + if (args.size() > 1) { + throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])"); + } + + CInputFilter::CLockCursorToScreenAction::Mode mode = + CInputFilter::CLockCursorToScreenAction::kToggle; + if (args.size() == 1) { + if (args[0] == "off") { + mode = CInputFilter::CLockCursorToScreenAction::kOff; + } + else if (args[0] == "on") { + mode = CInputFilter::CLockCursorToScreenAction::kOn; + } + else if (args[0] == "toggle") { + mode = CInputFilter::CLockCursorToScreenAction::kToggle; + } + else { + throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])"); + } + } + + if (mode != CInputFilter::CLockCursorToScreenAction::kOff) { + m_hasLockToScreenAction = true; + } + + action = new CInputFilter::CLockCursorToScreenAction(mode); + } + + else { + throw XConfigRead(s, "unknown action argument \"%{1}\"", name); + } + + rule.adoptAction(action, activate); +} + +void +CConfig::parseScreens(CConfigReadContext& c, + const CString& s, std::set& screens) const +{ + screens.clear(); + + CString::size_type i = 0; + while (i < s.size()) { + // find end of next screen name + CString::size_type j = s.find(':', i); + if (j == CString::npos) { + j = s.size(); + } + + // extract name + CString rawName; + i = s.find_first_not_of(" \t", i); + if (i < j) { + rawName = s.substr(i, s.find_last_not_of(" \t", j - 1) - i + 1); + } + + // add name + if (rawName == "*") { + screens.insert("*"); + } + else if (!rawName.empty()) { + CString name = getCanonicalName(rawName); + if (name.empty()) { + throw XConfigRead(c, "unknown screen name \"%{1}\"", rawName); + } + screens.insert(name); + } + + // next + i = j + 1; } - throw XConfigRead("invalid argument"); } const char* @@ -632,6 +1278,12 @@ CConfig::getOptionName(OptionID id) if (id == kOptionHeartbeat) { return "heartbeat"; } + if (id == kOptionScreenSwitchCorners) { + return "switchCorners"; + } + if (id == kOptionScreenSwitchCornerSize) { + return "switchCornerSize"; + } if (id == kOptionScreenSwitchDelay) { return "switchDelay"; } @@ -647,6 +1299,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionRelativeMouseMoves) { return "relativeMouseMoves"; } + if (id == kOptionWin32KeepForeground) { + return "win32KeepForeground"; + } return NULL; } @@ -658,7 +1313,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) id == kOptionHalfDuplexScrollLock || id == kOptionScreenSaverSync || id == kOptionXTestXineramaUnaware || - id == kOptionRelativeMouseMoves) { + id == kOptionRelativeMouseMoves || + id == kOptionWin32KeepForeground) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -687,344 +1343,333 @@ CConfig::getOptionValue(OptionID id, OptionValue value) } } if (id == kOptionHeartbeat || + id == kOptionScreenSwitchCornerSize || id == kOptionScreenSwitchDelay || id == kOptionScreenSwitchTwoTap) { return CStringUtil::print("%d", value); } + if (id == kOptionScreenSwitchCorners) { + std::string result("none"); + if ((value & kTopLeftMask) != 0) { + result += " +top-left"; + } + if ((value & kTopRightMask) != 0) { + result += " +top-right"; + } + if ((value & kBottomLeftMask) != 0) { + result += " +bottom-left"; + } + if ((value & kBottomRightMask) != 0) { + result += " +bottom-right"; + } + return result; + } return ""; } -void -CConfig::readSection(std::istream& s) + +// +// CConfig::CName +// + +CConfig::CName::CName(CConfig* config, const CString& name) : + m_config(config), + m_name(config->getCanonicalName(name)) { - 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"; + // do nothing +} - CString line; - if (!readLine(s, line)) { - // no more sections - return; +bool +CConfig::CName::operator==(const CString& name) const +{ + CString canonical = m_config->getCanonicalName(name); + return CStringUtil::CaselessCmp::equal(canonical, m_name); +} + + +// +// CConfig::CCellEdge +// + +CConfig::CCellEdge::CCellEdge(EDirection side, float position) +{ + init("", side, CInterval(position, position)); +} + +CConfig::CCellEdge::CCellEdge(EDirection side, const CInterval& interval) +{ + assert(interval.first >= 0.0f); + assert(interval.second <= 1.0f); + assert(interval.first < interval.second); + + init("", side, interval); +} + +CConfig::CCellEdge::CCellEdge(const CString& name, + EDirection side, const CInterval& interval) +{ + assert(interval.first >= 0.0f); + assert(interval.second <= 1.0f); + assert(interval.first < interval.second); + + init(name, side, interval); +} + +CConfig::CCellEdge::~CCellEdge() +{ + // do nothing +} + +void +CConfig::CCellEdge::init(const CString& name, EDirection side, + const CInterval& interval) +{ + assert(side != kNoDirection); + + m_name = name; + m_side = side; + m_interval = interval; +} + +CConfig::CInterval +CConfig::CCellEdge::getInterval() const +{ + return m_interval; +} + +void +CConfig::CCellEdge::setName(const CString& newName) +{ + m_name = newName; +} + +CString +CConfig::CCellEdge::getName() const +{ + return m_name; +} + +EDirection +CConfig::CCellEdge::getSide() const +{ + return m_side; +} + +bool +CConfig::CCellEdge::overlaps(const CCellEdge& edge) const +{ + const CInterval& x = m_interval; + const CInterval& y = edge.m_interval; + if (m_side != edge.m_side) { + return false; + } + return (x.first >= y.first && x.first < y.second) || + (x.second > y.first && x.second <= y.second) || + (y.first >= x.first && y.first < x.second) || + (y.second > x.first && y.second <= x.second); +} + +bool +CConfig::CCellEdge::isInside(float x) const +{ + return (x >= m_interval.first && x < m_interval.second); +} + +float +CConfig::CCellEdge::transform(float x) const +{ + return (x - m_interval.first) / (m_interval.second - m_interval.first); +} + + +float +CConfig::CCellEdge::inverseTransform(float x) const +{ + return x * (m_interval.second - m_interval.first) + m_interval.first; +} + +bool +CConfig::CCellEdge::operator<(const CCellEdge& o) const +{ + if (static_cast(m_side) < static_cast(o.m_side)) { + return true; + } + else if (static_cast(m_side) > static_cast(o.m_side)) { + return false; } - // should be a section header - if (line.find(s_section) != 0) { - throw XConfigRead("found data outside section"); + return (m_interval.first < o.m_interval.first); +} + +bool +CConfig::CCellEdge::operator==(const CCellEdge& x) const +{ + return (m_side == x.m_side && m_interval == x.m_interval); +} + +bool +CConfig::CCellEdge::operator!=(const CCellEdge& x) const +{ + return !operator==(x); +} + + +// +// CConfig::CCell +// + +bool +CConfig::CCell::add(const CCellEdge& src, const CCellEdge& dst) +{ + // cannot add an edge that overlaps other existing edges but we + // can exactly replace an edge. + if (!hasEdge(src) && overlaps(src)) { + return false; } - // 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"); - } + m_neighbors.erase(src); + m_neighbors.insert(std::make_pair(src, dst)); + return true; +} - // 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::CCell::remove(EDirection side) +{ + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ) { + if (j->first.getSide() == side) { + m_neighbors.erase(j++); + } + else { + ++j; + } } } void -CConfig::readSectionOptions(std::istream& s) +CConfig::CCell::remove(EDirection side, float position) { - 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); - m_synergyAddress.resolve(); - } - catch (XSocketAddress& e) { - throw XConfigRead(CString("invalid address argument: ") + - e.what()); - } - } - 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 if (name == "relativeMouseMoves") { - addOption("", kOptionRelativeMouseMoves, parseBoolean(value)); - } - else { - throw XConfigRead("unknown argument"); + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ++j) { + if (j->first.getSide() == side && j->first.isInside(position)) { + m_neighbors.erase(j); + break; + } + } +} +void +CConfig::CCell::remove(const CName& name) +{ + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ) { + if (name == j->second.getName()) { + m_neighbors.erase(j++); + } + else { + ++j; } } - throw XConfigRead("unexpected end of screens section"); } void -CConfig::readSectionScreens(std::istream& s) +CConfig::CCell::rename(const CName& oldName, const CString& newName) { - 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 == "halfDuplexScrollLock") { - addOption(screen, kOptionHalfDuplexScrollLock, - 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"); - } + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ++j) { + if (oldName == j->second.getName()) { + j->second.setName(newName); } } - throw XConfigRead("unexpected end of screens section"); } -void -CConfig::readSectionLinks(std::istream& s) +bool +CConfig::CCell::hasEdge(const CCellEdge& edge) const { - 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"); + CEdgeLinks::const_iterator i = m_neighbors.find(edge); + return (i != m_neighbors.end() && i->first == edge); } -void -CConfig::readSectionAliases(std::istream& s) +bool +CConfig::CCell::overlaps(const CCellEdge& edge) const { - CString line; - CString screen; - while (readLine(s, line)) { - // check for end of section - if (line == "end") { - return; + CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge); + if (i != m_neighbors.end() && i->first.overlaps(edge)) { + return true; + } + if (i != m_neighbors.begin() && (--i)->first.overlaps(edge)) { + return true; + } + return false; +} + +bool +CConfig::CCell::getLink(EDirection side, float position, + const CCellEdge*& src, const CCellEdge*& dst) const +{ + CCellEdge edge(side, position); + CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge); + if (i == m_neighbors.begin()) { + return false; + } + --i; + if (i->first.getSide() == side && i->first.isInside(position)) { + src = &i->first; + dst = &i->second; + return true; + } + return false; +} + +bool +CConfig::CCell::operator==(const CCell& x) const +{ + // compare options + if (m_options != x.m_options) { + return false; + } + + // compare links + if (m_neighbors.size() != x.m_neighbors.size()) { + return false; + } + for (CEdgeLinks::const_iterator index1 = m_neighbors.begin(), + index2 = x.m_neighbors.begin(); + index1 != m_neighbors.end(); + ++index1, ++index2) { + if (index1->first != index2->first) { + return false; + } + if (index1->second != index2->second) { + return false; } - // 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"); - } + // operator== doesn't compare names. only compare destination + // names. + if (!CStringUtil::CaselessCmp::equal(index1->second.getName(), + index2->second.getName())) { + return false; } } - throw XConfigRead("unexpected end of aliases section"); + return true; +} + +bool +CConfig::CCell::operator!=(const CCell& x) const +{ + return !operator==(x); +} + +CConfig::CCell::const_iterator +CConfig::CCell::begin() const +{ + return m_neighbors.begin(); +} + +CConfig::CCell::const_iterator +CConfig::CCell::end() const +{ + return m_neighbors.end(); } @@ -1035,40 +1680,14 @@ CConfig::readSectionAliases(std::istream& s) 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; + CConfigReadContext context(s); + config.read(context); 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; - } - s << "end" << std::endl; - // screens section s << "section: screens" << std::endl; for (CConfig::const_iterator screen = config.begin(); @@ -1097,24 +1716,14 @@ operator<<(std::ostream& s, const CConfig& config) 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; + for (CConfig::link_const_iterator + link = config.beginNeighbor(*screen), + nend = config.endNeighbor(*screen); link != nend; ++link) { + s << "\t\t" << CConfig::dirName(link->first.getSide()) << + CConfig::formatInterval(link->first.getInterval()) << + " = " << link->second.getName().c_str() << + CConfig::formatInterval(link->second.getInterval()) << + std::endl; } } s << "end" << std::endl; @@ -1148,6 +1757,460 @@ operator<<(std::ostream& s, const CConfig& config) s << "end" << std::endl; } + // 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; + } + s << config.m_inputFilter.format("\t"); + s << "end" << std::endl; + + return s; +} + + +// +// CConfigReadContext +// + +CConfigReadContext::CConfigReadContext(std::istream& s, SInt32 firstLine) : + m_stream(s), + m_line(firstLine - 1) +{ + // do nothing +} + +CConfigReadContext::~CConfigReadContext() +{ + // do nothing +} + +bool +CConfigReadContext::readLine(CString& line) +{ + ++m_line; + while (std::getline(m_stream, line)) { + // strip leading whitespace + CString::size_type i = line.find_first_not_of(" \t"); + if (i != CString::npos) { + line.erase(0, i); + } + + // strip comments and then trailing whitespace + 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()) { + // make sure there are no invalid characters + for (i = 0; i < line.length(); ++i) { + if (!isgraph(line[i]) && line[i] != ' ' && line[i] != '\t') { + throw XConfigRead(*this, + "invalid character %{1}", + CStringUtil::print("%#2x", line[i])); + } + } + + return true; + } + + // next line + ++m_line; + } + return false; +} + +UInt32 +CConfigReadContext::getLineNumber() const +{ + return m_line; +} + +CConfigReadContext::operator void*() const +{ + return m_stream; +} + +bool +CConfigReadContext::operator!() const +{ + return !m_stream; +} + +OptionValue +CConfigReadContext::parseBoolean(const CString& arg) const +{ + if (CStringUtil::CaselessCmp::equal(arg, "true")) { + return static_cast(true); + } + if (CStringUtil::CaselessCmp::equal(arg, "false")) { + return static_cast(false); + } + throw XConfigRead(*this, "invalid boolean argument \"%{1}\"", arg); +} + +OptionValue +CConfigReadContext::parseInt(const CString& arg) const +{ + const char* s = arg.c_str(); + char* end; + long tmp = strtol(s, &end, 10); + if (*end != '\0') { + // invalid characters + throw XConfigRead(*this, "invalid integer argument \"%{1}\"", arg); + } + OptionValue value = static_cast(tmp); + if (value != tmp) { + // out of range + throw XConfigRead(*this, "integer argument \"%{1}\" out of range", arg); + } + return value; +} + +OptionValue +CConfigReadContext::parseModifierKey(const CString& arg) const +{ + 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(*this, "invalid argument \"%{1}\"", arg); +} + +OptionValue +CConfigReadContext::parseCorner(const CString& arg) const +{ + if (CStringUtil::CaselessCmp::equal(arg, "left")) { + return kTopLeftMask | kBottomLeftMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "right")) { + return kTopRightMask | kBottomRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "top")) { + return kTopLeftMask | kTopRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "bottom")) { + return kBottomLeftMask | kBottomRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "top-left")) { + return kTopLeftMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "top-right")) { + return kTopRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "bottom-left")) { + return kBottomLeftMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "bottom-right")) { + return kBottomRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "none")) { + return kNoCornerMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "all")) { + return kAllCornersMask; + } + throw XConfigRead(*this, "invalid argument \"%{1}\"", arg); +} + +OptionValue +CConfigReadContext::parseCorners(const CString& args) const +{ + // find first token + CString::size_type i = args.find_first_not_of(" \t", 0); + if (i == CString::npos) { + throw XConfigRead(*this, "missing corner argument"); + } + CString::size_type j = args.find_first_of(" \t", i); + + // parse first corner token + OptionValue corners = parseCorner(args.substr(i, j - i)); + + // get +/- + i = args.find_first_not_of(" \t", j); + while (i != CString::npos) { + // parse +/- + bool add; + if (args[i] == '-') { + add = false; + } + else if (args[i] == '+') { + add = true; + } + else { + throw XConfigRead(*this, + "invalid corner operator \"%{1}\"", + CString(args.c_str() + i, 1)); + } + + // get next corner token + i = args.find_first_not_of(" \t", i + 1); + j = args.find_first_of(" \t", i); + if (i == CString::npos) { + throw XConfigRead(*this, "missing corner argument"); + } + + // parse next corner token + if (add) { + corners |= parseCorner(args.substr(i, j - i)); + } + else { + corners &= ~parseCorner(args.substr(i, j - i)); + } + i = args.find_first_not_of(" \t", j); + } + + return corners; +} + +CConfig::CInterval +CConfigReadContext::parseInterval(const ArgList& args) const +{ + if (args.size() == 0) { + return CConfig::CInterval(0.0f, 1.0f); + } + if (args.size() != 2 || args[0].empty() || args[1].empty()) { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + + char* end; + long startValue = strtol(args[0].c_str(), &end, 10); + if (end[0] != '\0') { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + long endValue = strtol(args[1].c_str(), &end, 10); + if (end[0] != '\0') { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + + if (startValue < 0 || startValue > 100 || + endValue < 0 || endValue > 100 || + startValue >= endValue) { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + + return CConfig::CInterval(startValue / 100.0f, endValue / 100.0f); +} + +void +CConfigReadContext::parseNameWithArgs( + const CString& type, const CString& line, + const CString& delim, CString::size_type& index, + CString& name, ArgList& args) const +{ + // skip leading whitespace + CString::size_type i = line.find_first_not_of(" \t", index); + if (i == CString::npos) { + throw XConfigRead(*this, CString("missing ") + type); + } + + // find end of name + CString::size_type j = line.find_first_of(" \t(" + delim, i); + if (j == CString::npos) { + j = line.length(); + } + + // save name + name = line.substr(i, j - i); + args.clear(); + + // is it okay to not find a delimiter? + bool needDelim = (!delim.empty() && delim.find('\n') == CString::npos); + + // skip whitespace + i = line.find_first_not_of(" \t", j); + if (i == CString::npos && needDelim) { + // expected delimiter but didn't find it + throw XConfigRead(*this, CString("missing ") + delim[0]); + } + if (i == CString::npos) { + // no arguments + index = line.length(); + return; + } + if (line[i] != '(') { + // no arguments + index = i; + return; + } + + // eat '(' + ++i; + + // parse arguments + j = line.find_first_of(",)", i); + while (j != CString::npos) { + // extract arg + CString arg(line.substr(i, j - i)); + i = j; + + // trim whitespace + j = arg.find_first_not_of(" \t"); + if (j != CString::npos) { + arg.erase(0, j); + } + j = arg.find_last_not_of(" \t"); + if (j != CString::npos) { + arg.erase(j + 1); + } + + // save arg + args.push_back(arg); + + // exit loop at end of arguments + if (line[i] == ')') { + break; + } + + // eat ',' + ++i; + + // next + j = line.find_first_of(",)", i); + } + + // verify ')' + if (j == CString::npos) { + // expected ) + throw XConfigRead(*this, "missing )"); + } + + // eat ')' + ++i; + + // skip whitespace + j = line.find_first_not_of(" \t", i); + if (j == CString::npos && needDelim) { + // expected delimiter but didn't find it + throw XConfigRead(*this, CString("missing ") + delim[0]); + } + + // verify delimiter + if (needDelim && delim.find(line[j]) == CString::npos) { + throw XConfigRead(*this, CString("expected ") + delim[0]); + } + + if (j == CString::npos) { + j = line.length(); + } + + index = j; + return; +} + +IPlatformScreen::CKeyInfo* +CConfigReadContext::parseKeystroke(const CString& keystroke) const +{ + return parseKeystroke(keystroke, std::set()); +} + +IPlatformScreen::CKeyInfo* +CConfigReadContext::parseKeystroke(const CString& keystroke, + const std::set& screens) const +{ + CString s = keystroke; + + KeyModifierMask mask; + if (!CKeyMap::parseModifiers(s, mask)) { + throw XConfigRead(*this, "unable to parse key modifiers"); + } + + KeyID key; + if (!CKeyMap::parseKey(s, key)) { + throw XConfigRead(*this, "unable to parse key"); + } + + if (key == kKeyNone && mask == 0) { + throw XConfigRead(*this, "missing key and/or modifiers in keystroke"); + } + + return IPlatformScreen::CKeyInfo::alloc(key, mask, 0, 0, screens); +} + +IPlatformScreen::CButtonInfo* +CConfigReadContext::parseMouse(const CString& mouse) const +{ + CString s = mouse; + + KeyModifierMask mask; + if (!CKeyMap::parseModifiers(s, mask)) { + throw XConfigRead(*this, "unable to parse button modifiers"); + } + + char* end; + ButtonID button = (ButtonID)strtol(s.c_str(), &end, 10); + if (*end != '\0') { + throw XConfigRead(*this, "unable to parse button"); + } + if (s.empty() || button <= 0) { + throw XConfigRead(*this, "invalid button"); + } + + return IPlatformScreen::CButtonInfo::alloc(button, mask); +} + +KeyModifierMask +CConfigReadContext::parseModifier(const CString& modifiers) const +{ + CString s = modifiers; + + KeyModifierMask mask; + if (!CKeyMap::parseModifiers(s, mask)) { + throw XConfigRead(*this, "unable to parse modifiers"); + } + + if (mask == 0) { + throw XConfigRead(*this, "no modifiers specified"); + } + + return mask; +} + +CString +CConfigReadContext::concatArgs(const ArgList& args) +{ + CString s("("); + for (size_t i = 0; i < args.size(); ++i) { + if (i != 0) { + s += ","; + } + s += args[i]; + } + s += ")"; return s; } @@ -1156,8 +2219,18 @@ operator<<(std::ostream& s, const CConfig& config) // CConfig I/O exceptions // -XConfigRead::XConfigRead(const CString& error) : - m_error(error) +XConfigRead::XConfigRead(const CConfigReadContext& context, + const CString& error) : + m_error(CStringUtil::print("line %d: %s", + context.getLineNumber(), error.c_str())) +{ + // do nothing +} + +XConfigRead::XConfigRead(const CConfigReadContext& context, + const char* errorFmt, const CString& arg) : + m_error(CStringUtil::print("line %d: ", context.getLineNumber()) + + CStringUtil::format(errorFmt, arg.c_str())) { // do nothing } diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index a2a9eed5..c0d2faa8 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -19,12 +19,15 @@ #include "ProtocolTypes.h" #include "CNetworkAddress.h" #include "CStringUtil.h" +#include "CInputFilter.h" #include "XBase.h" #include "stdmap.h" #include "stdset.h" +#include "IPlatformScreen.h" #include class CConfig; +class CConfigReadContext; namespace std { template <> @@ -50,17 +53,93 @@ namespace and must be unique. class CConfig { public: typedef std::map CScreenOptions; + typedef std::pair CInterval; + + class CCellEdge { + public: + CCellEdge(EDirection side, float position); + CCellEdge(EDirection side, const CInterval&); + CCellEdge(const CString& name, EDirection side, const CInterval&); + ~CCellEdge(); + + CInterval getInterval() const; + void setName(const CString& newName); + CString getName() const; + EDirection getSide() const; + bool overlaps(const CCellEdge&) const; + bool isInside(float x) const; + + // transform position to [0,1] + float transform(float x) const; + + // transform [0,1] to position + float inverseTransform(float x) const; + + // compares side and start of interval + bool operator<(const CCellEdge&) const; + + // compares side and interval + bool operator==(const CCellEdge&) const; + bool operator!=(const CCellEdge&) const; + + private: + void init(const CString& name, EDirection side, + const CInterval&); + + private: + CString m_name; + EDirection m_side; + CInterval m_interval; + }; private: - class CCell { + class CName { + public: + CName(CConfig*, const CString& name); + + bool operator==(const CString& name) const; + + private: + CConfig* m_config; + CString m_name; + }; + + class CCell { + private: + typedef std::map CEdgeLinks; + + public: + typedef CEdgeLinks::const_iterator const_iterator; + + bool add(const CCellEdge& src, const CCellEdge& dst); + void remove(EDirection side); + void remove(EDirection side, float position); + void remove(const CName& destinationName); + void rename(const CName& oldName, const CString& newName); + + bool hasEdge(const CCellEdge&) const; + bool overlaps(const CCellEdge&) const; + + bool getLink(EDirection side, float position, + const CCellEdge*& src, const CCellEdge*& dst) const; + + bool operator==(const CCell&) const; + bool operator!=(const CCell&) const; + + const_iterator begin() const; + const_iterator end() const; + + private: + CEdgeLinks m_neighbors; + public: - CString m_neighbor[kLastDirection - kFirstDirection + 1]; CScreenOptions m_options; }; typedef std::map CCellMap; typedef std::map CNameMap; public: + typedef CCell::const_iterator link_const_iterator; typedef CCellMap::const_iterator internal_const_iterator; typedef CNameMap::const_iterator all_const_iterator; class const_iterator : std::iterator_traits { @@ -141,6 +220,13 @@ public: */ bool removeAlias(const CString& alias); + //! Remove aliases + /*! + Removes all aliases for a canonical screen name. It returns false + if the canonical name is unknown, otherwise returns true. + */ + bool removeAliases(const CString& canonical); + //! Remove all aliases /*! This removes all aliases but not the screens. @@ -149,25 +235,43 @@ public: //! 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. + Establishes a one-way connection between portions of opposite edges + of two screens. Each portion is described by an interval defined + by two numbers, the start and end of the interval half-open on the + end. The numbers range from 0 to 1, inclusive, for the left/top + to the right/bottom. The user will be able to jump from the + \c srcStart to \c srcSend interval of \c srcSide of screen + \c srcName to the opposite side of screen \c dstName in the interval + \c dstStart and \c dstEnd when both screens are connected to the + server and the user isn't locked to a screen. Returns false if + \c srcName is unknown. \c srcStart must be less than or equal to + \c srcEnd and \c dstStart must be less then or equal to \c dstEnd + and all of \c srcStart, \c srcEnd, \c dstStart, or \c dstEnd must + be inside the range [0,1]. */ bool connect(const CString& srcName, EDirection srcSide, - const CString& dstName); + float srcStart, float srcEnd, + const CString& dstName, + float dstStart, float dstEnd); //! Disconnect screens /*! - Removes a connection created by connect(). Returns false if - \c srcName is unknown. + Removes all connections created by connect() on side \c srcSide. + Returns false if \c srcName is unknown. */ bool disconnect(const CString& srcName, EDirection srcSide); + //! Disconnect screens + /*! + Removes the connections created by connect() on side \c srcSide + covering position \c position. Returns false if \c srcName is + unknown. + */ + bool disconnect(const CString& srcName, + EDirection srcSide, float position); + //! Set server address /*! Set the synergy listen addresses. There is no default address so @@ -199,6 +303,13 @@ public: */ bool removeOptions(const CString& name); + //! Get the hot key input filter + /*! + Returns the hot key input filter. Clients can modify hotkeys using + that object. + */ + CInputFilter* getInputFilter(); + //@} //! @name accessors //@{ @@ -241,10 +352,33 @@ public: //! 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. + direction (set through connect()) at position \c position. Returns + the empty string if there is no neighbor in that direction, otherwise + saves the position on the neighbor in \c positionOut if it's not + \c NULL. */ - CString getNeighbor(const CString&, EDirection) const; + CString getNeighbor(const CString&, EDirection, + float position, float* positionOut) const; + + //! Check for neighbor + /*! + Returns \c true if the screen has a neighbor anywhere along the edge + given by the direction. + */ + bool hasNeighbor(const CString&, EDirection) const; + + //! Check for neighbor + /*! + Returns \c true if the screen has a neighbor in the given range along + the edge given by the direction. + */ + bool hasNeighbor(const CString&, EDirection, + float start, float end) const; + + //! Get beginning neighbor iterator + link_const_iterator beginNeighbor(const CString&) const; + //! Get ending neighbor iterator + link_const_iterator endNeighbor(const CString&) const; //! Get the server address const CNetworkAddress& getSynergyAddress() const; @@ -257,11 +391,25 @@ public: */ const CScreenOptions* getOptions(const CString& name) const; + //! Check for lock to screen action + /*! + Returns \c true if this configuration has a lock to screen action. + This is for backwards compatible support of ScrollLock locking. + */ + bool hasLockToScreenAction() const; + //! Compare configurations bool operator==(const CConfig&) const; //! Compare configurations bool operator!=(const CConfig&) const; + //! Read configuration + /*! + Reads a configuration from a context. Throws XConfigRead on error + and context is unchanged. + */ + void read(CConfigReadContext& context); + //! Read configuration /*! Reads a configuration from a stream. Throws XConfigRead on error. @@ -280,26 +428,90 @@ public: */ static const char* dirName(EDirection); + //! Get interval as string + /*! + Returns an interval as a parseable string. + */ + static CString formatInterval(const CInterval&); + //@} private: - static bool readLine(std::istream&, CString&); - static OptionValue parseBoolean(const CString&); - static OptionValue parseInt(const CString&); - static OptionValue parseModifierKey(const CString&); + void readSection(CConfigReadContext&); + void readSectionOptions(CConfigReadContext&); + void readSectionScreens(CConfigReadContext&); + void readSectionLinks(CConfigReadContext&); + void readSectionAliases(CConfigReadContext&); + + CInputFilter::CCondition* + parseCondition(CConfigReadContext&, + const CString& condition, + const std::vector& args); + void parseAction(CConfigReadContext&, + const CString& action, + const std::vector& args, + CInputFilter::CRule&, bool activate); + + void parseScreens(CConfigReadContext&, const CString&, + std::set& screens) const; 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; CScreenOptions m_globalOptions; + CInputFilter m_inputFilter; + bool m_hasLockToScreenAction; +}; + +//! Configuration read context +/*! +Maintains a context when reading a configuration from a stream. +*/ +class CConfigReadContext { +public: + typedef std::vector ArgList; + + CConfigReadContext(std::istream&, SInt32 firstLine = 1); + ~CConfigReadContext(); + + bool readLine(CString&); + UInt32 getLineNumber() const; + + operator void*() const; + bool operator!() const; + + OptionValue parseBoolean(const CString&) const; + OptionValue parseInt(const CString&) const; + OptionValue parseModifierKey(const CString&) const; + OptionValue parseCorner(const CString&) const; + OptionValue parseCorners(const CString&) const; + CConfig::CInterval + parseInterval(const ArgList& args) const; + void parseNameWithArgs( + const CString& type, const CString& line, + const CString& delim, CString::size_type& index, + CString& name, ArgList& args) const; + IPlatformScreen::CKeyInfo* + parseKeystroke(const CString& keystroke) const; + IPlatformScreen::CKeyInfo* + parseKeystroke(const CString& keystroke, + const std::set& screens) const; + IPlatformScreen::CButtonInfo* + parseMouse(const CString& mouse) const; + KeyModifierMask parseModifier(const CString& modifiers) const; + +private: + // not implemented + CConfigReadContext& operator=(const CConfigReadContext&); + + static CString concatArgs(const ArgList& args); + +private: + std::istream& m_stream; + SInt32 m_line; }; //! Configuration stream read exception @@ -308,7 +520,9 @@ Thrown when a configuration stream cannot be parsed. */ class XConfigRead : public XBase { public: - XConfigRead(const CString&); + XConfigRead(const CConfigReadContext& context, const CString&); + XConfigRead(const CConfigReadContext& context, + const char* errorFmt, const CString& arg); ~XConfigRead(); protected: diff --git a/lib/server/CInputFilter.cpp b/lib/server/CInputFilter.cpp new file mode 100644 index 00000000..2a6c0e3a --- /dev/null +++ b/lib/server/CInputFilter.cpp @@ -0,0 +1,999 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 "CInputFilter.h" +#include "CServer.h" +#include "CPrimaryClient.h" +#include "CKeyMap.h" +#include "CEventQueue.h" +#include "CLog.h" +#include "TMethodEventJob.h" +#include +#include + +// ----------------------------------------------------------------------------- +// Input Filter Condition Classes +// ----------------------------------------------------------------------------- +CInputFilter::CCondition::CCondition() +{ + // do nothing +} + +CInputFilter::CCondition::~CCondition() +{ + // do nothing +} + +void +CInputFilter::CCondition::enablePrimary(CPrimaryClient*) +{ + // do nothing +} + +void +CInputFilter::CCondition::disablePrimary(CPrimaryClient*) +{ + // do nothing +} + +CInputFilter::CKeystrokeCondition::CKeystrokeCondition( + IPlatformScreen::CKeyInfo* info) : + m_id(0), + m_key(info->m_key), + m_mask(info->m_mask) +{ + free(info); +} + +CInputFilter::CKeystrokeCondition::CKeystrokeCondition( + KeyID key, KeyModifierMask mask) : + m_id(0), + m_key(key), + m_mask(mask) +{ + // do nothing +} + +CInputFilter::CKeystrokeCondition::~CKeystrokeCondition() +{ + // do nothing +} + +KeyID +CInputFilter::CKeystrokeCondition::getKey() const +{ + return m_key; +} + +KeyModifierMask +CInputFilter::CKeystrokeCondition::getMask() const +{ + return m_mask; +} + +CInputFilter::CCondition* +CInputFilter::CKeystrokeCondition::clone() const +{ + return new CKeystrokeCondition(m_key, m_mask); +} + +CString +CInputFilter::CKeystrokeCondition::format() const +{ + return CStringUtil::print("keystroke(%s)", + CKeyMap::formatKey(m_key, m_mask).c_str()); +} + +CInputFilter::EFilterStatus +CInputFilter::CKeystrokeCondition::match(const CEvent& event) +{ + EFilterStatus status; + + // check for hotkey events + CEvent::Type type = event.getType(); + if (type == IPrimaryScreen::getHotKeyDownEvent()) { + status = kActivate; + } + else if (type == IPrimaryScreen::getHotKeyUpEvent()) { + status = kDeactivate; + } + else { + return kNoMatch; + } + + // check if it's our hotkey + IPrimaryScreen::CHotKeyInfo* kinfo = + reinterpret_cast(event.getData()); + if (kinfo->m_id != m_id) { + return kNoMatch; + } + + return status; +} + +void +CInputFilter::CKeystrokeCondition::enablePrimary(CPrimaryClient* primary) +{ + m_id = primary->registerHotKey(m_key, m_mask); +} + +void +CInputFilter::CKeystrokeCondition::disablePrimary(CPrimaryClient* primary) +{ + primary->unregisterHotKey(m_id); + m_id = 0; +} + +CInputFilter::CMouseButtonCondition::CMouseButtonCondition( + IPlatformScreen::CButtonInfo* info) : + m_button(info->m_button), + m_mask(info->m_mask) +{ + free(info); +} + +CInputFilter::CMouseButtonCondition::CMouseButtonCondition( + ButtonID button, KeyModifierMask mask) : + m_button(button), + m_mask(mask) +{ + // do nothing +} + +CInputFilter::CMouseButtonCondition::~CMouseButtonCondition() +{ + // do nothing +} + +ButtonID +CInputFilter::CMouseButtonCondition::getButton() const +{ + return m_button; +} + +KeyModifierMask +CInputFilter::CMouseButtonCondition::getMask() const +{ + return m_mask; +} + +CInputFilter::CCondition* +CInputFilter::CMouseButtonCondition::clone() const +{ + return new CMouseButtonCondition(m_button, m_mask); +} + +CString +CInputFilter::CMouseButtonCondition::format() const +{ + CString key = CKeyMap::formatKey(kKeyNone, m_mask); + if (!key.empty()) { + key += "+"; + } + return CStringUtil::print("mousebutton(%s%d)", key.c_str(), m_button); +} + +CInputFilter::EFilterStatus +CInputFilter::CMouseButtonCondition::match(const CEvent& event) +{ + static const KeyModifierMask s_ignoreMask = + KeyModifierAltGr | KeyModifierCapsLock | + KeyModifierNumLock | KeyModifierScrollLock; + + EFilterStatus status; + + // check for hotkey events + CEvent::Type type = event.getType(); + if (type == IPrimaryScreen::getButtonDownEvent()) { + status = kActivate; + } + else if (type == IPrimaryScreen::getButtonUpEvent()) { + status = kDeactivate; + } + else { + return kNoMatch; + } + + // check if it's the right button and modifiers. ignore modifiers + // that cannot be combined with a mouse button. + IPlatformScreen::CButtonInfo* minfo = + reinterpret_cast(event.getData()); + if (minfo->m_button != m_button || + (minfo->m_mask & ~s_ignoreMask) != m_mask) { + return kNoMatch; + } + + return status; +} + +CInputFilter::CScreenConnectedCondition::CScreenConnectedCondition( + const CString& screen) : + m_screen(screen) +{ + // do nothing +} + +CInputFilter::CScreenConnectedCondition::~CScreenConnectedCondition() +{ + // do nothing +} + +CInputFilter::CCondition* +CInputFilter::CScreenConnectedCondition::clone() const +{ + return new CScreenConnectedCondition(m_screen); +} + +CString +CInputFilter::CScreenConnectedCondition::format() const +{ + return CStringUtil::print("connect(%s)", m_screen.c_str()); +} + +CInputFilter::EFilterStatus +CInputFilter::CScreenConnectedCondition::match(const CEvent& event) +{ + if (event.getType() == CServer::getConnectedEvent()) { + CServer::CScreenConnectedInfo* info = + reinterpret_cast(event.getData()); + if (m_screen == info->m_screen || m_screen.empty()) { + return kActivate; + } + } + + return kNoMatch; +} + +// ----------------------------------------------------------------------------- +// Input Filter Action Classes +// ----------------------------------------------------------------------------- +CInputFilter::CAction::CAction() +{ + // do nothing +} + +CInputFilter::CAction::~CAction() +{ + // do nothing +} + +CInputFilter::CLockCursorToScreenAction::CLockCursorToScreenAction(Mode mode): + m_mode(mode) +{ + // do nothing +} + + +CInputFilter::CLockCursorToScreenAction::Mode +CInputFilter::CLockCursorToScreenAction::getMode() const +{ + return m_mode; +} + +CInputFilter::CAction* +CInputFilter::CLockCursorToScreenAction::clone() const +{ + return new CLockCursorToScreenAction(*this); +} + +CString +CInputFilter::CLockCursorToScreenAction::format() const +{ + static const char* s_mode[] = { "off", "on", "toggle" }; + + return CStringUtil::print("lockCursorToScreen(%s)", s_mode[m_mode]); +} + +void +CInputFilter::CLockCursorToScreenAction::perform(const CEvent& event) +{ + static const CServer::CLockCursorToScreenInfo::State s_state[] = { + CServer::CLockCursorToScreenInfo::kOff, + CServer::CLockCursorToScreenInfo::kOn, + CServer::CLockCursorToScreenInfo::kToggle + }; + + // send event + CServer::CLockCursorToScreenInfo* info = + CServer::CLockCursorToScreenInfo::alloc(s_state[m_mode]); + EVENTQUEUE->addEvent(CEvent(CServer::getLockCursorToScreenEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + +CInputFilter::CSwitchToScreenAction::CSwitchToScreenAction( + const CString& screen) : + m_screen(screen) +{ + // do nothing +} + +CString +CInputFilter::CSwitchToScreenAction::getScreen() const +{ + return m_screen; +} + +CInputFilter::CAction* +CInputFilter::CSwitchToScreenAction::clone() const +{ + return new CSwitchToScreenAction(*this); +} + +CString +CInputFilter::CSwitchToScreenAction::format() const +{ + return CStringUtil::print("switchToScreen(%s)", m_screen.c_str()); +} + +void +CInputFilter::CSwitchToScreenAction::perform(const CEvent& event) +{ + // pick screen name. if m_screen is empty then use the screen from + // event if it has one. + CString screen = m_screen; + if (screen.empty() && event.getType() == CServer::getConnectedEvent()) { + CServer::CScreenConnectedInfo* info = + reinterpret_cast(event.getData()); + screen = info->m_screen; + } + + // send event + CServer::CSwitchToScreenInfo* info = + CServer::CSwitchToScreenInfo::alloc(screen); + EVENTQUEUE->addEvent(CEvent(CServer::getSwitchToScreenEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + +CInputFilter::CSwitchInDirectionAction::CSwitchInDirectionAction( + EDirection direction) : + m_direction(direction) +{ + // do nothing +} + +EDirection +CInputFilter::CSwitchInDirectionAction::getDirection() const +{ + return m_direction; +} + +CInputFilter::CAction* +CInputFilter::CSwitchInDirectionAction::clone() const +{ + return new CSwitchInDirectionAction(*this); +} + +CString +CInputFilter::CSwitchInDirectionAction::format() const +{ + static const char* s_names[] = { + "", + "left", + "right", + "up", + "down" + }; + + return CStringUtil::print("switchInDirection(%s)", s_names[m_direction]); +} + +void +CInputFilter::CSwitchInDirectionAction::perform(const CEvent& event) +{ + CServer::CSwitchInDirectionInfo* info = + CServer::CSwitchInDirectionInfo::alloc(m_direction); + EVENTQUEUE->addEvent(CEvent(CServer::getSwitchInDirectionEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + +CInputFilter::CKeystrokeAction::CKeystrokeAction( + IPlatformScreen::CKeyInfo* info, bool press) : + m_keyInfo(info), + m_press(press) +{ + // do nothing +} + +CInputFilter::CKeystrokeAction::~CKeystrokeAction() +{ + free(m_keyInfo); +} + +void +CInputFilter::CKeystrokeAction::adoptInfo(IPlatformScreen::CKeyInfo* info) +{ + free(m_keyInfo); + m_keyInfo = info; +} + +const IPlatformScreen::CKeyInfo* +CInputFilter::CKeystrokeAction::getInfo() const +{ + return m_keyInfo; +} + +bool +CInputFilter::CKeystrokeAction::isOnPress() const +{ + return m_press; +} + +CInputFilter::CAction* +CInputFilter::CKeystrokeAction::clone() const +{ + IKeyState::CKeyInfo* info = IKeyState::CKeyInfo::alloc(*m_keyInfo); + return new CKeystrokeAction(info, m_press); +} + +CString +CInputFilter::CKeystrokeAction::format() const +{ + const char* type = formatName(); + + if (m_keyInfo->m_screens[0] == '\0') { + return CStringUtil::print("%s(%s)", type, + CKeyMap::formatKey(m_keyInfo->m_key, + m_keyInfo->m_mask).c_str()); + } + else if (m_keyInfo->m_screens[0] == '*') { + return CStringUtil::print("%s(%s,*)", type, + CKeyMap::formatKey(m_keyInfo->m_key, + m_keyInfo->m_mask).c_str()); + } + else { + return CStringUtil::print("%s(%s,%.*s)", type, + CKeyMap::formatKey(m_keyInfo->m_key, + m_keyInfo->m_mask).c_str(), + strlen(m_keyInfo->m_screens + 1) - 1, + m_keyInfo->m_screens + 1); + } +} + +void +CInputFilter::CKeystrokeAction::perform(const CEvent& event) +{ + CEvent::Type type = m_press ? IPlatformScreen::getKeyDownEvent() : + IPlatformScreen::getKeyUpEvent(); + EVENTQUEUE->addEvent(CEvent(IPlatformScreen::getFakeInputBeginEvent(), + event.getTarget(), NULL, + CEvent::kDeliverImmediately)); + EVENTQUEUE->addEvent(CEvent(type, event.getTarget(), m_keyInfo, + CEvent::kDeliverImmediately | + CEvent::kDontFreeData)); + EVENTQUEUE->addEvent(CEvent(IPlatformScreen::getFakeInputEndEvent(), + event.getTarget(), NULL, + CEvent::kDeliverImmediately)); +} + +const char* +CInputFilter::CKeystrokeAction::formatName() const +{ + return (m_press ? "keyDown" : "keyUp"); +} + +CInputFilter::CMouseButtonAction::CMouseButtonAction( + IPlatformScreen::CButtonInfo* info, bool press) : + m_buttonInfo(info), + m_press(press) +{ + // do nothing +} + +CInputFilter::CMouseButtonAction::~CMouseButtonAction() +{ + free(m_buttonInfo); +} + +const IPlatformScreen::CButtonInfo* +CInputFilter::CMouseButtonAction::getInfo() const +{ + return m_buttonInfo; +} + +bool +CInputFilter::CMouseButtonAction::isOnPress() const +{ + return m_press; +} + +CInputFilter::CAction* +CInputFilter::CMouseButtonAction::clone() const +{ + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(*m_buttonInfo); + return new CMouseButtonAction(info, m_press); +} + +CString +CInputFilter::CMouseButtonAction::format() const +{ + const char* type = formatName(); + + CString key = CKeyMap::formatKey(kKeyNone, m_buttonInfo->m_mask); + return CStringUtil::print("%s(%s%s%d)", type, + key.c_str(), key.empty() ? "" : "+", + m_buttonInfo->m_button); +} + +void +CInputFilter::CMouseButtonAction::perform(const CEvent& event) + +{ + // send modifiers + IPlatformScreen::CKeyInfo* modifierInfo = NULL; + if (m_buttonInfo->m_mask != 0) { + KeyID key = m_press ? kKeySetModifiers : kKeyClearModifiers; + modifierInfo = + IKeyState::CKeyInfo::alloc(key, m_buttonInfo->m_mask, 0, 1); + EVENTQUEUE->addEvent(CEvent(IPlatformScreen::getKeyDownEvent(), + event.getTarget(), modifierInfo, + CEvent::kDeliverImmediately)); + } + + // send button + CEvent::Type type = m_press ? IPlatformScreen::getButtonDownEvent() : + IPlatformScreen::getButtonUpEvent(); + EVENTQUEUE->addEvent(CEvent(type, event.getTarget(), m_buttonInfo, + CEvent::kDeliverImmediately | + CEvent::kDontFreeData)); +} + +const char* +CInputFilter::CMouseButtonAction::formatName() const +{ + return (m_press ? "mouseDown" : "mouseUp"); +} + +// +// CInputFilter::CRule +// + +CInputFilter::CRule::CRule() : + m_condition(NULL) +{ + // do nothing +} + +CInputFilter::CRule::CRule(CCondition* adoptedCondition) : + m_condition(adoptedCondition) +{ + // do nothing +} + +CInputFilter::CRule::CRule(const CRule& rule) : + m_condition(NULL) +{ + copy(rule); +} + +CInputFilter::CRule::~CRule() +{ + clear(); +} + +CInputFilter::CRule& +CInputFilter::CRule::operator=(const CRule& rule) +{ + if (&rule != this) { + copy(rule); + } + return *this; +} + +void +CInputFilter::CRule::clear() +{ + delete m_condition; + for (CActionList::iterator i = m_activateActions.begin(); + i != m_activateActions.end(); ++i) { + delete *i; + } + for (CActionList::iterator i = m_deactivateActions.begin(); + i != m_deactivateActions.end(); ++i) { + delete *i; + } + + m_condition = NULL; + m_activateActions.clear(); + m_deactivateActions.clear(); +} + +void +CInputFilter::CRule::copy(const CRule& rule) +{ + clear(); + if (rule.m_condition != NULL) { + m_condition = rule.m_condition->clone(); + } + for (CActionList::const_iterator i = rule.m_activateActions.begin(); + i != rule.m_activateActions.end(); ++i) { + m_activateActions.push_back((*i)->clone()); + } + for (CActionList::const_iterator i = rule.m_deactivateActions.begin(); + i != rule.m_deactivateActions.end(); ++i) { + m_deactivateActions.push_back((*i)->clone()); + } +} + +void +CInputFilter::CRule::setCondition(CCondition* adopted) +{ + delete m_condition; + m_condition = adopted; +} + +void +CInputFilter::CRule::adoptAction(CAction* action, bool onActivation) +{ + if (action != NULL) { + if (onActivation) { + m_activateActions.push_back(action); + } + else { + m_deactivateActions.push_back(action); + } + } +} + +void +CInputFilter::CRule::removeAction(bool onActivation, UInt32 index) +{ + if (onActivation) { + delete m_activateActions[index]; + m_activateActions.erase(m_activateActions.begin() + index); + } + else { + delete m_deactivateActions[index]; + m_deactivateActions.erase(m_deactivateActions.begin() + index); + } +} + +void +CInputFilter::CRule::replaceAction(CAction* adopted, + bool onActivation, UInt32 index) +{ + if (adopted == NULL) { + removeAction(onActivation, index); + } + else if (onActivation) { + delete m_activateActions[index]; + m_activateActions[index] = adopted; + } + else { + delete m_deactivateActions[index]; + m_deactivateActions[index] = adopted; + } +} + +void +CInputFilter::CRule::enable(CPrimaryClient* primaryClient) +{ + if (m_condition != NULL) { + m_condition->enablePrimary(primaryClient); + } +} + +void +CInputFilter::CRule::disable(CPrimaryClient* primaryClient) +{ + if (m_condition != NULL) { + m_condition->disablePrimary(primaryClient); + } +} + +bool +CInputFilter::CRule::handleEvent(const CEvent& event) +{ + // NULL condition never matches + if (m_condition == NULL) { + return false; + } + + // match + const CActionList* actions; + switch (m_condition->match(event)) { + default: + // not handled + return false; + + case kActivate: + actions = &m_activateActions; + LOG((CLOG_DEBUG1 "activate actions")); + break; + + case kDeactivate: + actions = &m_deactivateActions; + LOG((CLOG_DEBUG1 "deactivate actions")); + break; + } + + // perform actions + for (CActionList::const_iterator i = actions->begin(); + i != actions->end(); ++i) { + LOG((CLOG_DEBUG1 "hotkey: %s", (*i)->format().c_str())); + (*i)->perform(event); + } + + return true; +} + +CString +CInputFilter::CRule::format() const +{ + CString s; + if (m_condition != NULL) { + // condition + s += m_condition->format(); + s += " = "; + + // activate actions + CActionList::const_iterator i = m_activateActions.begin(); + if (i != m_activateActions.end()) { + s += (*i)->format(); + while (++i != m_activateActions.end()) { + s += ", "; + s += (*i)->format(); + } + } + + // deactivate actions + if (!m_deactivateActions.empty()) { + s += "; "; + i = m_deactivateActions.begin(); + if (i != m_deactivateActions.end()) { + s += (*i)->format(); + while (++i != m_deactivateActions.end()) { + s += ", "; + s += (*i)->format(); + } + } + } + } + return s; +} + +const CInputFilter::CCondition* +CInputFilter::CRule::getCondition() const +{ + return m_condition; +} + +UInt32 +CInputFilter::CRule::getNumActions(bool onActivation) const +{ + if (onActivation) { + return static_cast(m_activateActions.size()); + } + else { + return static_cast(m_deactivateActions.size()); + } +} + +const CInputFilter::CAction& +CInputFilter::CRule::getAction(bool onActivation, UInt32 index) const +{ + if (onActivation) { + return *m_activateActions[index]; + } + else { + return *m_deactivateActions[index]; + } +} + + +// ----------------------------------------------------------------------------- +// Input Filter Class +// ----------------------------------------------------------------------------- +CInputFilter::CInputFilter() : + m_primaryClient(NULL) +{ + // do nothing +} + +CInputFilter::CInputFilter(const CInputFilter& x) : + m_ruleList(x.m_ruleList), + m_primaryClient(NULL) +{ + setPrimaryClient(x.m_primaryClient); +} + +CInputFilter::~CInputFilter() +{ + setPrimaryClient(NULL); +} + +CInputFilter& +CInputFilter::operator=(const CInputFilter& x) +{ + if (&x != this) { + CPrimaryClient* oldClient = m_primaryClient; + setPrimaryClient(NULL); + + m_ruleList = x.m_ruleList; + + setPrimaryClient(oldClient); + } + return *this; +} + +void +CInputFilter::addFilterRule(const CRule& rule) +{ + m_ruleList.push_back(rule); + if (m_primaryClient != NULL) { + m_ruleList.back().enable(m_primaryClient); + } +} + +void +CInputFilter::removeFilterRule(UInt32 index) +{ + if (m_primaryClient != NULL) { + m_ruleList[index].disable(m_primaryClient); + } + m_ruleList.erase(m_ruleList.begin() + index); +} + +CInputFilter::CRule& +CInputFilter::getRule(UInt32 index) +{ + return m_ruleList[index]; +} + +void +CInputFilter::setPrimaryClient(CPrimaryClient* client) +{ + if (m_primaryClient == client) { + return; + } + + if (m_primaryClient != NULL) { + for (CRuleList::iterator rule = m_ruleList.begin(); + rule != m_ruleList.end(); ++rule) { + rule->disable(m_primaryClient); + } + + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getHotKeyDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getHotKeyUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(CServer::getConnectedEvent(), + m_primaryClient->getEventTarget()); + } + + m_primaryClient = client; + + if (m_primaryClient != NULL) { + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getHotKeyDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getHotKeyUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(CServer::getConnectedEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + + for (CRuleList::iterator rule = m_ruleList.begin(); + rule != m_ruleList.end(); ++rule) { + rule->enable(m_primaryClient); + } + } +} + +CString +CInputFilter::format(const CString& linePrefix) const +{ + CString s; + for (CRuleList::const_iterator i = m_ruleList.begin(); + i != m_ruleList.end(); ++i) { + s += linePrefix; + s += i->format(); + s += "\n"; + } + return s; +} + +UInt32 +CInputFilter::getNumRules() const +{ + return static_cast(m_ruleList.size()); +} + +bool +CInputFilter::operator==(const CInputFilter& x) const +{ + // if there are different numbers of rules then we can't be equal + if (m_ruleList.size() != x.m_ruleList.size()) { + return false; + } + + // compare rule lists. the easiest way to do that is to format each + // rule into a string, sort the strings, then compare the results. + std::vector aList, bList; + for (CRuleList::const_iterator i = m_ruleList.begin(); + i != m_ruleList.end(); ++i) { + aList.push_back(i->format()); + } + for (CRuleList::const_iterator i = x.m_ruleList.begin(); + i != x.m_ruleList.end(); ++i) { + bList.push_back(i->format()); + } + std::partial_sort(aList.begin(), aList.end(), aList.end()); + std::partial_sort(bList.begin(), bList.end(), bList.end()); + return (aList == bList); +} + +bool +CInputFilter::operator!=(const CInputFilter& x) const +{ + return !operator==(x); +} + +void +CInputFilter::handleEvent(const CEvent& event, void*) +{ + // copy event and adjust target + CEvent myEvent(event.getType(), this, event.getData(), + event.getFlags() | CEvent::kDontFreeData | + CEvent::kDeliverImmediately); + + // let each rule try to match the event until one does + for (CRuleList::iterator rule = m_ruleList.begin(); + rule != m_ruleList.end(); ++rule) { + if (rule->handleEvent(myEvent)) { + // handled + return; + } + } + + // not handled so pass through + EVENTQUEUE->addEvent(myEvent); +} diff --git a/lib/server/CInputFilter.h b/lib/server/CInputFilter.h new file mode 100644 index 00000000..1c64636c --- /dev/null +++ b/lib/server/CInputFilter.h @@ -0,0 +1,322 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 CINPUTFILTER_H +#define CINPUTFILTER_H + +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "ProtocolTypes.h" +#include "IPlatformScreen.h" +#include "CString.h" +#include "stdmap.h" + +class CPrimaryClient; +class CEvent; + +class CInputFilter { +public: + // ------------------------------------------------------------------------- + // Input Filter Condition Classes + // ------------------------------------------------------------------------- + enum EFilterStatus { + kNoMatch, + kActivate, + kDeactivate + }; + + class CCondition { + public: + CCondition(); + virtual ~CCondition(); + + virtual CCondition* clone() const = 0; + virtual CString format() const = 0; + + virtual EFilterStatus match(const CEvent&) = 0; + + virtual void enablePrimary(CPrimaryClient*); + virtual void disablePrimary(CPrimaryClient*); + }; + + // CKeystrokeCondition + class CKeystrokeCondition : public CCondition { + public: + CKeystrokeCondition(IPlatformScreen::CKeyInfo*); + CKeystrokeCondition(KeyID key, KeyModifierMask mask); + virtual ~CKeystrokeCondition(); + + KeyID getKey() const; + KeyModifierMask getMask() const; + + // CCondition overrides + virtual CCondition* clone() const; + virtual CString format() const; + virtual EFilterStatus match(const CEvent&); + virtual void enablePrimary(CPrimaryClient*); + virtual void disablePrimary(CPrimaryClient*); + + private: + UInt32 m_id; + KeyID m_key; + KeyModifierMask m_mask; + }; + + // CMouseButtonCondition + class CMouseButtonCondition : public CCondition { + public: + CMouseButtonCondition(IPlatformScreen::CButtonInfo*); + CMouseButtonCondition(ButtonID, KeyModifierMask mask); + virtual ~CMouseButtonCondition(); + + ButtonID getButton() const; + KeyModifierMask getMask() const; + + // CCondition overrides + virtual CCondition* clone() const; + virtual CString format() const; + virtual EFilterStatus match(const CEvent&); + + private: + ButtonID m_button; + KeyModifierMask m_mask; + }; + + // CScreenConnectedCondition + class CScreenConnectedCondition : public CCondition { + public: + CScreenConnectedCondition(const CString& screen); + virtual ~CScreenConnectedCondition(); + + // CCondition overrides + virtual CCondition* clone() const; + virtual CString format() const; + virtual EFilterStatus match(const CEvent&); + + private: + CString m_screen; + }; + + // ------------------------------------------------------------------------- + // Input Filter Action Classes + // ------------------------------------------------------------------------- + + class CAction { + public: + CAction(); + virtual ~CAction(); + + virtual CAction* clone() const = 0; + virtual CString format() const = 0; + + virtual void perform(const CEvent&) = 0; + }; + + // CLockCursorToScreenAction + class CLockCursorToScreenAction : public CAction { + public: + enum Mode { kOff, kOn, kToggle }; + + CLockCursorToScreenAction(Mode = kToggle); + + Mode getMode() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + Mode m_mode; + }; + + // CSwitchToScreenAction + class CSwitchToScreenAction : public CAction { + public: + CSwitchToScreenAction(const CString& screen); + + CString getScreen() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + CString m_screen; + }; + + // CSwitchInDirectionAction + class CSwitchInDirectionAction : public CAction { + public: + CSwitchInDirectionAction(EDirection); + + EDirection getDirection() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + EDirection m_direction; + }; + + // CKeystrokeAction + class CKeystrokeAction : public CAction { + public: + CKeystrokeAction(IPlatformScreen::CKeyInfo* adoptedInfo, bool press); + ~CKeystrokeAction(); + + void adoptInfo(IPlatformScreen::CKeyInfo*); + const IPlatformScreen::CKeyInfo* + getInfo() const; + bool isOnPress() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + protected: + virtual const char* formatName() const; + + private: + IPlatformScreen::CKeyInfo* m_keyInfo; + bool m_press; + }; + + // CMouseButtonAction -- modifier combinations not implemented yet + class CMouseButtonAction : public CAction { + public: + CMouseButtonAction(IPlatformScreen::CButtonInfo* adoptedInfo, + bool press); + ~CMouseButtonAction(); + + const IPlatformScreen::CButtonInfo* + getInfo() const; + bool isOnPress() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + protected: + virtual const char* formatName() const; + + private: + IPlatformScreen::CButtonInfo* m_buttonInfo; + bool m_press; + }; + + class CRule { + public: + CRule(); + CRule(CCondition* adopted); + CRule(const CRule&); + ~CRule(); + + CRule& operator=(const CRule&); + + // replace the condition + void setCondition(CCondition* adopted); + + // add an action to the rule + void adoptAction(CAction*, bool onActivation); + + // remove an action from the rule + void removeAction(bool onActivation, UInt32 index); + + // replace an action in the rule + void replaceAction(CAction* adopted, + bool onActivation, UInt32 index); + + // enable/disable + void enable(CPrimaryClient*); + void disable(CPrimaryClient*); + + // event handling + bool handleEvent(const CEvent&); + + // convert rule to a string + CString format() const; + + // get the rule's condition + const CCondition* + getCondition() const; + + // get number of actions + UInt32 getNumActions(bool onActivation) const; + + // get action by index + const CAction& getAction(bool onActivation, UInt32 index) const; + + private: + void clear(); + void copy(const CRule&); + + private: + typedef std::vector CActionList; + + CCondition* m_condition; + CActionList m_activateActions; + CActionList m_deactivateActions; + }; + + // ------------------------------------------------------------------------- + // Input Filter Class + // ------------------------------------------------------------------------- + typedef std::vector CRuleList; + + CInputFilter(); + CInputFilter(const CInputFilter&); + virtual ~CInputFilter(); + + CInputFilter& operator=(const CInputFilter&); + + // add rule, adopting the condition and the actions + void addFilterRule(const CRule& rule); + + // remove a rule + void removeFilterRule(UInt32 index); + + // get rule by index + CRule& getRule(UInt32 index); + + // enable event filtering using the given primary client. disable + // if client is NULL. + void setPrimaryClient(CPrimaryClient* client); + + // convert rules to a string + CString format(const CString& linePrefix) const; + + // get number of rules + UInt32 getNumRules() const; + + //! Compare filters + bool operator==(const CInputFilter&) const; + //! Compare filters + bool operator!=(const CInputFilter&) const; + +private: + // event handling + void handleEvent(const CEvent&, void*); + +private: + CRuleList m_ruleList; + CPrimaryClient* m_primaryClient; +}; + +#endif diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 7ec681bc..03146655 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -22,8 +22,9 @@ // CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) : - m_name(name), - m_screen(screen) + CBaseClientProxy(name), + m_screen(screen), + m_fakeInputCount(0) { // all clipboards are clean for (UInt32 i = 0; i < kClipboardEnd; ++i) { @@ -42,6 +43,34 @@ CPrimaryClient::reconfigure(UInt32 activeSides) m_screen->reconfigure(activeSides); } +UInt32 +CPrimaryClient::registerHotKey(KeyID key, KeyModifierMask mask) +{ + return m_screen->registerHotKey(key, mask); +} + +void +CPrimaryClient::unregisterHotKey(UInt32 id) +{ + m_screen->unregisterHotKey(id); +} + +void +CPrimaryClient::fakeInputBegin() +{ + if (++m_fakeInputCount == 1) { + m_screen->fakeInputBegin(); + } +} + +void +CPrimaryClient::fakeInputEnd() +{ + if (--m_fakeInputCount == 0) { + m_screen->fakeInputEnd(); + } +} + SInt32 CPrimaryClient::getJumpZoneSize() const { @@ -57,7 +86,7 @@ CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const KeyModifierMask CPrimaryClient::getToggleMask() const { - return m_screen->getActiveModifiers(); + return m_screen->pollActiveModifiers(); } bool @@ -150,9 +179,15 @@ CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty) } void -CPrimaryClient::keyDown(KeyID, KeyModifierMask, KeyButton) +CPrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) { - // ignore + if (m_fakeInputCount > 0) { +// XXX -- don't forward keystrokes to primary screen for now + (void)key; + (void)mask; + (void)button; +// m_screen->keyDown(key, mask, button); + } } void @@ -162,9 +197,15 @@ CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton) } void -CPrimaryClient::keyUp(KeyID, KeyModifierMask, KeyButton) +CPrimaryClient::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) { - // ignore + if (m_fakeInputCount > 0) { +// XXX -- don't forward keystrokes to primary screen for now + (void)key; + (void)mask; + (void)button; +// m_screen->keyUp(key, mask, button); + } } void @@ -192,7 +233,7 @@ CPrimaryClient::mouseRelativeMove(SInt32, SInt32) } void -CPrimaryClient::mouseWheel(SInt32) +CPrimaryClient::mouseWheel(SInt32, SInt32) { // ignore } @@ -214,9 +255,3 @@ CPrimaryClient::setOptions(const COptionsList& options) { m_screen->setOptions(options); } - -CString -CPrimaryClient::getName() const -{ - return m_name; -} diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 2006508c..e768a21d 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -15,7 +15,7 @@ #ifndef CPRIMARYCLIENT_H #define CPRIMARYCLIENT_H -#include "IClient.h" +#include "CBaseClientProxy.h" #include "ProtocolTypes.h" class CScreen; @@ -26,7 +26,7 @@ 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 a client. */ -class CPrimaryClient : public IClient { +class CPrimaryClient : public CBaseClientProxy { public: /*! \c name is the name of the server and \p screen is primary screen. @@ -43,6 +43,34 @@ public: */ void reconfigure(UInt32 activeSides); + //! Register a system hotkey + /*! + Registers a system-wide hotkey for key \p key with modifiers \p mask. + Returns an id used to unregister the hotkey. + */ + UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + + //! Unregister a system hotkey + /*! + Unregisters a previously registered hot key. + */ + void unregisterHotKey(UInt32 id); + + //! Prepare to synthesize input on primary screen + /*! + Prepares the primary screen to receive synthesized input. We do not + want to receive this synthesized input as user input so this method + ensures that we ignore it. Calls to \c fakeInputBegin() and + \c fakeInputEnd() may be nested; only the outermost have an effect. + */ + void fakeInputBegin(); + + //! Done synthesizing input on primary screen + /*! + Undoes whatever \c fakeInputBegin() did. + */ + void fakeInputEnd(); + //@} //! @name accessors //@{ @@ -61,7 +89,7 @@ public: the edges of the screen, typically the center. */ void getCursorCenter(SInt32& x, SInt32& y) const; - + //! Get toggle key state /*! Returns the primary screen's current toggle modifier key state. @@ -103,16 +131,15 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); - virtual void mouseWheel(SInt32 delta); + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); - virtual CString getName() const; private: - CString m_name; CScreen* m_screen; bool m_clipboardDirty[kClipboardEnd]; + SInt32 m_fakeInputCount; }; #endif diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index fce2584a..ab7a7ade 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -28,13 +28,18 @@ #include "CLog.h" #include "TMethodEventJob.h" #include "CArch.h" +#include // // CServer // -CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; -CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; +CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; +CEvent::Type CServer::s_connectedEvent = CEvent::kUnknown; +CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; +CEvent::Type CServer::s_switchToScreen = CEvent::kUnknown; +CEvent::Type CServer::s_switchInDirection = CEvent::kUnknown; +CEvent::Type CServer::s_lockCursorToScreen = CEvent::kUnknown; CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_primaryClient(primaryClient), @@ -44,7 +49,8 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_yDelta(0), m_xDelta2(0), m_yDelta2(0), - m_config(config), + m_config(), + m_inputFilter(m_config.getInputFilter()), m_activeSaver(NULL), m_switchDir(kNoDirection), m_switchScreen(NULL), @@ -54,11 +60,12 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_switchTwoTapEngaged(false), m_switchTwoTapArmed(false), m_switchTwoTapZone(3), - m_relativeMoves(false) + m_relativeMoves(false), + m_lockedToScreen(false) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); - assert(m_config.isScreen(primaryClient->getName())); + assert(config.isScreen(primaryClient->getName())); CString primaryName = getName(primaryClient); @@ -79,23 +86,23 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : new TMethodEventJob(this, &CServer::handleSwitchWaitTimeout)); EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleKeyDownEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleKeyUpEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleKeyRepeatEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleButtonDownEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleButtonUpEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnPrimaryEvent(), @@ -118,32 +125,51 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_primaryClient->getEventTarget(), new TMethodEventJob(this, &CServer::handleScreensaverDeactivatedEvent)); + EVENTQUEUE->adoptHandler(getSwitchToScreenEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleSwitchToScreenEvent)); + EVENTQUEUE->adoptHandler(getSwitchInDirectionEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleSwitchInDirectionEvent)); + EVENTQUEUE->adoptHandler(getLockCursorToScreenEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleLockCursorToScreenEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputBeginEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleFakeInputBeginEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputEndEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleFakeInputEndEvent)); // add connection addClient(m_primaryClient); - // process options locally - processOptions(); - - // tell primary client about its options - sendOptions(m_primaryClient); + // set initial configuration + setConfig(config); + // enable primary client m_primaryClient->enable(); + m_inputFilter->setPrimaryClient(m_primaryClient); } CServer::~CServer() { // remove event handlers and timers EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnPrimaryEvent(), m_primaryClient->getEventTarget()); EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnSecondaryEvent(), @@ -154,6 +180,10 @@ CServer::~CServer() m_primaryClient->getEventTarget()); EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputBeginEvent(), + m_inputFilter); + EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputEndEvent(), + m_inputFilter); EVENTQUEUE->removeHandler(CEvent::kTimer, this); stopSwitch(); @@ -161,14 +191,18 @@ CServer::~CServer() disconnect(); for (COldClients::iterator index = m_oldClients.begin(); index != m_oldClients.begin(); ++index) { - IClient* client = index->first; + CBaseClientProxy* client = index->first; EVENTQUEUE->deleteTimer(index->second); EVENTQUEUE->removeHandler(CEvent::kTimer, client); EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); delete client; } - // disconnect primary client + // remove input filter + m_inputFilter->setPrimaryClient(NULL); + + // disable and disconnect primary client + m_primaryClient->disable(); removeClient(m_primaryClient); } @@ -188,13 +222,28 @@ CServer::setConfig(const CConfig& config) m_config = config; processOptions(); + // add ScrollLock as a hotkey to lock to the screen. this was a + // built-in feature in earlier releases and is now supported via + // the user configurable hotkey mechanism. if the user has already + // registered ScrollLock for something else then that will win but + // we will unfortunately generate a warning. if the user has + // configured a CLockCursorToScreenAction then we don't add + // ScrollLock as a hotkey. + if (!m_config.hasLockToScreenAction()) { + IPlatformScreen::CKeyInfo* key = + IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0); + CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(key)); + rule.adoptAction(new CInputFilter::CLockCursorToScreenAction, true); + m_inputFilter->addFilterRule(rule); + } + // tell primary screen about reconfiguration 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; + CBaseClientProxy* client = index->second; sendOptions(client); } @@ -202,7 +251,7 @@ CServer::setConfig(const CConfig& config) } void -CServer::adoptClient(IClient* client) +CServer::adoptClient(CBaseClientProxy* client) { assert(client != NULL); @@ -234,6 +283,12 @@ CServer::adoptClient(IClient* client) if (m_activeSaver != NULL) { client->screensaver(true); } + + // send notification + CServer::CScreenConnectedInfo* info = + CServer::CScreenConnectedInfo::alloc(getName(client)); + EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(), + m_primaryClient->getEventTarget(), info)); } void @@ -272,6 +327,13 @@ CServer::getErrorEvent() "CServer::error"); } +CEvent::Type +CServer::getConnectedEvent() +{ + return CEvent::registerTypeOnce(s_connectedEvent, + "CServer::connected"); +} + CEvent::Type CServer::getDisconnectedEvent() { @@ -279,20 +341,29 @@ CServer::getDisconnectedEvent() "CServer::disconnected"); } -bool -CServer::onCommandKey(KeyID id, KeyModifierMask /*mask*/, bool /*down*/) +CEvent::Type +CServer::getSwitchToScreenEvent() { - if (id == kKeyScrollLock) { - m_primaryClient->reconfigure(getActivePrimarySides()); - if (!isLockedToScreenServer()) { - stopRelativeMoves(); - } - } - return false; + return CEvent::registerTypeOnce(s_switchToScreen, + "CServer::switchToScreen"); +} + +CEvent::Type +CServer::getSwitchInDirectionEvent() +{ + return CEvent::registerTypeOnce(s_switchInDirection, + "CServer::switchInDirection"); +} + +CEvent::Type +CServer::getLockCursorToScreenEvent() +{ + return CEvent::registerTypeOnce(s_lockCursorToScreen, + "CServer::lockCursorToScreen"); } CString -CServer::getName(const IClient* client) const +CServer::getName(const CBaseClientProxy* client) const { CString name = m_config.getCanonicalName(client->getName()); if (name.empty()) { @@ -306,16 +377,16 @@ CServer::getActivePrimarySides() const { UInt32 sides = 0; if (!isLockedToScreenServer()) { - if (getNeighbor(m_primaryClient, kLeft) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kLeft)) { sides |= kLeftMask; } - if (getNeighbor(m_primaryClient, kRight) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kRight)) { sides |= kRightMask; } - if (getNeighbor(m_primaryClient, kTop) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kTop)) { sides |= kTopMask; } - if (getNeighbor(m_primaryClient, kBottom) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kBottom)) { sides |= kBottomMask; } } @@ -326,7 +397,7 @@ bool CServer::isLockedToScreenServer() const { // locked if scroll-lock is toggled on - return ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0); + return m_lockedToScreen; } bool @@ -334,7 +405,7 @@ CServer::isLockedToScreen() const { // locked if we say we're locked if (isLockedToScreenServer()) { - LOG((CLOG_DEBUG "locked by ScrollLock")); + LOG((CLOG_DEBUG "locked to screen")); return true; } @@ -348,7 +419,7 @@ CServer::isLockedToScreen() const } SInt32 -CServer::getJumpZoneSize(IClient* client) const +CServer::getJumpZoneSize(CBaseClientProxy* client) const { if (client == m_primaryClient) { return m_primaryClient->getJumpZoneSize(); @@ -359,7 +430,8 @@ CServer::getJumpZoneSize(IClient* client) const } void -CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) +CServer::switchScreen(CBaseClientProxy* dst, + SInt32 x, SInt32 y, bool forScreensaver) { assert(dst != NULL); #ifndef NDEBUG @@ -428,8 +500,77 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) } } -IClient* -CServer::getNeighbor(IClient* src, EDirection dir) const +void +CServer::jumpToScreen(CBaseClientProxy* newScreen) +{ + assert(newScreen != NULL); + + // record the current cursor position on the active screen + m_active->setJumpCursorPos(m_x, m_y); + + // get the last cursor position on the target screen + SInt32 x, y; + newScreen->getJumpCursorPos(x, y); + + switchScreen(newScreen, x, y, false); +} + +float +CServer::mapToFraction(CBaseClientProxy* client, + EDirection dir, SInt32 x, SInt32 y) const +{ + SInt32 sx, sy, sw, sh; + client->getShape(sx, sy, sw, sh); + switch (dir) { + case kLeft: + case kRight: + return static_cast(y - sy + 0.5f) / static_cast(sh); + + case kTop: + case kBottom: + return static_cast(x - sx + 0.5f) / static_cast(sw); + + case kNoDirection: + assert(0 && "bad direction"); + break; + } + return 0.0f; +} + +void +CServer::mapToPixel(CBaseClientProxy* client, + EDirection dir, float f, SInt32& x, SInt32& y) const +{ + SInt32 sx, sy, sw, sh; + client->getShape(sx, sy, sw, sh); + switch (dir) { + case kLeft: + case kRight: + y = static_cast(f * sh) + sy; + break; + + case kTop: + case kBottom: + x = static_cast(f * sw) + sx; + break; + + case kNoDirection: + assert(0 && "bad direction"); + break; + } +} + +bool +CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const +{ + assert(client != NULL); + + return m_config.hasNeighbor(getName(client), dir); +} + +CBaseClientProxy* +CServer::getNeighbor(CBaseClientProxy* src, + EDirection dir, SInt32& x, SInt32& y) const { // note -- must be locked on entry @@ -440,21 +581,19 @@ CServer::getNeighbor(IClient* src, EDirection dir) const 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; - } + // convert position to fraction + float t = mapToFraction(src, dir, x, y); - // keep checking + // search for the closest neighbor that exists in direction dir + float tTmp; for (;;) { + CString dstName(m_config.getNeighbor(srcName, dir, t, &tTmp)); + // 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) { + if (dstName.empty()) { LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); return NULL; } @@ -463,7 +602,8 @@ CServer::getNeighbor(IClient* src, EDirection dir) const // 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())); + LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\" at %f", dstName.c_str(), CConfig::dirName(dir), srcName.c_str(), t)); + mapToPixel(index->second, dir, tTmp, x, y); return index->second; } @@ -471,13 +611,13 @@ CServer::getNeighbor(IClient* src, EDirection dir) const 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); + // use position on skipped screen + t = tTmp; } } -IClient* -CServer::getNeighbor(IClient* src, +CBaseClientProxy* +CServer::mapToNeighbor(CBaseClientProxy* src, EDirection srcSide, SInt32& x, SInt32& y) const { // note -- must be locked on entry @@ -485,16 +625,14 @@ CServer::getNeighbor(IClient* src, assert(src != NULL); // get the first neighbor - IClient* dst = getNeighbor(src, srcSide); + CBaseClientProxy* dst = getNeighbor(src, srcSide, x, y); if (dst == NULL) { return NULL; } - // get the source screen's size (needed for kRight and kBottom) - SInt32 sx, sy, sw, sh; + // get the source screen's size SInt32 dx, dy, dw, dh; - IClient* lastGoodScreen = src; - lastGoodScreen->getShape(sx, sy, sw, sh); + CBaseClientProxy* lastGoodScreen = src; lastGoodScreen->getShape(dx, dy, dw, dh); // find destination screen, adjusting x or y (but not both). the @@ -513,7 +651,7 @@ CServer::getNeighbor(IClient* src, break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); x += dx; @@ -529,7 +667,7 @@ CServer::getNeighbor(IClient* src, break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); x += dx; @@ -545,7 +683,7 @@ CServer::getNeighbor(IClient* src, break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); y += dy; @@ -557,11 +695,11 @@ CServer::getNeighbor(IClient* src, y -= dh; lastGoodScreen = dst; lastGoodScreen->getShape(dx, dy, dw, dh); - if (y < sh) { + if (y < dh) { break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); y += dy; @@ -580,88 +718,63 @@ CServer::getNeighbor(IClient* src, // 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(getName(dst)); - switch (srcSide) { - case kLeft: - if (!m_config.getNeighbor(dstName, kRight).empty() && - x > dx + dw - 1 - getJumpZoneSize(dst)) - x = dx + dw - 1 - getJumpZoneSize(dst); - break; - - case kRight: - if (!m_config.getNeighbor(dstName, kLeft).empty() && - x < dx + getJumpZoneSize(dst)) - x = dx + getJumpZoneSize(dst); - break; - - case kTop: - if (!m_config.getNeighbor(dstName, kBottom).empty() && - y > dy + dh - 1 - getJumpZoneSize(dst)) - y = dy + dh - 1 - getJumpZoneSize(dst); - break; - - case kBottom: - if (!m_config.getNeighbor(dstName, kTop).empty() && - y < dy + getJumpZoneSize(dst)) - y = dy + getJumpZoneSize(dst); - break; - - case kNoDirection: - assert(0 && "bad direction"); - return NULL; - } - } - - // 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; - - case kNoDirection: - assert(0 && "bad direction"); - return NULL; - } + avoidJumpZone(dst, srcSide, x, y); return dst; } +void +CServer::avoidJumpZone(CBaseClientProxy* dst, + EDirection dir, SInt32& x, SInt32& y) const +{ + // we only need to avoid jump zones on the primary screen + if (dst != m_primaryClient) { + return; + } + + const CString dstName(getName(dst)); + SInt32 dx, dy, dw, dh; + dst->getShape(dx, dy, dw, dh); + float t = mapToFraction(dst, dir, x, y); + SInt32 z = getJumpZoneSize(dst); + + // 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. + switch (dir) { + case kLeft: + if (!m_config.getNeighbor(dstName, kRight, t, NULL).empty() && + x > dx + dw - 1 - z) + x = dx + dw - 1 - z; + break; + + case kRight: + if (!m_config.getNeighbor(dstName, kLeft, t, NULL).empty() && + x < dx + z) + x = dx + z; + break; + + case kTop: + if (!m_config.getNeighbor(dstName, kBottom, t, NULL).empty() && + y > dy + dh - 1 - z) + y = dy + dh - 1 - z; + break; + + case kBottom: + if (!m_config.getNeighbor(dstName, kTop, t, NULL).empty() && + y < dy + z) + y = dy + z; + break; + + case kNoDirection: + assert(0 && "bad direction"); + } +} + bool -CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) +CServer::isSwitchOkay(CBaseClientProxy* newScreen, + EDirection dir, SInt32 x, SInt32 y, + SInt32 xActive, SInt32 yActive) { LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", getName(m_active).c_str(), CConfig::dirName(dir))); @@ -709,6 +822,33 @@ CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) preventSwitch = true; } + // are we in a locked corner? first check if screen has the option set + // and, if not, check the global options. + const CConfig::CScreenOptions* options = + m_config.getOptions(getName(m_active)); + if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) { + options = m_config.getOptions(""); + } + if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) { + // get corner mask and size + CConfig::CScreenOptions::const_iterator i = + options->find(kOptionScreenSwitchCorners); + UInt32 corners = static_cast(i->second); + i = options->find(kOptionScreenSwitchCornerSize); + SInt32 size = 0; + if (i != options->end()) { + size = i->second; + } + + // see if we're in a locked corner + if ((getCorner(m_active, xActive, yActive, size) & corners) != 0) { + // yep, no switching + LOG((CLOG_DEBUG1 "locked in corner")); + preventSwitch = true; + stopSwitch(); + } + } + // ignore if mouse is locked to screen and don't try to switch later if (!preventSwitch && isLockedToScreen()) { LOG((CLOG_DEBUG1 "locked to screen")); @@ -840,6 +980,63 @@ CServer::isSwitchWaitStarted() const return (m_switchWaitTimer != NULL); } +UInt32 +CServer::getCorner(CBaseClientProxy* client, + SInt32 x, SInt32 y, SInt32 size) const +{ + assert(client != NULL); + + // get client screen shape + SInt32 ax, ay, aw, ah; + client->getShape(ax, ay, aw, ah); + + // check for x,y on the left or right + SInt32 xSide; + if (x <= ax) { + xSide = -1; + } + else if (x >= ax + aw - 1) { + xSide = 1; + } + else { + xSide = 0; + } + + // check for x,y on the top or bottom + SInt32 ySide; + if (y <= ay) { + ySide = -1; + } + else if (y >= ay + ah - 1) { + ySide = 1; + } + else { + ySide = 0; + } + + // if against the left or right then check if y is within size + if (xSide != 0) { + if (y < ay + size) { + return (xSide < 0) ? kTopLeftMask : kTopRightMask; + } + else if (y >= ay + ah - size) { + return (xSide < 0) ? kBottomLeftMask : kBottomRightMask; + } + } + + // if against the left or right then check if y is within size + if (ySide != 0) { + if (x < ax + size) { + return (ySide < 0) ? kTopLeftMask : kBottomLeftMask; + } + else if (x >= ax + aw - size) { + return (ySide < 0) ? kTopRightMask : kBottomRightMask; + } + } + + return kNoCornerMask; +} + void CServer::stopRelativeMoves() { @@ -859,7 +1056,7 @@ CServer::stopRelativeMoves() } void -CServer::sendOptions(IClient* client) const +CServer::sendOptions(CBaseClientProxy* client) const { COptionsList optionsList; @@ -889,6 +1086,7 @@ CServer::sendOptions(IClient* client) const } // send the options + client->resetOptions(); client->setOptions(optionsList); } @@ -934,16 +1132,23 @@ void CServer::handleShapeChanged(const CEvent&, void* vclient) { // ignore events from unknown clients - IClient* client = reinterpret_cast(vclient); + CBaseClientProxy* client = reinterpret_cast(vclient); if (m_clientSet.count(client) == 0) { return; } + LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str())); + + // update jump coordinate + SInt32 x, y; + client->getCursorPos(x, y); + client->setJumpCursorPos(x, y); + // update the mouse coordinates if (client == m_active) { - client->getCursorPos(m_x, m_y); + m_x = x; + m_y = y; } - LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str())); // handle resolution change to primary screen if (client == m_primaryClient) { @@ -960,7 +1165,7 @@ void CServer::handleClipboardGrabbed(const CEvent& event, void* vclient) { // ignore events from unknown clients - IClient* grabber = reinterpret_cast(vclient); + CBaseClientProxy* grabber = reinterpret_cast(vclient); if (m_clientSet.count(grabber) == 0) { return; } @@ -992,7 +1197,7 @@ CServer::handleClipboardGrabbed(const CEvent& event, void* vclient) // grabber that it's clipboard isn't dirty. for (CClientList::iterator index = m_clients.begin(); index != m_clients.end(); ++index) { - IClient* client = index->second; + CBaseClientProxy* client = index->second; if (client == grabber) { client->setClipboardDirty(info->m_id, false); } @@ -1006,7 +1211,7 @@ void CServer::handleClipboardChanged(const CEvent& event, void* vclient) { // ignore events from unknown clients - IClient* sender = reinterpret_cast(vclient); + CBaseClientProxy* sender = reinterpret_cast(vclient); if (m_clientSet.count(sender) == 0) { return; } @@ -1020,7 +1225,7 @@ CServer::handleKeyDownEvent(const CEvent& event, void*) { IPlatformScreen::CKeyInfo* info = reinterpret_cast(event.getData()); - onKeyDown(info->m_key, info->m_mask, info->m_button); + onKeyDown(info->m_key, info->m_mask, info->m_button, info->m_screens); } void @@ -1028,7 +1233,7 @@ CServer::handleKeyUpEvent(const CEvent& event, void*) { IPlatformScreen::CKeyInfo* info = reinterpret_cast(event.getData()); - onKeyUp(info->m_key, info->m_mask, info->m_button); + onKeyUp(info->m_key, info->m_mask, info->m_button, info->m_screens); } void @@ -1076,7 +1281,7 @@ CServer::handleWheelEvent(const CEvent& event, void*) { IPlatformScreen::CWheelInfo* info = reinterpret_cast(event.getData()); - onMouseWheel(info->m_wheel); + onMouseWheel(info->m_xDelta, info->m_yDelta); } void @@ -1110,7 +1315,7 @@ CServer::handleClientDisconnected(const CEvent&, void* vclient) { // client has disconnected. it might be an old client or an // active client. we don't care so just handle it both ways. - IClient* client = reinterpret_cast(vclient); + CBaseClientProxy* client = reinterpret_cast(vclient); removeActiveClient(client); removeOldClient(client); delete client; @@ -1120,20 +1325,100 @@ void CServer::handleClientCloseTimeout(const CEvent&, void* vclient) { // client took too long to disconnect. just dump it. - IClient* client = reinterpret_cast(vclient); + CBaseClientProxy* client = reinterpret_cast(vclient); LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str())); removeOldClient(client); delete client; } void -CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum) +CServer::handleSwitchToScreenEvent(const CEvent& event, void*) +{ + CSwitchToScreenInfo* info = + reinterpret_cast(event.getData()); + + CClientList::const_iterator index = m_clients.find(info->m_screen); + if (index == m_clients.end()) { + LOG((CLOG_DEBUG1 "screen \"%s\" not active", info->m_screen)); + } + else { + jumpToScreen(index->second); + } +} + +void +CServer::handleSwitchInDirectionEvent(const CEvent& event, void*) +{ + CSwitchInDirectionInfo* info = + reinterpret_cast(event.getData()); + + // jump to screen in chosen direction from center of this screen + SInt32 x = m_x, y = m_y; + CBaseClientProxy* newScreen = + getNeighbor(m_active, info->m_direction, x, y); + if (newScreen == NULL) { + LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(info->m_direction))); + } + else { + jumpToScreen(newScreen); + } +} + +void +CServer::handleLockCursorToScreenEvent(const CEvent& event, void*) +{ + CLockCursorToScreenInfo* info = (CLockCursorToScreenInfo*)event.getData(); + + // choose new state + bool newState; + switch (info->m_state) { + case CLockCursorToScreenInfo::kOff: + newState = false; + break; + + default: + case CLockCursorToScreenInfo::kOn: + newState = true; + break; + + case CLockCursorToScreenInfo::kToggle: + newState = !m_lockedToScreen; + break; + } + + // enter new state + if (newState != m_lockedToScreen) { + m_lockedToScreen = newState; + LOG((CLOG_DEBUG "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); + + m_primaryClient->reconfigure(getActivePrimarySides()); + if (!isLockedToScreenServer()) { + stopRelativeMoves(); + } + } +} + +void +CServer::handleFakeInputBeginEvent(const CEvent&, void*) +{ + m_primaryClient->fakeInputBegin(); +} + +void +CServer::handleFakeInputEndEvent(const CEvent&, void*) +{ + m_primaryClient->fakeInputEnd(); +} + +void +CServer::onClipboardChanged(CBaseClientProxy* sender, + ClipboardID id, UInt32 seqNum) { 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)); + LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", getName(sender).c_str(), id)); return; } @@ -1157,7 +1442,7 @@ CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum) // tell all clients except the sender that the clipboard is dirty for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { - IClient* client = index->second; + CBaseClientProxy* client = index->second; client->setClipboardDirty(id, client != sender); } @@ -1187,7 +1472,7 @@ CServer::onScreensaver(bool activated) // changed resolutions while the screen saver was running. if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { // check position - IClient* screen = m_activeSaver; + CBaseClientProxy* screen = m_activeSaver; SInt32 x, y, w, h; screen->getShape(x, y, w, h); SInt32 zoneSize = getJumpZoneSize(screen); @@ -1215,39 +1500,51 @@ CServer::onScreensaver(bool activated) // send message to all clients for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { - IClient* client = index->second; + CBaseClientProxy* client = index->second; client->screensaver(activated); } } void -CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button, + const char* screens) { LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); - // handle command keys - if (onCommandKey(id, mask, true)) { - return; - } - // relay - m_active->keyDown(id, mask, button); + if (IKeyState::CKeyInfo::isDefault(screens)) { + m_active->keyDown(id, mask, button); + } + else { + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + if (IKeyState::CKeyInfo::contains(screens, index->first)) { + index->second->keyDown(id, mask, button); + } + } + } } void -CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) +CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button, + const char* screens) { LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); - // handle command keys - if (onCommandKey(id, mask, false)) { - return; - } - // relay - m_active->keyUp(id, mask, button); + if (IKeyState::CKeyInfo::isDefault(screens)) { + m_active->keyUp(id, mask, button); + } + else { + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + if (IKeyState::CKeyInfo::contains(screens, index->first)) { + index->second->keyUp(id, mask, button); + } + } + } } void @@ -1257,12 +1554,6 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); 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); } @@ -1315,6 +1606,21 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) m_active->getShape(ax, ay, aw, ah); SInt32 zoneSize = getJumpZoneSize(m_active); + // clamp position to screen + SInt32 xc = x, yc = y; + if (xc < ax + zoneSize) { + xc = ax; + } + else if (xc >= ax + aw - zoneSize) { + xc = ax + aw - 1; + } + if (yc < ay + zoneSize) { + yc = ay; + } + else if (yc >= ay + ah - zoneSize) { + yc = ay + ah - 1; + } + // see if we should change screens EDirection dir; if (x < ax + zoneSize) { @@ -1340,10 +1646,10 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) } // get jump destination - IClient* newScreen = getNeighbor(m_active, dir, x, y); + CBaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y); // should we switch or not? - if (isSwitchOkay(newScreen, dir, x, y)) { + if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { // switch screen switchScreen(newScreen, x, y, false); return true; @@ -1399,8 +1705,23 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) // find direction of neighbor and get the neighbor bool jump = true; - IClient* newScreen; + CBaseClientProxy* newScreen; do { + // clamp position to screen + SInt32 xc = m_x, yc = m_y; + if (xc < ax) { + xc = ax; + } + else if (xc >= ax + aw) { + xc = ax + aw - 1; + } + if (yc < ay) { + yc = ay; + } + else if (yc >= ay + ah) { + yc = ay + ah - 1; + } + EDirection dir; if (m_x < ax) { dir = kLeft; @@ -1457,10 +1778,10 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) } // try to switch screen. get the neighbor. - newScreen = getNeighbor(m_active, dir, m_x, m_y); + newScreen = mapToNeighbor(m_active, dir, m_x, m_y); // see if we should switch - if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { + if (!isSwitchOkay(newScreen, dir, m_x, m_y, xc, yc)) { newScreen = m_active; jump = false; } @@ -1500,17 +1821,17 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) } void -CServer::onMouseWheel(SInt32 delta) +CServer::onMouseWheel(SInt32 xDelta, SInt32 yDelta) { - LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta)); + LOG((CLOG_DEBUG1 "onMouseWheel %+d,%+d", xDelta, yDelta)); assert(m_active != NULL); // relay - m_active->mouseWheel(delta); + m_active->mouseWheel(xDelta, yDelta); } bool -CServer::addClient(IClient* client) +CServer::addClient(CBaseClientProxy* client) { CString name = getName(client); if (m_clients.count(name) != 0) { @@ -1535,6 +1856,11 @@ CServer::addClient(IClient* client) m_clientSet.insert(client); m_clients.insert(std::make_pair(name, client)); + // initialize client data + SInt32 x, y; + client->getCursorPos(x, y); + client->setJumpCursorPos(x, y); + // tell primary client about the active sides m_primaryClient->reconfigure(getActivePrimarySides()); @@ -1542,7 +1868,7 @@ CServer::addClient(IClient* client) } bool -CServer::removeClient(IClient* client) +CServer::removeClient(CBaseClientProxy* client) { // return false if not in list CClientSet::iterator i = m_clientSet.find(client); @@ -1566,7 +1892,7 @@ CServer::removeClient(IClient* client) } void -CServer::closeClient(IClient* client, const char* msg) +CServer::closeClient(CBaseClientProxy* client, const char* msg) { assert(client != m_primaryClient); assert(msg != NULL); @@ -1606,7 +1932,7 @@ CServer::closeClients(const CConfig& config) { // collect the clients that are connected but are being dropped // from the configuration (or who's canonical name is changing). - typedef std::set CRemovedClients; + typedef std::set CRemovedClients; CRemovedClients removed; for (CClientList::iterator index = m_clients.begin(); index != m_clients.end(); ++index) { @@ -1627,7 +1953,7 @@ CServer::closeClients(const CConfig& config) } void -CServer::removeActiveClient(IClient* client) +CServer::removeActiveClient(CBaseClientProxy* client) { if (removeClient(client)) { forceLeaveClient(client); @@ -1639,7 +1965,7 @@ CServer::removeActiveClient(IClient* client) } void -CServer::removeOldClient(IClient* client) +CServer::removeOldClient(CBaseClientProxy* client) { COldClients::iterator i = m_oldClients.find(client); if (i != m_oldClients.end()) { @@ -1654,9 +1980,10 @@ CServer::removeOldClient(IClient* client) } void -CServer::forceLeaveClient(IClient* client) +CServer::forceLeaveClient(CBaseClientProxy* client) { - IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + CBaseClientProxy* active = + (m_activeSaver != NULL) ? m_activeSaver : m_active; if (active == client) { // record new position (center of primary screen) m_primaryClient->getCursorCenter(m_x, m_y); @@ -1705,3 +2032,61 @@ CServer::CClipboardInfo::CClipboardInfo() : { // do nothing } + + +// +// CServer::CLockCursorToScreenInfo +// + +CServer::CLockCursorToScreenInfo* +CServer::CLockCursorToScreenInfo::alloc(State state) +{ + CLockCursorToScreenInfo* info = + (CLockCursorToScreenInfo*)malloc(sizeof(CLockCursorToScreenInfo)); + info->m_state = state; + return info; +} + + +// +// CServer::CSwitchToScreenInfo +// + +CServer::CSwitchToScreenInfo* +CServer::CSwitchToScreenInfo::alloc(const CString& screen) +{ + CSwitchToScreenInfo* info = + (CSwitchToScreenInfo*)malloc(sizeof(CSwitchToScreenInfo) + + screen.size()); + strcpy(info->m_screen, screen.c_str()); + return info; +} + + +// +// CServer::CSwitchInDirectionInfo +// + +CServer::CSwitchInDirectionInfo* +CServer::CSwitchInDirectionInfo::alloc(EDirection direction) +{ + CSwitchInDirectionInfo* info = + (CSwitchInDirectionInfo*)malloc(sizeof(CSwitchInDirectionInfo)); + info->m_direction = direction; + return info; +} + + +// +// CServer::CScreenConnectedInfo +// + +CServer::CScreenConnectedInfo* +CServer::CScreenConnectedInfo::alloc(const CString& screen) +{ + CScreenConnectedInfo* info = + (CScreenConnectedInfo*)malloc(sizeof(CScreenConnectedInfo) + + screen.size()); + strcpy(info->m_screen, screen.c_str()); + return info; +} diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 3272d837..c05d138d 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -26,9 +26,10 @@ #include "stdset.h" #include "stdvector.h" +class CBaseClientProxy; class CEventQueueTimer; class CPrimaryClient; -class IClient; +class CInputFilter; //! Synergy server /*! @@ -36,6 +37,46 @@ This class implements the top-level server algorithms for synergy. */ class CServer { public: + //! Lock cursor to screen data + class CLockCursorToScreenInfo { + public: + enum State { kOff, kOn, kToggle }; + + static CLockCursorToScreenInfo* alloc(State state = kToggle); + + public: + State m_state; + }; + + //! Switch to screen data + class CSwitchToScreenInfo { + public: + static CSwitchToScreenInfo* alloc(const CString& screen); + + public: + // this is a C-string; this type is a variable size structure + char m_screen[1]; + }; + + //! Switch in direction data + class CSwitchInDirectionInfo { + public: + static CSwitchInDirectionInfo* alloc(EDirection direction); + + public: + EDirection m_direction; + }; + + //! Screen connected data + class CScreenConnectedInfo { + public: + static CScreenConnectedInfo* alloc(const CString& screen); + + public: + // this is a C-string; this type is a variable size structure + char m_screen[1]; + }; + /*! Start the server with the configuration \p config and the primary client (local screen) \p primaryClient. The client retains @@ -60,7 +101,7 @@ public: Adds \p client to the server. The client is adopted and will be destroyed when the client disconnects or is disconnected. */ - void adoptClient(IClient* client); + void adoptClient(CBaseClientProxy* client); //! Disconnect clients /*! @@ -94,6 +135,14 @@ public: */ static CEvent::Type getErrorEvent(); + //! Get connected event type + /*! + Returns the connected event type. This is sent when a client screen + has connected. The event data is a \c CScreenConnectedInfo* that + indicates the connected screen. + */ + static CEvent::Type getConnectedEvent(); + //! Get disconnected event type /*! Returns the disconnected event type. This is sent when all the @@ -101,18 +150,35 @@ public: */ static CEvent::Type getDisconnectedEvent(); - //@} - -protected: - //! Handle special keys + //! Get switch to screen event type /*! - Handles keys with special meaning. + Returns the switch to screen event type. The server responds to this + by switching screens. The event data is a \c CSwitchToScreenInfo* + that indicates the target screen. */ - bool onCommandKey(KeyID, KeyModifierMask, bool down); + static CEvent::Type getSwitchToScreenEvent(); + + //! Get switch in direction event type + /*! + Returns the switch in direction event type. The server responds to this + by switching screens. The event data is a \c CSwitchInDirectionInfo* + that indicates the target direction. + */ + static CEvent::Type getSwitchInDirectionEvent(); + + //! Get lock cursor event type + /*! + Returns the lock cursor event type. The server responds to this + by locking the cursor to the active screen or unlocking it. The + event data is a \c CLockCursorToScreenInfo*. + */ + static CEvent::Type getLockCursorToScreenEvent(); + + //@} private: // get canonical name of client - CString getName(const IClient*) const; + CString getName(const CBaseClientProxy*) const; // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; @@ -127,28 +193,52 @@ private: bool isLockedToScreen() const; // returns the jump zone of the client - SInt32 getJumpZoneSize(IClient*) const; + SInt32 getJumpZoneSize(CBaseClientProxy*) const; // change the active screen - void switchScreen(IClient*, + void switchScreen(CBaseClientProxy*, SInt32 x, SInt32 y, bool forScreenSaver); - // lookup neighboring screen - IClient* getNeighbor(IClient*, EDirection) const; + // jump to screen + void jumpToScreen(CBaseClientProxy*); + + // convert pixel position to fraction, using x or y depending on the + // direction. + float mapToFraction(CBaseClientProxy*, EDirection, + SInt32 x, SInt32 y) const; + + // convert fraction to pixel position, writing only x or y depending + // on the direction. + void mapToPixel(CBaseClientProxy*, EDirection, float f, + SInt32& x, SInt32& y) const; + + // returns true if the client has a neighbor anywhere along the edge + // indicated by the direction. + bool hasAnyNeighbor(CBaseClientProxy*, EDirection) const; + + // lookup neighboring screen, mapping the coordinate independent of + // the direction to the neighbor's coordinate space. + CBaseClientProxy* getNeighbor(CBaseClientProxy*, EDirection, + SInt32& x, SInt32& y) 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, + CBaseClientProxy* mapToNeighbor(CBaseClientProxy*, EDirection, + SInt32& x, SInt32& y) const; + + // adjusts x and y or neither to avoid ending up in a jump zone + // after entering the client in the given direction. + void avoidJumpZone(CBaseClientProxy*, 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); + bool isSwitchOkay(CBaseClientProxy* dst, EDirection, + SInt32 x, SInt32 y, SInt32 xActive, SInt32 yActive); // update switch state due to a mouse move at \p x, \p y that // doesn't switch screens. @@ -181,11 +271,16 @@ private: // returns true iff the delay switch timer is started bool isSwitchWaitStarted() const; + // returns the corner (EScreenSwitchCornerMasks) where x,y is on the + // given client. corners have the given size. + UInt32 getCorner(CBaseClientProxy*, + SInt32 x, SInt32 y, SInt32 size) const; + // stop relative mouse moves void stopRelativeMoves(); // send screen options to \c client - void sendOptions(IClient* client) const; + void sendOptions(CBaseClientProxy* client) const; // process options from configuration void processOptions(); @@ -207,28 +302,35 @@ private: void handleSwitchWaitTimeout(const CEvent&, void*); void handleClientDisconnected(const CEvent&, void*); void handleClientCloseTimeout(const CEvent&, void*); + void handleSwitchToScreenEvent(const CEvent&, void*); + void handleSwitchInDirectionEvent(const CEvent&, void*); + void handleLockCursorToScreenEvent(const CEvent&, void*); + void handleFakeInputBeginEvent(const CEvent&, void*); + void handleFakeInputEndEvent(const CEvent&, void*); // event processing - void onClipboardChanged(IClient* sender, + void onClipboardChanged(CBaseClientProxy* sender, ClipboardID id, UInt32 seqNum); void onScreensaver(bool activated); - void onKeyDown(KeyID, KeyModifierMask, KeyButton); - void onKeyUp(KeyID, KeyModifierMask, KeyButton); + void onKeyDown(KeyID, KeyModifierMask, KeyButton, + const char* screens); + void onKeyUp(KeyID, KeyModifierMask, KeyButton, + const char* screens); void onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton); void onMouseDown(ButtonID); void onMouseUp(ButtonID); bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); - void onMouseWheel(SInt32 delta); + void onMouseWheel(SInt32 xDelta, SInt32 yDelta); // add client to list and attach event handlers for client - bool addClient(IClient*); + bool addClient(CBaseClientProxy*); // remove client from list and detach event handlers for client - bool removeClient(IClient*); + bool removeClient(CBaseClientProxy*); // close a client - void closeClient(IClient*, const char* msg); + void closeClient(CBaseClientProxy*, const char* msg); // close clients not in \p config void closeClients(const CConfig& config); @@ -238,11 +340,11 @@ private: void closeAllClients(); // remove clients from internal state - void removeActiveClient(IClient*); - void removeOldClient(IClient*); + void removeActiveClient(CBaseClientProxy*); + void removeOldClient(CBaseClientProxy*); // force the cursor off of \p client - void forceLeaveClient(IClient* client); + void forceLeaveClient(CBaseClientProxy* client); private: class CClipboardInfo { @@ -260,17 +362,17 @@ private: CPrimaryClient* m_primaryClient; // all clients (including the primary client) indexed by name - typedef std::map CClientList; - typedef std::set CClientSet; + typedef std::map CClientList; + typedef std::set CClientSet; CClientList m_clients; CClientSet m_clientSet; // all old connections that we're waiting to hangup - typedef std::map COldClients; + typedef std::map COldClients; COldClients m_oldClients; // the client with focus - IClient* m_active; + CBaseClientProxy* m_active; // the sequence number of enter messages UInt32 m_seqNum; @@ -289,17 +391,20 @@ private: // current configuration CConfig m_config; + // input filter (from m_config); + CInputFilter* m_inputFilter; + // clipboard cache CClipboardInfo m_clipboards[kClipboardEnd]; // state saved when screen saver activates - IClient* m_activeSaver; + CBaseClientProxy* m_activeSaver; SInt32 m_xSaver, m_ySaver; // 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; + CBaseClientProxy* m_switchScreen; // state for delayed screen switching double m_switchWaitDelay; @@ -316,8 +421,15 @@ private: // relative mouse move option bool m_relativeMoves; + // screen locking (former scroll lock) + bool m_lockedToScreen; + static CEvent::Type s_errorEvent; + static CEvent::Type s_connectedEvent; static CEvent::Type s_disconnectedEvent; + static CEvent::Type s_switchToScreen; + static CEvent::Type s_switchInDirection; + static CEvent::Type s_lockCursorToScreen; }; #endif diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 90f3d6e1..ec56c582 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -23,22 +23,28 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libserver.a libserver_a_SOURCES = \ + CBaseClientProxy.cpp \ CClientListener.cpp \ CClientProxy.cpp \ CClientProxy1_0.cpp \ CClientProxy1_1.cpp \ CClientProxy1_2.cpp \ + CClientProxy1_3.cpp \ CClientProxyUnknown.cpp \ CConfig.cpp \ + CInputFilter.cpp \ CPrimaryClient.cpp \ CServer.cpp \ + CBaseClientProxy.h \ CClientListener.h \ CClientProxy.h \ CClientProxy1_0.h \ CClientProxy1_1.h \ CClientProxy1_2.h \ + CClientProxy1_3.h \ CClientProxyUnknown.h \ CConfig.h \ + CInputFilter.h \ CPrimaryClient.h \ CServer.h \ $(NULL) diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 992fd7c3..72268967 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\server.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\server.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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 "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\server.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\server.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -87,6 +87,10 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CBaseClientProxy.cpp +# End Source File +# Begin Source File + SOURCE=.\CClientListener.cpp # End Source File # Begin Source File @@ -107,6 +111,10 @@ SOURCE=.\CClientProxy1_2.cpp # End Source File # Begin Source File +SOURCE=.\CClientProxy1_3.cpp +# End Source File +# Begin Source File + SOURCE=.\CClientProxyUnknown.cpp # End Source File # Begin Source File @@ -115,6 +123,10 @@ SOURCE=.\CConfig.cpp # End Source File # Begin Source File +SOURCE=.\CInputFilter.cpp +# End Source File +# Begin Source File + SOURCE=.\CPrimaryClient.cpp # End Source File # Begin Source File @@ -127,6 +139,10 @@ SOURCE=.\CServer.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CBaseClientProxy.h +# End Source File +# Begin Source File + SOURCE=.\CClientListener.h # End Source File # Begin Source File @@ -147,6 +163,10 @@ SOURCE=.\CClientProxy1_2.h # End Source File # Begin Source File +SOURCE=.\CClientProxy1_3.h +# End Source File +# Begin Source File + SOURCE=.\CClientProxyUnknown.h # End Source File # Begin Source File @@ -155,6 +175,10 @@ SOURCE=.\CConfig.h # End Source File # Begin Source File +SOURCE=.\CInputFilter.h +# End Source File +# Begin Source File + SOURCE=.\CPrimaryClient.h # End Source File # Begin Source File diff --git a/lib/synergy/CKeyMap.cpp b/lib/synergy/CKeyMap.cpp new file mode 100644 index 00000000..4141c8b1 --- /dev/null +++ b/lib/synergy/CKeyMap.cpp @@ -0,0 +1,1330 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 "CKeyMap.h" +#include "KeyTypes.h" +#include "CLog.h" +#include +#include +#include + +CKeyMap::CNameToKeyMap* CKeyMap::s_nameToKeyMap = NULL; +CKeyMap::CNameToModifierMap* CKeyMap::s_nameToModifierMap = NULL; +CKeyMap::CKeyToNameMap* CKeyMap::s_keyToNameMap = NULL; +CKeyMap::CModifierToNameMap* CKeyMap::s_modifierToNameMap = NULL; + +CKeyMap::CKeyMap() : + m_numGroups(0), + m_composeAcrossGroups(false) +{ + m_modifierKeyItem.m_id = kKeyNone; + m_modifierKeyItem.m_group = 0; + m_modifierKeyItem.m_button = 0; + m_modifierKeyItem.m_required = 0; + m_modifierKeyItem.m_sensitive = 0; + m_modifierKeyItem.m_generates = 0; + m_modifierKeyItem.m_dead = false; + m_modifierKeyItem.m_lock = false; + m_modifierKeyItem.m_client = 0; +} + +CKeyMap::~CKeyMap() +{ + // do nothing +} + +void +CKeyMap::swap(CKeyMap& x) +{ + m_keyIDMap.swap(x.m_keyIDMap); + m_modifierKeys.swap(x.m_modifierKeys); + m_halfDuplex.swap(x.m_halfDuplex); + m_halfDuplexMods.swap(x.m_halfDuplexMods); + SInt32 tmp1 = m_numGroups; + m_numGroups = x.m_numGroups; + x.m_numGroups = tmp1; + bool tmp2 = m_composeAcrossGroups; + m_composeAcrossGroups = x.m_composeAcrossGroups; + x.m_composeAcrossGroups = tmp2; +} + +void +CKeyMap::addKeyEntry(const KeyItem& item) +{ + // ignore kKeyNone + if (item.m_id == kKeyNone) { + return; + } + + // resize number of groups for key + SInt32 numGroups = item.m_group + 1; + if (getNumGroups() > numGroups) { + numGroups = getNumGroups(); + } + KeyGroupTable& groupTable = m_keyIDMap[item.m_id]; + if (groupTable.size() < static_cast(numGroups)) { + groupTable.resize(numGroups); + } + + // make a list from the item + KeyItemList items; + items.push_back(item); + + // set group and dead key flag on the item + KeyItem& newItem = items.back(); + newItem.m_dead = isDeadKey(item.m_id); + + // mask the required bits with the sensitive bits + newItem.m_required &= newItem.m_sensitive; + + // see if we already have this item; just return if so + KeyEntryList& entries = groupTable[item.m_group]; + for (size_t i = 0, n = entries.size(); i < n; ++i) { + if (entries[i].size() == 1 && newItem == entries[i][0]) { + return; + } + } + + // add item list + entries.push_back(items); + LOG((CLOG_DEBUG1 "add key: %04x %d %03x %04x (%04x %04x %04x)%s", newItem.m_id, newItem.m_group, newItem.m_button, newItem.m_client, newItem.m_required, newItem.m_sensitive, newItem.m_generates, newItem.m_dead ? " dead" : "")); +} + +void +CKeyMap::addKeyAliasEntry(KeyID targetID, SInt32 group, + KeyModifierMask targetRequired, + KeyModifierMask targetSensitive, + KeyID sourceID, + KeyModifierMask sourceRequired, + KeyModifierMask sourceSensitive) +{ + // if we can already generate the target as desired then we're done. + if (findCompatibleKey(targetID, group, targetRequired, + targetSensitive) != NULL) { + return; + } + + // find a compatible source, preferably in the same group + for (SInt32 gd = 0, n = getNumGroups(); gd < n; ++gd) { + SInt32 eg = getEffectiveGroup(group, gd); + const KeyItemList* sourceEntry = + findCompatibleKey(sourceID, eg, + sourceRequired, sourceSensitive); + if (sourceEntry != NULL && sourceEntry->size() == 1) { + CKeyMap::KeyItem targetItem = sourceEntry->back(); + targetItem.m_id = targetID; + targetItem.m_group = eg; + addKeyEntry(targetItem); + break; + } + } +} + +bool +CKeyMap::addKeyCombinationEntry(KeyID id, SInt32 group, + const KeyID* keys, UInt32 numKeys) +{ + // disallow kKeyNone + if (id == kKeyNone) { + return false; + } + + SInt32 numGroups = group + 1; + if (getNumGroups() > numGroups) { + numGroups = getNumGroups(); + } + KeyGroupTable& groupTable = m_keyIDMap[id]; + if (groupTable.size() < static_cast(numGroups)) { + groupTable.resize(numGroups); + } + if (!groupTable[group].empty()) { + // key is already in the table + return false; + } + + // convert to buttons + KeyItemList items; + for (UInt32 i = 0; i < numKeys; ++i) { + KeyIDMap::const_iterator gtIndex = m_keyIDMap.find(keys[i]); + if (gtIndex == m_keyIDMap.end()) { + return false; + } + const KeyGroupTable& groupTable = gtIndex->second; + + // if we allow group switching during composition then search all + // groups for keys, otherwise search just the given group. + SInt32 n = 1; + if (m_composeAcrossGroups) { + n = (SInt32)groupTable.size(); + } + + bool found = false; + for (SInt32 gd = 0; gd < n && !found; ++gd) { + SInt32 eg = (group + gd) % getNumGroups(); + const KeyEntryList& entries = groupTable[eg]; + for (size_t j = 0; j < entries.size(); ++j) { + if (entries[j].size() == 1) { + found = true; + items.push_back(entries[j][0]); + break; + } + } + } + if (!found) { + // required key is not in keyboard group + return false; + } + } + + // add key + groupTable[group].push_back(items); + return true; +} + +void +CKeyMap::allowGroupSwitchDuringCompose() +{ + m_composeAcrossGroups = true; +} + +void +CKeyMap::addHalfDuplexButton(KeyButton button) +{ + m_halfDuplex.insert(button); +} + +void +CKeyMap::clearHalfDuplexModifiers() +{ + m_halfDuplexMods.clear(); +} + +void +CKeyMap::addHalfDuplexModifier(KeyID key) +{ + m_halfDuplexMods.insert(key); +} + +void +CKeyMap::finish() +{ + m_numGroups = findNumGroups(); + + // make sure every key has the same number of groups + for (KeyIDMap::iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + i->second.resize(m_numGroups); + } + + // compute keys that generate each modifier + setModifierKeys(); +} + +void +CKeyMap::foreachKey(ForeachKeyCallback cb, void* userData) +{ + for (KeyIDMap::iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + KeyGroupTable& groupTable = i->second; + for (size_t group = 0; group < groupTable.size(); ++group) { + KeyEntryList& entryList = groupTable[group]; + for (size_t j = 0; j < entryList.size(); ++j) { + KeyItemList& itemList = entryList[j]; + for (size_t k = 0; k < itemList.size(); ++k) { + (*cb)(i->first, static_cast(group), + itemList[k], userData); + } + } + } + } +} + +const CKeyMap::KeyItem* +CKeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + LOG((CLOG_DEBUG1 "mapKey %04x (%d) with mask %04x, start state: %04x", id, id, desiredMask, currentState)); + + // handle group change + if (id == kKeyNextGroup) { + keys.push_back(Keystroke(1, false, false)); + return NULL; + } + else if (id == kKeyPrevGroup) { + keys.push_back(Keystroke(-1, false, false)); + return NULL; + } + + const KeyItem* item; + switch (id) { + case kKeyShift_L: + case kKeyShift_R: + case kKeyControl_L: + case kKeyControl_R: + case kKeyAlt_L: + case kKeyAlt_R: + case kKeyMeta_L: + case kKeyMeta_R: + case kKeySuper_L: + case kKeySuper_R: + case kKeyAltGr: + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: + item = mapModifierKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); + break; + + case kKeySetModifiers: + if (!keysForModifierState(0, group, activeModifiers, currentState, + desiredMask, desiredMask, 0, keys)) { + LOG((CLOG_DEBUG1 "unable to set modifiers %04x", desiredMask)); + return NULL; + } + return &m_modifierKeyItem; + + case kKeyClearModifiers: + if (!keysForModifierState(0, group, activeModifiers, currentState, + currentState & ~desiredMask, + desiredMask, 0, keys)) { + LOG((CLOG_DEBUG1 "unable to clear modifiers %04x", desiredMask)); + return NULL; + } + return &m_modifierKeyItem; + + default: + if (isCommand(desiredMask)) { + item = mapCommandKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); + } + else { + item = mapCharacterKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); + } + break; + } + + if (item != NULL) { + LOG((CLOG_DEBUG1 "mapped to %03x, new state %04x", item->m_button, currentState)); + } + return item; +} + +SInt32 +CKeyMap::getNumGroups() const +{ + return m_numGroups; +} + +SInt32 +CKeyMap::getEffectiveGroup(SInt32 group, SInt32 offset) const +{ + return (group + offset + getNumGroups()) % getNumGroups(); +} + +const CKeyMap::KeyItemList* +CKeyMap::findCompatibleKey(KeyID id, SInt32 group, + KeyModifierMask required, KeyModifierMask sensitive) const +{ + assert(group >= 0 && group < getNumGroups()); + + KeyIDMap::const_iterator i = m_keyIDMap.find(id); + if (i == m_keyIDMap.end()) { + return NULL; + } + + const KeyEntryList& entries = i->second[group]; + for (size_t j = 0; j < entries.size(); ++j) { + if ((entries[j].back().m_sensitive & sensitive) == 0 || + (entries[j].back().m_required & sensitive) == + (required & sensitive)) { + return &entries[j]; + } + } + + return NULL; +} + +bool +CKeyMap::isHalfDuplex(KeyID key, KeyButton button) const +{ + return (m_halfDuplex.count(button) + m_halfDuplexMods.count(key) > 0); +} + +bool +CKeyMap::isCommand(KeyModifierMask mask) const +{ + return ((mask & getCommandModifiers()) != 0); +} + +KeyModifierMask +CKeyMap::getCommandModifiers() const +{ + // we currently treat ctrl, alt, meta and super as command modifiers. + // some platforms may have a more limited set (OS X only needs Alt) + // but this works anyway. + return KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper; +} + +void +CKeyMap::collectButtons(const ModifierToKeys& mods, ButtonToKeyMap& keys) +{ + keys.clear(); + for (ModifierToKeys::const_iterator i = mods.begin(); + i != mods.end(); ++i) { + keys.insert(std::make_pair(i->second.m_button, &i->second)); + } +} + +void +CKeyMap::initModifierKey(KeyItem& item) +{ + item.m_generates = 0; + item.m_lock = false; + switch (item.m_id) { + case kKeyShift_L: + case kKeyShift_R: + item.m_generates = KeyModifierShift; + break; + + case kKeyControl_L: + case kKeyControl_R: + item.m_generates = KeyModifierControl; + break; + + case kKeyAlt_L: + case kKeyAlt_R: + item.m_generates = KeyModifierAlt; + break; + + case kKeyMeta_L: + case kKeyMeta_R: + item.m_generates = KeyModifierMeta; + break; + + case kKeySuper_L: + case kKeySuper_R: + item.m_generates = KeyModifierSuper; + break; + + case kKeyAltGr: + item.m_generates = KeyModifierAltGr; + break; + + case kKeyCapsLock: + item.m_generates = KeyModifierCapsLock; + item.m_lock = true; + break; + + case kKeyNumLock: + item.m_generates = KeyModifierNumLock; + item.m_lock = true; + break; + + case kKeyScrollLock: + item.m_generates = KeyModifierScrollLock; + item.m_lock = true; + break; + + default: + // not a modifier + break; + } +} + +SInt32 +CKeyMap::findNumGroups() const +{ + size_t max = 0; + for (KeyIDMap::const_iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + if (i->second.size() > max) { + max = i->second.size(); + } + } + return static_cast(max); +} + +void +CKeyMap::setModifierKeys() +{ + m_modifierKeys.clear(); + m_modifierKeys.resize(kKeyModifierNumBits * getNumGroups()); + for (KeyIDMap::const_iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + const KeyGroupTable& groupTable = i->second; + for (size_t g = 0; g < groupTable.size(); ++g) { + const KeyEntryList& entries = groupTable[g]; + for (size_t j = 0; j < entries.size(); ++j) { + // skip multi-key sequences + if (entries[j].size() != 1) { + continue; + } + + // skip keys that don't generate a modifier + const KeyItem& item = entries[j].back(); + if (item.m_generates == 0) { + continue; + } + + // add key to each indicated modifier in this group + for (SInt32 b = 0; b < kKeyModifierNumBits; ++b) { + // skip if item doesn't generate bit b + if (((1u << b) & item.m_generates) != 0) { + SInt32 mIndex = g * kKeyModifierNumBits + b; + m_modifierKeys[mIndex].push_back(&item); + } + } + } + } + } +} + +const CKeyMap::KeyItem* +CKeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + static const KeyModifierMask s_overrideModifiers = 0xffffu; + + // find KeySym in table + KeyIDMap::const_iterator i = m_keyIDMap.find(id); + if (i == m_keyIDMap.end()) { + // unknown key + LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id)); + return NULL; + } + const KeyGroupTable& keyGroupTable = i->second; + + // find the first key that generates this KeyID + const KeyItem* keyItem = NULL; + SInt32 numGroups = getNumGroups(); + for (SInt32 groupOffset = 0; groupOffset < numGroups; ++groupOffset) { + SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset); + const KeyEntryList& entryList = keyGroupTable[effectiveGroup]; + for (size_t i = 0; i < entryList.size(); ++i) { + if (entryList[i].size() != 1) { + // ignore multikey entries + continue; + } + + // only match based on shift; we're after the right button + // not the right character. we'll use desiredMask as-is, + // overriding the key's required modifiers, when synthesizing + // this button. + const KeyItem& item = entryList[i].back(); + if ((item.m_required & KeyModifierShift & desiredMask) == + (item.m_sensitive & KeyModifierShift & desiredMask)) { + LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup)); + keyItem = &item; + break; + } + } + if (keyItem != NULL) { + break; + } + } + if (keyItem == NULL) { + // no mapping for this keysym + LOG((CLOG_DEBUG1 "no mapping for key %04x", id)); + return NULL; + } + + // make working copy of modifiers + ModifierToKeys newModifiers = activeModifiers; + KeyModifierMask newState = currentState; + SInt32 newGroup = group; + + // don't try to change CapsLock + desiredMask = (desiredMask & ~KeyModifierCapsLock) | + (currentState & KeyModifierCapsLock); + + // add the key + if (!keysForKeyItem(*keyItem, newGroup, newModifiers, + newState, desiredMask, + s_overrideModifiers, isAutoRepeat, keys)) { + LOG((CLOG_DEBUG1 "can't map key")); + keys.clear(); + return NULL; + } + + // add keystrokes to restore modifier keys + if (!keysToRestoreModifiers(*keyItem, group, newModifiers, newState, + activeModifiers, keys)) { + LOG((CLOG_DEBUG1 "failed to restore modifiers")); + keys.clear(); + return NULL; + } + + // add keystrokes to restore group + if (newGroup != group) { + keys.push_back(Keystroke(group, true, true)); + } + + // save new modifiers + activeModifiers = newModifiers; + currentState = newState; + + return keyItem; +} + +const CKeyMap::KeyItem* +CKeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + // find KeySym in table + KeyIDMap::const_iterator i = m_keyIDMap.find(id); + if (i == m_keyIDMap.end()) { + // unknown key + LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id)); + return NULL; + } + const KeyGroupTable& keyGroupTable = i->second; + + // find best key in any group, starting with the active group + SInt32 keyIndex = -1; + SInt32 numGroups = getNumGroups(); + SInt32 groupOffset; + LOG((CLOG_DEBUG1 "find best: %04x %04x", currentState, desiredMask)); + for (groupOffset = 0; groupOffset < numGroups; ++groupOffset) { + SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset); + keyIndex = findBestKey(keyGroupTable[effectiveGroup], + currentState, desiredMask); + if (keyIndex != -1) { + LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup)); + break; + } + } + if (keyIndex == -1) { + // no mapping for this keysym + LOG((CLOG_DEBUG1 "no mapping for key %04x", id)); + return NULL; + } + + // get keys to press for key + SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset); + const KeyItemList& itemList = keyGroupTable[effectiveGroup][keyIndex]; + if (itemList.empty()) { + return NULL; + } + const KeyItem& keyItem = itemList.back(); + + // make working copy of modifiers + ModifierToKeys newModifiers = activeModifiers; + KeyModifierMask newState = currentState; + SInt32 newGroup = group; + + // add each key + for (size_t j = 0; j < itemList.size(); ++j) { + if (!keysForKeyItem(itemList[j], newGroup, newModifiers, + newState, desiredMask, + 0, isAutoRepeat, keys)) { + LOG((CLOG_DEBUG1 "can't map key")); + keys.clear(); + return NULL; + } + } + + // add keystrokes to restore modifier keys + if (!keysToRestoreModifiers(keyItem, group, newModifiers, newState, + activeModifiers, keys)) { + LOG((CLOG_DEBUG1 "failed to restore modifiers")); + keys.clear(); + return NULL; + } + + // add keystrokes to restore group + if (newGroup != group) { + keys.push_back(Keystroke(group, true, true)); + } + + // save new modifiers + activeModifiers = newModifiers; + currentState = newState; + + return &keyItem; +} + +const CKeyMap::KeyItem* +CKeyMap::mapModifierKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + return mapCharacterKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); +} + +SInt32 +CKeyMap::findBestKey(const KeyEntryList& entryList, + KeyModifierMask /*currentState*/, + KeyModifierMask desiredState) const +{ + // check for an item that can accommodate the desiredState exactly + for (size_t i = 0; i < entryList.size(); ++i) { + const KeyItem& item = entryList[i].back(); + if ((item.m_required & desiredState) == + (item.m_sensitive & desiredState)) { + LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i, entryList.size())); + return i; + } + } + + // choose the item that requires the fewest modifier changes + SInt32 bestCount = 32; + SInt32 bestIndex = -1; + for (size_t i = 0; i < entryList.size(); ++i) { + const KeyItem& item = entryList[i].back(); + KeyModifierMask change = + ((item.m_required ^ desiredState) & item.m_sensitive); + SInt32 n = getNumModifiers(change); + if (n < bestCount) { + bestCount = n; + bestIndex = i; + } + } + if (bestIndex != -1) { + LOG((CLOG_DEBUG1 "best key index %d of %d (%d modifiers)", bestIndex, entryList.size(), bestCount)); + } + + return bestIndex; +} + + +const CKeyMap::KeyItem* +CKeyMap::keyForModifier(KeyButton button, SInt32 group, + SInt32 modifierBit) const +{ + assert(modifierBit >= 0 && modifierBit < kKeyModifierNumBits); + assert(group >= 0 && group < getNumGroups()); + + // find a key that generates the given modifier in the given group + // but doesn't use the given button, presumably because we're trying + // to generate a KeyID that's only bound the the given button. + // this is important when a shift button is modified by shift; we + // must use the other shift button to do the shifting. + const ModifierKeyItemList& items = + m_modifierKeys[group * kKeyModifierNumBits + modifierBit]; + for (ModifierKeyItemList::const_iterator i = items.begin(); + i != items.end(); ++i) { + if ((*i)->m_button != button) { + return (*i); + } + } + return NULL; +} + +bool +CKeyMap::keysForKeyItem(const KeyItem& keyItem, SInt32& group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, KeyModifierMask desiredState, + KeyModifierMask overrideModifiers, + bool isAutoRepeat, + Keystrokes& keystrokes) const +{ + static const KeyModifierMask s_notRequiredMask = + KeyModifierAltGr | KeyModifierNumLock | KeyModifierScrollLock; + + // add keystrokes to adjust the group + if (group != keyItem.m_group) { + group = keyItem.m_group; + keystrokes.push_back(Keystroke(group, true, false)); + } + + EKeystroke type; + if (keyItem.m_dead) { + // adjust modifiers for dead key + if (!keysForModifierState(keyItem.m_button, group, + activeModifiers, currentState, + keyItem.m_required, keyItem.m_sensitive, + 0, keystrokes)) { + LOG((CLOG_DEBUG1 "unable to match modifier state for dead key %d", keyItem.m_button)); + return false; + } + + // press and release the dead key + type = kKeystrokeClick; + } + else { + // if this a command key then we don't have to match some of the + // key's required modifiers. + KeyModifierMask sensitive = keyItem.m_sensitive & ~overrideModifiers; + + // XXX -- must handle pressing a modifier. in particular, if we want + // to synthesize a KeyID on level 1 of a KeyButton that has Shift_L + // mapped to level 0 then we must release that button if it's down + // (in order to satisfy a shift modifier) then press a different + // button (any other button) mapped to the shift modifier and then + // the Shift_L button. + // match key's required state + LOG((CLOG_DEBUG1 "state: %04x,%04x,%04x", currentState, keyItem.m_required, sensitive)); + if (!keysForModifierState(keyItem.m_button, group, + activeModifiers, currentState, + keyItem.m_required, sensitive, + 0, keystrokes)) { + LOG((CLOG_DEBUG1 "unable to match modifier state (%04x,%04x) for key %d", keyItem.m_required, keyItem.m_sensitive, keyItem.m_button)); + return false; + } + + // match desiredState as closely as possible. we must not + // change any modifiers in keyItem.m_sensitive. and if the key + // is a modifier, we don't want to change that modifier. + LOG((CLOG_DEBUG1 "desired state: %04x %04x,%04x,%04x", desiredState, currentState, keyItem.m_required, keyItem.m_sensitive)); + if (!keysForModifierState(keyItem.m_button, group, + activeModifiers, currentState, + desiredState, + ~(sensitive | keyItem.m_generates), + s_notRequiredMask, keystrokes)) { + LOG((CLOG_DEBUG1 "unable to match desired modifier state (%04x,%04x) for key %d", desiredState, ~keyItem.m_sensitive & 0xffffu, keyItem.m_button)); + return false; + } + + // repeat or press of key + type = isAutoRepeat ? kKeystrokeRepeat : kKeystrokePress; + } + addKeystrokes(type, keyItem, activeModifiers, currentState, keystrokes); + + return true; +} + +bool +CKeyMap::keysToRestoreModifiers(const KeyItem& keyItem, SInt32, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + const ModifierToKeys& desiredModifiers, + Keystrokes& keystrokes) const +{ + // XXX -- we're not considering modified modifiers here + + ModifierToKeys oldModifiers = activeModifiers; + + // get the pressed modifier buttons before and after + ButtonToKeyMap oldKeys, newKeys; + collectButtons(oldModifiers, oldKeys); + collectButtons(desiredModifiers, newKeys); + + // release unwanted keys + for (ModifierToKeys::const_iterator i = oldModifiers.begin(); + i != oldModifiers.end(); ++i) { + KeyButton button = i->second.m_button; + if (button != keyItem.m_button && newKeys.count(button) == 0) { + EKeystroke type = kKeystrokeRelease; + if (i->second.m_lock) { + type = kKeystrokeUnmodify; + } + addKeystrokes(type, i->second, + activeModifiers, currentState, keystrokes); + } + } + + // press wanted keys + for (ModifierToKeys::const_iterator i = desiredModifiers.begin(); + i != desiredModifiers.end(); ++i) { + KeyButton button = i->second.m_button; + if (button != keyItem.m_button && oldKeys.count(button) == 0) { + EKeystroke type = kKeystrokePress; + if (i->second.m_lock) { + type = kKeystrokeModify; + } + addKeystrokes(type, i->second, + activeModifiers, currentState, keystrokes); + } + } + + return true; +} + +bool +CKeyMap::keysForModifierState(KeyButton button, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask requiredState, KeyModifierMask sensitiveMask, + KeyModifierMask notRequiredMask, + Keystrokes& keystrokes) const +{ + // compute which modifiers need changing + KeyModifierMask flipMask = ((currentState ^ requiredState) & sensitiveMask); + // if a modifier is not required then don't even try to match it. if + // we don't mask out notRequiredMask then we'll try to match those + // modifiers but succeed if we can't. however, this is known not + // to work if the key itself is a modifier (the numlock toggle can + // interfere) so we don't try to match at all. + flipMask &= ~notRequiredMask; + LOG((CLOG_DEBUG1 "flip: %04x (%04x vs %04x in %04x - %04x)", flipMask, currentState, requiredState, sensitiveMask & 0xffffu, notRequiredMask & 0xffffu)); + if (flipMask == 0) { + return true; + } + + // fix modifiers. this is complicated by the fact that a modifier may + // be sensitive to other modifiers! (who thought that up?) + // + // we'll assume that modifiers with higher bits are affected by modifiers + // with lower bits. there's not much basis for that assumption except + // that we're pretty sure shift isn't changed by other modifiers. + for (SInt32 bit = kKeyModifierNumBits; bit-- > 0; ) { + KeyModifierMask mask = (1u << bit); + if ((flipMask & mask) == 0) { + // modifier is already correct + continue; + } + + // do we want the modifier active or inactive? + bool active = ((requiredState & mask) != 0); + + // get the KeyItem for the modifier in the group + const KeyItem* keyItem = keyForModifier(button, group, bit); + if (keyItem == NULL) { + if ((mask & notRequiredMask) == 0) { + LOG((CLOG_DEBUG1 "no key for modifier %04x", mask)); + return false; + } + else { + continue; + } + } + + // if this modifier is sensitive to modifiers then adjust those + // modifiers. also check if our assumption was correct. note + // that we only need to adjust the modifiers on key down. + KeyModifierMask sensitive = keyItem->m_sensitive; + if ((sensitive & mask) != 0) { + // modifier is sensitive to itself. that makes no sense + // so ignore it. + LOG((CLOG_DEBUG1 "modifier %04x modified by itself", mask)); + sensitive &= ~mask; + } + if (sensitive != 0) { + if (sensitive > mask) { + // our assumption is incorrect + LOG((CLOG_DEBUG1 "modifier %04x modified by %04x", mask, sensitive)); + return false; + } + if (active && !keysForModifierState(button, group, + activeModifiers, currentState, + keyItem->m_required, sensitive, + notRequiredMask, keystrokes)) { + return false; + } + else if (!active) { + // release the modifier + // XXX -- this doesn't work! if Alt and Meta are mapped + // to one key and we want to release Meta we can't do + // that without also releasing Alt. + // need to think about support for modified modifiers. + } + } + + // current state should match required state + if ((currentState & sensitive) != (keyItem->m_required & sensitive)) { + LOG((CLOG_DEBUG1 "unable to match modifier state for modifier %04x (%04x vs %04x in %04x)", mask, currentState, keyItem->m_required, sensitive)); + return false; + } + + // add keystrokes + EKeystroke type = active ? kKeystrokeModify : kKeystrokeUnmodify; + addKeystrokes(type, *keyItem, activeModifiers, currentState, + keystrokes); + } + + return true; +} + +void +CKeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + Keystrokes& keystrokes) const +{ + KeyButton button = keyItem.m_button; + UInt32 data = keyItem.m_client; + switch (type) { + case kKeystrokePress: + keystrokes.push_back(Keystroke(button, true, false, data)); + if (keyItem.m_generates != 0) { + if (!keyItem.m_lock || (currentState & keyItem.m_generates) == 0) { + // add modifier key and activate modifier + activeModifiers.insert(std::make_pair( + keyItem.m_generates, keyItem)); + currentState |= keyItem.m_generates; + } + else { + // deactivate locking modifier + activeModifiers.erase(keyItem.m_generates); + currentState &= ~keyItem.m_generates; + } + } + break; + + case kKeystrokeRelease: + keystrokes.push_back(Keystroke(button, false, false, data)); + if (keyItem.m_generates != 0 && !keyItem.m_lock) { + // remove key from active modifiers + std::pair range = + activeModifiers.equal_range(keyItem.m_generates); + for (ModifierToKeys::iterator i = range.first; + i != range.second; ++i) { + if (i->second.m_button == button) { + activeModifiers.erase(i); + break; + } + } + + // if no more keys for this modifier then deactivate modifier + if (activeModifiers.count(keyItem.m_generates) == 0) { + currentState &= ~keyItem.m_generates; + } + } + break; + + case kKeystrokeRepeat: + keystrokes.push_back(Keystroke(button, false, true, data)); + keystrokes.push_back(Keystroke(button, true, true, data)); + // no modifier changes on key repeat + break; + + case kKeystrokeClick: + keystrokes.push_back(Keystroke(button, true, false, data)); + keystrokes.push_back(Keystroke(button, false, false, data)); + // no modifier changes on key click + break; + + case kKeystrokeModify: + case kKeystrokeUnmodify: + if (keyItem.m_lock) { + // we assume there's just one button for this modifier + if (m_halfDuplex.count(button) > 0) { + if (type == kKeystrokeModify) { + // turn half-duplex toggle on (press) + keystrokes.push_back(Keystroke(button, true, false, data)); + } + else { + // turn half-duplex toggle off (release) + keystrokes.push_back(Keystroke(button, false, false, data)); + } + } + else { + // toggle (click) + keystrokes.push_back(Keystroke(button, true, false, data)); + keystrokes.push_back(Keystroke(button, false, false, data)); + } + } + else if (type == kKeystrokeModify) { + // press modifier + keystrokes.push_back(Keystroke(button, true, false, data)); + } + else { + // release all the keys that generate the modifier that are + // currently down + std::pair range = + activeModifiers.equal_range(keyItem.m_generates); + for (ModifierToKeys::const_iterator i = range.first; + i != range.second; ++i) { + keystrokes.push_back(Keystroke(i->second.m_button, + false, false, i->second.m_client)); + } + } + + if (type == kKeystrokeModify) { + activeModifiers.insert(std::make_pair( + keyItem.m_generates, keyItem)); + currentState |= keyItem.m_generates; + } + else { + activeModifiers.erase(keyItem.m_generates); + currentState &= ~keyItem.m_generates; + } + break; + } +} + +SInt32 +CKeyMap::getNumModifiers(KeyModifierMask state) +{ + SInt32 n = 0; + for (; state != 0; state >>= 1) { + if ((state & 1) != 0) { + ++n; + } + } + return n; +} + +bool +CKeyMap::isDeadKey(KeyID key) +{ + return (key == kKeyCompose || (key >= 0x0300 && key <= 0x036f)); +} + +KeyID +CKeyMap::getDeadKey(KeyID key) +{ + if (isDeadKey(key)) { + // already dead + return key; + } + + switch (key) { + case '`': + return kKeyDeadGrave; + + case 0xb4u: + return kKeyDeadAcute; + + case '^': + case 0x2c6: + return kKeyDeadCircumflex; + + case '~': + case 0x2dcu: + return kKeyDeadTilde; + + case 0xafu: + return kKeyDeadMacron; + + case 0x2d8u: + return kKeyDeadBreve; + + case 0x2d9u: + return kKeyDeadAbovedot; + + case 0xa8u: + return kKeyDeadDiaeresis; + + case 0xb0u: + case 0x2dau: + return kKeyDeadAbovering; + + case '\"': + case 0x2ddu: + return kKeyDeadDoubleacute; + + case 0x2c7u: + return kKeyDeadCaron; + + case 0xb8u: + return kKeyDeadCedilla; + + case 0x2dbu: + return kKeyDeadOgonek; + + default: + // unknown + return kKeyNone; + } +} + +CString +CKeyMap::formatKey(KeyID key, KeyModifierMask mask) +{ + // initialize tables + initKeyNameMaps(); + + CString x; + for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) { + KeyModifierMask mod = (1u << i); + if ((mask & mod) != 0 && s_modifierToNameMap->count(mod) > 0) { + x += s_modifierToNameMap->find(mod)->second; + x += "+"; + } + } + if (key != kKeyNone) { + if (s_keyToNameMap->count(key) > 0) { + x += s_keyToNameMap->find(key)->second; + } + // XXX -- we're assuming ASCII here + else if (key >= 33 && key < 127) { + x += (char)key; + } + else { + x += CStringUtil::print("\\u%04x", key); + } + } + else if (!x.empty()) { + // remove trailing '+' + x.erase(x.size() - 1); + } + return x; +} + +bool +CKeyMap::parseKey(const CString& x, KeyID& key) +{ + // initialize tables + initKeyNameMaps(); + + // parse the key + key = kKeyNone; + if (s_nameToKeyMap->count(x) > 0) { + key = s_nameToKeyMap->find(x)->second; + } + // XXX -- we're assuming ASCII encoding here + else if (x.size() == 1) { + if (!isgraph(x[0])) { + // unknown key + return false; + } + key = (KeyID)x[0]; + } + else if (x.size() == 6 && x[0] == '\\' && x[1] == 'u') { + // escaped unicode (\uXXXX where XXXX is a hex number) + char* end; + key = (KeyID)strtol(x.c_str() + 2, &end, 16); + if (*end != '\0') { + return false; + } + } + else if (!x.empty()) { + // unknown key + return false; + } + + return true; +} + +bool +CKeyMap::parseModifiers(CString& x, KeyModifierMask& mask) +{ + // initialize tables + initKeyNameMaps(); + + mask = 0; + CString::size_type tb = x.find_first_not_of(" \t", 0); + while (tb != CString::npos) { + // get next component + CString::size_type te = x.find_first_of(" \t+)", tb); + if (te == CString::npos) { + te = x.size(); + } + CString c = x.substr(tb, te - tb); + if (c.empty()) { + // missing component + return false; + } + + if (s_nameToModifierMap->count(c) > 0) { + KeyModifierMask mod = s_nameToModifierMap->find(c)->second; + if ((mask & mod) != 0) { + // modifier appears twice + return false; + } + mask |= mod; + } + else { + // unknown string + x.erase(0, tb); + CString::size_type tb = x.find_first_not_of(" \t"); + CString::size_type te = x.find_last_not_of(" \t"); + if (tb == CString::npos) { + x = ""; + } + else { + x = x.substr(tb, te - tb + 1); + } + return true; + } + + // check for '+' or end of string + tb = x.find_first_not_of(" \t", te); + if (tb != CString::npos) { + if (x[tb] != '+') { + // expected '+' + return false; + } + tb = x.find_first_not_of(" \t", tb + 1); + } + } + + // parsed the whole thing + x = ""; + return true; +} + +void +CKeyMap::initKeyNameMaps() +{ + // initialize tables + if (s_nameToKeyMap == NULL) { + s_nameToKeyMap = new CNameToKeyMap; + s_keyToNameMap = new CKeyToNameMap; + for (const KeyNameMapEntry* i = kKeyNameMap; i->m_name != NULL; ++i) { + (*s_nameToKeyMap)[i->m_name] = i->m_id; + (*s_keyToNameMap)[i->m_id] = i->m_name; + } + } + if (s_nameToModifierMap == NULL) { + s_nameToModifierMap = new CNameToModifierMap; + s_modifierToNameMap = new CModifierToNameMap; + for (const KeyModifierNameMapEntry* i = kModifierNameMap; + i->m_name != NULL; ++i) { + (*s_nameToModifierMap)[i->m_name] = i->m_mask; + (*s_modifierToNameMap)[i->m_mask] = i->m_name; + } + } +} + + +// +// CKeyMap::KeyItem +// + +bool +CKeyMap::KeyItem::operator==(const KeyItem& x) const +{ + return (m_id == x.m_id && + m_group == x.m_group && + m_button == x.m_button && + m_required == x.m_required && + m_sensitive == x.m_sensitive && + m_generates == x.m_generates && + m_dead == x.m_dead && + m_lock == x.m_lock && + m_client == x.m_client); +} + + +// +// CKeyMap::Keystroke +// + +CKeyMap::Keystroke::Keystroke(KeyButton button, + bool press, bool repeat, UInt32 data) : + m_type(kButton) +{ + m_data.m_button.m_button = button; + m_data.m_button.m_press = press; + m_data.m_button.m_repeat = repeat; + m_data.m_button.m_client = data; +} + +CKeyMap::Keystroke::Keystroke(SInt32 group, bool absolute, bool restore) : + m_type(kGroup) +{ + m_data.m_group.m_group = group; + m_data.m_group.m_absolute = absolute; + m_data.m_group.m_restore = restore; +} diff --git a/lib/synergy/CKeyMap.h b/lib/synergy/CKeyMap.h new file mode 100644 index 00000000..7f34113b --- /dev/null +++ b/lib/synergy/CKeyMap.h @@ -0,0 +1,491 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT 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 CKEYMAP_H +#define CKEYMAP_H + +#include "KeyTypes.h" +#include "CString.h" +#include "CStringUtil.h" +#include "stdmap.h" +#include "stdset.h" +#include "stdvector.h" + +//! Key map +/*! +This class provides a keyboard mapping. +*/ +class CKeyMap { +public: + CKeyMap(); + ~CKeyMap(); + + //! KeyID synthesis info + /*! + This structure contains the information necessary to synthesize a + keystroke that generates a KeyID (stored elsewhere). \c m_sensitive + lists the modifiers that the key is affected by and must therefore + be in the correct state, which is listed in \c m_required. If the + key is mapped to a modifier, that modifier is in \c m_generates and + is not in \c m_sensitive. + */ + struct KeyItem { + public: + KeyID m_id; //!< KeyID + SInt32 m_group; //!< Group for key + KeyButton m_button; //!< Button to generate KeyID + KeyModifierMask m_required; //!< Modifiers required for KeyID + KeyModifierMask m_sensitive; //!< Modifiers key is sensitive to + KeyModifierMask m_generates; //!< Modifiers key is mapped to + bool m_dead; //!< \c true if this is a dead KeyID + bool m_lock; //!< \c true if this locks a modifier + UInt32 m_client; //!< Client data + + public: + bool operator==(const KeyItem&) const; + }; + + //! The KeyButtons needed to synthesize a KeyID + /*! + An ordered list of \c KeyItems produces a particular KeyID. If + the KeyID can be synthesized directly then there is one entry in + the list. If dead keys are required then they're listed first. + A list is the minimal set of keystrokes necessary to synthesize + the KeyID, so it doesn't include no-ops. A list does not include + any modifier keys unless the KeyID is a modifier, in which case + it has exactly one KeyItem for the modifier itself. + */ + typedef std::vector KeyItemList; + + //! A keystroke + class Keystroke { + public: + enum EType { + kButton, //!< Synthesize button + kGroup //!< Set new group + }; + + Keystroke(KeyButton, bool press, bool repeat, UInt32 clientData); + Keystroke(SInt32 group, bool absolute, bool restore); + + public: + struct CButton { + public: + KeyButton m_button; //!< Button to synthesize + bool m_press; //!< \c true iff press + bool m_repeat; //!< \c true iff for an autorepeat + UInt32 m_client; //!< Client data + }; + struct CGroup { + public: + SInt32 m_group; //!< Group/offset to change to/by + bool m_absolute; //!< \c true iff change to, else by + bool m_restore; //!< \c true iff for restoring state + }; + union CData { + public: + CButton m_button; + CGroup m_group; + }; + + EType m_type; + CData m_data; + }; + + //! A sequence of keystrokes + typedef std::vector Keystrokes; + + //! A mapping of a modifier to keys for that modifier + typedef std::multimap ModifierToKeys; + + //! A set of buttons + typedef std::map ButtonToKeyMap; + + //! Callback type for \c foreachKey + typedef void (*ForeachKeyCallback)(KeyID, SInt32 group, + KeyItem&, void* userData); + + //! @name manipulators + //@{ + + //! Swap with another \c CKeyMap + void swap(CKeyMap&); + + //! Add a key entry + /*! + Adds \p item to the entries for the item's id and group. The + \c m_dead member is set automatically. + */ + void addKeyEntry(const KeyItem& item); + + //! Add an alias key entry + /*! + If \p targetID with the modifiers given by \p targetRequired and + \p targetSensitive is not available in group \p group then find an + entry for \p sourceID with modifiers given by \p sourceRequired and + \p sourceSensitive in any group with exactly one item and, if found, + add a new item just like it except using id \p targetID. This + effectively makes the \p sourceID an alias for \p targetID (i.e. we + can generate \p targetID using \p sourceID). + */ + void addKeyAliasEntry(KeyID targetID, SInt32 group, + KeyModifierMask targetRequired, + KeyModifierMask targetSensitive, + KeyID sourceID, + KeyModifierMask sourceRequired, + KeyModifierMask sourceSensitive); + + //! Add a key sequence entry + /*! + Adds the sequence of keys \p keys (\p numKeys elements long) to + synthesize key \p id in group \p group. This looks up in the + map each key in \p keys. If all are found then each key is + converted to the button for that key and the buttons are added + as the entry for \p id. If \p id is already in the map or at + least one key in \p keys is not in the map then nothing is added + and this returns \c false, otherwise it returns \c true. + */ + bool addKeyCombinationEntry(KeyID id, SInt32 group, + const KeyID* keys, UInt32 numKeys); + + //! Enable composition across groups + /*! + If called then the keyboard map will allow switching between groups + during key composition. Not all systems allow that. + */ + void allowGroupSwitchDuringCompose(); + + //! Add a half-duplex button + /*! + Records that button \p button is a half-duplex key. This is called + when translating the system's keyboard map. It's independent of the + half-duplex modifier calls. + */ + void addHalfDuplexButton(KeyButton button); + + //! Remove all half-duplex modifiers + /*! + Removes all half-duplex modifiers. This is called to set user + configurable half-duplex settings. + */ + void clearHalfDuplexModifiers(); + + //! Add a half-duplex modifier + /*! + Records that modifier key \p key is half-duplex. This is called to + set user configurable half-duplex settings. + */ + void addHalfDuplexModifier(KeyID key); + + //! Finish adding entries + /*! + Called after adding entries, this does some internal housekeeping. + */ + void finish(); + + //! Iterate over all added keys items + /*! + Calls \p cb for every key item. + */ + void foreachKey(ForeachKeyCallback cb, void* userData); + + //@} + //! @name accessors + //@{ + + //! Map key press/repeat to keystrokes. + /*! + Converts press/repeat of key \p id in group \p group with current + modifiers as given in \p currentState and the desired modifiers in + \p desiredMask into the keystrokes necessary to synthesize that key + event in \p keys. It returns the \c KeyItem of the key being + pressed/repeated, or NULL if the key cannot be mapped. + */ + const KeyItem* mapKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + //! Get number of groups + /*! + Returns the number of keyboard groups (independent layouts) in the map. + */ + SInt32 getNumGroups() const; + + //! Compute a group number + /*! + Returns the number of the group \p offset groups after group \p group. + */ + SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const; + + //! Find key entry compatible with modifiers + /*! + Returns the \c KeyItemList for the first entry for \p id in group + \p group that is compatible with the given modifiers, or NULL + if there isn't one. A button list is compatible with a modifiers + if it is either insensitive to all modifiers in \p sensitive or + it requires the modifiers to be in the state indicated by \p required + for every modifier indicated by \p sensitive. + */ + const KeyItemList* findCompatibleKey(KeyID id, SInt32 group, + KeyModifierMask required, + KeyModifierMask sensitive) const; + + //! Test if modifier is half-duplex + /*! + Returns \c true iff modifier key \p key or button \p button is + half-duplex. + */ + bool isHalfDuplex(KeyID key, KeyButton button) const; + + //! Test if modifiers indicate a command + /*! + Returns \c true iff the modifiers in \p mask contain any command + modifiers. A command modifier is used for keyboard shortcuts and + hotkeys, Rather than trying to synthesize a character, a command + is trying to synthesize a particular set of buttons. So it's not + important to match the shift or AltGr state to achieve a character + but it is important to match the modifier state exactly. + */ + bool isCommand(KeyModifierMask mask) const; + + // Get the modifiers that indicate a command + /*! + Returns the modifiers that when combined with other keys indicate + a command (e.g. shortcut or hotkey). + */ + KeyModifierMask getCommandModifiers() const; + + //! Get buttons from modifier map + /*! + Put all the keys in \p modifiers into \p keys. + */ + static void collectButtons(const ModifierToKeys& modifiers, + ButtonToKeyMap& keys); + + //! Set modifier key state + /*! + Sets the modifier key state (\c m_generates and \c m_lock) in \p item + based on the \c m_id in \p item. + */ + static void initModifierKey(KeyItem& item); + + //! Test for a dead key + /*! + Returns \c true if \p key is a dead key. + */ + static bool isDeadKey(KeyID key); + + //! Get corresponding dead key + /*! + Returns the dead key corresponding to \p key if one exists, otherwise + return \c kKeyNone. This returns \p key if it's already a dead key. + */ + static KeyID getDeadKey(KeyID key); + + //! Get string for a key and modifier mask + /*! + Converts a key and modifier mask into a string representing the + combination. + */ + static CString formatKey(KeyID key, KeyModifierMask); + + //! Parse a string into a key + /*! + Converts a string into a key. Returns \c true on success and \c false + if the string cannot be parsed. + */ + static bool parseKey(const CString&, KeyID&); + + //! Parse a string into a modifier mask + /*! + Converts a string into a modifier mask. Returns \c true on success + and \c false if the string cannot be parsed. The modifiers plus any + remaining leading and trailing whitespace is stripped from the input + string. + */ + static bool parseModifiers(CString&, KeyModifierMask&); + + //@} + +private: + //! Ways to synthesize a key + enum EKeystroke { + kKeystrokePress, //!< Synthesize a press + kKeystrokeRelease, //!< Synthesize a release + kKeystrokeRepeat, //!< Synthesize an autorepeat + kKeystrokeClick, //!< Synthesize a press and release + kKeystrokeModify, //!< Synthesize pressing a modifier + kKeystrokeUnmodify //!< Synthesize releasing a modifier + }; + + // A list of ways to synthesize a KeyID + typedef std::vector KeyEntryList; + + // computes the number of groups + SInt32 findNumGroups() const; + + // computes the map of modifiers to the keys that generate the modifiers + void setModifierKeys(); + + // maps a command key. a command key is a keyboard shortcut and we're + // trying to synthesize a button press with an exact sets of modifiers, + // not trying to synthesize a character. so we just need to find the + // right button and synthesize the requested modifiers without regard + // to what character they would synthesize. we disallow multikey + // entries since they don't make sense as hotkeys. + const KeyItem* mapCommandKey(Keystrokes& keys, + KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + // maps a character key. a character key is trying to synthesize a + // particular KeyID and isn't entirely concerned with the modifiers + // used to do it. + const KeyItem* mapCharacterKey(Keystrokes& keys, + KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + // maps a modifier key + const KeyItem* mapModifierKey(Keystrokes& keys, + KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + // returns the index into \p entryList of the KeyItemList requiring + // the fewest modifier changes between \p currentState and + // \p desiredState. + SInt32 findBestKey(const KeyEntryList& entryList, + KeyModifierMask currentState, + KeyModifierMask desiredState) const; + + // gets the \c KeyItem used to synthesize the modifier who's bit is + // given by \p modifierBit in group \p group and does not synthesize + // the key \p button. + const KeyItem* keyForModifier(KeyButton button, SInt32 group, + SInt32 modifierBit) const; + + // fills \p keystrokes with the keys to synthesize the key in + // \p keyItem taking the modifiers into account. returns \c true + // iff successful and sets \p currentState to the + // resulting modifier state. + bool keysForKeyItem(const KeyItem& keyItem, + SInt32& group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredState, + KeyModifierMask overrideModifiers, + bool isAutoRepeat, + Keystrokes& keystrokes) const; + + // fills \p keystrokes with the keys to synthesize the modifiers + // in \p desiredModifiers from the active modifiers listed in + // \p activeModifiers not including the key in \p keyItem. + // returns \c true iff successful. + bool keysToRestoreModifiers(const KeyItem& keyItem, + SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + const ModifierToKeys& desiredModifiers, + Keystrokes& keystrokes) const; + + // fills \p keystrokes and \p undo with the keys to change the + // current modifier state in \p currentState to match the state in + // \p requiredState for each modifier indicated in \p sensitiveMask. + // returns \c true iff successful and sets \p currentState to the + // resulting modifier state. + bool keysForModifierState(KeyButton button, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask requiredState, + KeyModifierMask sensitiveMask, + KeyModifierMask notRequiredMask, + Keystrokes& keystrokes) const; + + // Adds keystrokes to synthesize key \p keyItem in mode \p type to + // \p keystrokes and to undo the synthesis to \p undo. + void addKeystrokes(EKeystroke type, + const KeyItem& keyItem, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + Keystrokes& keystrokes) const; + + // Returns the number of modifiers indicated in \p state. + static SInt32 getNumModifiers(KeyModifierMask state); + + // Initialize key name/id maps + static void initKeyNameMaps(); + + // not implemented + CKeyMap(const CKeyMap&); + CKeyMap& operator=(const CKeyMap&); + +private: + // Ways to synthesize a KeyID over multiple keyboard groups + typedef std::vector KeyGroupTable; + + // Table of KeyID to ways to synthesize that KeyID + typedef std::map KeyIDMap; + + // List of KeyItems that generate a particular modifier + typedef std::vector ModifierKeyItemList; + + // Map a modifier to the KeyItems that synthesize that modifier + typedef std::vector ModifierToKeyTable; + + // A set of keys + typedef std::set KeySet; + + // A set of buttons + typedef std::set KeyButtonSet; + + // Key maps for parsing/formatting + typedef std::map CNameToKeyMap; + typedef std::map CNameToModifierMap; + typedef std::map CKeyToNameMap; + typedef std::map CModifierToNameMap; + + // KeyID info + KeyIDMap m_keyIDMap; + SInt32 m_numGroups; + ModifierToKeyTable m_modifierKeys; + + // composition info + bool m_composeAcrossGroups; + + // half-duplex info + KeyButtonSet m_halfDuplex; // half-duplex set by synergy + KeySet m_halfDuplexMods; // half-duplex set by user + + // dummy KeyItem for changing modifiers + KeyItem m_modifierKeyItem; + + // parsing/formatting tables + static CNameToKeyMap* s_nameToKeyMap; + static CNameToModifierMap* s_nameToModifierMap; + static CKeyToNameMap* s_keyToNameMap; + static CModifierToNameMap* s_modifierToNameMap; +}; + +#endif diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp index 481354fe..ff374828 100644 --- a/lib/synergy/CKeyState.cpp +++ b/lib/synergy/CKeyState.cpp @@ -16,18 +16,374 @@ #include "IEventQueue.h" #include "CLog.h" #include +#include + +static const KeyButton kButtonMask = (KeyButton)(IKeyState::kNumButtons - 1); + +static const KeyID s_decomposeTable[] = { + // spacing version of dead keys + 0x0060, 0x0300, 0x0020, 0, // grave, dead_grave, space + 0x00b4, 0x0301, 0x0020, 0, // acute, dead_acute, space + 0x005e, 0x0302, 0x0020, 0, // asciicircum, dead_circumflex, space + 0x007e, 0x0303, 0x0020, 0, // asciitilde, dead_tilde, space + 0x00a8, 0x0308, 0x0020, 0, // diaeresis, dead_diaeresis, space + 0x00b0, 0x030a, 0x0020, 0, // degree, dead_abovering, space + 0x00b8, 0x0327, 0x0020, 0, // cedilla, dead_cedilla, space + 0x02db, 0x0328, 0x0020, 0, // ogonek, dead_ogonek, space + 0x02c7, 0x030c, 0x0020, 0, // caron, dead_caron, space + 0x02d9, 0x0307, 0x0020, 0, // abovedot, dead_abovedot, space + 0x02dd, 0x030b, 0x0020, 0, // doubleacute, dead_doubleacute, space + 0x02d8, 0x0306, 0x0020, 0, // breve, dead_breve, space + 0x00af, 0x0304, 0x0020, 0, // macron, dead_macron, space + + // Latin-1 (ISO 8859-1) + 0x00c0, 0x0300, 0x0041, 0, // Agrave, dead_grave, A + 0x00c1, 0x0301, 0x0041, 0, // Aacute, dead_acute, A + 0x00c2, 0x0302, 0x0041, 0, // Acircumflex, dead_circumflex, A + 0x00c3, 0x0303, 0x0041, 0, // Atilde, dead_tilde, A + 0x00c4, 0x0308, 0x0041, 0, // Adiaeresis, dead_diaeresis, A + 0x00c5, 0x030a, 0x0041, 0, // Aring, dead_abovering, A + 0x00c7, 0x0327, 0x0043, 0, // Ccedilla, dead_cedilla, C + 0x00c8, 0x0300, 0x0045, 0, // Egrave, dead_grave, E + 0x00c9, 0x0301, 0x0045, 0, // Eacute, dead_acute, E + 0x00ca, 0x0302, 0x0045, 0, // Ecircumflex, dead_circumflex, E + 0x00cb, 0x0308, 0x0045, 0, // Ediaeresis, dead_diaeresis, E + 0x00cc, 0x0300, 0x0049, 0, // Igrave, dead_grave, I + 0x00cd, 0x0301, 0x0049, 0, // Iacute, dead_acute, I + 0x00ce, 0x0302, 0x0049, 0, // Icircumflex, dead_circumflex, I + 0x00cf, 0x0308, 0x0049, 0, // Idiaeresis, dead_diaeresis, I + 0x00d1, 0x0303, 0x004e, 0, // Ntilde, dead_tilde, N + 0x00d2, 0x0300, 0x004f, 0, // Ograve, dead_grave, O + 0x00d3, 0x0301, 0x004f, 0, // Oacute, dead_acute, O + 0x00d4, 0x0302, 0x004f, 0, // Ocircumflex, dead_circumflex, O + 0x00d5, 0x0303, 0x004f, 0, // Otilde, dead_tilde, O + 0x00d6, 0x0308, 0x004f, 0, // Odiaeresis, dead_diaeresis, O + 0x00d9, 0x0300, 0x0055, 0, // Ugrave, dead_grave, U + 0x00da, 0x0301, 0x0055, 0, // Uacute, dead_acute, U + 0x00db, 0x0302, 0x0055, 0, // Ucircumflex, dead_circumflex, U + 0x00dc, 0x0308, 0x0055, 0, // Udiaeresis, dead_diaeresis, U + 0x00dd, 0x0301, 0x0059, 0, // Yacute, dead_acute, Y + 0x00e0, 0x0300, 0x0061, 0, // agrave, dead_grave, a + 0x00e1, 0x0301, 0x0061, 0, // aacute, dead_acute, a + 0x00e2, 0x0302, 0x0061, 0, // acircumflex, dead_circumflex, a + 0x00e3, 0x0303, 0x0061, 0, // atilde, dead_tilde, a + 0x00e4, 0x0308, 0x0061, 0, // adiaeresis, dead_diaeresis, a + 0x00e5, 0x030a, 0x0061, 0, // aring, dead_abovering, a + 0x00e7, 0x0327, 0x0063, 0, // ccedilla, dead_cedilla, c + 0x00e8, 0x0300, 0x0065, 0, // egrave, dead_grave, e + 0x00e9, 0x0301, 0x0065, 0, // eacute, dead_acute, e + 0x00ea, 0x0302, 0x0065, 0, // ecircumflex, dead_circumflex, e + 0x00eb, 0x0308, 0x0065, 0, // ediaeresis, dead_diaeresis, e + 0x00ec, 0x0300, 0x0069, 0, // igrave, dead_grave, i + 0x00ed, 0x0301, 0x0069, 0, // iacute, dead_acute, i + 0x00ee, 0x0302, 0x0069, 0, // icircumflex, dead_circumflex, i + 0x00ef, 0x0308, 0x0069, 0, // idiaeresis, dead_diaeresis, i + 0x00f1, 0x0303, 0x006e, 0, // ntilde, dead_tilde, n + 0x00f2, 0x0300, 0x006f, 0, // ograve, dead_grave, o + 0x00f3, 0x0301, 0x006f, 0, // oacute, dead_acute, o + 0x00f4, 0x0302, 0x006f, 0, // ocircumflex, dead_circumflex, o + 0x00f5, 0x0303, 0x006f, 0, // otilde, dead_tilde, o + 0x00f6, 0x0308, 0x006f, 0, // odiaeresis, dead_diaeresis, o + 0x00f9, 0x0300, 0x0075, 0, // ugrave, dead_grave, u + 0x00fa, 0x0301, 0x0075, 0, // uacute, dead_acute, u + 0x00fb, 0x0302, 0x0075, 0, // ucircumflex, dead_circumflex, u + 0x00fc, 0x0308, 0x0075, 0, // udiaeresis, dead_diaeresis, u + 0x00fd, 0x0301, 0x0079, 0, // yacute, dead_acute, y + 0x00ff, 0x0308, 0x0079, 0, // ydiaeresis, dead_diaeresis, y + + // Latin-2 (ISO 8859-2) + 0x0104, 0x0328, 0x0041, 0, // Aogonek, dead_ogonek, A + 0x013d, 0x030c, 0x004c, 0, // Lcaron, dead_caron, L + 0x015a, 0x0301, 0x0053, 0, // Sacute, dead_acute, S + 0x0160, 0x030c, 0x0053, 0, // Scaron, dead_caron, S + 0x015e, 0x0327, 0x0053, 0, // Scedilla, dead_cedilla, S + 0x0164, 0x030c, 0x0054, 0, // Tcaron, dead_caron, T + 0x0179, 0x0301, 0x005a, 0, // Zacute, dead_acute, Z + 0x017d, 0x030c, 0x005a, 0, // Zcaron, dead_caron, Z + 0x017b, 0x0307, 0x005a, 0, // Zabovedot, dead_abovedot, Z + 0x0105, 0x0328, 0x0061, 0, // aogonek, dead_ogonek, a + 0x013e, 0x030c, 0x006c, 0, // lcaron, dead_caron, l + 0x015b, 0x0301, 0x0073, 0, // sacute, dead_acute, s + 0x0161, 0x030c, 0x0073, 0, // scaron, dead_caron, s + 0x015f, 0x0327, 0x0073, 0, // scedilla, dead_cedilla, s + 0x0165, 0x030c, 0x0074, 0, // tcaron, dead_caron, t + 0x017a, 0x0301, 0x007a, 0, // zacute, dead_acute, z + 0x017e, 0x030c, 0x007a, 0, // zcaron, dead_caron, z + 0x017c, 0x0307, 0x007a, 0, // zabovedot, dead_abovedot, z + 0x0154, 0x0301, 0x0052, 0, // Racute, dead_acute, R + 0x0102, 0x0306, 0x0041, 0, // Abreve, dead_breve, A + 0x0139, 0x0301, 0x004c, 0, // Lacute, dead_acute, L + 0x0106, 0x0301, 0x0043, 0, // Cacute, dead_acute, C + 0x010c, 0x030c, 0x0043, 0, // Ccaron, dead_caron, C + 0x0118, 0x0328, 0x0045, 0, // Eogonek, dead_ogonek, E + 0x011a, 0x030c, 0x0045, 0, // Ecaron, dead_caron, E + 0x010e, 0x030c, 0x0044, 0, // Dcaron, dead_caron, D + 0x0143, 0x0301, 0x004e, 0, // Nacute, dead_acute, N + 0x0147, 0x030c, 0x004e, 0, // Ncaron, dead_caron, N + 0x0150, 0x030b, 0x004f, 0, // Odoubleacute, dead_doubleacute, O + 0x0158, 0x030c, 0x0052, 0, // Rcaron, dead_caron, R + 0x016e, 0x030a, 0x0055, 0, // Uring, dead_abovering, U + 0x0170, 0x030b, 0x0055, 0, // Udoubleacute, dead_doubleacute, U + 0x0162, 0x0327, 0x0054, 0, // Tcedilla, dead_cedilla, T + 0x0155, 0x0301, 0x0072, 0, // racute, dead_acute, r + 0x0103, 0x0306, 0x0061, 0, // abreve, dead_breve, a + 0x013a, 0x0301, 0x006c, 0, // lacute, dead_acute, l + 0x0107, 0x0301, 0x0063, 0, // cacute, dead_acute, c + 0x010d, 0x030c, 0x0063, 0, // ccaron, dead_caron, c + 0x0119, 0x0328, 0x0065, 0, // eogonek, dead_ogonek, e + 0x011b, 0x030c, 0x0065, 0, // ecaron, dead_caron, e + 0x010f, 0x030c, 0x0064, 0, // dcaron, dead_caron, d + 0x0144, 0x0301, 0x006e, 0, // nacute, dead_acute, n + 0x0148, 0x030c, 0x006e, 0, // ncaron, dead_caron, n + 0x0151, 0x030b, 0x006f, 0, // odoubleacute, dead_doubleacute, o + 0x0159, 0x030c, 0x0072, 0, // rcaron, dead_caron, r + 0x016f, 0x030a, 0x0075, 0, // uring, dead_abovering, u + 0x0171, 0x030b, 0x0075, 0, // udoubleacute, dead_doubleacute, u + 0x0163, 0x0327, 0x0074, 0, // tcedilla, dead_cedilla, t + + // Latin-3 (ISO 8859-3) + 0x0124, 0x0302, 0x0048, 0, // Hcircumflex, dead_circumflex, H + 0x0130, 0x0307, 0x0049, 0, // Iabovedot, dead_abovedot, I + 0x011e, 0x0306, 0x0047, 0, // Gbreve, dead_breve, G + 0x0134, 0x0302, 0x004a, 0, // Jcircumflex, dead_circumflex, J + 0x0125, 0x0302, 0x0068, 0, // hcircumflex, dead_circumflex, h + 0x011f, 0x0306, 0x0067, 0, // gbreve, dead_breve, g + 0x0135, 0x0302, 0x006a, 0, // jcircumflex, dead_circumflex, j + 0x010a, 0x0307, 0x0043, 0, // Cabovedot, dead_abovedot, C + 0x0108, 0x0302, 0x0043, 0, // Ccircumflex, dead_circumflex, C + 0x0120, 0x0307, 0x0047, 0, // Gabovedot, dead_abovedot, G + 0x011c, 0x0302, 0x0047, 0, // Gcircumflex, dead_circumflex, G + 0x016c, 0x0306, 0x0055, 0, // Ubreve, dead_breve, U + 0x015c, 0x0302, 0x0053, 0, // Scircumflex, dead_circumflex, S + 0x010b, 0x0307, 0x0063, 0, // cabovedot, dead_abovedot, c + 0x0109, 0x0302, 0x0063, 0, // ccircumflex, dead_circumflex, c + 0x0121, 0x0307, 0x0067, 0, // gabovedot, dead_abovedot, g + 0x011d, 0x0302, 0x0067, 0, // gcircumflex, dead_circumflex, g + 0x016d, 0x0306, 0x0075, 0, // ubreve, dead_breve, u + 0x015d, 0x0302, 0x0073, 0, // scircumflex, dead_circumflex, s + + // Latin-4 (ISO 8859-4) + 0x0156, 0x0327, 0x0052, 0, // Rcedilla, dead_cedilla, R + 0x0128, 0x0303, 0x0049, 0, // Itilde, dead_tilde, I + 0x013b, 0x0327, 0x004c, 0, // Lcedilla, dead_cedilla, L + 0x0112, 0x0304, 0x0045, 0, // Emacron, dead_macron, E + 0x0122, 0x0327, 0x0047, 0, // Gcedilla, dead_cedilla, G + 0x0157, 0x0327, 0x0072, 0, // rcedilla, dead_cedilla, r + 0x0129, 0x0303, 0x0069, 0, // itilde, dead_tilde, i + 0x013c, 0x0327, 0x006c, 0, // lcedilla, dead_cedilla, l + 0x0113, 0x0304, 0x0065, 0, // emacron, dead_macron, e + 0x0123, 0x0327, 0x0067, 0, // gcedilla, dead_cedilla, g + 0x0100, 0x0304, 0x0041, 0, // Amacron, dead_macron, A + 0x012e, 0x0328, 0x0049, 0, // Iogonek, dead_ogonek, I + 0x0116, 0x0307, 0x0045, 0, // Eabovedot, dead_abovedot, E + 0x012a, 0x0304, 0x0049, 0, // Imacron, dead_macron, I + 0x0145, 0x0327, 0x004e, 0, // Ncedilla, dead_cedilla, N + 0x014c, 0x0304, 0x004f, 0, // Omacron, dead_macron, O + 0x0136, 0x0327, 0x004b, 0, // Kcedilla, dead_cedilla, K + 0x0172, 0x0328, 0x0055, 0, // Uogonek, dead_ogonek, U + 0x0168, 0x0303, 0x0055, 0, // Utilde, dead_tilde, U + 0x016a, 0x0304, 0x0055, 0, // Umacron, dead_macron, U + 0x0101, 0x0304, 0x0061, 0, // amacron, dead_macron, a + 0x012f, 0x0328, 0x0069, 0, // iogonek, dead_ogonek, i + 0x0117, 0x0307, 0x0065, 0, // eabovedot, dead_abovedot, e + 0x012b, 0x0304, 0x0069, 0, // imacron, dead_macron, i + 0x0146, 0x0327, 0x006e, 0, // ncedilla, dead_cedilla, n + 0x014d, 0x0304, 0x006f, 0, // omacron, dead_macron, o + 0x0137, 0x0327, 0x006b, 0, // kcedilla, dead_cedilla, k + 0x0173, 0x0328, 0x0075, 0, // uogonek, dead_ogonek, u + 0x0169, 0x0303, 0x0075, 0, // utilde, dead_tilde, u + 0x016b, 0x0304, 0x0075, 0, // umacron, dead_macron, u + + // Latin-8 (ISO 8859-14) + 0x1e02, 0x0307, 0x0042, 0, // Babovedot, dead_abovedot, B + 0x1e03, 0x0307, 0x0062, 0, // babovedot, dead_abovedot, b + 0x1e0a, 0x0307, 0x0044, 0, // Dabovedot, dead_abovedot, D + 0x1e80, 0x0300, 0x0057, 0, // Wgrave, dead_grave, W + 0x1e82, 0x0301, 0x0057, 0, // Wacute, dead_acute, W + 0x1e0b, 0x0307, 0x0064, 0, // dabovedot, dead_abovedot, d + 0x1ef2, 0x0300, 0x0059, 0, // Ygrave, dead_grave, Y + 0x1e1e, 0x0307, 0x0046, 0, // Fabovedot, dead_abovedot, F + 0x1e1f, 0x0307, 0x0066, 0, // fabovedot, dead_abovedot, f + 0x1e40, 0x0307, 0x004d, 0, // Mabovedot, dead_abovedot, M + 0x1e41, 0x0307, 0x006d, 0, // mabovedot, dead_abovedot, m + 0x1e56, 0x0307, 0x0050, 0, // Pabovedot, dead_abovedot, P + 0x1e81, 0x0300, 0x0077, 0, // wgrave, dead_grave, w + 0x1e57, 0x0307, 0x0070, 0, // pabovedot, dead_abovedot, p + 0x1e83, 0x0301, 0x0077, 0, // wacute, dead_acute, w + 0x1e60, 0x0307, 0x0053, 0, // Sabovedot, dead_abovedot, S + 0x1ef3, 0x0300, 0x0079, 0, // ygrave, dead_grave, y + 0x1e84, 0x0308, 0x0057, 0, // Wdiaeresis, dead_diaeresis, W + 0x1e85, 0x0308, 0x0077, 0, // wdiaeresis, dead_diaeresis, w + 0x1e61, 0x0307, 0x0073, 0, // sabovedot, dead_abovedot, s + 0x0174, 0x0302, 0x0057, 0, // Wcircumflex, dead_circumflex, W + 0x1e6a, 0x0307, 0x0054, 0, // Tabovedot, dead_abovedot, T + 0x0176, 0x0302, 0x0059, 0, // Ycircumflex, dead_circumflex, Y + 0x0175, 0x0302, 0x0077, 0, // wcircumflex, dead_circumflex, w + 0x1e6b, 0x0307, 0x0074, 0, // tabovedot, dead_abovedot, t + 0x0177, 0x0302, 0x0079, 0, // ycircumflex, dead_circumflex, y + + // Latin-9 (ISO 8859-15) + 0x0178, 0x0308, 0x0059, 0, // Ydiaeresis, dead_diaeresis, Y + + // Compose key sequences + 0x00c6, kKeyCompose, 0x0041, 0x0045, 0, // AE, A, E + 0x00c1, kKeyCompose, 0x0041, 0x0027, 0, // Aacute, A, apostrophe + 0x00c2, kKeyCompose, 0x0041, 0x0053, 0, // Acircumflex, A, asciicircum + 0x00c3, kKeyCompose, 0x0041, 0x0022, 0, // Adiaeresis, A, quotedbl + 0x00c0, kKeyCompose, 0x0041, 0x0060, 0, // Agrave, A, grave + 0x00c5, kKeyCompose, 0x0041, 0x002a, 0, // Aring, A, asterisk + 0x00c3, kKeyCompose, 0x0041, 0x007e, 0, // Atilde, A, asciitilde + 0x00c7, kKeyCompose, 0x0043, 0x002c, 0, // Ccedilla, C, comma + 0x00d0, kKeyCompose, 0x0044, 0x002d, 0, // ETH, D, minus + 0x00c9, kKeyCompose, 0x0045, 0x0027, 0, // Eacute, E, apostrophe + 0x00ca, kKeyCompose, 0x0045, 0x0053, 0, // Ecircumflex, E, asciicircum + 0x00cb, kKeyCompose, 0x0045, 0x0022, 0, // Ediaeresis, E, quotedbl + 0x00c8, kKeyCompose, 0x0045, 0x0060, 0, // Egrave, E, grave + 0x00cd, kKeyCompose, 0x0049, 0x0027, 0, // Iacute, I, apostrophe + 0x00ce, kKeyCompose, 0x0049, 0x0053, 0, // Icircumflex, I, asciicircum + 0x00cf, kKeyCompose, 0x0049, 0x0022, 0, // Idiaeresis, I, quotedbl + 0x00cc, kKeyCompose, 0x0049, 0x0060, 0, // Igrave, I, grave + 0x00d1, kKeyCompose, 0x004e, 0x007e, 0, // Ntilde, N, asciitilde + 0x00d3, kKeyCompose, 0x004f, 0x0027, 0, // Oacute, O, apostrophe + 0x00d4, kKeyCompose, 0x004f, 0x0053, 0, // Ocircumflex, O, asciicircum + 0x00d6, kKeyCompose, 0x004f, 0x0022, 0, // Odiaeresis, O, quotedbl + 0x00d2, kKeyCompose, 0x004f, 0x0060, 0, // Ograve, O, grave + 0x00d8, kKeyCompose, 0x004f, 0x002f, 0, // Ooblique, O, slash + 0x00d5, kKeyCompose, 0x004f, 0x007e, 0, // Otilde, O, asciitilde + 0x00de, kKeyCompose, 0x0054, 0x0048, 0, // THORN, T, H + 0x00da, kKeyCompose, 0x0055, 0x0027, 0, // Uacute, U, apostrophe + 0x00db, kKeyCompose, 0x0055, 0x0053, 0, // Ucircumflex, U, asciicircum + 0x00dc, kKeyCompose, 0x0055, 0x0022, 0, // Udiaeresis, U, quotedbl + 0x00d9, kKeyCompose, 0x0055, 0x0060, 0, // Ugrave, U, grave + 0x00dd, kKeyCompose, 0x0059, 0x0027, 0, // Yacute, Y, apostrophe + 0x00e1, kKeyCompose, 0x0061, 0x0027, 0, // aacute, a, apostrophe + 0x00e2, kKeyCompose, 0x0061, 0x0053, 0, // acircumflex, a, asciicircum + 0x00b4, kKeyCompose, 0x0027, 0x0027, 0, // acute, apostrophe, apostrophe + 0x00e4, kKeyCompose, 0x0061, 0x0022, 0, // adiaeresis, a, quotedbl + 0x00e6, kKeyCompose, 0x0061, 0x0065, 0, // ae, a, e + 0x00e0, kKeyCompose, 0x0061, 0x0060, 0, // agrave, a, grave + 0x00e5, kKeyCompose, 0x0061, 0x002a, 0, // aring, a, asterisk + 0x0040, kKeyCompose, 0x0041, 0x0054, 0, // at, A, T + 0x00e3, kKeyCompose, 0x0061, 0x007e, 0, // atilde, a, asciitilde + 0x005c, kKeyCompose, 0x002f, 0x002f, 0, // backslash, slash, slash + 0x007c, kKeyCompose, 0x004c, 0x0056, 0, // bar, L, V + 0x007b, kKeyCompose, 0x0028, 0x002d, 0, // braceleft, parenleft, minus + 0x007d, kKeyCompose, 0x0029, 0x002d, 0, // braceright, parenright, minus + 0x005b, kKeyCompose, 0x0028, 0x0028, 0, // bracketleft, parenleft, parenleft + 0x005d, kKeyCompose, 0x0029, 0x0029, 0, // bracketright, parenright, parenright + 0x00a6, kKeyCompose, 0x0042, 0x0056, 0, // brokenbar, B, V + 0x00e7, kKeyCompose, 0x0063, 0x002c, 0, // ccedilla, c, comma + 0x00b8, kKeyCompose, 0x002c, 0x002c, 0, // cedilla, comma, comma + 0x00a2, kKeyCompose, 0x0063, 0x002f, 0, // cent, c, slash + 0x00a9, kKeyCompose, 0x0028, 0x0063, 0, // copyright, parenleft, c + 0x00a4, kKeyCompose, 0x006f, 0x0078, 0, // currency, o, x + 0x00b0, kKeyCompose, 0x0030, 0x0053, 0, // degree, 0, asciicircum + 0x00a8, kKeyCompose, 0x0022, 0x0022, 0, // diaeresis, quotedbl, quotedbl + 0x00f7, kKeyCompose, 0x003a, 0x002d, 0, // division, colon, minus + 0x00e9, kKeyCompose, 0x0065, 0x0027, 0, // eacute, e, apostrophe + 0x00ea, kKeyCompose, 0x0065, 0x0053, 0, // ecircumflex, e, asciicircum + 0x00eb, kKeyCompose, 0x0065, 0x0022, 0, // ediaeresis, e, quotedbl + 0x00e8, kKeyCompose, 0x0065, 0x0060, 0, // egrave, e, grave + 0x00f0, kKeyCompose, 0x0064, 0x002d, 0, // eth, d, minus + 0x00a1, kKeyCompose, 0x0021, 0x0021, 0, // exclamdown, exclam, exclam + 0x00ab, kKeyCompose, 0x003c, 0x003c, 0, // guillemotleft, less, less + 0x00bb, kKeyCompose, 0x003e, 0x003e, 0, // guillemotright, greater, greater + 0x0023, kKeyCompose, 0x002b, 0x002b, 0, // numbersign, plus, plus + 0x00ad, kKeyCompose, 0x002d, 0x002d, 0, // hyphen, minus, minus + 0x00ed, kKeyCompose, 0x0069, 0x0027, 0, // iacute, i, apostrophe + 0x00ee, kKeyCompose, 0x0069, 0x0053, 0, // icircumflex, i, asciicircum + 0x00ef, kKeyCompose, 0x0069, 0x0022, 0, // idiaeresis, i, quotedbl + 0x00ec, kKeyCompose, 0x0069, 0x0060, 0, // igrave, i, grave + 0x00af, kKeyCompose, 0x002d, 0x0053, 0, // macron, minus, asciicircum + 0x00ba, kKeyCompose, 0x006f, 0x005f, 0, // masculine, o, underscore + 0x00b5, kKeyCompose, 0x0075, 0x002f, 0, // mu, u, slash + 0x00d7, kKeyCompose, 0x0078, 0x0078, 0, // multiply, x, x + 0x00a0, kKeyCompose, 0x0020, 0x0020, 0, // nobreakspace, space, space + 0x00ac, kKeyCompose, 0x002c, 0x002d, 0, // notsign, comma, minus + 0x00f1, kKeyCompose, 0x006e, 0x007e, 0, // ntilde, n, asciitilde + 0x00f3, kKeyCompose, 0x006f, 0x0027, 0, // oacute, o, apostrophe + 0x00f4, kKeyCompose, 0x006f, 0x0053, 0, // ocircumflex, o, asciicircum + 0x00f6, kKeyCompose, 0x006f, 0x0022, 0, // odiaeresis, o, quotedbl + 0x00f2, kKeyCompose, 0x006f, 0x0060, 0, // ograve, o, grave + 0x00bd, kKeyCompose, 0x0031, 0x0032, 0, // onehalf, 1, 2 + 0x00bc, kKeyCompose, 0x0031, 0x0034, 0, // onequarter, 1, 4 + 0x00b9, kKeyCompose, 0x0031, 0x0053, 0, // onesuperior, 1, asciicircum + 0x00aa, kKeyCompose, 0x0061, 0x005f, 0, // ordfeminine, a, underscore + 0x00f8, kKeyCompose, 0x006f, 0x002f, 0, // oslash, o, slash + 0x00f5, kKeyCompose, 0x006f, 0x007e, 0, // otilde, o, asciitilde + 0x00b6, kKeyCompose, 0x0070, 0x0021, 0, // paragraph, p, exclam + 0x00b7, kKeyCompose, 0x002e, 0x002e, 0, // periodcentered, period, period + 0x00b1, kKeyCompose, 0x002b, 0x002d, 0, // plusminus, plus, minus + 0x00bf, kKeyCompose, 0x003f, 0x003f, 0, // questiondown, question, question + 0x00ae, kKeyCompose, 0x0028, 0x0072, 0, // registered, parenleft, r + 0x00a7, kKeyCompose, 0x0073, 0x006f, 0, // section, s, o + 0x00df, kKeyCompose, 0x0073, 0x0073, 0, // ssharp, s, s + 0x00a3, kKeyCompose, 0x004c, 0x002d, 0, // sterling, L, minus + 0x00fe, kKeyCompose, 0x0074, 0x0068, 0, // thorn, t, h + 0x00be, kKeyCompose, 0x0033, 0x0034, 0, // threequarters, 3, 4 + 0x00b3, kKeyCompose, 0x0033, 0x0053, 0, // threesuperior, 3, asciicircum + 0x00b2, kKeyCompose, 0x0032, 0x0053, 0, // twosuperior, 2, asciicircum + 0x00fa, kKeyCompose, 0x0075, 0x0027, 0, // uacute, u, apostrophe + 0x00fb, kKeyCompose, 0x0075, 0x0053, 0, // ucircumflex, u, asciicircum + 0x00fc, kKeyCompose, 0x0075, 0x0022, 0, // udiaeresis, u, quotedbl + 0x00f9, kKeyCompose, 0x0075, 0x0060, 0, // ugrave, u, grave + 0x00fd, kKeyCompose, 0x0079, 0x0027, 0, // yacute, y, apostrophe + 0x00ff, kKeyCompose, 0x0079, 0x0022, 0, // ydiaeresis, y, quotedbl + 0x00a5, kKeyCompose, 0x0079, 0x003d, 0, // yen, y, equal + + // end of table + 0 +}; + +static const KeyID s_numpadTable[] = { + kKeyKP_Space, 0x0020, + kKeyKP_Tab, kKeyTab, + kKeyKP_Enter, kKeyReturn, + kKeyKP_F1, kKeyF1, + kKeyKP_F2, kKeyF2, + kKeyKP_F3, kKeyF3, + kKeyKP_F4, kKeyF4, + kKeyKP_Home, kKeyHome, + kKeyKP_Left, kKeyLeft, + kKeyKP_Up, kKeyUp, + kKeyKP_Right, kKeyRight, + kKeyKP_Down, kKeyDown, + kKeyKP_PageUp, kKeyPageUp, + kKeyKP_PageDown, kKeyPageDown, + kKeyKP_End, kKeyEnd, + kKeyKP_Begin, kKeyBegin, + kKeyKP_Insert, kKeyInsert, + kKeyKP_Delete, kKeyDelete, + kKeyKP_Equal, 0x003d, + kKeyKP_Multiply, 0x002a, + kKeyKP_Add, 0x002b, + kKeyKP_Separator, 0x002c, + kKeyKP_Subtract, 0x002d, + kKeyKP_Decimal, 0x002e, + kKeyKP_Divide, 0x002f, + kKeyKP_0, 0x0030, + kKeyKP_1, 0x0031, + kKeyKP_2, 0x0032, + kKeyKP_3, 0x0033, + kKeyKP_4, 0x0034, + kKeyKP_5, 0x0035, + kKeyKP_6, 0x0036, + kKeyKP_7, 0x0037, + kKeyKP_8, 0x0038, + kKeyKP_9, 0x0039 +}; // // CKeyState // CKeyState::CKeyState() : - m_halfDuplex(0), m_mask(0) { memset(&m_keys, 0, sizeof(m_keys)); - memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap)); - memset(&m_keyToMask, 0, sizeof(m_keyToMask)); + memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys)); + memset(&m_keyClientData, 0, sizeof(m_keyClientData)); + memset(&m_serverKeys, 0, sizeof(m_serverKeys)); } CKeyState::~CKeyState() @@ -36,21 +392,26 @@ CKeyState::~CKeyState() } void -CKeyState::setKeyDown(KeyButton button, bool down) +CKeyState::onKey(KeyButton button, bool down, KeyModifierMask newState) { - button &= kButtonMask; - updateKeyState(button, button, down, false); -} + // update modifier state + m_mask = newState; + LOG((CLOG_DEBUG1 "new mask: 0x%04x", m_mask)); -void -CKeyState::setToggled(KeyModifierMask modifier) -{ - if (isToggle(modifier)) { - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)]; - for (KeyButtons::const_iterator j = buttons.begin(); - j != buttons.end(); ++j) { - m_keys[(*j) & kButtonMask] |= kToggled; - } + // ignore bogus buttons + button &= kButtonMask; + if (button == 0) { + return; + } + + // update key state + if (down) { + m_keys[button] = 1; + m_syntheticKeys[button] = 1; + } + else { + m_keys[button] = 0; + m_syntheticKeys[button] = 0; } } @@ -60,7 +421,7 @@ CKeyState::sendKeyEvent( KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button) { - if (isHalfDuplex(m_keyToMask[button])) { + if (m_keyMap.isHalfDuplex(key, button)) { if (isAutoRepeat) { // ignore auto-repeat on half-duplex keys } @@ -88,182 +449,239 @@ CKeyState::sendKeyEvent( } void -CKeyState::updateKeys() +CKeyState::updateKeyMap() { - static const KeyModifierMask s_masks[] = { - KeyModifierShift, - KeyModifierControl, - KeyModifierAlt, - KeyModifierMeta, - KeyModifierSuper, - KeyModifierModeSwitch, - KeyModifierCapsLock, - KeyModifierNumLock, - KeyModifierScrollLock - }; + // get the current keyboard map + CKeyMap keyMap; + getKeyMap(keyMap); + m_keyMap.swap(keyMap); + m_keyMap.finish(); + // add special keys + addCombinationEntries(); + addKeypadEntries(); + addAliasEntries(); +} + +void +CKeyState::updateKeyState() +{ // reset our state memset(&m_keys, 0, sizeof(m_keys)); - memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap)); - memset(&m_keyToMask, 0, sizeof(m_keyToMask)); - for (UInt32 i = 0; i < sizeof(m_maskToKeys)/sizeof(m_maskToKeys[0]); ++i) { - m_maskToKeys[i].clear(); + memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys)); + memset(&m_keyClientData, 0, sizeof(m_keyClientData)); + memset(&m_serverKeys, 0, sizeof(m_serverKeys)); + m_activeModifiers.clear(); + + // get the current keyboard state + KeyButtonSet keysDown; + pollPressedKeys(keysDown); + for (KeyButtonSet::const_iterator i = keysDown.begin(); + i != keysDown.end(); ++i) { + m_keys[*i] = 1; } - // let subclass set the state - doUpdateKeys(); + // get the current modifier state + m_mask = pollActiveModifiers(); - // figure out the active modifiers - m_mask = 0; - for (UInt32 i = 0; i < sizeof(s_masks) / sizeof(s_masks[0]); ++i) { - if (isModifierActive(s_masks[i])) { - m_mask |= s_masks[i]; - } - } - LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); + // set active modifiers + CAddActiveModifierContext addModifierContext(pollActiveGroup(), m_mask, + m_activeModifiers); + m_keyMap.foreachKey(&CKeyState::addActiveModifierCB, &addModifierContext); + + LOG((CLOG_DEBUG1 "modifiers on update: 0x%04x", m_mask)); +} + +void +CKeyState::addActiveModifierCB(KeyID, SInt32 group, + CKeyMap::KeyItem& keyItem, void* vcontext) +{ + CAddActiveModifierContext* context = + reinterpret_cast(vcontext); + if (group == context->m_activeGroup && + (keyItem.m_generates & context->m_mask) != 0) { + context->m_activeModifiers.insert(std::make_pair( + keyItem.m_generates, keyItem)); + } } void CKeyState::setHalfDuplexMask(KeyModifierMask mask) { - m_halfDuplex = mask & (KeyModifierCapsLock | - KeyModifierNumLock | - KeyModifierScrollLock); + m_keyMap.clearHalfDuplexModifiers(); + if ((mask & KeyModifierCapsLock) != 0) { + m_keyMap.addHalfDuplexModifier(kKeyCapsLock); + } + if ((mask & KeyModifierNumLock) != 0) { + m_keyMap.addHalfDuplexModifier(kKeyNumLock); + } + if ((mask & KeyModifierScrollLock) != 0) { + m_keyMap.addHalfDuplexModifier(kKeyScrollLock); + } } void -CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID) { - // get the sequence of keys to simulate key press and the final - // modifier state. - Keystrokes keys; - KeyButton localID = - (KeyButton)(mapKey(keys, id, mask, false) & kButtonMask); - if (keys.empty()) { - // do nothing if there are no associated keys - LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + // if this server key is already down then this is probably a + // mis-reported autorepeat. + serverID &= kButtonMask; + if (m_serverKeys[serverID] != 0) { + fakeKeyRepeat(id, mask, 1, serverID); return; } - // generate key events - fakeKeyEvents(keys, 1); + // ignore certain keys + if (isIgnoredKey(id, mask)) { + LOG((CLOG_DEBUG1 "ignored key %04x %04x", id, mask)); + return; + } - // note that key is down - updateKeyState((KeyButton)(button & kButtonMask), localID, true, true); + // get keys for key press + Keystrokes keys; + ModifierToKeys oldActiveModifiers = m_activeModifiers; + const CKeyMap::KeyItem* keyItem = + m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers, + getActiveModifiersRValue(), mask, false); + if (keyItem == NULL) { + return; + } + KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask); + updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers); + if (localID != 0) { + // note keys down + ++m_keys[localID]; + ++m_syntheticKeys[localID]; + m_keyClientData[localID] = keyItem->m_client; + m_serverKeys[serverID] = localID; + } + + // generate key events + fakeKeys(keys, 1); } void CKeyState::fakeKeyRepeat( KeyID id, KeyModifierMask mask, - SInt32 count, KeyButton button) + SInt32 count, KeyButton serverID) { - button &= kButtonMask; + serverID &= kButtonMask; // if we haven't seen this button go down then ignore it - KeyButton oldLocalID = m_serverKeyMap[button]; + KeyButton oldLocalID = m_serverKeys[serverID]; if (oldLocalID == 0) { return; } - // get the sequence of keys to simulate key repeat and the final - // modifier state. + // get keys for key repeat Keystrokes keys; - KeyButton localID = (KeyButton)(mapKey(keys, id, mask, true) & kButtonMask); - if (localID == 0) { - LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + ModifierToKeys oldActiveModifiers = m_activeModifiers; + const CKeyMap::KeyItem* keyItem = + m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers, + getActiveModifiersRValue(), mask, true); + if (keyItem == NULL) { return; } - if (keys.empty()) { - // do nothing if there are no associated keys + KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask); + if (localID == 0) { return; } - // if the keycode for the auto-repeat is not the same as for the + // if the KeyButton 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. + // KeyButtons for the two KeyIDs might be different. if (localID != oldLocalID) { - // replace key up with previous key id but leave key down - // alone so it uses the new keycode. + // replace key up with previous KeyButton but leave key down + // alone so it uses the new KeyButton. for (Keystrokes::iterator index = keys.begin(); index != keys.end(); ++index) { - if (index->m_key == localID) { - index->m_key = oldLocalID; + if (index->m_type == Keystroke::kButton && + index->m_data.m_button.m_button == localID) { + index->m_data.m_button.m_button = oldLocalID; break; } } // note that old key is now up - m_keys[oldLocalID] &= ~kDown; + --m_keys[oldLocalID]; + --m_syntheticKeys[oldLocalID]; - // map server key to new key - m_serverKeyMap[button] = localID; - - // note that new key is now down - m_keys[localID] |= kDown; + // note keys down + updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers); + ++m_keys[localID]; + ++m_syntheticKeys[localID]; + m_keyClientData[localID] = keyItem->m_client; + m_serverKeys[serverID] = localID; } // generate key events - fakeKeyEvents(keys, count); + fakeKeys(keys, count); } void -CKeyState::fakeKeyUp(KeyButton button) +CKeyState::fakeKeyUp(KeyButton serverID) { // if we haven't seen this button go down then ignore it - KeyButton localID = m_serverKeyMap[button & kButtonMask]; + KeyButton localID = m_serverKeys[serverID & kButtonMask]; if (localID == 0) { return; } // get the sequence of keys to simulate key release Keystrokes keys; - Keystroke keystroke; - keystroke.m_key = localID; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); + keys.push_back(Keystroke(localID, false, false, m_keyClientData[localID])); + + // note keys down + --m_keys[localID]; + --m_syntheticKeys[localID]; + m_serverKeys[serverID] = 0; + + // check if this is a modifier + for (ModifierToKeys::iterator i = m_activeModifiers.begin(); + i != m_activeModifiers.end(); ++i) { + if (i->second.m_button == localID && !i->second.m_lock) { + // modifier is no longer down + KeyModifierMask mask = i->first; + m_activeModifiers.erase(i); + + if (m_activeModifiers.count(mask) == 0) { + // no key for modifier is down so deactivate modifier + m_mask &= ~mask; + LOG((CLOG_DEBUG1 "new state %04x", m_mask)); + } + + break; + } + } // generate key events - fakeKeyEvents(keys, 1); - - // note that key is now up - updateKeyState(button, localID, false, true); + fakeKeys(keys, 1); } void -CKeyState::fakeToggle(KeyModifierMask modifier) +CKeyState::fakeAllKeysUp() { - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)]; - if (buttons.empty() || !isToggle(modifier)) { - return; - } - KeyButton button = buttons[0]; - - // get the sequence of keys to simulate key toggle Keystrokes keys; - Keystroke keystroke; - keystroke.m_key = button; - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - - // generate key events - fakeKeyEvents(keys, 1); - - // note the toggle - m_keys[button] ^= kToggled; - m_mask ^= modifier; + for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { + if (m_syntheticKeys[i] > 0) { + keys.push_back(Keystroke(i, false, false, m_keyClientData[i])); + m_keys[i] = 0; + m_syntheticKeys[i] = 0; + } + } + fakeKeys(keys, 1); + memset(&m_serverKeys, 0, sizeof(m_serverKeys)); + m_activeModifiers.clear(); + m_mask = pollActiveModifiers(); } bool CKeyState::isKeyDown(KeyButton button) const { - return ((m_keys[button & kButtonMask] & kDown) != 0); + return (m_keys[button & kButtonMask] > 0); } KeyModifierMask @@ -272,166 +690,106 @@ CKeyState::getActiveModifiers() const return m_mask; } -void -CKeyState::addModifier(KeyModifierMask modifier, const KeyButtons& buttons) +KeyModifierMask& +CKeyState::getActiveModifiersRValue() { - // the mask must not be zero - assert(modifier != 0); + return m_mask; +} - // the mask must have exactly one high bit - assert((modifier & (modifier - 1)) == 0); - - for (KeyButtons::const_iterator j = buttons.begin(); - j != buttons.end(); ++j) { - KeyButton button = static_cast(((*j) & kButtonMask)); - if (button != 0) { - m_keyToMask[button] = modifier; - } - } - - // index keys by mask - m_maskToKeys[getIndexForModifier(modifier)] = buttons; +SInt32 +CKeyState::getEffectiveGroup(SInt32 group, SInt32 offset) const +{ + return m_keyMap.getEffectiveGroup(group, offset); } bool -CKeyState::mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive, bool force) const +CKeyState::isIgnoredKey(KeyID key, KeyModifierMask) const { - // look up modifier - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; - if (buttons.empty()) { - return false; - } - - // ignore if already in desired state - if (!force && isModifierActive(mask) == desireActive) { + switch (key) { + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: return true; - } - - // initialize keystroke - Keystroke keystroke; - keystroke.m_repeat = false; - - // handle toggles - if (isToggle(mask)) { - keystroke.m_key = buttons[0]; - keystroke.m_press = true; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - keystroke.m_press = false; - undo.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - - else if (desireActive) { - // press - keystroke.m_key = buttons[0]; - keystroke.m_press = true; - keys.push_back(keystroke); - keystroke.m_press = false; - undo.push_back(keystroke); - } - - else { - // 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. - for (KeyButtons::const_iterator j = buttons.begin(); - j != buttons.end(); ++j) { - if (isKeyDown(*j)) { - keystroke.m_key = *j; - keystroke.m_press = false; - keys.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - } - } - - return true; -} - -bool -CKeyState::isToggle(KeyModifierMask mask) const -{ - return (mask == KeyModifierCapsLock || - mask == KeyModifierNumLock || - mask == KeyModifierScrollLock); -} - -bool -CKeyState::isHalfDuplex(KeyModifierMask mask) const -{ - return ((mask & m_halfDuplex) != 0); -} - -bool -CKeyState::isModifierActive(KeyModifierMask mask) const -{ - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; - KeyButtons::const_iterator j = buttons.begin(); - if (j != buttons.end()) { - if (isToggle(mask)) { - // modifier is a toggle - if ((m_keys[*j] & kToggled) != 0) { - return true; - } - } - else { - // modifier is not a toggle - for (; j != buttons.end(); ++j) { - if ((m_keys[*j] & kDown) != 0) { - return true; - } - } - } - } - return false; -} - -UInt32 -CKeyState::getIndexForModifier(KeyModifierMask mask) const -{ - switch (mask) { - case KeyModifierShift: - return 0; - - case KeyModifierControl: - return 1; - - case KeyModifierAlt: - return 2; - - case KeyModifierMeta: - return 3; - - case KeyModifierSuper: - return 4; - - case KeyModifierModeSwitch: - return 5; - - case KeyModifierCapsLock: - return 6; - - case KeyModifierNumLock: - return 7; - - case KeyModifierScrollLock: - return 8; default: - assert(0 && "invalid modifier mask"); + return false; + } +} + +KeyButton +CKeyState::getButton(KeyID id, SInt32 group) const +{ + const CKeyMap::KeyItemList* items = + m_keyMap.findCompatibleKey(id, group, 0, 0); + if (items == NULL) { return 0; } + else { + return items->back().m_button; + } } void -CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) +CKeyState::addAliasEntries() +{ + for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) { + // if we can't shift any kKeyTab key in a particular group but we can + // shift kKeyLeftTab then add a shifted kKeyTab entry that matches a + // shifted kKeyLeftTab entry. + m_keyMap.addKeyAliasEntry(kKeyTab, g, + KeyModifierShift, KeyModifierShift, + kKeyLeftTab, + KeyModifierShift, KeyModifierShift); + + // if we have no kKeyLeftTab but we do have a kKeyTab that can be + // shifted then add kKeyLeftTab that matches a kKeyTab. + m_keyMap.addKeyAliasEntry(kKeyLeftTab, g, + KeyModifierShift, KeyModifierShift, + kKeyTab, + 0, KeyModifierShift); + + // map non-breaking space to space + m_keyMap.addKeyAliasEntry(0x20, g, 0, 0, 0xa0, 0, 0); + } +} + +void +CKeyState::addKeypadEntries() +{ + // map every numpad key to its equivalent non-numpad key if it's not + // on the keyboard. + for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) { + for (size_t i = 0; i < sizeof(s_numpadTable) / + sizeof(s_numpadTable[0]); i += 2) { + m_keyMap.addKeyCombinationEntry(s_numpadTable[i], g, + s_numpadTable + i + 1, 1); + } + } +} + +void +CKeyState::addCombinationEntries() +{ + for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) { + // add dead and compose key composition sequences + for (const KeyID* i = s_decomposeTable; *i != 0; ++i) { + // count the decomposed keys for this key + UInt32 numKeys = 0; + for (const KeyID* j = i; *++j != 0; ) { + ++numKeys; + } + + // add an entry for this key + m_keyMap.addKeyCombinationEntry(*i, g, i + 1, numKeys); + + // next key + i += numKeys + 1; + } + } +} + +void +CKeyState::fakeKeys(const Keystrokes& keys, UInt32 count) { // do nothing if no keys or no repeats if (count == 0 || keys.empty()) { @@ -439,16 +797,18 @@ CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) } // generate key events - LOG((CLOG_DEBUG2 "keystrokes:")); + LOG((CLOG_DEBUG1 "keystrokes:")); for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { - if (k->m_repeat) { + if (k->m_type == Keystroke::kButton && k->m_data.m_button.m_repeat) { // repeat from here up to but not including the next key // with m_repeat == false count times. Keystrokes::const_iterator start = k; while (count-- > 0) { // send repeating events - for (k = start; k != keys.end() && k->m_repeat; ++k) { - fakeKeyEvent(k->m_key, k->m_press, true); + for (k = start; k != keys.end() && + k->m_type == Keystroke::kButton && + k->m_data.m_button.m_repeat; ++k) { + fakeKey(*k); } } @@ -457,7 +817,7 @@ CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) } else { // send event - fakeKeyEvent(k->m_key, k->m_press, false); + fakeKey(*k); // next key ++k; @@ -466,77 +826,61 @@ CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) } void -CKeyState::fakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) +CKeyState::updateModifierKeyState(KeyButton button, + const ModifierToKeys& oldModifiers, + const ModifierToKeys& newModifiers) { - // half-duplex keys are special. we ignore releases and convert - // a press when the toggle is active to a release. - KeyModifierMask mask = m_keyToMask[button]; - if (isHalfDuplex(mask)) { - if (isAutoRepeat || !press) { - return; - } - if (isModifierActive(mask)) { - press = false; - } + // get the pressed modifier buttons before and after + CKeyMap::ButtonToKeyMap oldKeys, newKeys; + for (ModifierToKeys::const_iterator i = oldModifiers.begin(); + i != oldModifiers.end(); ++i) { + oldKeys.insert(std::make_pair(i->second.m_button, &i->second)); + } + for (ModifierToKeys::const_iterator i = newModifiers.begin(); + i != newModifiers.end(); ++i) { + newKeys.insert(std::make_pair(i->second.m_button, &i->second)); } - // send key event - LOG((CLOG_DEBUG2 " %d %s%s", button, press ? "down" : "up", isAutoRepeat ? " repeat" : "")); - doFakeKeyEvent(button, press, isAutoRepeat); + // get the modifier buttons that were pressed or released + CKeyMap::ButtonToKeyMap pressed, released; + std::set_difference(oldKeys.begin(), oldKeys.end(), + newKeys.begin(), newKeys.end(), + std::inserter(released, released.end()), + ButtonToKeyLess()); + std::set_difference(newKeys.begin(), newKeys.end(), + oldKeys.begin(), oldKeys.end(), + std::inserter(pressed, pressed.end()), + ButtonToKeyLess()); + + // update state + for (CKeyMap::ButtonToKeyMap::const_iterator i = released.begin(); + i != released.end(); ++i) { + if (i->first != button) { + m_keys[i->first] = 0; + m_syntheticKeys[i->first] = 0; + } + } + for (CKeyMap::ButtonToKeyMap::const_iterator i = pressed.begin(); + i != pressed.end(); ++i) { + if (i->first != button) { + m_keys[i->first] = 1; + m_syntheticKeys[i->first] = 1; + m_keyClientData[i->first] = i->second->m_client; + } + } } -void -CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, - bool press, bool fake) + +// +// CKeyState::CAddActiveModifierContext +// + +CKeyState::CAddActiveModifierContext::CAddActiveModifierContext( + SInt32 group, KeyModifierMask mask, + ModifierToKeys& activeModifiers) : + m_activeGroup(group), + m_mask(mask), + m_activeModifiers(activeModifiers) { - // ignore bogus keys - if (serverID == 0 || localID == 0) { - return; - } - - // update key state. state doesn't change when auto-repeating. - if (press) { - m_serverKeyMap[serverID] = localID; - m_keys[localID] |= kDown; - } - else { - m_serverKeyMap[serverID] = 0; - m_keys[localID] &= ~kDown; - } - - // update modifier state - KeyModifierMask mask = m_keyToMask[localID]; - if (mask != 0) { - if (isToggle(mask)) { - // never report half-duplex keys as down - if (isHalfDuplex(mask)) { - m_keys[localID] &= ~kDown; - // half-duplex keys on the primary screen don't send the - // usual press/release pairs but instead send the press - // when toggling on and the release when toggleing off. - // since we normally toggle our shadow state on the press - // we need to treat the release as a press on the primary - // screen. we know we're on the primary screen if fake is - // false. secondary screens always get press/release pairs. - if (!fake) { - press = true; - } - } - - // toggle on the press - if (press) { - m_keys[localID] ^= kToggled; - m_mask ^= mask; - } - } - else { - if (press) { - m_mask |= mask; - } - else if (!isModifierActive(mask)) { - m_mask &= ~mask; - } - } - LOG((CLOG_DEBUG2 "new mask: 0x%04x", m_mask)); - } + // do nothing } diff --git a/lib/synergy/CKeyState.h b/lib/synergy/CKeyState.h index 2e8c44b4..6bfd9d8b 100644 --- a/lib/synergy/CKeyState.h +++ b/lib/synergy/CKeyState.h @@ -16,7 +16,7 @@ #define CKEYSTATE_H #include "IKeyState.h" -#include "stdvector.h" +#include "CKeyMap.h" //! Core key state /*! @@ -31,18 +31,15 @@ public: //! @name manipulators //@{ - //! Mark key as being down + //! Handle key event /*! - Sets the state of \p button to down or up. + Sets the state of \p button to down or up and updates the current + modifier state to \p newState. This method should be called by + primary screens only in response to local events. For auto-repeat + set \p down to \c true. Overrides must forward to the superclass. */ - void setKeyDown(KeyButton button, bool down); - - //! Mark modifier as being toggled on - /*! - Sets the state of the keys for the given (single) \p modifier to be - toggled on. - */ - void setToggled(KeyModifierMask modifier); + virtual void onKey(KeyButton button, bool down, + KeyModifierMask newState); //! Post a key event /*! @@ -62,129 +59,158 @@ public: //@} // IKeyState overrides - virtual void updateKeys(); + virtual void updateKeyMap(); + virtual void updateKeyState(); virtual void setHalfDuplexMask(KeyModifierMask); virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button); virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); - virtual void fakeToggle(KeyModifierMask modifier); + virtual void fakeAllKeysUp(); virtual bool fakeCtrlAltDel() = 0; virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; - virtual const char* getKeyName(KeyButton) const = 0; + virtual KeyModifierMask + pollActiveModifiers() const = 0; + virtual SInt32 pollActiveGroup() const = 0; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0; protected: - class Keystroke { - public: - KeyButton m_key; - bool m_press; - bool m_repeat; - }; - typedef std::vector Keystrokes; - typedef std::vector KeyButtons; + typedef CKeyMap::Keystroke Keystroke; - //! @name protocted manipulators + //! @name protected manipulators //@{ - //! Add keys for modifier + //! Get the keyboard map /*! - Sets the buttons that are mapped to the given (single) \p modifier. For - example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps - as left and right shift keys) then the mask would be KeyModifierShift - and \c buttons would contain 5 and 23. A modifier with no keys is - ignored. Buttons that are zero are ignored. + Fills \p keyMap with the current keyboard map. */ - void addModifier(KeyModifierMask modifier, - const KeyButtons& buttons); - - //! Get key events to change modifier state - /*! - Retrieves the key events necessary to activate (\p desireActive is true) - or deactivate (\p desireActive is false) the modifier given by \p mask - by pushing them onto the back of \p keys. \p mask must specify exactly - one modifier. \p undo receives the key events necessary to restore the - modifier's previous state. They're pushed onto \p undo in the reverse - order they should be executed. If \p force is false then \p keys and - \p undo are only changed if the modifier is not currently in the - desired state. If \p force is true then \p keys and \p undo are always - changed. Returns true if the modifier can be adjusted, false otherwise. - */ - bool mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive, - bool force = false) const; - - //! Update the key state - /*! - Update the key state to reflect the physical keyboard state and - current keyboard mapping. This must call \c setKeyDown, \c setToggled, - and \c addModifier to set the current state. - */ - virtual void doUpdateKeys() = 0; + virtual void getKeyMap(CKeyMap& keyMap) = 0; //! Fake a key event /*! - Synthesize a key event for \p button. If \p press is true then - synthesize a key press and, if false, a key release. If - \p isAutoRepeat is true then the event is an auto-repeat. + Synthesize an event for \p keystroke. */ - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat) = 0; + virtual void fakeKey(const Keystroke& keystroke) = 0; - //! Map key press/repeat to keystrokes + //! Get the active modifiers /*! - Converts a press/repeat of key \p id with the modifiers as given - in \p desiredMask into the keystrokes necessary to synthesize - that key event. Returns the platform specific code of the key - being pressed, or 0 if the key cannot be mapped or \p isAutoRepeat - is true and the key does not auto-repeat. + Returns the modifiers that are currently active according to our + shadowed state. The state may be modified. */ - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const = 0; + virtual KeyModifierMask& + getActiveModifiersRValue(); + + //@} + //! @name protected accessors + //@{ + + //! Compute a group number + /*! + Returns the number of the group \p offset groups after group \p group. + */ + SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const; + + //! Check if key is ignored + /*! + Returns \c true if and only if the key should always be ignored. + The default returns \c true only for the toggle keys. + */ + virtual bool isIgnoredKey(KeyID key, KeyModifierMask mask) const; + + //! Get button for a KeyID + /*! + Return the button mapped to key \p id in group \p group if any, + otherwise returns 0. + */ + KeyButton getButton(KeyID id, SInt32 group) const; //@} private: - bool isHalfDuplex(KeyModifierMask) const; - bool isToggle(KeyModifierMask) const; - bool isModifierActive(KeyModifierMask) const; - UInt32 getIndexForModifier(KeyModifierMask) const; - void fakeKeyEvents(const Keystrokes&, UInt32 count); - void fakeKeyEvent(KeyButton, bool press, bool isAutoRepeat); - void updateKeyState(KeyButton serverID, - KeyButton localID, bool press, bool fake); + typedef CKeyMap::Keystrokes Keystrokes; + typedef CKeyMap::ModifierToKeys ModifierToKeys; + struct CAddActiveModifierContext { + public: + CAddActiveModifierContext(SInt32 group, KeyModifierMask mask, + ModifierToKeys& activeModifiers); + + public: + SInt32 m_activeGroup; + KeyModifierMask m_mask; + ModifierToKeys& m_activeModifiers; + + private: + // not implemented + CAddActiveModifierContext(const CAddActiveModifierContext&); + CAddActiveModifierContext& operator=(const CAddActiveModifierContext&); + }; + + class ButtonToKeyLess { + public: + bool operator()(const CKeyMap::ButtonToKeyMap::value_type& a, + const CKeyMap::ButtonToKeyMap::value_type b) const + { + return (a.first < b.first); + } + }; + + // not implemented + CKeyState(const CKeyState&); + CKeyState& operator=(const CKeyState&); + + // adds alias key sequences. these are sequences that are equivalent + // to other sequences. + void addAliasEntries(); + + // adds non-keypad key sequences for keypad KeyIDs + void addKeypadEntries(); + + // adds key sequences for combination KeyIDs (those built using + // dead keys) + void addCombinationEntries(); + + // synthesize key events. synthesize auto-repeat events count times. + void fakeKeys(const Keystrokes&, UInt32 count); + + // update key state to match changes to modifiers + void updateModifierKeyState(KeyButton button, + const ModifierToKeys& oldModifiers, + const ModifierToKeys& newModifiers); + + // active modifiers collection callback + static void addActiveModifierCB(KeyID id, SInt32 group, + CKeyMap::KeyItem& keyItem, void* vcontext); private: - enum { - kNumModifiers = 9, - kButtonMask = kNumButtons - 1 - }; - typedef UInt8 KeyState; - enum EKeyState { - kDown = 0x01, //!< Key is down - kToggled = 0x02 //!< Key is toggled on - }; - - // modifiers that are half-duplex - KeyModifierMask m_halfDuplex; + // the keyboard map + CKeyMap m_keyMap; // current modifier state KeyModifierMask m_mask; - // current keyboard state - KeyState m_keys[kNumButtons]; + // the active modifiers and the buttons activating them + ModifierToKeys m_activeModifiers; - // map from server button ID to local button ID for pressed keys - KeyButton m_serverKeyMap[kNumButtons]; + // current keyboard state (> 0 if pressed, 0 otherwise). this is + // initialized to the keyboard state according to the system then + // it tracks synthesized events. + SInt32 m_keys[kNumButtons]; - // map button to the modifier mask it represents - KeyModifierMask m_keyToMask[kNumButtons]; + // synthetic keyboard state (> 0 if pressed, 0 otherwise). this + // tracks the synthesized keyboard state. if m_keys[n] > 0 but + // m_syntheticKeys[n] == 0 then the key was pressed locally and + // not synthesized yet. + SInt32 m_syntheticKeys[kNumButtons]; - // map modifier to buttons with that modifier - KeyButtons m_maskToKeys[kNumModifiers]; + // client data for each pressed key + UInt32 m_keyClientData[kNumButtons]; + + // server keyboard state. an entry is 0 if not the key isn't pressed + // otherwise it's the local KeyButton synthesized for the server key. + KeyButton m_serverKeys[kNumButtons]; }; #endif diff --git a/lib/synergy/CPlatformScreen.cpp b/lib/synergy/CPlatformScreen.cpp index ea0760a5..ca33d5a9 100644 --- a/lib/synergy/CPlatformScreen.cpp +++ b/lib/synergy/CPlatformScreen.cpp @@ -25,9 +25,15 @@ CPlatformScreen::~CPlatformScreen() } void -CPlatformScreen::updateKeys() +CPlatformScreen::updateKeyMap() { - getKeyState()->updateKeys(); + getKeyState()->updateKeyMap(); +} + +void +CPlatformScreen::updateKeyState() +{ + getKeyState()->updateKeyState(); updateButtons(); } @@ -58,9 +64,9 @@ CPlatformScreen::fakeKeyUp(KeyButton button) } void -CPlatformScreen::fakeToggle(KeyModifierMask modifier) +CPlatformScreen::fakeAllKeysUp() { - getKeyState()->fakeToggle(modifier); + getKeyState()->fakeAllKeysUp(); } bool @@ -81,8 +87,20 @@ CPlatformScreen::getActiveModifiers() const return getKeyState()->getActiveModifiers(); } -const char* -CPlatformScreen::getKeyName(KeyButton button) const +KeyModifierMask +CPlatformScreen::pollActiveModifiers() const { - return getKeyState()->getKeyName(button); + return getKeyState()->pollActiveModifiers(); +} + +SInt32 +CPlatformScreen::pollActiveGroup() const +{ + return getKeyState()->pollActiveGroup(); +} + +void +CPlatformScreen::pollPressedKeys(KeyButtonSet& pressedKeys) const +{ + getKeyState()->pollPressedKeys(pressedKeys); } diff --git a/lib/synergy/CPlatformScreen.h b/lib/synergy/CPlatformScreen.h index d5bb7fbb..2e5c87f1 100644 --- a/lib/synergy/CPlatformScreen.h +++ b/lib/synergy/CPlatformScreen.h @@ -38,6 +38,11 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides) = 0; virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual UInt32 registerHotKey(KeyID key, + KeyModifierMask mask) = 0; + virtual void unregisterHotKey(UInt32 id) = 0; + virtual void fakeInputBegin() = 0; + virtual void fakeInputEnd() = 0; virtual SInt32 getJumpZoneSize() const = 0; virtual bool isAnyMouseButtonDown() const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; @@ -46,22 +51,26 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; - virtual void fakeMouseWheel(SInt32 delta) const = 0; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; // IKeyState overrides - virtual void updateKeys(); + virtual void updateKeyMap(); + virtual void updateKeyState(); virtual void setHalfDuplexMask(KeyModifierMask); virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button); virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); - virtual void fakeToggle(KeyModifierMask modifier); + virtual void fakeAllKeysUp(); virtual bool fakeCtrlAltDel(); virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; - virtual const char* getKeyName(KeyButton) const; + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; // IPlatformScreen overrides virtual void enable() = 0; diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index c7543645..bff8daca 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -28,7 +28,7 @@ CScreen::CScreen(IPlatformScreen* platformScreen) : m_enabled(false), m_entered(m_isPrimary), m_screenSaverSync(true), - m_toggleKeys(0) + m_fakeInput(false) { assert(m_screen != NULL); @@ -54,6 +54,8 @@ CScreen::enable() { assert(!m_enabled); + m_screen->updateKeyMap(); + m_screen->updateKeyState(); m_screen->enable(); if (m_isPrimary) { enablePrimary(); @@ -172,7 +174,7 @@ CScreen::screensaver(bool activate) void CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) { - assert(!m_isPrimary); + assert(!m_isPrimary || m_fakeInput); // check for ctrl+alt+del emulation if (id == kKeyDelete && @@ -197,7 +199,7 @@ CScreen::keyRepeat(KeyID id, void CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) { - assert(!m_isPrimary); + assert(!m_isPrimary || m_fakeInput); m_screen->fakeKeyUp(button); } @@ -230,10 +232,10 @@ CScreen::mouseRelativeMove(SInt32 dx, SInt32 dy) } void -CScreen::mouseWheel(SInt32 delta) +CScreen::mouseWheel(SInt32 xDelta, SInt32 yDelta) { assert(!m_isPrimary); - m_screen->fakeMouseWheel(delta); + m_screen->fakeMouseWheel(xDelta, yDelta); } void @@ -272,7 +274,6 @@ CScreen::setOptions(const COptionsList& options) else { m_halfDuplex &= ~KeyModifierCapsLock; } - m_screen->setHalfDuplexMask(m_halfDuplex); LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", ((m_halfDuplex & KeyModifierCapsLock) != 0) ? "on" : "off")); } else if (options[i] == kOptionHalfDuplexNumLock) { @@ -282,7 +283,6 @@ CScreen::setOptions(const COptionsList& options) else { m_halfDuplex &= ~KeyModifierNumLock; } - m_screen->setHalfDuplexMask(m_halfDuplex); LOG((CLOG_DEBUG1 "half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off")); } else if (options[i] == kOptionHalfDuplexScrollLock) { @@ -292,11 +292,13 @@ CScreen::setOptions(const COptionsList& options) else { m_halfDuplex &= ~KeyModifierScrollLock; } - m_screen->setHalfDuplexMask(m_halfDuplex); LOG((CLOG_DEBUG1 "half-duplex scroll-lock %s", ((m_halfDuplex & KeyModifierScrollLock) != 0) ? "on" : "off")); } } + // update half-duplex options + m_screen->setHalfDuplexMask(m_halfDuplex); + // update screen saver synchronization if (!m_isPrimary && oldScreenSaverSync != m_screenSaverSync) { if (m_screenSaverSync) { @@ -317,6 +319,36 @@ CScreen::setSequenceNumber(UInt32 seqNum) m_screen->setSequenceNumber(seqNum); } +UInt32 +CScreen::registerHotKey(KeyID key, KeyModifierMask mask) +{ + return m_screen->registerHotKey(key, mask); +} + +void +CScreen::unregisterHotKey(UInt32 id) +{ + m_screen->unregisterHotKey(id); +} + +void +CScreen::fakeInputBegin() +{ + assert(!m_fakeInput); + + m_fakeInput = true; + m_screen->fakeInputBegin(); +} + +void +CScreen::fakeInputEnd() +{ + assert(m_fakeInput); + + m_fakeInput = false; + m_screen->fakeInputEnd(); +} + bool CScreen::isOnScreen() const { @@ -332,35 +364,6 @@ CScreen::isLockedToScreen() const return true; } -// note -- we don't lock to the screen if a key is down. key -// reporting is simply not reliable enough to trust. the effect -// of switching screens with a key down is that the client will -// receive key repeats and key releases for keys that it hasn't -// see go down. that's okay because CKeyState will ignore those -// events. the user might be surprised that any modifier keys -// held while crossing to another screen don't apply on the -// target screen. if that ends up being a problem we can try -// to synthesize a key press for those modifiers on entry. -/* - // check for any pressed key - KeyButton key = isAnyKeyDown(); - if (key != 0) { - // double check current state of the keys. this shouldn't - // be necessary but we don't seem to get some key release - // events sometimes. this is an emergency backup so the - // client doesn't get stuck on the screen. - m_screen->updateKeys(); - KeyButton key2 = isAnyKeyDown(); - if (key2 != 0) { - LOG((CLOG_DEBUG "locked by %s", m_screen->getKeyName(key2))); - return true; - } - else { - LOG((CLOG_DEBUG "spuriously locked by %s", m_screen->getKeyName(key))); - } - } -*/ - // not locked return false; } @@ -388,6 +391,12 @@ CScreen::getActiveModifiers() const return m_screen->getActiveModifiers(); } +KeyModifierMask +CScreen::pollActiveModifiers() const +{ + return m_screen->pollActiveModifiers(); +} + void* CScreen::getEventTarget() const { @@ -457,13 +466,9 @@ CScreen::enterPrimary() } void -CScreen::enterSecondary(KeyModifierMask toggleMask) +CScreen::enterSecondary(KeyModifierMask) { - // remember toggle key state. we'll restore this when we leave. - m_toggleKeys = getActiveModifiers(); - - // restore toggle key state - setToggleState(toggleMask); + // do nothing } void @@ -472,55 +477,12 @@ CScreen::leavePrimary() // we don't track keys while on the primary screen so update our // idea of them now. this is particularly to update the state of // the toggle modifiers. - m_screen->updateKeys(); + m_screen->updateKeyState(); } void CScreen::leaveSecondary() { // release any keys we think are still down - releaseKeys(); - - // restore toggle key state - setToggleState(m_toggleKeys); -} - -void -CScreen::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. - for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) { - if (m_screen->isKeyDown(i)) { - m_screen->fakeKeyUp(i); - } - } -} - -void -CScreen::setToggleState(KeyModifierMask mask) -{ - // toggle modifiers that don't match the desired state - KeyModifierMask different = (m_screen->getActiveModifiers() ^ mask); - if ((different & KeyModifierCapsLock) != 0) { - m_screen->fakeToggle(KeyModifierCapsLock); - } - if ((different & KeyModifierNumLock) != 0) { - m_screen->fakeToggle(KeyModifierNumLock); - } - if ((different & KeyModifierScrollLock) != 0) { - m_screen->fakeToggle(KeyModifierScrollLock); - } -} - -KeyButton -CScreen::isAnyKeyDown() const -{ - for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) { - if (m_screen->isKeyDown(i)) { - return i; - } - } - return 0; + m_screen->fakeAllKeysUp(); } diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index 01b10417..4d216f8b 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -155,12 +155,12 @@ public: //! 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. + Synthesize mouse events to generate mouse wheel motion of \c xDelta + and \c yDelta. Deltas are positive for motion away from the user or + to the right and negative for motion towards the user or to the left. + Each wheel click should generate a delta of +/-120. */ - void mouseWheel(SInt32 delta); + void mouseWheel(SInt32 xDelta, SInt32 yDelta); //! Notify of options changes /*! @@ -181,6 +181,34 @@ public: */ void setSequenceNumber(UInt32); + //! Register a system hotkey + /*! + Registers a system-wide hotkey for key \p key with modifiers \p mask. + Returns an id used to unregister the hotkey. + */ + UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + + //! Unregister a system hotkey + /*! + Unregisters a previously registered hot key. + */ + void unregisterHotKey(UInt32 id); + + //! Prepare to synthesize input on primary screen + /*! + Prepares the primary screen to receive synthesized input. We do not + want to receive this synthesized input as user input so this method + ensures that we ignore it. Calls to \c fakeInputBegin() may not be + nested. + */ + void fakeInputBegin(); + + //! Done synthesizing input on primary screen + /*! + Undoes whatever \c fakeInputBegin() did. + */ + void fakeInputEnd(); + //@} //! @name accessors //@{ @@ -217,10 +245,18 @@ public: //! Get the active modifiers /*! - Returns the modifiers that are currently active. + Returns the modifiers that are currently active according to our + shadowed state. */ KeyModifierMask getActiveModifiers() const; + //! Get the active modifiers from OS + /*! + Returns the modifiers that are currently active according to the + operating system. + */ + KeyModifierMask pollActiveModifiers() const; + //@} // IScreen overrides @@ -241,11 +277,6 @@ protected: void leavePrimary(); void leaveSecondary(); -private: - void releaseKeys(); - void setToggleState(KeyModifierMask); - KeyButton isAnyKeyDown() const; - private: // our platform dependent screen IPlatformScreen* m_screen; @@ -266,8 +297,8 @@ private: // transition (true) KeyModifierMask m_halfDuplex; - // the toggle key state when this screen was last entered - KeyModifierMask m_toggleKeys; + // true if we're faking input on a primary screen + bool m_fakeInput; }; #endif diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 7679d104..81cbf06a 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -129,12 +129,12 @@ public: //! 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. + Synthesize mouse events to generate mouse wheel motion of \c xDelta + and \c yDelta. Deltas are positive for motion away from the user or + to the right and negative for motion towards the user or to the left. + Each wheel click should generate a delta of +/-120. */ - virtual void mouseWheel(SInt32 delta) = 0; + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0; //! Notify of screen saver change virtual void screensaver(bool activate) = 0; diff --git a/lib/synergy/IKeyState.cpp b/lib/synergy/IKeyState.cpp index bcc1ea2a..28cd3313 100644 --- a/lib/synergy/IKeyState.cpp +++ b/lib/synergy/IKeyState.cpp @@ -13,6 +13,7 @@ */ #include "IKeyState.h" +#include // // IKeyState @@ -52,10 +53,113 @@ IKeyState::CKeyInfo* IKeyState::CKeyInfo::alloc(KeyID id, KeyModifierMask mask, KeyButton button, SInt32 count) { - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + info->m_screens[0] = '\0'; + return info; +} + +IKeyState::CKeyInfo* +IKeyState::CKeyInfo::alloc(KeyID id, + KeyModifierMask mask, KeyButton button, SInt32 count, + const std::set& destinations) +{ + // collect destinations into a string. names are surrounded by ':' + // which makes searching easy later. the string is empty if there + // are no destinations and "*" means all destinations. + CString screens; + for (std::set::const_iterator i = destinations.begin(); + i != destinations.end(); ++i) { + if (*i == "*") { + screens = "*"; + break; + } + else { + if (screens.empty()) { + screens = ":"; + } + screens += *i; + screens += ":"; + } + } + + // build structure + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + screens.size()); info->m_key = id; info->m_mask = mask; info->m_button = button; info->m_count = count; + strcpy(info->m_screens, screens.c_str()); return info; } + +IKeyState::CKeyInfo* +IKeyState::CKeyInfo::alloc(const CKeyInfo& x) +{ + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + strlen(x.m_screens)); + info->m_key = x.m_key; + info->m_mask = x.m_mask; + info->m_button = x.m_button; + info->m_count = x.m_count; + strcpy(info->m_screens, x.m_screens); + return info; +} + +bool +IKeyState::CKeyInfo::isDefault(const char* screens) +{ + return (screens == NULL || screens[0] == '\0'); +} + +bool +IKeyState::CKeyInfo::contains(const char* screens, const CString& name) +{ + // special cases + if (isDefault(screens)) { + return false; + } + if (screens[0] == '*') { + return true; + } + + // search + CString match; + match.reserve(name.size() + 2); + match += ":"; + match += name; + match += ":"; + return (strstr(screens, match.c_str()) != NULL); +} + +bool +IKeyState::CKeyInfo::equal(const CKeyInfo* a, const CKeyInfo* b) +{ + return (a->m_key == b->m_key && + a->m_mask == b->m_mask && + a->m_button == b->m_button && + a->m_count == b->m_count && + strcmp(a->m_screens, b->m_screens) == 0); +} + +void +IKeyState::CKeyInfo::split(const char* screens, std::set& dst) +{ + dst.clear(); + if (isDefault(screens)) { + return; + } + if (screens[0] == '*') { + dst.insert("*"); + return; + } + + const char* i = screens + 1; + while (*i != '\0') { + const char* j = strchr(i, ':'); + dst.insert(CString(i, j - i)); + i = j + 1; + } +} diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index 04e7be8e..1985a630 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -18,6 +18,8 @@ #include "IInterface.h" #include "KeyTypes.h" #include "CEvent.h" +#include "CString.h" +#include "stdset.h" //! Key state interface /*! @@ -34,23 +36,41 @@ public: class CKeyInfo { public: static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); + static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count, + const std::set& destinations); + static CKeyInfo* alloc(const CKeyInfo&); + + static bool isDefault(const char* screens); + static bool contains(const char* screens, const CString& name); + static bool equal(const CKeyInfo*, const CKeyInfo*); + static void split(const char* screens, std::set&); public: KeyID m_key; KeyModifierMask m_mask; KeyButton m_button; SInt32 m_count; + char m_screens[1]; }; + typedef std::set KeyButtonSet; + //! @name manipulators //@{ + //! Update the keyboard map + /*! + Causes the key state to get updated to reflect the current keyboard + mapping. + */ + virtual void updateKeyMap() = 0; + //! Update the key state /*! Causes the key state to get updated to reflect the physical keyboard - state and current keyboard mapping. + state. */ - virtual void updateKeys() = 0; + virtual void updateKeyState() = 0; //! Set half-duplex mask /*! @@ -80,12 +100,12 @@ public: */ virtual void fakeKeyUp(KeyButton button) = 0; - //! Fake a modifier toggle + //! Fake key releases for all fake pressed keys /*! - Synthesizes key press/release events to toggle the given \p modifier - and updates the key state. + Synthesizes a key release event for every key that is synthetically + pressed and updates the key state. */ - virtual void fakeToggle(KeyModifierMask modifier) = 0; + virtual void fakeAllKeysUp() = 0; //! Fake ctrl+alt+del /*! @@ -107,16 +127,32 @@ public: //! Get the active modifiers /*! - Returns the modifiers that are currently active. + Returns the modifiers that are currently active according to our + shadowed state. */ virtual KeyModifierMask getActiveModifiers() const = 0; - //! Get name of key + //! Get the active modifiers from OS /*! - Return a string describing the given key. + Returns the modifiers that are currently active according to the + operating system. */ - virtual const char* getKeyName(KeyButton) const = 0; + virtual KeyModifierMask + pollActiveModifiers() const = 0; + + //! Get the active keyboard layout from OS + /*! + Returns the active keyboard layout according to the operating system. + */ + virtual SInt32 pollActiveGroup() const = 0; + + //! Get the keys currently pressed from OS + /*! + Adds any keys that are currently pressed according to the operating + system to \p pressedKeys. + */ + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0; //! Get key down event type. Event data is CKeyInfo*, count == 1. static CEvent::Type getKeyDownEvent(); diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 47f5d36a..b4d1b785 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -146,6 +146,10 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides) = 0; virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0; + virtual void unregisterHotKey(UInt32 id) = 0; + virtual void fakeInputBegin() = 0; + virtual void fakeInputEnd() = 0; virtual SInt32 getJumpZoneSize() const = 0; virtual bool isAnyMouseButtonDown() const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; @@ -154,22 +158,26 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; - virtual void fakeMouseWheel(SInt32 delta) const = 0; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; // IKeyState overrides - virtual void updateKeys() = 0; + virtual void updateKeyMap() = 0; + virtual void updateKeyState() = 0; virtual void setHalfDuplexMask(KeyModifierMask) = 0; virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) = 0; virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button) = 0; virtual void fakeKeyUp(KeyButton button) = 0; - virtual void fakeToggle(KeyModifierMask modifier) = 0; + virtual void fakeAllKeysUp() = 0; virtual bool fakeCtrlAltDel() = 0; virtual bool isKeyDown(KeyButton) const = 0; virtual KeyModifierMask getActiveModifiers() const = 0; - virtual const char* getKeyName(KeyButton) const = 0; + virtual KeyModifierMask + pollActiveModifiers() const = 0; + virtual SInt32 pollActiveGroup() const = 0; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0; protected: //! Handle system event @@ -188,9 +196,9 @@ protected: A primary screen has further responsibilities. It should post the events in \c IPrimaryScreen as appropriate. It should also - call \c setKeyDown() on its \c CKeyState whenever a key is pressed + call \c onKey() on its \c CKeyState whenever a key is pressed or released (but not for key repeats). And it should call - \c updateKeys() on its \c CKeyState if necessary when the keyboard + \c updateKeyMap() on its \c CKeyState if necessary when the keyboard mapping changes. The target of all events should be the value returned by diff --git a/lib/synergy/IPrimaryScreen.cpp b/lib/synergy/IPrimaryScreen.cpp index 62d3c4b9..71b103f3 100644 --- a/lib/synergy/IPrimaryScreen.cpp +++ b/lib/synergy/IPrimaryScreen.cpp @@ -25,6 +25,10 @@ CEvent::Type IPrimaryScreen::s_motionSecondaryEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_wheelEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_ssActivatedEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_ssDeactivatedEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_hotKeyDownEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_hotKeyUpEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_fakeInputBegin = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_fakeInputEnd = CEvent::kUnknown; CEvent::Type IPrimaryScreen::getButtonDownEvent() @@ -75,19 +79,63 @@ IPrimaryScreen::getScreensaverDeactivatedEvent() "IPrimaryScreen::screensaverDeactivated"); } +CEvent::Type +IPrimaryScreen::getHotKeyDownEvent() +{ + return CEvent::registerTypeOnce(s_hotKeyDownEvent, + "IPrimaryScreen::hotKeyDown"); +} + +CEvent::Type +IPrimaryScreen::getHotKeyUpEvent() +{ + return CEvent::registerTypeOnce(s_hotKeyUpEvent, + "IPrimaryScreen::hotKeyUp"); +} + +CEvent::Type +IPrimaryScreen::getFakeInputBeginEvent() +{ + return CEvent::registerTypeOnce(s_fakeInputBegin, + "IPrimaryScreen::fakeInputBegin"); +} + +CEvent::Type +IPrimaryScreen::getFakeInputEndEvent() +{ + return CEvent::registerTypeOnce(s_fakeInputEnd, + "IPrimaryScreen::fakeInputEnd"); +} + // // IPrimaryScreen::CButtonInfo // IPrimaryScreen::CButtonInfo* -IPrimaryScreen::CButtonInfo::alloc(ButtonID id) +IPrimaryScreen::CButtonInfo::alloc(ButtonID id, KeyModifierMask mask) { CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); info->m_button = id; + info->m_mask = mask; return info; } +IPrimaryScreen::CButtonInfo* +IPrimaryScreen::CButtonInfo::alloc(const CButtonInfo& x) +{ + CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); + info->m_button = x.m_button; + info->m_mask = x.m_mask; + return info; +} + +bool +IPrimaryScreen::CButtonInfo::equal(const CButtonInfo* a, const CButtonInfo* b) +{ + return (a->m_button == b->m_button && a->m_mask == b->m_mask); +} + // // IPrimaryScreen::CMotionInfo @@ -108,9 +156,23 @@ IPrimaryScreen::CMotionInfo::alloc(SInt32 x, SInt32 y) // IPrimaryScreen::CWheelInfo* -IPrimaryScreen::CWheelInfo::alloc(SInt32 wheel) +IPrimaryScreen::CWheelInfo::alloc(SInt32 xDelta, SInt32 yDelta) { CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo)); - info->m_wheel = wheel; + info->m_xDelta = xDelta; + info->m_yDelta = yDelta; + return info; +} + + +// +// IPrimaryScreen::CHotKeyInfo +// + +IPrimaryScreen::CHotKeyInfo* +IPrimaryScreen::CHotKeyInfo::alloc(UInt32 id) +{ + CHotKeyInfo* info = (CHotKeyInfo*)malloc(sizeof(CHotKeyInfo)); + info->m_id = id; return info; } diff --git a/lib/synergy/IPrimaryScreen.h b/lib/synergy/IPrimaryScreen.h index 6718a17b..93166d23 100644 --- a/lib/synergy/IPrimaryScreen.h +++ b/lib/synergy/IPrimaryScreen.h @@ -16,6 +16,7 @@ #define IPRIMARYSCREEN_H #include "IInterface.h" +#include "KeyTypes.h" #include "MouseTypes.h" #include "CEvent.h" @@ -29,10 +30,14 @@ public: //! Button event data class CButtonInfo { public: - static CButtonInfo* alloc(ButtonID); + static CButtonInfo* alloc(ButtonID, KeyModifierMask); + static CButtonInfo* alloc(const CButtonInfo&); + + static bool equal(const CButtonInfo*, const CButtonInfo*); public: ButtonID m_button; + KeyModifierMask m_mask; }; //! Motion event data class CMotionInfo { @@ -46,10 +51,19 @@ public: //! Wheel motion event data class CWheelInfo { public: - static CWheelInfo* alloc(SInt32); + static CWheelInfo* alloc(SInt32 xDelta, SInt32 yDelta); public: - SInt32 m_wheel; + SInt32 m_xDelta; + SInt32 m_yDelta; + }; + //! Hot key event data + class CHotKeyInfo { + public: + static CHotKeyInfo* alloc(UInt32 id); + + public: + UInt32 m_id; }; //! @name manipulators @@ -72,6 +86,50 @@ public: */ virtual void warpCursor(SInt32 x, SInt32 y) = 0; + //! Register a system hotkey + /*! + Registers a system-wide hotkey. The screen should arrange for an event + to be delivered to itself when the hot key is pressed or released. When + that happens the screen should post a \c getHotKeyDownEvent() or + \c getHotKeyUpEvent(), respectively. The hot key is key \p key with + exactly the modifiers \p mask. Returns 0 on failure otherwise an id + that can be used to unregister the hotkey. + + A hot key is a set of modifiers and a key, which may itself be a modifier. + The hot key is pressed when the hot key's modifiers and only those + modifiers are logically down (active) and the key is pressed. The hot + key is released when the key is released, regardless of the modifiers. + + The hot key event should be generated no matter what window or application + has the focus. No other window or application should receive the key + press or release events (they can and should see the modifier key events). + When the key is a modifier, it's acceptable to allow the user to press + the modifiers in any order or to require the user to press the given key + last. + */ + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0; + + //! Unregister a system hotkey + /*! + Unregisters a previously registered hot key. + */ + virtual void unregisterHotKey(UInt32 id) = 0; + + //! Prepare to synthesize input on primary screen + /*! + Prepares the primary screen to receive synthesized input. We do not + want to receive this synthesized input as user input so this method + ensures that we ignore it. Calls to \c fakeInputBegin() may not be + nested. + */ + virtual void fakeInputBegin() = 0; + + //! Done synthesizing input on primary screen + /*! + Undoes whatever \c fakeInputBegin() did. + */ + virtual void fakeInputEnd() = 0; + //@} //! @name accessors //@{ @@ -98,7 +156,7 @@ public: the edges of the screen, typically the center. */ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - + //! Get button down event type. Event data is CButtonInfo*. static CEvent::Type getButtonDownEvent(); //! Get button up event type. Event data is CButtonInfo*. @@ -120,6 +178,14 @@ public: static CEvent::Type getScreensaverActivatedEvent(); //! Get screensaver deactivated event type static CEvent::Type getScreensaverDeactivatedEvent(); + //! Get hot key down event type. Event data is CHotKeyInfo*. + static CEvent::Type getHotKeyDownEvent(); + //! Get hot key up event type. Event data is CHotKeyInfo*. + static CEvent::Type getHotKeyUpEvent(); + //! Get start of fake input event type + static CEvent::Type getFakeInputBeginEvent(); + //! Get end of fake input event type + static CEvent::Type getFakeInputEndEvent(); //@} @@ -131,6 +197,10 @@ private: static CEvent::Type s_wheelEvent; static CEvent::Type s_ssActivatedEvent; static CEvent::Type s_ssDeactivatedEvent; + static CEvent::Type s_hotKeyDownEvent; + static CEvent::Type s_hotKeyUpEvent; + static CEvent::Type s_fakeInputBegin; + static CEvent::Type s_fakeInputEnd; }; #endif diff --git a/lib/synergy/IScreen.cpp b/lib/synergy/IScreen.cpp index fadbe8b3..3095b9d4 100644 --- a/lib/synergy/IScreen.cpp +++ b/lib/synergy/IScreen.cpp @@ -21,6 +21,8 @@ CEvent::Type IScreen::s_errorEvent = CEvent::kUnknown; CEvent::Type IScreen::s_shapeChangedEvent = CEvent::kUnknown; CEvent::Type IScreen::s_clipboardGrabbedEvent = CEvent::kUnknown; +CEvent::Type IScreen::s_suspendEvent = CEvent::kUnknown; +CEvent::Type IScreen::s_resumeEvent = CEvent::kUnknown; CEvent::Type IScreen::getErrorEvent() @@ -42,3 +44,17 @@ IScreen::getClipboardGrabbedEvent() return CEvent::registerTypeOnce(s_clipboardGrabbedEvent, "IScreen::clipboardGrabbed"); } + +CEvent::Type +IScreen::getSuspendEvent() +{ + return CEvent::registerTypeOnce(s_suspendEvent, + "IScreen::suspend"); +} + +CEvent::Type +IScreen::getResumeEvent() +{ + return CEvent::registerTypeOnce(s_resumeEvent, + "IScreen::resume"); +} diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h index a36f8fb1..e1f94d59 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IScreen.h @@ -85,12 +85,28 @@ public: */ static CEvent::Type getClipboardGrabbedEvent(); + //! Get suspend event type + /*! + Returns the suspend event type. This is sent whenever the system goes + to sleep or a user session is deactivated (fast user switching). + */ + static CEvent::Type getSuspendEvent(); + + //! Get resume event type + /*! + Returns the suspend event type. This is sent whenever the system wakes + up or a user session is activated (fast user switching). + */ + static CEvent::Type getResumeEvent(); + //@} private: static CEvent::Type s_errorEvent; static CEvent::Type s_shapeChangedEvent; static CEvent::Type s_clipboardGrabbedEvent; + static CEvent::Type s_suspendEvent; + static CEvent::Type s_resumeEvent; }; #endif diff --git a/lib/synergy/ISecondaryScreen.h b/lib/synergy/ISecondaryScreen.h index 092cd805..5b3a150d 100644 --- a/lib/synergy/ISecondaryScreen.h +++ b/lib/synergy/ISecondaryScreen.h @@ -48,9 +48,9 @@ public: //! Fake mouse wheel /*! - Synthesize a mouse wheel event of amount \c delta. + Synthesize a mouse wheel event of amount \c xDelta and \c yDelta. */ - virtual void fakeMouseWheel(SInt32 delta) const = 0; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; //@} }; diff --git a/lib/synergy/KeyTypes.cpp b/lib/synergy/KeyTypes.cpp new file mode 100644 index 00000000..b483745d --- /dev/null +++ b/lib/synergy/KeyTypes.cpp @@ -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. + */ + +#include "KeyTypes.h" + +const KeyNameMapEntry kKeyNameMap[] = { + { "AltGr", kKeyAltGr }, + { "Alt_L", kKeyAlt_L }, + { "Alt_R", kKeyAlt_R }, + { "AppMail", kKeyAppMail }, + { "AppMedia", kKeyAppMedia }, + { "AppUser1", kKeyAppUser1 }, + { "AppUser2", kKeyAppUser2 }, + { "AudioDown", kKeyAudioDown }, + { "AudioMute", kKeyAudioMute }, + { "AudioNext", kKeyAudioNext }, + { "AudioPlay", kKeyAudioPlay }, + { "AudioPrev", kKeyAudioPrev }, + { "AudioStop", kKeyAudioStop }, + { "AudioUp", kKeyAudioUp }, + { "BackSpace", kKeyBackSpace }, + { "Begin", kKeyBegin }, + { "Break", kKeyBreak }, + { "Cancel", kKeyCancel }, + { "CapsLock", kKeyCapsLock }, + { "Clear", kKeyClear }, + { "Control_L", kKeyControl_L }, + { "Control_R", kKeyControl_R }, + { "Delete", kKeyDelete }, + { "Down", kKeyDown }, + { "Eject", kKeyEject }, + { "End", kKeyEnd }, + { "Escape", kKeyEscape }, + { "Execute", kKeyExecute }, + { "F1", kKeyF1 }, + { "F2", kKeyF2 }, + { "F3", kKeyF3 }, + { "F4", kKeyF4 }, + { "F5", kKeyF5 }, + { "F6", kKeyF6 }, + { "F7", kKeyF7 }, + { "F8", kKeyF8 }, + { "F9", kKeyF9 }, + { "F10", kKeyF10 }, + { "F11", kKeyF11 }, + { "F12", kKeyF12 }, + { "F13", kKeyF13 }, + { "F14", kKeyF14 }, + { "F15", kKeyF15 }, + { "F16", kKeyF16 }, + { "F17", kKeyF17 }, + { "F18", kKeyF18 }, + { "F19", kKeyF19 }, + { "F20", kKeyF20 }, + { "F21", kKeyF21 }, + { "F22", kKeyF22 }, + { "F23", kKeyF23 }, + { "F24", kKeyF24 }, + { "F25", kKeyF25 }, + { "F26", kKeyF26 }, + { "F27", kKeyF27 }, + { "F28", kKeyF28 }, + { "F29", kKeyF29 }, + { "F30", kKeyF30 }, + { "F31", kKeyF31 }, + { "F32", kKeyF32 }, + { "F33", kKeyF33 }, + { "F34", kKeyF34 }, + { "F35", kKeyF35 }, + { "Find", kKeyFind }, + { "Help", kKeyHelp }, + { "Henkan", kKeyHenkan }, + { "Home", kKeyHome }, + { "Hyper_L", kKeyHyper_L }, + { "Hyper_R", kKeyHyper_R }, + { "Insert", kKeyInsert }, + { "KP_0", kKeyKP_0 }, + { "KP_1", kKeyKP_1 }, + { "KP_2", kKeyKP_2 }, + { "KP_3", kKeyKP_3 }, + { "KP_4", kKeyKP_4 }, + { "KP_5", kKeyKP_5 }, + { "KP_6", kKeyKP_6 }, + { "KP_7", kKeyKP_7 }, + { "KP_8", kKeyKP_8 }, + { "KP_9", kKeyKP_9 }, + { "KP_Add", kKeyKP_Add }, + { "KP_Begin", kKeyKP_Begin }, + { "KP_Decimal", kKeyKP_Decimal }, + { "KP_Delete", kKeyKP_Delete }, + { "KP_Divide", kKeyKP_Divide }, + { "KP_Down", kKeyKP_Down }, + { "KP_End", kKeyKP_End }, + { "KP_Enter", kKeyKP_Enter }, + { "KP_Equal", kKeyKP_Equal }, + { "KP_F1", kKeyKP_F1 }, + { "KP_F2", kKeyKP_F2 }, + { "KP_F3", kKeyKP_F3 }, + { "KP_F4", kKeyKP_F4 }, + { "KP_Home", kKeyKP_Home }, + { "KP_Insert", kKeyKP_Insert }, + { "KP_Left", kKeyKP_Left }, + { "KP_Multiply", kKeyKP_Multiply }, + { "KP_PageDown", kKeyKP_PageDown }, + { "KP_PageUp", kKeyKP_PageUp }, + { "KP_Right", kKeyKP_Right }, + { "KP_Separator", kKeyKP_Separator }, + { "KP_Space", kKeyKP_Space }, + { "KP_Subtract", kKeyKP_Subtract }, + { "KP_Tab", kKeyKP_Tab }, + { "KP_Up", kKeyKP_Up }, + { "Left", kKeyLeft }, + { "LeftTab", kKeyLeftTab }, + { "Linefeed", kKeyLinefeed }, + { "Menu", kKeyMenu }, + { "Meta_L", kKeyMeta_L }, + { "Meta_R", kKeyMeta_R }, + { "NumLock", kKeyNumLock }, + { "PageDown", kKeyPageDown }, + { "PageUp", kKeyPageUp }, + { "Pause", kKeyPause }, + { "Print", kKeyPrint }, + { "Redo", kKeyRedo }, + { "Return", kKeyReturn }, + { "Right", kKeyRight }, + { "ScrollLock", kKeyScrollLock }, + { "Select", kKeySelect }, + { "ShiftLock", kKeyShiftLock }, + { "Shift_L", kKeyShift_L }, + { "Shift_R", kKeyShift_R }, + { "Sleep", kKeySleep }, + { "Super_L", kKeySuper_L }, + { "Super_R", kKeySuper_R }, + { "SysReq", kKeySysReq }, + { "Tab", kKeyTab }, + { "Undo", kKeyUndo }, + { "Up", kKeyUp }, + { "WWWBack", kKeyWWWBack }, + { "WWWFavorites", kKeyWWWFavorites }, + { "WWWForward", kKeyWWWForward }, + { "WWWHome", kKeyWWWHome }, + { "WWWRefresh", kKeyWWWRefresh }, + { "WWWSearch", kKeyWWWSearch }, + { "WWWStop", kKeyWWWStop }, + { "Zenkaku", kKeyZenkaku }, + { "Space", 0x0020 }, + { "Exclaim", 0x0021 }, + { "DoubleQuote", 0x0022 }, + { "Number", 0x0023 }, + { "Dollar", 0x0024 }, + { "Percent", 0x0025 }, + { "Ampersand", 0x0026 }, + { "Apostrophe", 0x0027 }, + { "ParenthesisL", 0x0028 }, + { "ParenthesisR", 0x0029 }, + { "Asterisk", 0x002a }, + { "Plus", 0x002b }, + { "Comma", 0x002c }, + { "Minus", 0x002d }, + { "Period", 0x002e }, + { "Slash", 0x002f }, + { "Colon", 0x003a }, + { "Semicolon", 0x003b }, + { "Less", 0x003c }, + { "Equal", 0x003d }, + { "Greater", 0x003e }, + { "Question", 0x003f }, + { "At", 0x0040 }, + { "BracketL", 0x005b }, + { "Backslash", 0x005c }, + { "BracketR", 0x005d }, + { "Circumflex", 0x005e }, + { "Underscore", 0x005f }, + { "Grave", 0x0060 }, + { "BraceL", 0x007b }, + { "Bar", 0x007c }, + { "BraceR", 0x007d }, + { "Tilde", 0x007e }, + { NULL, 0 }, +}; + +const KeyModifierNameMapEntry kModifierNameMap[] = { + { "Alt", KeyModifierAlt }, + { "AltGr", KeyModifierAltGr }, +// { "CapsLock", KeyModifierCapsLock }, + { "Control", KeyModifierControl }, + { "Meta", KeyModifierMeta }, +// { "NumLock", KeyModifierNumLock }, +// { "ScrollLock", KeyModifierScrollLock }, + { "Shift", KeyModifierShift }, + { "Super", KeyModifierSuper }, + { NULL, 0 }, +}; diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index c6ff7148..039f9030 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -53,12 +53,27 @@ 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 KeyModifierAltGr = 0x0020; static const KeyModifierMask KeyModifierCapsLock = 0x1000; static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; //@} +//! @name Modifier key bits +//@{ +static const UInt32 kKeyModifierBitNone = 16; +static const UInt32 kKeyModifierBitShift = 0; +static const UInt32 kKeyModifierBitControl = 1; +static const UInt32 kKeyModifierBitAlt = 2; +static const UInt32 kKeyModifierBitMeta = 3; +static const UInt32 kKeyModifierBitSuper = 4; +static const UInt32 kKeyModifierBitAltGr = 5; +static const UInt32 kKeyModifierBitCapsLock = 12; +static const UInt32 kKeyModifierBitNumLock = 13; +static const UInt32 kKeyModifierBitScrollLock = 14; +static const SInt32 kKeyModifierNumBits = 16; +//@} + //! @name Modifier key identifiers //@{ static const KeyModifierID kKeyModifierIDNull = 0; @@ -92,9 +107,6 @@ static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */ static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ 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 */ @@ -118,7 +130,7 @@ 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 kKeyAltGr = 0xEF7E; /* Character set switch */ static const KeyID kKeyNumLock = 0xEF7F; // keypad @@ -134,9 +146,7 @@ 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; @@ -213,8 +223,32 @@ static const KeyID kKeySuper_R = 0xEFEC; /* Right super */ static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */ static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ +// multi-key character composition +static const KeyID kKeyCompose = 0xEF20; +static const KeyID kKeyDeadGrave = 0x0300; +static const KeyID kKeyDeadAcute = 0x0301; +static const KeyID kKeyDeadCircumflex = 0x0302; +static const KeyID kKeyDeadTilde = 0x0303; +static const KeyID kKeyDeadMacron = 0x0304; +static const KeyID kKeyDeadBreve = 0x0306; +static const KeyID kKeyDeadAbovedot = 0x0307; +static const KeyID kKeyDeadDiaeresis = 0x0308; +static const KeyID kKeyDeadAbovering = 0x030a; +static const KeyID kKeyDeadDoubleacute = 0x030b; +static const KeyID kKeyDeadCaron = 0x030c; +static const KeyID kKeyDeadCedilla = 0x0327; +static const KeyID kKeyDeadOgonek = 0x0328; + // more function and modifier keys -static const KeyID kKeyLeftTab = 0xEE20; +static const KeyID kKeyLeftTab = 0xEE20; + +// update modifiers +static const KeyID kKeySetModifiers = 0xEE06; +static const KeyID kKeyClearModifiers = 0xEE07; + +// group change +static const KeyID kKeyNextGroup = 0xEE08; +static const KeyID kKeyPrevGroup = 0xEE0A; // extended keys static const KeyID kKeyEject = 0xE001; @@ -240,4 +274,30 @@ static const KeyID kKeyAppUser2 = 0xE0B7; //@} +struct KeyNameMapEntry { +public: + const char* m_name; + KeyID m_id; +}; +struct KeyModifierNameMapEntry { +public: + const char* m_name; + KeyModifierMask m_mask; +}; + +//! Key name to KeyID table +/*! +A table of key names to the corresponding KeyID. Only the keys listed +above plus non-alphanumeric ASCII characters are in the table. The end +of the table is the first pair with a NULL m_name. +*/ +extern const KeyNameMapEntry kKeyNameMap[]; + +//! Modifier key name to KeyModifierMask table +/*! +A table of modifier key names to the corresponding KeyModifierMask. +The end of the table is the first pair with a NULL m_name. +*/ +extern const KeyModifierNameMapEntry kModifierNameMap[]; + #endif diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 0265490c..0b481eef 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -24,6 +24,7 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libsynergy.a libsynergy_a_SOURCES = \ CClipboard.cpp \ + CKeyMap.cpp \ CKeyState.cpp \ CPacketStreamFilter.cpp \ CPlatformScreen.cpp \ @@ -33,9 +34,12 @@ libsynergy_a_SOURCES = \ IKeyState.cpp \ IPrimaryScreen.cpp \ IScreen.cpp \ + KeyTypes.cpp \ + ProtocolTypes.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ + CKeyMap.h \ CKeyState.h \ CPacketStreamFilter.h \ CPlatformScreen.h \ diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 31504993..74beda00 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -42,20 +42,49 @@ typedef std::vector COptionsList; //! @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"); -static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); +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 kOptionScreenSwitchCorners = OPTION_CODE("SSCM"); +static const OptionID kOptionScreenSwitchCornerSize = OPTION_CODE("SSCS"); +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"); +static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); +static const OptionID kOptionWin32KeepForeground = OPTION_CODE("_KFW"); +//@} + +//! @name Screen switch corner enumeration +//@{ +enum EScreenSwitchCorners { + kNoCorner, + kTopLeft, + kTopRight, + kBottomLeft, + kBottomRight, + kFirstCorner = kTopLeft, + kLastCorner = kBottomRight +}; +//@} + +//! @name Screen switch corner masks +//@{ +enum EScreenSwitchCornerMasks { + kNoCornerMask = 0, + kTopLeftMask = 1 << (kTopLeft - kFirstCorner), + kTopRightMask = 1 << (kTopRight - kFirstCorner), + kBottomLeftMask = 1 << (kBottomLeft - kFirstCorner), + kBottomRightMask = 1 << (kBottomRight - kFirstCorner), + kAllCornersMask = kTopLeftMask | kTopRightMask | + kBottomLeftMask | kBottomRightMask +}; //@} #undef OPTION_CODE diff --git a/lib/synergy/ProtocolTypes.cpp b/lib/synergy/ProtocolTypes.cpp new file mode 100644 index 00000000..441b2f33 --- /dev/null +++ b/lib/synergy/ProtocolTypes.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 "ProtocolTypes.h" + +const char* kMsgHello = "Synergy%2i%2i"; +const char* kMsgHelloBack = "Synergy%2i%2i%s"; +const char* kMsgCNoop = "CNOP"; +const char* kMsgCClose = "CBYE"; +const char* kMsgCEnter = "CINN%2i%2i%4i%2i"; +const char* kMsgCLeave = "COUT"; +const char* kMsgCClipboard = "CCLP%1i%4i"; +const char* kMsgCScreenSaver = "CSEC%1i"; +const char* kMsgCResetOptions = "CROP"; +const char* kMsgCInfoAck = "CIAK"; +const char* kMsgCKeepAlive = "CALV"; +const char* kMsgDKeyDown = "DKDN%2i%2i%2i"; +const char* kMsgDKeyDown1_0 = "DKDN%2i%2i"; +const char* kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i"; +const char* kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i"; +const char* kMsgDKeyUp = "DKUP%2i%2i%2i"; +const char* kMsgDKeyUp1_0 = "DKUP%2i%2i"; +const char* kMsgDMouseDown = "DMDN%1i"; +const char* kMsgDMouseUp = "DMUP%1i"; +const char* kMsgDMouseMove = "DMMV%2i%2i"; +const char* kMsgDMouseRelMove = "DMRM%2i%2i"; +const char* kMsgDMouseWheel = "DMWM%2i%2i"; +const char* kMsgDMouseWheel1_0 = "DMWM%2i"; +const char* kMsgDClipboard = "DCLP%1i%4i%s"; +const char* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i"; +const char* kMsgDSetOptions = "DSOP%4I"; +const char* kMsgQInfo = "QINF"; +const char* kMsgEIncompatible = "EICV%2i%2i"; +const char* kMsgEBusy = "EBSY"; +const char* kMsgEUnknown = "EUNK"; +const char* kMsgEBad = "EBAD"; diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index b4b8ae22..c4d3c99d 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -20,8 +20,11 @@ // protocol version number // 1.0: initial protocol // 1.1: adds KeyCode to key press, release, and repeat +// 1.2: adds mouse relative motion +// 1.3: adds keep alive and deprecates heartbeats, +// adds horizontal mouse scrolling static const SInt16 kProtocolMajorVersion = 1; -static const SInt16 kProtocolMinorVersion = 2; +static const SInt16 kProtocolMinorVersion = 3; // default contact port number static const UInt16 kDefaultPort = 24800; @@ -29,11 +32,16 @@ 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; +// time between kMsgCKeepAlive (in seconds). a non-positive value disables +// keep alives. this is the default rate that can be overridden using an +// option. +static const double kKeepAliveRate = 3.0; -// number of skipped heartbeats that constitutes death +// number of skipped kMsgCKeepAlive messages that indicates a problem +static const double kKeepAlivesUntilDeath = 3.0; + +// obsolete heartbeat stuff +static const double kHeartRate = -1.0; static const double kHeartBeatsUntilDeath = 3.0; // direction constants @@ -74,13 +82,13 @@ enum EDirectionMask { // 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"; +extern const char* kMsgHello; // 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"; +extern const char* kMsgHelloBack; // @@ -88,10 +96,10 @@ static const char kMsgHelloBack[] = "Synergy%2i%2i%s"; // // no operation; secondary -> primary -static const char kMsgCNoop[] = "CNOP"; +extern const char* kMsgCNoop; // close connection; primary -> secondary -static const char kMsgCClose[] = "CBYE"; +extern const char* kMsgCClose; // enter screen: primary -> secondary // entering screen at screen position $1 = x, $2 = y. x,y are @@ -101,7 +109,7 @@ static const char kMsgCClose[] = "CBYE"; // 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"; +extern const char* kMsgCEnter; // leave screen: primary -> secondary // leaving screen. the secondary screen should send clipboard @@ -110,28 +118,38 @@ static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i"; // 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"; +extern const char* kMsgCLeave; // 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"; +extern const char* kMsgCClipboard; // screensaver change: primary -> secondary // screensaver on primary has started ($1 == 1) or closed ($1 == 0) -static const char kMsgCScreenSaver[] = "CSEC%1i"; +extern const char* kMsgCScreenSaver; // reset options: primary -> secondary // client should reset all of its options to their defaults. -static const char kMsgCResetOptions[] = "CROP"; +extern const char* kMsgCResetOptions; // 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"; +extern const char* kMsgCInfoAck; + +// keep connection alive: primary <-> secondary +// sent by the server periodically to verify that connections are still +// up and running. clients must reply in kind on receipt. if the server +// gets an error sending the message or does not receive a reply within +// a reasonable time then the server disconnects the client. if the +// client doesn't receive these (or any message) periodically then it +// should disconnect from the server. the appropriate interval is +// defined by an option. +extern const char* kMsgCKeepAlive; // @@ -149,52 +167,57 @@ static const char kMsgCInfoAck[] = "CIAK"; // 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"; +extern const char* kMsgDKeyDown; // key pressed 1.0: same as above but without KeyButton -static const char kMsgDKeyDown1_0[] = "DKDN%2i%2i"; +extern const char* kMsgDKeyDown1_0; // key auto-repeat: primary -> secondary // $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton -static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i%2i"; +extern const char* kMsgDKeyRepeat; // key auto-repeat 1.0: same as above but without KeyButton -static const char kMsgDKeyRepeat1_0[] = "DKRP%2i%2i%2i"; +extern const char* kMsgDKeyRepeat1_0; // key released: primary -> secondary // $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton -static const char kMsgDKeyUp[] = "DKUP%2i%2i%2i"; +extern const char* kMsgDKeyUp; // key released 1.0: same as above but without KeyButton -static const char kMsgDKeyUp1_0[] = "DKUP%2i%2i"; +extern const char* kMsgDKeyUp1_0; // mouse button pressed: primary -> secondary // $1 = ButtonID -static const char kMsgDMouseDown[] = "DMDN%1i"; +extern const char* kMsgDMouseDown; // mouse button released: primary -> secondary // $1 = ButtonID -static const char kMsgDMouseUp[] = "DMUP%1i"; +extern const char* kMsgDMouseUp; // mouse moved: primary -> secondary // $1 = x, $2 = y. x,y are absolute screen coordinates. -static const char kMsgDMouseMove[] = "DMMV%2i%2i"; +extern const char* kMsgDMouseMove; // relative mouse move: primary -> secondary // $1 = dx, $2 = dy. dx,dy are motion deltas. -static const char kMsgDMouseRelMove[] = "DMRM%2i%2i"; +extern const char* kMsgDMouseRelMove; -// 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"; +// mouse scroll: primary -> secondary +// $1 = xDelta, $2 = yDelta. the delta should be +120 for one tick forward +// (away from the user) or right and -120 for one tick backward (toward +// the user) or left. +extern const char* kMsgDMouseWheel; + +// mouse vertical scroll: primary -> secondary +// like as kMsgDMouseWheel except only sends $1 = yDelta. +extern const char* kMsgDMouseWheel1_0; // 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"; +extern const char* kMsgDClipboard; // client data: secondary -> primary // $1 = coordinate of leftmost pixel on secondary screen, @@ -210,12 +233,12 @@ static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // 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"; +extern const char* kMsgDInfo; // set options: primary -> secondary // client should set the given option/value pairs. $1 = option/value // pairs. -static const char kMsgDSetOptions[] = "DSOP%4I"; +extern const char* kMsgDSetOptions; // @@ -224,7 +247,7 @@ static const char kMsgDSetOptions[] = "DSOP%4I"; // query screen info: primary -> secondary // client should reply with a kMsgDInfo. -static const char kMsgQInfo[] = "QINF"; +extern const char* kMsgQInfo; // @@ -233,19 +256,19 @@ static const char kMsgQInfo[] = "QINF"; // incompatible versions: primary -> secondary // $1 = major version of primary, $2 = minor version of primary. -static const char kMsgEIncompatible[] = "EICV%2i%2i"; +extern const char* kMsgEIncompatible; // name provided when connecting is already in use: primary -> secondary -static const char kMsgEBusy[] = "EBSY"; +extern const char* kMsgEBusy; // unknown client: primary -> secondary // name provided when connecting is not in primary's screen // configuration map. -static const char kMsgEUnknown[] = "EUNK"; +extern const char* kMsgEUnknown; // protocol violation: primary -> secondary // primary should disconnect after sending this message. -static const char kMsgEBad[] = "EBAD"; +extern const char* kMsgEBad; // diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 9cf43e79..33300e0c 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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"..\..\gen\build\libsynergy.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\libsynergy.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\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"..\..\gen\debug\libsynergy.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\libsynergy.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -91,6 +91,10 @@ SOURCE=.\CClipboard.cpp # End Source File # Begin Source File +SOURCE=.\CKeyMap.cpp +# End Source File +# Begin Source File + SOURCE=.\CKeyState.cpp # End Source File # Begin Source File @@ -127,6 +131,14 @@ SOURCE=.\IScreen.cpp # End Source File # Begin Source File +SOURCE=.\KeyTypes.cpp +# End Source File +# Begin Source File + +SOURCE=.\ProtocolTypes.cpp +# End Source File +# Begin Source File + SOURCE=.\XScreen.cpp # End Source File # Begin Source File @@ -143,6 +155,10 @@ SOURCE=.\CClipboard.h # End Source File # Begin Source File +SOURCE=.\CKeyMap.h +# End Source File +# Begin Source File + SOURCE=.\CKeyState.h # End Source File # Begin Source File diff --git a/synergy.dsw b/synergy.dsw index 225c09ed..455df6a7 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -126,9 +126,15 @@ Package=<4> Project_Dep_Name base End Project Dependency Begin Project Dependency + Project_Dep_Name exec + End Project Dependency + Begin Project Dependency Project_Dep_Name io End Project Dependency Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency Project_Dep_Name mt End Project Dependency Begin Project Dependency @@ -140,9 +146,6 @@ Package=<4> Begin Project Dependency Project_Dep_Name server End Project Dependency - Begin Project Dependency - Project_Dep_Name exec - End Project Dependency }}} ############################################################################### diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj index ae2d35d4..75cd3385 100644 --- a/synergy.xcode/project.pbxproj +++ b/synergy.xcode/project.pbxproj @@ -3,11 +3,394 @@ archiveVersion = 1; classes = { }; - objectVersion = 39; + objectVersion = 42; objects = { - 014CEA460018CE2711CA2923 = { - buildRules = ( + +/* Begin PBXAggregateTarget section */ + 4CD033E80650965F00525ED1 /* ALL */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 68FBBB18089F06C20016DF44 /* Build configuration list for PBXAggregateTarget "ALL" */; + buildPhases = ( ); + buildSettings = { + PRODUCT_NAME = ALL; + }; + dependencies = ( + 4CD033EA0650968500525ED1 /* PBXTargetDependency */, + 4CD033EC0650968500525ED1 /* PBXTargetDependency */, + ); + name = ALL; + productName = ALL; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 4C537F14064E293000D3815C /* COSXServerTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */; }; + 4C537F15064E293000D3815C /* COSXServerTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */; }; + 4C537F16064E293000D3815C /* CServerTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */; }; + 4C537F17064E293000D3815C /* CServerTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */; }; + 4C537F18064E293000D3815C /* synergys.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F13064E293000D3815C /* synergys.cpp */; }; + 4C537F44064E2A0F00D3815C /* CClientListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F32064E2A0F00D3815C /* CClientListener.cpp */; }; + 4C537F45064E2A0F00D3815C /* CClientListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F33064E2A0F00D3815C /* CClientListener.h */; }; + 4C537F46064E2A0F00D3815C /* CClientProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */; }; + 4C537F47064E2A0F00D3815C /* CClientProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F35064E2A0F00D3815C /* CClientProxy.h */; }; + 4C537F48064E2A0F00D3815C /* CClientProxy1_0.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */; }; + 4C537F49064E2A0F00D3815C /* CClientProxy1_0.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */; }; + 4C537F4A064E2A0F00D3815C /* CClientProxy1_1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */; }; + 4C537F4B064E2A0F00D3815C /* CClientProxy1_1.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */; }; + 4C537F4C064E2A0F00D3815C /* CClientProxy1_2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */; }; + 4C537F4D064E2A0F00D3815C /* CClientProxy1_2.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */; }; + 4C537F4E064E2A0F00D3815C /* CClientProxyUnknown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */; }; + 4C537F4F064E2A0F00D3815C /* CClientProxyUnknown.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */; }; + 4C537F50064E2A0F00D3815C /* CConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3E064E2A0F00D3815C /* CConfig.cpp */; }; + 4C537F51064E2A0F00D3815C /* CConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3F064E2A0F00D3815C /* CConfig.h */; }; + 4C537F52064E2A0F00D3815C /* CPrimaryClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */; }; + 4C537F53064E2A0F00D3815C /* CPrimaryClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */; }; + 4C537F54064E2A0F00D3815C /* CServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F42064E2A0F00D3815C /* CServer.cpp */; }; + 4C537F55064E2A0F00D3815C /* CServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F43064E2A0F00D3815C /* CServer.h */; }; + 4C537F5B064E2B4200D3815C /* libcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C5E868D0648C2ED003C637B /* libcommon.a */; }; + 4C537F5C064E2B4200D3815C /* libarch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437B5063E443800969041 /* libarch.a */; }; + 4C537F5D064E2B4200D3815C /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437D1063E44C200969041 /* libbase.a */; }; + 4C537F5E064E2B4200D3815C /* libmt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43801063E45F200969041 /* libmt.a */; }; + 4C537F5F064E2B4200D3815C /* libio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43824063E46AB00969041 /* libio.a */; }; + 4C537F60064E2B4200D3815C /* libnet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43848063E475800969041 /* libnet.a */; }; + 4C537F61064E2B4200D3815C /* libserver.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C537F31064E29F800D3815C /* libserver.a */; }; + 4C537F62064E2B4200D3815C /* libsynergy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB4386E063E47F800969041 /* libsynergy.a */; }; + 4C537F63064E2B4200D3815C /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB438B1063E488600969041 /* libplatform.a */; }; + 4C537F64064E2B4B00D3815C /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43923063E4B1300969041 /* Carbon.framework */; }; + 4C5E86A90648C412003C637B /* CArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86920648C412003C637B /* CArch.cpp */; }; + 4C5E86AA0648C412003C637B /* CArch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86930648C412003C637B /* CArch.h */; }; + 4C5E86AB0648C412003C637B /* CArchConsoleUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */; }; + 4C5E86AC0648C412003C637B /* CArchConsoleUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86950648C412003C637B /* CArchConsoleUnix.h */; }; + 4C5E86AD0648C412003C637B /* CArchDaemonUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */; }; + 4C5E86AE0648C412003C637B /* CArchDaemonUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86970648C412003C637B /* CArchDaemonUnix.h */; }; + 4C5E86AF0648C412003C637B /* CArchFileUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86980648C412003C637B /* CArchFileUnix.cpp */; }; + 4C5E86B00648C412003C637B /* CArchFileUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86990648C412003C637B /* CArchFileUnix.h */; }; + 4C5E86B10648C412003C637B /* CArchLogUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */; }; + 4C5E86B20648C412003C637B /* CArchLogUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869B0648C412003C637B /* CArchLogUnix.h */; }; + 4C5E86B30648C412003C637B /* CArchMultithreadPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */; }; + 4C5E86B40648C412003C637B /* CArchMultithreadPosix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */; }; + 4C5E86B50648C412003C637B /* CArchNetworkBSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */; }; + 4C5E86B60648C412003C637B /* CArchNetworkBSD.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */; }; + 4C5E86B70648C412003C637B /* CArchSleepUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */; }; + 4C5E86B80648C412003C637B /* CArchSleepUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A10648C412003C637B /* CArchSleepUnix.h */; }; + 4C5E86B90648C412003C637B /* CArchStringUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */; }; + 4C5E86BA0648C412003C637B /* CArchStringUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A30648C412003C637B /* CArchStringUnix.h */; }; + 4C5E86BB0648C412003C637B /* CArchTimeUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */; }; + 4C5E86BC0648C412003C637B /* CArchTimeUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A50648C412003C637B /* CArchTimeUnix.h */; }; + 4C5E86BE0648C412003C637B /* XArchUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A70648C412003C637B /* XArchUnix.cpp */; }; + 4C5E86BF0648C412003C637B /* XArchUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A80648C412003C637B /* XArchUnix.h */; }; + 4C5E86C30648C653003C637B /* CArchDaemonNone.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86C10648C653003C637B /* CArchDaemonNone.h */; }; + 4C5E86CC0648C726003C637B /* Version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CB0648C725003C637B /* Version.cpp */; }; + 4C5E86ED0648C7B9003C637B /* CEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CD0648C7B9003C637B /* CEvent.cpp */; }; + 4C5E86EE0648C7B9003C637B /* CEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86CE0648C7B9003C637B /* CEvent.h */; }; + 4C5E86EF0648C7B9003C637B /* CEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */; }; + 4C5E86F00648C7B9003C637B /* CEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D00648C7B9003C637B /* CEventQueue.h */; }; + 4C5E86F10648C7B9003C637B /* CFunctionEventJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */; }; + 4C5E86F20648C7B9003C637B /* CFunctionEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */; }; + 4C5E86F30648C7B9003C637B /* CFunctionJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */; }; + 4C5E86F40648C7B9003C637B /* CFunctionJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D40648C7B9003C637B /* CFunctionJob.h */; }; + 4C5E86F50648C7B9003C637B /* CLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D50648C7B9003C637B /* CLog.cpp */; }; + 4C5E86F60648C7B9003C637B /* CLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D60648C7B9003C637B /* CLog.h */; }; + 4C5E86F70648C7B9003C637B /* CPriorityQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */; }; + 4C5E86F80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */; }; + 4C5E86F90648C7B9003C637B /* CSimpleEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */; }; + 4C5E86FA0648C7B9003C637B /* CStopwatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */; }; + 4C5E86FB0648C7B9003C637B /* CStopwatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DB0648C7B9003C637B /* CStopwatch.h */; }; + 4C5E86FC0648C7B9003C637B /* CString.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DC0648C7B9003C637B /* CString.h */; }; + 4C5E86FD0648C7B9003C637B /* CStringUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */; }; + 4C5E86FE0648C7B9003C637B /* CStringUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DE0648C7B9003C637B /* CStringUtil.h */; }; + 4C5E86FF0648C7B9003C637B /* CUnicode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */; }; + 4C5E87000648C7B9003C637B /* CUnicode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E00648C7B9003C637B /* CUnicode.h */; }; + 4C5E87010648C7B9003C637B /* IEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E10648C7B9003C637B /* IEventJob.h */; }; + 4C5E87020648C7B9003C637B /* IEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */; }; + 4C5E87030648C7B9003C637B /* IEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E30648C7B9003C637B /* IEventQueue.h */; }; + 4C5E87040648C7B9003C637B /* IEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */; }; + 4C5E87050648C7B9003C637B /* IJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E50648C7B9003C637B /* IJob.h */; }; + 4C5E87060648C7B9003C637B /* ILogOutputter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E60648C7B9003C637B /* ILogOutputter.h */; }; + 4C5E87070648C7B9003C637B /* LogOutputters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */; }; + 4C5E87080648C7B9003C637B /* LogOutputters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E80648C7B9003C637B /* LogOutputters.h */; }; + 4C5E87090648C7B9003C637B /* TMethodEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */; }; + 4C5E870A0648C7B9003C637B /* TMethodJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86EA0648C7B9003C637B /* TMethodJob.h */; }; + 4C5E870B0648C7B9003C637B /* XBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86EB0648C7B9003C637B /* XBase.cpp */; }; + 4C5E870C0648C7B9003C637B /* XBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86EC0648C7B9003C637B /* XBase.h */; }; + 4C5E87180648C809003C637B /* CCondVar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E870D0648C809003C637B /* CCondVar.cpp */; }; + 4C5E87190648C809003C637B /* CCondVar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E870E0648C809003C637B /* CCondVar.h */; }; + 4C5E871A0648C809003C637B /* CLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E870F0648C809003C637B /* CLock.cpp */; }; + 4C5E871B0648C809003C637B /* CLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87100648C809003C637B /* CLock.h */; }; + 4C5E871C0648C809003C637B /* CMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87110648C809003C637B /* CMutex.cpp */; }; + 4C5E871D0648C809003C637B /* CMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87120648C809003C637B /* CMutex.h */; }; + 4C5E871E0648C809003C637B /* CThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87130648C809003C637B /* CThread.cpp */; }; + 4C5E871F0648C809003C637B /* CThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87140648C809003C637B /* CThread.h */; }; + 4C5E87200648C809003C637B /* XMT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87150648C809003C637B /* XMT.cpp */; }; + 4C5E87210648C809003C637B /* XMT.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87160648C809003C637B /* XMT.h */; }; + 4C5E87220648C809003C637B /* XThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87170648C809003C637B /* XThread.h */; }; + 4C5E872C0648C83C003C637B /* CStreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */; }; + 4C5E872D0648C83C003C637B /* CStreamBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87240648C83C003C637B /* CStreamBuffer.h */; }; + 4C5E872E0648C83C003C637B /* CStreamFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87250648C83C003C637B /* CStreamFilter.cpp */; }; + 4C5E872F0648C83C003C637B /* CStreamFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87260648C83C003C637B /* CStreamFilter.h */; }; + 4C5E87300648C83C003C637B /* IStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87270648C83C003C637B /* IStream.cpp */; }; + 4C5E87310648C83C003C637B /* IStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87280648C83C003C637B /* IStream.h */; }; + 4C5E87320648C83C003C637B /* IStreamFilterFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */; }; + 4C5E87330648C83C003C637B /* XIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E872A0648C83C003C637B /* XIO.cpp */; }; + 4C5E87340648C83C003C637B /* XIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E872B0648C83C003C637B /* XIO.h */; }; + 4C5E874A0648C870003C637B /* CNetworkAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87350648C870003C637B /* CNetworkAddress.cpp */; }; + 4C5E874B0648C870003C637B /* CNetworkAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87360648C870003C637B /* CNetworkAddress.h */; }; + 4C5E874C0648C870003C637B /* CSocketMultiplexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */; }; + 4C5E874D0648C870003C637B /* CSocketMultiplexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87380648C870003C637B /* CSocketMultiplexer.h */; }; + 4C5E874E0648C870003C637B /* CTCPListenSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */; }; + 4C5E874F0648C870003C637B /* CTCPListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873A0648C870003C637B /* CTCPListenSocket.h */; }; + 4C5E87500648C870003C637B /* CTCPSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873B0648C870003C637B /* CTCPSocket.cpp */; }; + 4C5E87510648C870003C637B /* CTCPSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873C0648C870003C637B /* CTCPSocket.h */; }; + 4C5E87520648C870003C637B /* CTCPSocketFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */; }; + 4C5E87530648C870003C637B /* CTCPSocketFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */; }; + 4C5E87540648C870003C637B /* IDataSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873F0648C870003C637B /* IDataSocket.cpp */; }; + 4C5E87550648C870003C637B /* IDataSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87400648C870003C637B /* IDataSocket.h */; }; + 4C5E87560648C870003C637B /* IListenSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87410648C870003C637B /* IListenSocket.cpp */; }; + 4C5E87570648C870003C637B /* IListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87420648C870003C637B /* IListenSocket.h */; }; + 4C5E87580648C870003C637B /* ISocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87430648C870003C637B /* ISocket.cpp */; }; + 4C5E87590648C870003C637B /* ISocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87440648C870003C637B /* ISocket.h */; }; + 4C5E875A0648C870003C637B /* ISocketFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87450648C870003C637B /* ISocketFactory.h */; }; + 4C5E875B0648C870003C637B /* ISocketMultiplexerJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */; }; + 4C5E875C0648C870003C637B /* TSocketMultiplexerMethodJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */; }; + 4C5E875D0648C870003C637B /* XSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87480648C870003C637B /* XSocket.cpp */; }; + 4C5E875E0648C870003C637B /* XSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87490648C870003C637B /* XSocket.h */; }; + 4C5E87800648C8BD003C637B /* CClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E875F0648C8BD003C637B /* CClipboard.cpp */; }; + 4C5E87810648C8BD003C637B /* CClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87600648C8BD003C637B /* CClipboard.h */; }; + 4C5E87820648C8BD003C637B /* CKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87610648C8BD003C637B /* CKeyState.cpp */; }; + 4C5E87830648C8BD003C637B /* CKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87620648C8BD003C637B /* CKeyState.h */; }; + 4C5E87840648C8BD003C637B /* ClipboardTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87630648C8BD003C637B /* ClipboardTypes.h */; }; + 4C5E87850648C8BD003C637B /* CPacketStreamFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */; }; + 4C5E87860648C8BD003C637B /* CPacketStreamFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */; }; + 4C5E87870648C8BD003C637B /* CPlatformScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */; }; + 4C5E87880648C8BD003C637B /* CPlatformScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87670648C8BD003C637B /* CPlatformScreen.h */; }; + 4C5E87890648C8BD003C637B /* CProtocolUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */; }; + 4C5E878A0648C8BD003C637B /* CProtocolUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87690648C8BD003C637B /* CProtocolUtil.h */; }; + 4C5E878B0648C8BD003C637B /* CScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876A0648C8BD003C637B /* CScreen.cpp */; }; + 4C5E878C0648C8BD003C637B /* CScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876B0648C8BD003C637B /* CScreen.h */; }; + 4C5E878D0648C8BD003C637B /* IClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876C0648C8BD003C637B /* IClient.h */; }; + 4C5E878E0648C8BD003C637B /* IClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876D0648C8BD003C637B /* IClipboard.cpp */; }; + 4C5E878F0648C8BD003C637B /* IClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876E0648C8BD003C637B /* IClipboard.h */; }; + 4C5E87900648C8BD003C637B /* IKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876F0648C8BD003C637B /* IKeyState.cpp */; }; + 4C5E87910648C8BD003C637B /* IKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87700648C8BD003C637B /* IKeyState.h */; }; + 4C5E87920648C8BD003C637B /* IPlatformScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87710648C8BD003C637B /* IPlatformScreen.h */; }; + 4C5E87930648C8BD003C637B /* IPrimaryScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */; }; + 4C5E87940648C8BD003C637B /* IPrimaryScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */; }; + 4C5E87950648C8BD003C637B /* IScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87740648C8BD003C637B /* IScreen.cpp */; }; + 4C5E87960648C8BD003C637B /* IScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87750648C8BD003C637B /* IScreen.h */; }; + 4C5E87970648C8BD003C637B /* IScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87760648C8BD003C637B /* IScreenSaver.h */; }; + 4C5E87980648C8BD003C637B /* ISecondaryScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */; }; + 4C5E87990648C8BD003C637B /* KeyTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87780648C8BD003C637B /* KeyTypes.h */; }; + 4C5E879A0648C8BD003C637B /* MouseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87790648C8BD003C637B /* MouseTypes.h */; }; + 4C5E879B0648C8BD003C637B /* OptionTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877A0648C8BD003C637B /* OptionTypes.h */; }; + 4C5E879C0648C8BD003C637B /* ProtocolTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */; }; + 4C5E879D0648C8BD003C637B /* XScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E877C0648C8BD003C637B /* XScreen.cpp */; }; + 4C5E879E0648C8BD003C637B /* XScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877D0648C8BD003C637B /* XScreen.h */; }; + 4C5E879F0648C8BD003C637B /* XSynergy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E877E0648C8BD003C637B /* XSynergy.cpp */; }; + 4C5E87A00648C8BD003C637B /* XSynergy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877F0648C8BD003C637B /* XSynergy.h */; }; + 4C5E87AD0648C913003C637B /* COSXClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A10648C913003C637B /* COSXClipboard.cpp */; }; + 4C5E87AE0648C913003C637B /* COSXClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A20648C913003C637B /* COSXClipboard.h */; }; + 4C5E87AF0648C913003C637B /* COSXEventQueueBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */; }; + 4C5E87B00648C913003C637B /* COSXEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */; }; + 4C5E87B10648C913003C637B /* COSXKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A50648C913003C637B /* COSXKeyState.cpp */; }; + 4C5E87B20648C913003C637B /* COSXKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A60648C913003C637B /* COSXKeyState.h */; }; + 4C5E87B30648C913003C637B /* COSXScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A70648C913003C637B /* COSXScreen.cpp */; }; + 4C5E87B40648C913003C637B /* COSXScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A80648C913003C637B /* COSXScreen.h */; }; + 4C5E87BD0648C969003C637B /* CClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87B90648C969003C637B /* CClient.cpp */; }; + 4C5E87BE0648C969003C637B /* CClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87BA0648C969003C637B /* CClient.h */; }; + 4C5E87BF0648C969003C637B /* CServerProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87BB0648C969003C637B /* CServerProxy.cpp */; }; + 4C5E87C00648C969003C637B /* CServerProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87BC0648C969003C637B /* CServerProxy.h */; }; + 4C5E87C60648C9D2003C637B /* CClientTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */; }; + 4C5E87C70648C9D2003C637B /* CClientTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */; }; + 4C5E87C80648C9D2003C637B /* COSXClientTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */; }; + 4C5E87C90648C9D2003C637B /* COSXClientTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */; }; + 4C5E87CA0648C9D2003C637B /* synergyc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C50648C9D2003C637B /* synergyc.cpp */; }; + 4C5E87CF0648CA4B003C637B /* libcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C5E868D0648C2ED003C637B /* libcommon.a */; }; + 4C5E87D10648CA75003C637B /* XArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87D00648CA75003C637B /* XArch.cpp */; }; + 4C7D7CDA066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDB066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDC066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDD066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDE066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDF066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE0066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE1066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE2066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE3066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE4066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE5066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4CB43934063E4B4A00969041 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43923063E4B1300969041 /* Carbon.framework */; }; + 4CD034950650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */; }; + 4CD034960650B6F100525ED1 /* COSXClipboardAnyTextConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */; }; + 4CD034970650B6F100525ED1 /* COSXClipboardTextConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */; }; + 4CD034980650B6F100525ED1 /* COSXClipboardTextConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */; }; + 4CD034990650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */; }; + 4CD0349A0650B6F100525ED1 /* COSXClipboardUTF16Converter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */; }; + 6828A1AA081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */; }; + 6828A1AB081DF7AB003D9989 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A8081DF7AB003D9989 /* Foundation.framework */; }; + 6828A1AC081DF7AB003D9989 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A9081DF7AB003D9989 /* IOKit.framework */; }; + 6828A1AD081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */; }; + 6828A1AE081DF7AB003D9989 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A8081DF7AB003D9989 /* Foundation.framework */; }; + 6828A1AF081DF7AB003D9989 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A9081DF7AB003D9989 /* IOKit.framework */; }; + 6828A1C2081DF9EB003D9989 /* OSXScreenSaverControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */; }; + 6828A238081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A237081DFAF9003D9989 /* ScreenSaver.framework */; }; + 6828A239081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A237081DFAF9003D9989 /* ScreenSaver.framework */; }; + 684B63620996FB2800FE7CC7 /* CClientProxy1_3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */; }; + 684B63630996FB2800FE7CC7 /* CClientProxy1_3.h in Headers */ = {isa = PBXBuildFile; fileRef = 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */; }; + 68871685073EBF7000C5ABE7 /* CArchSystemUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */; }; + 68871686073EBF7000C5ABE7 /* CArchSystemUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */; }; + 68871687073EBF7000C5ABE7 /* IArchConsole.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871678073EBF6F00C5ABE7 /* IArchConsole.h */; }; + 68871688073EBF7000C5ABE7 /* IArchDaemon.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */; }; + 68871689073EBF7000C5ABE7 /* IArchFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167A073EBF6F00C5ABE7 /* IArchFile.h */; }; + 6887168A073EBF7000C5ABE7 /* IArchLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167B073EBF6F00C5ABE7 /* IArchLog.h */; }; + 6887168B073EBF7000C5ABE7 /* IArchMultithread.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */; }; + 6887168C073EBF7000C5ABE7 /* IArchNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */; }; + 6887168D073EBF7000C5ABE7 /* IArchSleep.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */; }; + 6887168E073EBF7000C5ABE7 /* IArchString.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167F073EBF6F00C5ABE7 /* IArchString.h */; }; + 6887168F073EBF7000C5ABE7 /* IArchSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871680073EBF6F00C5ABE7 /* IArchSystem.h */; }; + 68871690073EBF7000C5ABE7 /* IArchTaskBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */; }; + 68871691073EBF7000C5ABE7 /* IArchTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */; }; + 68871692073EBF7000C5ABE7 /* IArchTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871683073EBF7000C5ABE7 /* IArchTime.h */; }; + 68871693073EBF7000C5ABE7 /* XArch.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871684073EBF7000C5ABE7 /* XArch.h */; }; + 6887169B073EC03800C5ABE7 /* MacOSXPrecomp.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */; }; + 688925A909DF64B6002EB18C /* CBaseClientProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */; }; + 688925AA09DF64B6002EB18C /* CBaseClientProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 688925A809DF64B6002EB18C /* CBaseClientProxy.h */; }; + 68968F5D073EC484004B2F9B /* CArchDaemonNone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */; }; + 68968F5E073EC484004B2F9B /* CArchTaskBarXWindows.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */; }; + 68968F5F073EC484004B2F9B /* CArchTaskBarXWindows.h in Headers */ = {isa = PBXBuildFile; fileRef = 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */; }; + 689D73340956490500FFAB1D /* CInputFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 689D73320956490500FFAB1D /* CInputFilter.cpp */; }; + 689D73350956490500FFAB1D /* CInputFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 689D73330956490500FFAB1D /* CInputFilter.h */; }; + 689D7339095649AF00FFAB1D /* CKeyMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 689D7337095649AF00FFAB1D /* CKeyMap.cpp */; }; + 689D733A095649AF00FFAB1D /* CKeyMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 689D7338095649AF00FFAB1D /* CKeyMap.h */; }; + 68D87A6509A00D8800856835 /* KeyTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D87A6409A00D8800856835 /* KeyTypes.cpp */; }; + 68FBBB21089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; + 68FBBB22089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; + 68FBBB24089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; + 68FBBB25089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; + 68FBBB28089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; + 68FBBB29089F072D0016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; + 68FBBB2A089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; + 68FBBB2B089F072D0016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; + 68FBBB2C089F07810016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; + 68FBBB2D089F07920016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; + 68FBBB2E089F07970016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; + 68FBBB2F089F079B0016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; + 68FBBB5B089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; + 68FBBB5C089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; + 68FBBB5D089F1BCE0016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; + CAB4475F081E367700724B8D /* libclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43778063E406A00969041 /* libclient.a */; }; + CAB44760081E368100724B8D /* libarch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437B5063E443800969041 /* libarch.a */; }; + CAB44761081E368200724B8D /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437D1063E44C200969041 /* libbase.a */; }; + CAB44762081E368300724B8D /* libclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43778063E406A00969041 /* libclient.a */; }; + CAB44764081E368700724B8D /* libio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43824063E46AB00969041 /* libio.a */; }; + CAB44765081E368800724B8D /* libmt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43801063E45F200969041 /* libmt.a */; }; + CAB44766081E368A00724B8D /* libnet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43848063E475800969041 /* libnet.a */; }; + CAB44767081E368B00724B8D /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB438B1063E488600969041 /* libplatform.a */; }; + CAB44768081E368D00724B8D /* libsynergy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB4386E063E47F800969041 /* libsynergy.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 68FBBB32089F07E40016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB37089F08160016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB3A089F08200016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB3D089F082E0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB3E089F084C0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB41089F08590016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB44089F08620016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB47089F086B0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB4A089F08750016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB4D089F087F0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB50089F08890016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB53089F08940016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXBuildStyle section */ + 014CEA460018CE2711CA2923 /* Development */ = { + isa = PBXBuildStyle; buildSettings = { COPY_PHASE_STRIP = NO; DEBUGGING_SYMBOLS = YES; @@ -15,4862 +398,2485 @@ GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; + MACOSX_DEPLOYMENT_TARGET = 10.2; OPTIMIZATION_CFLAGS = "-O0"; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; ZERO_LINK = NO; }; - isa = PBXBuildStyle; name = Development; }; - 014CEA470018CE2711CA2923 = { - buildRules = ( - ); + 014CEA470018CE2711CA2923 /* Deployment */ = { + isa = PBXBuildStyle; buildSettings = { COPY_PHASE_STRIP = YES; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + MACOSX_DEPLOYMENT_TARGET = 10.2; + SYMROOT = gen/build; ZERO_LINK = NO; }; - isa = PBXBuildStyle; name = Deployment; }; -//010 -//011 -//012 -//013 -//014 -//020 -//021 -//022 -//023 -//024 - 0249A662FF388D9811CA2CEA = { - children = ( - 4CDF3892063E561B007B20A1, - 4CDF3893063E561B007B20A1, - 4CDF3894063E561B007B20A1, - 4CDF3895063E561B007B20A1, - 4CDF3896063E561B007B20A1, - 4CDF3897063E561B007B20A1, - 4CDF3898063E561B007B20A1, - 4CDF3899063E561B007B20A1, - 4CB43923063E4B1300969041, - ); - isa = PBXGroup; - name = "External Frameworks and Libraries"; - refType = 4; - sourceTree = ""; - }; -//020 -//021 -//022 -//023 -//024 -//080 -//081 -//082 -//083 -//084 - 08FB7793FE84155DC02AAC07 = { - buildSettings = { - }; - buildStyles = ( - 014CEA460018CE2711CA2923, - 014CEA470018CE2711CA2923, - ); - hasScannedForEncodings = 1; - isa = PBXProject; - mainGroup = 08FB7794FE84155DC02AAC07; - projectDirPath = ""; - targets = ( - 4CD033E80650965F00525ED1, - 4CB437B4063E443800969041, - 4C5E868C0648C2ED003C637B, - 4CB437D0063E44C200969041, - 4CB43800063E45F200969041, - 4CB43823063E46AB00969041, - 4CB43847063E475800969041, - 4CB4386D063E47F800969041, - 4CB438B0063E488600969041, - 4CB43777063E406A00969041, - 4C537F30064E29F800D3815C, - 4CB43913063E497700969041, - 4C537F0C064E286700D3815C, - ); - }; - 08FB7794FE84155DC02AAC07 = { - children = ( - 08FB7795FE84155DC02AAC07, - 0249A662FF388D9811CA2CEA, - 1AB674ADFE9D54B511CA2CBB, - ); - isa = PBXGroup; - name = synergy; - refType = 4; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 = { - children = ( - 4CB4378B063E432C00969041, - 4CB437D2063E44E400969041, - 4CB43779063E40B600969041, - 4C5E86CA0648C6FB003C637B, - 4CB4381E063E469300969041, - 4CB437FB063E45D700969041, - 4CB43849063E478900969041, - 4CB438AD063E487200969041, - 4C537F2B064E29C900D3815C, - 4CB43866063E47C800969041, - 4CB43916063E4A1A00969041, - 4C537F07064E283300D3815C, - ); - isa = PBXGroup; - name = Source; - refType = 4; - sourceTree = ""; - }; -//080 -//081 -//082 -//083 -//084 -//1A0 -//1A1 -//1A2 -//1A3 -//1A4 - 1AB674ADFE9D54B511CA2CBB = { - children = ( - 4CB43778063E406A00969041, - 4C5E868D0648C2ED003C637B, - 4CB437B5063E443800969041, - 4CB437D1063E44C200969041, - 4CB43801063E45F200969041, - 4CB43824063E46AB00969041, - 4CB43848063E475800969041, - 4C537F31064E29F800D3815C, - 4CB4386E063E47F800969041, - 4CB438B1063E488600969041, - 4CB43914063E497700969041, - 4C537F0D064E286700D3815C, - ); - isa = PBXGroup; - name = Products; - refType = 4; - sourceTree = ""; - }; -//1A0 -//1A1 -//1A2 -//1A3 -//1A4 -//4C0 -//4C1 -//4C2 -//4C3 -//4C4 - 4C537F07064E283300D3815C = { - children = ( - 4C537F0F064E293000D3815C, - 4C537F10064E293000D3815C, - 4C537F11064E293000D3815C, - 4C537F12064E293000D3815C, - 4C537F13064E293000D3815C, - ); - isa = PBXGroup; - name = SynergyS; - path = ""; - refType = 4; - sourceTree = ""; - }; - 4C537F0A064E286700D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F15064E293000D3815C, - 4C537F17064E293000D3815C, - 4C7D7CDA066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F0B064E286700D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F14064E293000D3815C, - 4C537F16064E293000D3815C, - 4C537F18064E293000D3815C, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F0C064E286700D3815C = { - buildPhases = ( - 4C537F0A064E286700D3815C, - 4C537F0B064E286700D3815C, - 4C537F5A064E2B0700D3815C, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; - INSTALL_PATH = /usr/local/bin; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = synergys; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - 4C537F1A064E298800D3815C, - 4C537F1C064E298800D3815C, - 4C537F1E064E298800D3815C, - 4C537F20064E298800D3815C, - 4C537F22064E298800D3815C, - 4C537F24064E298800D3815C, - 4C537F26064E298800D3815C, - 4C537F28064E298800D3815C, - 4C537F57064E2AA300D3815C, - ); - isa = PBXNativeTarget; - name = synergys; - productName = synergys; - productReference = 4C537F0D064E286700D3815C; - productType = "com.apple.product-type.tool"; - }; - 4C537F0D064E286700D3815C = { - explicitFileType = "compiled.mach-o.executable"; - includeInIndex = 0; - isa = PBXFileReference; - path = synergys; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4C537F0F064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXServerTaskBarReceiver.cpp; - path = cmd/synergys/COSXServerTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F10064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXServerTaskBarReceiver.h; - path = cmd/synergys/COSXServerTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C537F11064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CServerTaskBarReceiver.cpp; - path = cmd/synergys/CServerTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F12064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CServerTaskBarReceiver.h; - path = cmd/synergys/CServerTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C537F13064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = synergys.cpp; - path = cmd/synergys/synergys.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F14064E293000D3815C = { - fileRef = 4C537F0F064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F15064E293000D3815C = { - fileRef = 4C537F10064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F16064E293000D3815C = { - fileRef = 4C537F11064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F17064E293000D3815C = { - fileRef = 4C537F12064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F18064E293000D3815C = { - fileRef = 4C537F13064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F19064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; +/* End PBXBuildStyle section */ + +/* Begin PBXContainerItemProxy section */ + 4C537F19064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437B4063E443800969041; remoteInfo = arch; }; - 4C537F1A064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB437B4063E443800969041; - targetProxy = 4C537F19064E298800D3815C; - }; - 4C537F1B064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F1B064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C5E868C0648C2ED003C637B; remoteInfo = common; }; - 4C537F1C064E298800D3815C = { - isa = PBXTargetDependency; - target = 4C5E868C0648C2ED003C637B; - targetProxy = 4C537F1B064E298800D3815C; - }; - 4C537F1D064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F1D064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437D0063E44C200969041; remoteInfo = base; }; - 4C537F1E064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB437D0063E44C200969041; - targetProxy = 4C537F1D064E298800D3815C; - }; - 4C537F1F064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F1F064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43800063E45F200969041; remoteInfo = mt; }; - 4C537F20064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB43800063E45F200969041; - targetProxy = 4C537F1F064E298800D3815C; - }; - 4C537F21064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F21064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43823063E46AB00969041; remoteInfo = io; }; - 4C537F22064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB43823063E46AB00969041; - targetProxy = 4C537F21064E298800D3815C; - }; - 4C537F23064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F23064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43847063E475800969041; remoteInfo = net; }; - 4C537F24064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB43847063E475800969041; - targetProxy = 4C537F23064E298800D3815C; - }; - 4C537F25064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F25064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB4386D063E47F800969041; remoteInfo = synergy; }; - 4C537F26064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB4386D063E47F800969041; - targetProxy = 4C537F25064E298800D3815C; - }; - 4C537F27064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F27064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB438B0063E488600969041; remoteInfo = platform; }; - 4C537F28064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB438B0063E488600969041; - targetProxy = 4C537F27064E298800D3815C; - }; - 4C537F2B064E29C900D3815C = { - children = ( - 4C537F32064E2A0F00D3815C, - 4C537F33064E2A0F00D3815C, - 4C537F34064E2A0F00D3815C, - 4C537F35064E2A0F00D3815C, - 4C537F36064E2A0F00D3815C, - 4C537F37064E2A0F00D3815C, - 4C537F38064E2A0F00D3815C, - 4C537F39064E2A0F00D3815C, - 4C537F3A064E2A0F00D3815C, - 4C537F3B064E2A0F00D3815C, - 4C537F3C064E2A0F00D3815C, - 4C537F3D064E2A0F00D3815C, - 4C537F3E064E2A0F00D3815C, - 4C537F3F064E2A0F00D3815C, - 4C537F40064E2A0F00D3815C, - 4C537F41064E2A0F00D3815C, - 4C537F42064E2A0F00D3815C, - 4C537F43064E2A0F00D3815C, - ); - isa = PBXGroup; - name = Server; - refType = 4; - sourceTree = ""; - }; - 4C537F2E064E29F800D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F45064E2A0F00D3815C, - 4C537F47064E2A0F00D3815C, - 4C537F49064E2A0F00D3815C, - 4C537F4B064E2A0F00D3815C, - 4C537F4D064E2A0F00D3815C, - 4C537F4F064E2A0F00D3815C, - 4C537F51064E2A0F00D3815C, - 4C537F53064E2A0F00D3815C, - 4C537F55064E2A0F00D3815C, - 4C7D7CE4066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F2F064E29F800D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F44064E2A0F00D3815C, - 4C537F46064E2A0F00D3815C, - 4C537F48064E2A0F00D3815C, - 4C537F4A064E2A0F00D3815C, - 4C537F4C064E2A0F00D3815C, - 4C537F4E064E2A0F00D3815C, - 4C537F50064E2A0F00D3815C, - 4C537F52064E2A0F00D3815C, - 4C537F54064E2A0F00D3815C, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F30064E29F800D3815C = { - buildPhases = ( - 4C537F2E064E29F800D3815C, - 4C537F2F064E29F800D3815C, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = server; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = server; - productName = server; - productReference = 4C537F31064E29F800D3815C; - productType = "com.apple.product-type.library.static"; - }; - 4C537F31064E29F800D3815C = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libserver.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4C537F32064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientListener.cpp; - path = lib/server/CClientListener.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F33064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientListener.h; - path = lib/server/CClientListener.h; - refType = 4; - sourceTree = ""; - }; - 4C537F34064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy.cpp; - path = lib/server/CClientProxy.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F35064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy.h; - path = lib/server/CClientProxy.h; - refType = 4; - sourceTree = ""; - }; - 4C537F36064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy1_0.cpp; - path = lib/server/CClientProxy1_0.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F37064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy1_0.h; - path = lib/server/CClientProxy1_0.h; - refType = 4; - sourceTree = ""; - }; - 4C537F38064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy1_1.cpp; - path = lib/server/CClientProxy1_1.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F39064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy1_1.h; - path = lib/server/CClientProxy1_1.h; - refType = 4; - sourceTree = ""; - }; - 4C537F3A064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy1_2.cpp; - path = lib/server/CClientProxy1_2.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F3B064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy1_2.h; - path = lib/server/CClientProxy1_2.h; - refType = 4; - sourceTree = ""; - }; - 4C537F3C064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxyUnknown.cpp; - path = lib/server/CClientProxyUnknown.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F3D064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxyUnknown.h; - path = lib/server/CClientProxyUnknown.h; - refType = 4; - sourceTree = ""; - }; - 4C537F3E064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CConfig.cpp; - path = lib/server/CConfig.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F3F064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CConfig.h; - path = lib/server/CConfig.h; - refType = 4; - sourceTree = ""; - }; - 4C537F40064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CPrimaryClient.cpp; - path = lib/server/CPrimaryClient.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F41064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPrimaryClient.h; - path = lib/server/CPrimaryClient.h; - refType = 4; - sourceTree = ""; - }; - 4C537F42064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CServer.cpp; - path = lib/server/CServer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F43064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CServer.h; - path = lib/server/CServer.h; - refType = 4; - sourceTree = ""; - }; - 4C537F44064E2A0F00D3815C = { - fileRef = 4C537F32064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F45064E2A0F00D3815C = { - fileRef = 4C537F33064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F46064E2A0F00D3815C = { - fileRef = 4C537F34064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F47064E2A0F00D3815C = { - fileRef = 4C537F35064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F48064E2A0F00D3815C = { - fileRef = 4C537F36064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F49064E2A0F00D3815C = { - fileRef = 4C537F37064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4A064E2A0F00D3815C = { - fileRef = 4C537F38064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4B064E2A0F00D3815C = { - fileRef = 4C537F39064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4C064E2A0F00D3815C = { - fileRef = 4C537F3A064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4D064E2A0F00D3815C = { - fileRef = 4C537F3B064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4E064E2A0F00D3815C = { - fileRef = 4C537F3C064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4F064E2A0F00D3815C = { - fileRef = 4C537F3D064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F50064E2A0F00D3815C = { - fileRef = 4C537F3E064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F51064E2A0F00D3815C = { - fileRef = 4C537F3F064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F52064E2A0F00D3815C = { - fileRef = 4C537F40064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F53064E2A0F00D3815C = { - fileRef = 4C537F41064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F54064E2A0F00D3815C = { - fileRef = 4C537F42064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F55064E2A0F00D3815C = { - fileRef = 4C537F43064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F56064E2AA300D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F56064E2AA300D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C537F30064E29F800D3815C; remoteInfo = server; }; - 4C537F57064E2AA300D3815C = { - isa = PBXTargetDependency; - target = 4C537F30064E29F800D3815C; - targetProxy = 4C537F56064E2AA300D3815C; - }; - 4C537F5A064E2B0700D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F64064E2B4B00D3815C, - 4C537F5B064E2B4200D3815C, - 4C537F5C064E2B4200D3815C, - 4C537F5D064E2B4200D3815C, - 4C537F5E064E2B4200D3815C, - 4C537F5F064E2B4200D3815C, - 4C537F60064E2B4200D3815C, - 4C537F61064E2B4200D3815C, - 4C537F62064E2B4200D3815C, - 4C537F63064E2B4200D3815C, - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F5B064E2B4200D3815C = { - fileRef = 4C5E868D0648C2ED003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5C064E2B4200D3815C = { - fileRef = 4CB437B5063E443800969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5D064E2B4200D3815C = { - fileRef = 4CB437D1063E44C200969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5E064E2B4200D3815C = { - fileRef = 4CB43801063E45F200969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5F064E2B4200D3815C = { - fileRef = 4CB43824063E46AB00969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F60064E2B4200D3815C = { - fileRef = 4CB43848063E475800969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F61064E2B4200D3815C = { - fileRef = 4C537F31064E29F800D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F62064E2B4200D3815C = { - fileRef = 4CB4386E063E47F800969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F63064E2B4200D3815C = { - fileRef = 4CB438B1063E488600969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F64064E2B4B00D3815C = { - fileRef = 4CB43923063E4B1300969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E868A0648C2ED003C637B = { - buildActionMask = 2147483647; - files = ( - 4C7D7CDD066319560097CA11, - 6887169B073EC03800C5ABE7, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C5E868B0648C2ED003C637B = { - buildActionMask = 2147483647; - files = ( - 4C5E86CC0648C726003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C5E868C0648C2ED003C637B = { - buildPhases = ( - 4C5E868A0648C2ED003C637B, - 4C5E868B0648C2ED003C637B, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRECOMP_DESTINATION_DIR = ""; - PRODUCT_NAME = common; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = common; - productName = common; - productReference = 4C5E868D0648C2ED003C637B; - productType = "com.apple.product-type.library.static"; - }; - 4C5E868D0648C2ED003C637B = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libcommon.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4C5E868F0648C32E003C637B = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C5E868F0648C32E003C637B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C5E868C0648C2ED003C637B; remoteInfo = common; }; - 4C5E86900648C32E003C637B = { - isa = PBXTargetDependency; - target = 4C5E868C0648C2ED003C637B; - targetProxy = 4C5E868F0648C32E003C637B; - }; - 4C5E86920648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArch.cpp; - path = lib/arch/CArch.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86930648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArch.h; - path = lib/arch/CArch.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86940648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchConsoleUnix.cpp; - path = lib/arch/CArchConsoleUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86950648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchConsoleUnix.h; - path = lib/arch/CArchConsoleUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86960648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchDaemonUnix.cpp; - path = lib/arch/CArchDaemonUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86970648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchDaemonUnix.h; - path = lib/arch/CArchDaemonUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86980648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchFileUnix.cpp; - path = lib/arch/CArchFileUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86990648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchFileUnix.h; - path = lib/arch/CArchFileUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E869A0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchLogUnix.cpp; - path = lib/arch/CArchLogUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E869B0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchLogUnix.h; - path = lib/arch/CArchLogUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E869C0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchMultithreadPosix.cpp; - path = lib/arch/CArchMultithreadPosix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E869D0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchMultithreadPosix.h; - path = lib/arch/CArchMultithreadPosix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E869E0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchNetworkBSD.cpp; - path = lib/arch/CArchNetworkBSD.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E869F0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchNetworkBSD.h; - path = lib/arch/CArchNetworkBSD.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A00648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchSleepUnix.cpp; - path = lib/arch/CArchSleepUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A10648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchSleepUnix.h; - path = lib/arch/CArchSleepUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A20648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchStringUnix.cpp; - path = lib/arch/CArchStringUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A30648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchStringUnix.h; - path = lib/arch/CArchStringUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A40648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchTimeUnix.cpp; - path = lib/arch/CArchTimeUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A50648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchTimeUnix.h; - path = lib/arch/CArchTimeUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A70648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XArchUnix.cpp; - path = lib/arch/XArchUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A80648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XArchUnix.h; - path = lib/arch/XArchUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A90648C412003C637B = { - fileRef = 4C5E86920648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AA0648C412003C637B = { - fileRef = 4C5E86930648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AB0648C412003C637B = { - fileRef = 4C5E86940648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AC0648C412003C637B = { - fileRef = 4C5E86950648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AD0648C412003C637B = { - fileRef = 4C5E86960648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AE0648C412003C637B = { - fileRef = 4C5E86970648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AF0648C412003C637B = { - fileRef = 4C5E86980648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B00648C412003C637B = { - fileRef = 4C5E86990648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B10648C412003C637B = { - fileRef = 4C5E869A0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B20648C412003C637B = { - fileRef = 4C5E869B0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B30648C412003C637B = { - fileRef = 4C5E869C0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B40648C412003C637B = { - fileRef = 4C5E869D0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B50648C412003C637B = { - fileRef = 4C5E869E0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B60648C412003C637B = { - fileRef = 4C5E869F0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B70648C412003C637B = { - fileRef = 4C5E86A00648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B80648C412003C637B = { - fileRef = 4C5E86A10648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B90648C412003C637B = { - fileRef = 4C5E86A20648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BA0648C412003C637B = { - fileRef = 4C5E86A30648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BB0648C412003C637B = { - fileRef = 4C5E86A40648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BC0648C412003C637B = { - fileRef = 4C5E86A50648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BE0648C412003C637B = { - fileRef = 4C5E86A70648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BF0648C412003C637B = { - fileRef = 4C5E86A80648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86C10648C653003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchDaemonNone.h; - path = lib/arch/CArchDaemonNone.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86C30648C653003C637B = { - fileRef = 4C5E86C10648C653003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86CA0648C6FB003C637B = { - children = ( - 4C7D7CD9066319560097CA11, - 68871699073EC02100C5ABE7, - 4C5E86CB0648C725003C637B, - ); - isa = PBXGroup; - name = Common; - refType = 4; - sourceTree = ""; - }; - 4C5E86CB0648C725003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = Version.cpp; - path = lib/common/Version.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86CC0648C726003C637B = { - fileRef = 4C5E86CB0648C725003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86CD0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CEvent.cpp; - path = lib/base/CEvent.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86CE0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CEvent.h; - path = lib/base/CEvent.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86CF0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CEventQueue.cpp; - path = lib/base/CEventQueue.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D00648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CEventQueue.h; - path = lib/base/CEventQueue.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D10648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CFunctionEventJob.cpp; - path = lib/base/CFunctionEventJob.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D20648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CFunctionEventJob.h; - path = lib/base/CFunctionEventJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D30648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CFunctionJob.cpp; - path = lib/base/CFunctionJob.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D40648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CFunctionJob.h; - path = lib/base/CFunctionJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D50648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CLog.cpp; - path = lib/base/CLog.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D60648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CLog.h; - path = lib/base/CLog.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D70648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPriorityQueue.h; - path = lib/base/CPriorityQueue.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D80648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CSimpleEventQueueBuffer.cpp; - path = lib/base/CSimpleEventQueueBuffer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D90648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CSimpleEventQueueBuffer.h; - path = lib/base/CSimpleEventQueueBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DA0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStopwatch.cpp; - path = lib/base/CStopwatch.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86DB0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStopwatch.h; - path = lib/base/CStopwatch.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DC0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CString.h; - path = lib/base/CString.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DD0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStringUtil.cpp; - path = lib/base/CStringUtil.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86DE0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStringUtil.h; - path = lib/base/CStringUtil.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DF0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CUnicode.cpp; - path = lib/base/CUnicode.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86E00648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CUnicode.h; - path = lib/base/CUnicode.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E10648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IEventJob.h; - path = lib/base/IEventJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E20648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IEventQueue.cpp; - path = lib/base/IEventQueue.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86E30648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IEventQueue.h; - path = lib/base/IEventQueue.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E40648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IEventQueueBuffer.h; - path = lib/base/IEventQueueBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E50648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IJob.h; - path = lib/base/IJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E60648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ILogOutputter.h; - path = lib/base/ILogOutputter.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E70648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = LogOutputters.cpp; - path = lib/base/LogOutputters.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86E80648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = LogOutputters.h; - path = lib/base/LogOutputters.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E90648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = TMethodEventJob.h; - path = lib/base/TMethodEventJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86EA0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = TMethodJob.h; - path = lib/base/TMethodJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86EB0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XBase.cpp; - path = lib/base/XBase.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86EC0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XBase.h; - path = lib/base/XBase.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86ED0648C7B9003C637B = { - fileRef = 4C5E86CD0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86EE0648C7B9003C637B = { - fileRef = 4C5E86CE0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86EF0648C7B9003C637B = { - fileRef = 4C5E86CF0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F00648C7B9003C637B = { - fileRef = 4C5E86D00648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F10648C7B9003C637B = { - fileRef = 4C5E86D10648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F20648C7B9003C637B = { - fileRef = 4C5E86D20648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F30648C7B9003C637B = { - fileRef = 4C5E86D30648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F40648C7B9003C637B = { - fileRef = 4C5E86D40648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F50648C7B9003C637B = { - fileRef = 4C5E86D50648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F60648C7B9003C637B = { - fileRef = 4C5E86D60648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F70648C7B9003C637B = { - fileRef = 4C5E86D70648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F80648C7B9003C637B = { - fileRef = 4C5E86D80648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F90648C7B9003C637B = { - fileRef = 4C5E86D90648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FA0648C7B9003C637B = { - fileRef = 4C5E86DA0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FB0648C7B9003C637B = { - fileRef = 4C5E86DB0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FC0648C7B9003C637B = { - fileRef = 4C5E86DC0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FD0648C7B9003C637B = { - fileRef = 4C5E86DD0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FE0648C7B9003C637B = { - fileRef = 4C5E86DE0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FF0648C7B9003C637B = { - fileRef = 4C5E86DF0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87000648C7B9003C637B = { - fileRef = 4C5E86E00648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87010648C7B9003C637B = { - fileRef = 4C5E86E10648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87020648C7B9003C637B = { - fileRef = 4C5E86E20648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87030648C7B9003C637B = { - fileRef = 4C5E86E30648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87040648C7B9003C637B = { - fileRef = 4C5E86E40648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87050648C7B9003C637B = { - fileRef = 4C5E86E50648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87060648C7B9003C637B = { - fileRef = 4C5E86E60648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87070648C7B9003C637B = { - fileRef = 4C5E86E70648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87080648C7B9003C637B = { - fileRef = 4C5E86E80648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87090648C7B9003C637B = { - fileRef = 4C5E86E90648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870A0648C7B9003C637B = { - fileRef = 4C5E86EA0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870B0648C7B9003C637B = { - fileRef = 4C5E86EB0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870C0648C7B9003C637B = { - fileRef = 4C5E86EC0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870D0648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CCondVar.cpp; - path = lib/mt/CCondVar.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E870E0648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CCondVar.h; - path = lib/mt/CCondVar.h; - refType = 4; - sourceTree = ""; - }; - 4C5E870F0648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CLock.cpp; - path = lib/mt/CLock.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87100648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CLock.h; - path = lib/mt/CLock.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87110648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CMutex.cpp; - path = lib/mt/CMutex.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87120648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CMutex.h; - path = lib/mt/CMutex.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87130648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CThread.cpp; - path = lib/mt/CThread.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87140648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CThread.h; - path = lib/mt/CThread.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87150648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XMT.cpp; - path = lib/mt/XMT.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87160648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XMT.h; - path = lib/mt/XMT.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87170648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XThread.h; - path = lib/mt/XThread.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87180648C809003C637B = { - fileRef = 4C5E870D0648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87190648C809003C637B = { - fileRef = 4C5E870E0648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871A0648C809003C637B = { - fileRef = 4C5E870F0648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871B0648C809003C637B = { - fileRef = 4C5E87100648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871C0648C809003C637B = { - fileRef = 4C5E87110648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871D0648C809003C637B = { - fileRef = 4C5E87120648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871E0648C809003C637B = { - fileRef = 4C5E87130648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871F0648C809003C637B = { - fileRef = 4C5E87140648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87200648C809003C637B = { - fileRef = 4C5E87150648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87210648C809003C637B = { - fileRef = 4C5E87160648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87220648C809003C637B = { - fileRef = 4C5E87170648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87230648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStreamBuffer.cpp; - path = lib/io/CStreamBuffer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87240648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStreamBuffer.h; - path = lib/io/CStreamBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87250648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStreamFilter.cpp; - path = lib/io/CStreamFilter.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87260648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStreamFilter.h; - path = lib/io/CStreamFilter.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87270648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IStream.cpp; - path = lib/io/IStream.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87280648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IStream.h; - path = lib/io/IStream.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87290648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IStreamFilterFactory.h; - path = lib/io/IStreamFilterFactory.h; - refType = 4; - sourceTree = ""; - }; - 4C5E872A0648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XIO.cpp; - path = lib/io/XIO.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E872B0648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XIO.h; - path = lib/io/XIO.h; - refType = 4; - sourceTree = ""; - }; - 4C5E872C0648C83C003C637B = { - fileRef = 4C5E87230648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E872D0648C83C003C637B = { - fileRef = 4C5E87240648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E872E0648C83C003C637B = { - fileRef = 4C5E87250648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E872F0648C83C003C637B = { - fileRef = 4C5E87260648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87300648C83C003C637B = { - fileRef = 4C5E87270648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87310648C83C003C637B = { - fileRef = 4C5E87280648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87320648C83C003C637B = { - fileRef = 4C5E87290648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87330648C83C003C637B = { - fileRef = 4C5E872A0648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87340648C83C003C637B = { - fileRef = 4C5E872B0648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87350648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CNetworkAddress.cpp; - path = lib/net/CNetworkAddress.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87360648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CNetworkAddress.h; - path = lib/net/CNetworkAddress.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87370648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CSocketMultiplexer.cpp; - path = lib/net/CSocketMultiplexer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87380648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CSocketMultiplexer.h; - path = lib/net/CSocketMultiplexer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87390648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CTCPListenSocket.cpp; - path = lib/net/CTCPListenSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E873A0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CTCPListenSocket.h; - path = lib/net/CTCPListenSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E873B0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CTCPSocket.cpp; - path = lib/net/CTCPSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E873C0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CTCPSocket.h; - path = lib/net/CTCPSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E873D0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CTCPSocketFactory.cpp; - path = lib/net/CTCPSocketFactory.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E873E0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CTCPSocketFactory.h; - path = lib/net/CTCPSocketFactory.h; - refType = 4; - sourceTree = ""; - }; - 4C5E873F0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IDataSocket.cpp; - path = lib/net/IDataSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87400648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IDataSocket.h; - path = lib/net/IDataSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87410648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IListenSocket.cpp; - path = lib/net/IListenSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87420648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IListenSocket.h; - path = lib/net/IListenSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87430648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = ISocket.cpp; - path = lib/net/ISocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87440648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISocket.h; - path = lib/net/ISocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87450648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISocketFactory.h; - path = lib/net/ISocketFactory.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87460648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISocketMultiplexerJob.h; - path = lib/net/ISocketMultiplexerJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87470648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = TSocketMultiplexerMethodJob.h; - path = lib/net/TSocketMultiplexerMethodJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87480648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XSocket.cpp; - path = lib/net/XSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87490648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XSocket.h; - path = lib/net/XSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E874A0648C870003C637B = { - fileRef = 4C5E87350648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874B0648C870003C637B = { - fileRef = 4C5E87360648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874C0648C870003C637B = { - fileRef = 4C5E87370648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874D0648C870003C637B = { - fileRef = 4C5E87380648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874E0648C870003C637B = { - fileRef = 4C5E87390648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874F0648C870003C637B = { - fileRef = 4C5E873A0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87500648C870003C637B = { - fileRef = 4C5E873B0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87510648C870003C637B = { - fileRef = 4C5E873C0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87520648C870003C637B = { - fileRef = 4C5E873D0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87530648C870003C637B = { - fileRef = 4C5E873E0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87540648C870003C637B = { - fileRef = 4C5E873F0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87550648C870003C637B = { - fileRef = 4C5E87400648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87560648C870003C637B = { - fileRef = 4C5E87410648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87570648C870003C637B = { - fileRef = 4C5E87420648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87580648C870003C637B = { - fileRef = 4C5E87430648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87590648C870003C637B = { - fileRef = 4C5E87440648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875A0648C870003C637B = { - fileRef = 4C5E87450648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875B0648C870003C637B = { - fileRef = 4C5E87460648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875C0648C870003C637B = { - fileRef = 4C5E87470648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875D0648C870003C637B = { - fileRef = 4C5E87480648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875E0648C870003C637B = { - fileRef = 4C5E87490648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875F0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClipboard.cpp; - path = lib/synergy/CClipboard.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87600648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClipboard.h; - path = lib/synergy/CClipboard.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87610648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CKeyState.cpp; - path = lib/synergy/CKeyState.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87620648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CKeyState.h; - path = lib/synergy/CKeyState.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87630648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ClipboardTypes.h; - path = lib/synergy/ClipboardTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87640648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CPacketStreamFilter.cpp; - path = lib/synergy/CPacketStreamFilter.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87650648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPacketStreamFilter.h; - path = lib/synergy/CPacketStreamFilter.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87660648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CPlatformScreen.cpp; - path = lib/synergy/CPlatformScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87670648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPlatformScreen.h; - path = lib/synergy/CPlatformScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87680648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CProtocolUtil.cpp; - path = lib/synergy/CProtocolUtil.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87690648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CProtocolUtil.h; - path = lib/synergy/CProtocolUtil.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876A0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CScreen.cpp; - path = lib/synergy/CScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E876B0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CScreen.h; - path = lib/synergy/CScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876C0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IClient.h; - path = lib/synergy/IClient.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876D0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IClipboard.cpp; - path = lib/synergy/IClipboard.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E876E0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IClipboard.h; - path = lib/synergy/IClipboard.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876F0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IKeyState.cpp; - path = lib/synergy/IKeyState.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87700648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IKeyState.h; - path = lib/synergy/IKeyState.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87710648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IPlatformScreen.h; - path = lib/synergy/IPlatformScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87720648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IPrimaryScreen.cpp; - path = lib/synergy/IPrimaryScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87730648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IPrimaryScreen.h; - path = lib/synergy/IPrimaryScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87740648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IScreen.cpp; - path = lib/synergy/IScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87750648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IScreen.h; - path = lib/synergy/IScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87760648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IScreenSaver.h; - path = lib/synergy/IScreenSaver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87770648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISecondaryScreen.h; - path = lib/synergy/ISecondaryScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87780648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = KeyTypes.h; - path = lib/synergy/KeyTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87790648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = MouseTypes.h; - path = lib/synergy/MouseTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877A0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = OptionTypes.h; - path = lib/synergy/OptionTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877B0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ProtocolTypes.h; - path = lib/synergy/ProtocolTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877C0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XScreen.cpp; - path = lib/synergy/XScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E877D0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XScreen.h; - path = lib/synergy/XScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877E0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XSynergy.cpp; - path = lib/synergy/XSynergy.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E877F0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XSynergy.h; - path = lib/synergy/XSynergy.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87800648C8BD003C637B = { - fileRef = 4C5E875F0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87810648C8BD003C637B = { - fileRef = 4C5E87600648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87820648C8BD003C637B = { - fileRef = 4C5E87610648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87830648C8BD003C637B = { - fileRef = 4C5E87620648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87840648C8BD003C637B = { - fileRef = 4C5E87630648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87850648C8BD003C637B = { - fileRef = 4C5E87640648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87860648C8BD003C637B = { - fileRef = 4C5E87650648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87870648C8BD003C637B = { - fileRef = 4C5E87660648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87880648C8BD003C637B = { - fileRef = 4C5E87670648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87890648C8BD003C637B = { - fileRef = 4C5E87680648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878A0648C8BD003C637B = { - fileRef = 4C5E87690648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878B0648C8BD003C637B = { - fileRef = 4C5E876A0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878C0648C8BD003C637B = { - fileRef = 4C5E876B0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878D0648C8BD003C637B = { - fileRef = 4C5E876C0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878E0648C8BD003C637B = { - fileRef = 4C5E876D0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878F0648C8BD003C637B = { - fileRef = 4C5E876E0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87900648C8BD003C637B = { - fileRef = 4C5E876F0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87910648C8BD003C637B = { - fileRef = 4C5E87700648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87920648C8BD003C637B = { - fileRef = 4C5E87710648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87930648C8BD003C637B = { - fileRef = 4C5E87720648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87940648C8BD003C637B = { - fileRef = 4C5E87730648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87950648C8BD003C637B = { - fileRef = 4C5E87740648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87960648C8BD003C637B = { - fileRef = 4C5E87750648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87970648C8BD003C637B = { - fileRef = 4C5E87760648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87980648C8BD003C637B = { - fileRef = 4C5E87770648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87990648C8BD003C637B = { - fileRef = 4C5E87780648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879A0648C8BD003C637B = { - fileRef = 4C5E87790648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879B0648C8BD003C637B = { - fileRef = 4C5E877A0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879C0648C8BD003C637B = { - fileRef = 4C5E877B0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879D0648C8BD003C637B = { - fileRef = 4C5E877C0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879E0648C8BD003C637B = { - fileRef = 4C5E877D0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879F0648C8BD003C637B = { - fileRef = 4C5E877E0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87A00648C8BD003C637B = { - fileRef = 4C5E877F0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87A10648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboard.cpp; - path = lib/platform/COSXClipboard.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A20648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboard.h; - path = lib/platform/COSXClipboard.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A30648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXEventQueueBuffer.cpp; - path = lib/platform/COSXEventQueueBuffer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A40648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXEventQueueBuffer.h; - path = lib/platform/COSXEventQueueBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A50648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXKeyState.cpp; - path = lib/platform/COSXKeyState.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A60648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXKeyState.h; - path = lib/platform/COSXKeyState.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A70648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXScreen.cpp; - path = lib/platform/COSXScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A80648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXScreen.h; - path = lib/platform/COSXScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A90648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXScreenSaver.cpp; - path = lib/platform/COSXScreenSaver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87AA0648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXScreenSaver.h; - path = lib/platform/COSXScreenSaver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87AD0648C913003C637B = { - fileRef = 4C5E87A10648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87AE0648C913003C637B = { - fileRef = 4C5E87A20648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87AF0648C913003C637B = { - fileRef = 4C5E87A30648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B00648C913003C637B = { - fileRef = 4C5E87A40648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B10648C913003C637B = { - fileRef = 4C5E87A50648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B20648C913003C637B = { - fileRef = 4C5E87A60648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B30648C913003C637B = { - fileRef = 4C5E87A70648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B40648C913003C637B = { - fileRef = 4C5E87A80648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B50648C913003C637B = { - fileRef = 4C5E87A90648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B60648C913003C637B = { - fileRef = 4C5E87AA0648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B90648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClient.cpp; - path = lib/client/CClient.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87BA0648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClient.h; - path = lib/client/CClient.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87BB0648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CServerProxy.cpp; - path = lib/client/CServerProxy.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87BC0648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CServerProxy.h; - path = lib/client/CServerProxy.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87BD0648C969003C637B = { - fileRef = 4C5E87B90648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87BE0648C969003C637B = { - fileRef = 4C5E87BA0648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87BF0648C969003C637B = { - fileRef = 4C5E87BB0648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C00648C969003C637B = { - fileRef = 4C5E87BC0648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C10648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientTaskBarReceiver.cpp; - path = cmd/synergyc/CClientTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87C20648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientTaskBarReceiver.h; - path = cmd/synergyc/CClientTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87C30648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClientTaskBarReceiver.cpp; - path = cmd/synergyc/COSXClientTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87C40648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClientTaskBarReceiver.h; - path = cmd/synergyc/COSXClientTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87C50648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = synergyc.cpp; - path = cmd/synergyc/synergyc.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87C60648C9D2003C637B = { - fileRef = 4C5E87C10648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C70648C9D2003C637B = { - fileRef = 4C5E87C20648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C80648C9D2003C637B = { - fileRef = 4C5E87C30648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C90648C9D2003C637B = { - fileRef = 4C5E87C40648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87CA0648C9D2003C637B = { - fileRef = 4C5E87C50648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87CF0648CA4B003C637B = { - fileRef = 4C5E868D0648C2ED003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87D00648CA75003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XArch.cpp; - path = lib/arch/XArch.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87D10648CA75003C637B = { - fileRef = 4C5E87D00648CA75003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CD9066319560097CA11 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = common.h; - path = lib/common/common.h; - refType = 4; - sourceTree = ""; - }; - 4C7D7CDA066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDB066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDC066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDD066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDE066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDF066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE0066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE1066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE2066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE3066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE4066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE5066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4CB43775063E406A00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87BE0648C969003C637B, - 4C5E87C00648C969003C637B, - 4C7D7CE5066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43776063E406A00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87BD0648C969003C637B, - 4C5E87BF0648C969003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43777063E406A00969041 = { - buildPhases = ( - 4CB43775063E406A00969041, - 4CB43776063E406A00969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = client; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = client; - productName = client; - productReference = 4CB43778063E406A00969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43778063E406A00969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libclient.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43779063E40B600969041 = { - children = ( - 4C5E87B90648C969003C637B, - 4C5E87BA0648C969003C637B, - 4C5E87BB0648C969003C637B, - 4C5E87BC0648C969003C637B, - ); - isa = PBXGroup; - name = Client; - refType = 4; - sourceTree = ""; - }; - 4CB4378B063E432C00969041 = { - children = ( - 4C5E86920648C412003C637B, - 4C5E86930648C412003C637B, - 4C5E86940648C412003C637B, - 4C5E86950648C412003C637B, - 68968F5A073EC484004B2F9B, - 4C5E86C10648C653003C637B, - 4C5E86960648C412003C637B, - 4C5E86970648C412003C637B, - 4C5E86980648C412003C637B, - 4C5E86990648C412003C637B, - 4C5E869A0648C412003C637B, - 4C5E869B0648C412003C637B, - 4C5E869C0648C412003C637B, - 4C5E869D0648C412003C637B, - 4C5E869E0648C412003C637B, - 4C5E869F0648C412003C637B, - 4C5E86A00648C412003C637B, - 4C5E86A10648C412003C637B, - 4C5E86A20648C412003C637B, - 4C5E86A30648C412003C637B, - 68871676073EBF6F00C5ABE7, - 68871677073EBF6F00C5ABE7, - 68968F5B073EC484004B2F9B, - 68968F5C073EC484004B2F9B, - 4C5E86A40648C412003C637B, - 4C5E86A50648C412003C637B, - 68871678073EBF6F00C5ABE7, - 68871679073EBF6F00C5ABE7, - 6887167A073EBF6F00C5ABE7, - 6887167B073EBF6F00C5ABE7, - 6887167C073EBF6F00C5ABE7, - 6887167D073EBF6F00C5ABE7, - 6887167E073EBF6F00C5ABE7, - 6887167F073EBF6F00C5ABE7, - 68871680073EBF6F00C5ABE7, - 68871681073EBF7000C5ABE7, - 68871682073EBF7000C5ABE7, - 68871683073EBF7000C5ABE7, - 68871684073EBF7000C5ABE7, - 4C5E87D00648CA75003C637B, - 4C5E86A70648C412003C637B, - 4C5E86A80648C412003C637B, - ); - isa = PBXGroup; - name = Arch; - refType = 4; - sourceTree = ""; - }; - 4CB437B2063E443800969041 = { - buildActionMask = 2147483647; - files = ( - 4C7D7CDC066319560097CA11, - 4C5E86AA0648C412003C637B, - 4C5E86AC0648C412003C637B, - 4C5E86C30648C653003C637B, - 4C5E86AE0648C412003C637B, - 4C5E86B00648C412003C637B, - 4C5E86B20648C412003C637B, - 4C5E86B40648C412003C637B, - 4C5E86B60648C412003C637B, - 4C5E86B80648C412003C637B, - 4C5E86BA0648C412003C637B, - 68871686073EBF7000C5ABE7, - 68968F5F073EC484004B2F9B, - 4C5E86BC0648C412003C637B, - 68871687073EBF7000C5ABE7, - 68871688073EBF7000C5ABE7, - 68871689073EBF7000C5ABE7, - 6887168A073EBF7000C5ABE7, - 6887168B073EBF7000C5ABE7, - 6887168C073EBF7000C5ABE7, - 6887168D073EBF7000C5ABE7, - 6887168E073EBF7000C5ABE7, - 6887168F073EBF7000C5ABE7, - 68871690073EBF7000C5ABE7, - 68871691073EBF7000C5ABE7, - 68871692073EBF7000C5ABE7, - 68871693073EBF7000C5ABE7, - 4C5E86BF0648C412003C637B, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437B3063E443800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E86A90648C412003C637B, - 4C5E86AB0648C412003C637B, - 68968F5D073EC484004B2F9B, - 4C5E86AD0648C412003C637B, - 4C5E86AF0648C412003C637B, - 4C5E86B10648C412003C637B, - 4C5E86B30648C412003C637B, - 4C5E86B50648C412003C637B, - 4C5E86B70648C412003C637B, - 4C5E86B90648C412003C637B, - 68871685073EBF7000C5ABE7, - 68968F5E073EC484004B2F9B, - 4C5E86BB0648C412003C637B, - 4C5E87D10648CA75003C637B, - 4C5E86BE0648C412003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437B4063E443800969041 = { - buildPhases = ( - 4CB437B2063E443800969041, - 4CB437B3063E443800969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = ./lib/common; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRECOMP_DESTINATION_DIR = lib/common/; - PRODUCT_NAME = arch; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = arch; - productName = arch; - productReference = 4CB437B5063E443800969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB437B5063E443800969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libarch.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB437CE063E44C200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E86EE0648C7B9003C637B, - 4C5E86F00648C7B9003C637B, - 4C5E86F20648C7B9003C637B, - 4C5E86F40648C7B9003C637B, - 4C5E86F60648C7B9003C637B, - 4C5E86F70648C7B9003C637B, - 4C5E86F90648C7B9003C637B, - 4C5E86FB0648C7B9003C637B, - 4C5E86FC0648C7B9003C637B, - 4C5E86FE0648C7B9003C637B, - 4C5E87000648C7B9003C637B, - 4C5E87010648C7B9003C637B, - 4C5E87030648C7B9003C637B, - 4C5E87040648C7B9003C637B, - 4C5E87050648C7B9003C637B, - 4C5E87060648C7B9003C637B, - 4C5E87080648C7B9003C637B, - 4C5E87090648C7B9003C637B, - 4C5E870A0648C7B9003C637B, - 4C5E870C0648C7B9003C637B, - 4C7D7CDE066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437CF063E44C200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E86ED0648C7B9003C637B, - 4C5E86EF0648C7B9003C637B, - 4C5E86F10648C7B9003C637B, - 4C5E86F30648C7B9003C637B, - 4C5E86F50648C7B9003C637B, - 4C5E86F80648C7B9003C637B, - 4C5E86FA0648C7B9003C637B, - 4C5E86FD0648C7B9003C637B, - 4C5E86FF0648C7B9003C637B, - 4C5E87020648C7B9003C637B, - 4C5E87070648C7B9003C637B, - 4C5E870B0648C7B9003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437D0063E44C200969041 = { - buildPhases = ( - 4CB437CE063E44C200969041, - 4CB437CF063E44C200969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = base; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = base; - productName = base; - productReference = 4CB437D1063E44C200969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB437D1063E44C200969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libbase.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB437D2063E44E400969041 = { - children = ( - 4C5E86CD0648C7B9003C637B, - 4C5E86CE0648C7B9003C637B, - 4C5E86CF0648C7B9003C637B, - 4C5E86D00648C7B9003C637B, - 4C5E86D10648C7B9003C637B, - 4C5E86D20648C7B9003C637B, - 4C5E86D30648C7B9003C637B, - 4C5E86D40648C7B9003C637B, - 4C5E86D50648C7B9003C637B, - 4C5E86D60648C7B9003C637B, - 4C5E86D70648C7B9003C637B, - 4C5E86D80648C7B9003C637B, - 4C5E86D90648C7B9003C637B, - 4C5E86DA0648C7B9003C637B, - 4C5E86DB0648C7B9003C637B, - 4C5E86DC0648C7B9003C637B, - 4C5E86DD0648C7B9003C637B, - 4C5E86DE0648C7B9003C637B, - 4C5E86DF0648C7B9003C637B, - 4C5E86E00648C7B9003C637B, - 4C5E86E10648C7B9003C637B, - 4C5E86E20648C7B9003C637B, - 4C5E86E30648C7B9003C637B, - 4C5E86E40648C7B9003C637B, - 4C5E86E50648C7B9003C637B, - 4C5E86E60648C7B9003C637B, - 4C5E86E70648C7B9003C637B, - 4C5E86E80648C7B9003C637B, - 4C5E86E90648C7B9003C637B, - 4C5E86EA0648C7B9003C637B, - 4C5E86EB0648C7B9003C637B, - 4C5E86EC0648C7B9003C637B, - ); - isa = PBXGroup; - name = Base; - refType = 4; - sourceTree = ""; - }; - 4CB437FB063E45D700969041 = { - children = ( - 4C5E870D0648C809003C637B, - 4C5E870E0648C809003C637B, - 4C5E870F0648C809003C637B, - 4C5E87100648C809003C637B, - 4C5E87110648C809003C637B, - 4C5E87120648C809003C637B, - 4C5E87130648C809003C637B, - 4C5E87140648C809003C637B, - 4C5E87150648C809003C637B, - 4C5E87160648C809003C637B, - 4C5E87170648C809003C637B, - ); - isa = PBXGroup; - name = MT; - refType = 4; - sourceTree = ""; - }; - 4CB437FE063E45F200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87190648C809003C637B, - 4C5E871B0648C809003C637B, - 4C5E871D0648C809003C637B, - 4C5E871F0648C809003C637B, - 4C5E87210648C809003C637B, - 4C5E87220648C809003C637B, - 4C7D7CDF066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437FF063E45F200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87180648C809003C637B, - 4C5E871A0648C809003C637B, - 4C5E871C0648C809003C637B, - 4C5E871E0648C809003C637B, - 4C5E87200648C809003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43800063E45F200969041 = { - buildPhases = ( - 4CB437FE063E45F200969041, - 4CB437FF063E45F200969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = mt; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = mt; - productName = mt; - productReference = 4CB43801063E45F200969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43801063E45F200969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libmt.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB4381E063E469300969041 = { - children = ( - 4C5E87230648C83C003C637B, - 4C5E87240648C83C003C637B, - 4C5E87250648C83C003C637B, - 4C5E87260648C83C003C637B, - 4C5E87270648C83C003C637B, - 4C5E87280648C83C003C637B, - 4C5E87290648C83C003C637B, - 4C5E872A0648C83C003C637B, - 4C5E872B0648C83C003C637B, - ); - isa = PBXGroup; - name = IO; - refType = 4; - sourceTree = ""; - }; - 4CB43821063E46AB00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E872D0648C83C003C637B, - 4C5E872F0648C83C003C637B, - 4C5E87310648C83C003C637B, - 4C5E87320648C83C003C637B, - 4C5E87340648C83C003C637B, - 4C7D7CE0066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43822063E46AB00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E872C0648C83C003C637B, - 4C5E872E0648C83C003C637B, - 4C5E87300648C83C003C637B, - 4C5E87330648C83C003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43823063E46AB00969041 = { - buildPhases = ( - 4CB43821063E46AB00969041, - 4CB43822063E46AB00969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = io; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = io; - productName = io; - productReference = 4CB43824063E46AB00969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43824063E46AB00969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libio.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43845063E475800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E874B0648C870003C637B, - 4C5E874D0648C870003C637B, - 4C5E874F0648C870003C637B, - 4C5E87510648C870003C637B, - 4C5E87530648C870003C637B, - 4C5E87550648C870003C637B, - 4C5E87570648C870003C637B, - 4C5E87590648C870003C637B, - 4C5E875A0648C870003C637B, - 4C5E875B0648C870003C637B, - 4C5E875C0648C870003C637B, - 4C5E875E0648C870003C637B, - 4C7D7CE1066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43846063E475800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E874A0648C870003C637B, - 4C5E874C0648C870003C637B, - 4C5E874E0648C870003C637B, - 4C5E87500648C870003C637B, - 4C5E87520648C870003C637B, - 4C5E87540648C870003C637B, - 4C5E87560648C870003C637B, - 4C5E87580648C870003C637B, - 4C5E875D0648C870003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43847063E475800969041 = { - buildPhases = ( - 4CB43845063E475800969041, - 4CB43846063E475800969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = net; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = net; - productName = net; - productReference = 4CB43848063E475800969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43848063E475800969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libnet.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43849063E478900969041 = { - children = ( - 4C5E87350648C870003C637B, - 4C5E87360648C870003C637B, - 4C5E87370648C870003C637B, - 4C5E87380648C870003C637B, - 4C5E87390648C870003C637B, - 4C5E873A0648C870003C637B, - 4C5E873B0648C870003C637B, - 4C5E873C0648C870003C637B, - 4C5E873D0648C870003C637B, - 4C5E873E0648C870003C637B, - 4C5E873F0648C870003C637B, - 4C5E87400648C870003C637B, - 4C5E87410648C870003C637B, - 4C5E87420648C870003C637B, - 4C5E87430648C870003C637B, - 4C5E87440648C870003C637B, - 4C5E87450648C870003C637B, - 4C5E87460648C870003C637B, - 4C5E87470648C870003C637B, - 4C5E87480648C870003C637B, - 4C5E87490648C870003C637B, - ); - isa = PBXGroup; - name = Net; - refType = 4; - sourceTree = ""; - }; - 4CB43866063E47C800969041 = { - children = ( - 4C5E875F0648C8BD003C637B, - 4C5E87600648C8BD003C637B, - 4C5E87610648C8BD003C637B, - 4C5E87620648C8BD003C637B, - 4C5E87630648C8BD003C637B, - 4C5E87640648C8BD003C637B, - 4C5E87650648C8BD003C637B, - 4C5E87660648C8BD003C637B, - 4C5E87670648C8BD003C637B, - 4C5E87680648C8BD003C637B, - 4C5E87690648C8BD003C637B, - 4C5E876A0648C8BD003C637B, - 4C5E876B0648C8BD003C637B, - 4C5E876C0648C8BD003C637B, - 4C5E876D0648C8BD003C637B, - 4C5E876E0648C8BD003C637B, - 4C5E876F0648C8BD003C637B, - 4C5E87700648C8BD003C637B, - 4C5E87710648C8BD003C637B, - 4C5E87720648C8BD003C637B, - 4C5E87730648C8BD003C637B, - 4C5E87740648C8BD003C637B, - 4C5E87750648C8BD003C637B, - 4C5E87760648C8BD003C637B, - 4C5E87770648C8BD003C637B, - 4C5E87780648C8BD003C637B, - 4C5E87790648C8BD003C637B, - 4C5E877A0648C8BD003C637B, - 4C5E877B0648C8BD003C637B, - 4C5E877C0648C8BD003C637B, - 4C5E877D0648C8BD003C637B, - 4C5E877E0648C8BD003C637B, - 4C5E877F0648C8BD003C637B, - ); - isa = PBXGroup; - name = Synergy; - refType = 4; - sourceTree = ""; - }; - 4CB4386B063E47F800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87810648C8BD003C637B, - 4C5E87830648C8BD003C637B, - 4C5E87840648C8BD003C637B, - 4C5E87860648C8BD003C637B, - 4C5E87880648C8BD003C637B, - 4C5E878A0648C8BD003C637B, - 4C5E878C0648C8BD003C637B, - 4C5E878D0648C8BD003C637B, - 4C5E878F0648C8BD003C637B, - 4C5E87910648C8BD003C637B, - 4C5E87920648C8BD003C637B, - 4C5E87940648C8BD003C637B, - 4C5E87960648C8BD003C637B, - 4C5E87970648C8BD003C637B, - 4C5E87980648C8BD003C637B, - 4C5E87990648C8BD003C637B, - 4C5E879A0648C8BD003C637B, - 4C5E879B0648C8BD003C637B, - 4C5E879C0648C8BD003C637B, - 4C5E879E0648C8BD003C637B, - 4C5E87A00648C8BD003C637B, - 4C7D7CE2066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB4386C063E47F800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87800648C8BD003C637B, - 4C5E87820648C8BD003C637B, - 4C5E87850648C8BD003C637B, - 4C5E87870648C8BD003C637B, - 4C5E87890648C8BD003C637B, - 4C5E878B0648C8BD003C637B, - 4C5E878E0648C8BD003C637B, - 4C5E87900648C8BD003C637B, - 4C5E87930648C8BD003C637B, - 4C5E87950648C8BD003C637B, - 4C5E879D0648C8BD003C637B, - 4C5E879F0648C8BD003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB4386D063E47F800969041 = { - buildPhases = ( - 4CB4386B063E47F800969041, - 4CB4386C063E47F800969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = synergy; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = synergy; - productName = synergy; - productReference = 4CB4386E063E47F800969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB4386E063E47F800969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libsynergy.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB438AD063E487200969041 = { - children = ( - 4C5E87A10648C913003C637B, - 4C5E87A20648C913003C637B, - 4CD0348F0650B6F100525ED1, - 4CD034900650B6F100525ED1, - 4CD034910650B6F100525ED1, - 4CD034920650B6F100525ED1, - 4CD034930650B6F100525ED1, - 4CD034940650B6F100525ED1, - 4C5E87A30648C913003C637B, - 4C5E87A40648C913003C637B, - 4C5E87A50648C913003C637B, - 4C5E87A60648C913003C637B, - 4C5E87A70648C913003C637B, - 4C5E87A80648C913003C637B, - 4C5E87A90648C913003C637B, - 4C5E87AA0648C913003C637B, - ); - isa = PBXGroup; - name = Platform; - refType = 4; - sourceTree = ""; - }; - 4CB438AE063E488600969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87AE0648C913003C637B, - 4C5E87B00648C913003C637B, - 4C5E87B20648C913003C637B, - 4C5E87B40648C913003C637B, - 4C5E87B60648C913003C637B, - 4CD034960650B6F100525ED1, - 4CD034980650B6F100525ED1, - 4CD0349A0650B6F100525ED1, - 4C7D7CE3066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB438AF063E488600969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87AD0648C913003C637B, - 4C5E87AF0648C913003C637B, - 4C5E87B10648C913003C637B, - 4C5E87B30648C913003C637B, - 4C5E87B50648C913003C637B, - 4CD034950650B6F100525ED1, - 4CD034970650B6F100525ED1, - 4CD034990650B6F100525ED1, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB438B0063E488600969041 = { - buildPhases = ( - 4CB438AE063E488600969041, - 4CB438AF063E488600969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io /lib/synergy"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = platform; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = platform; - productName = platform; - productReference = 4CB438B1063E488600969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB438B1063E488600969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libplatform.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43911063E497700969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87C70648C9D2003C637B, - 4C5E87C90648C9D2003C637B, - 4C7D7CDB066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43912063E497700969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87C60648C9D2003C637B, - 4C5E87C80648C9D2003C637B, - 4C5E87CA0648C9D2003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43913063E497700969041 = { - buildPhases = ( - 4CB43911063E497700969041, - 4CB43912063E497700969041, - 4CB43930063E4B4300969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; - INSTALL_PATH = /usr/local/bin; - LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = synergyc; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - 4CB43948063E4E1600969041, - 4C5E86900648C32E003C637B, - 4CB4394A063E4E1600969041, - 4CB4394C063E4E1600969041, - 4CB4394E063E4E1600969041, - 4CB43950063E4E1600969041, - 4CB43952063E4E1600969041, - 4CB43954063E4E1600969041, - 4CB43946063E4E1600969041, - ); - isa = PBXNativeTarget; - name = synergyc; - productName = synergyc; - productReference = 4CB43914063E497700969041; - productType = "com.apple.product-type.tool"; - }; - 4CB43914063E497700969041 = { - explicitFileType = "compiled.mach-o.executable"; - includeInIndex = 0; - isa = PBXFileReference; - path = synergyc; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43916063E4A1A00969041 = { - children = ( - 4C5E87C10648C9D2003C637B, - 4C5E87C20648C9D2003C637B, - 4C5E87C30648C9D2003C637B, - 4C5E87C40648C9D2003C637B, - 4C5E87C50648C9D2003C637B, - ); - isa = PBXGroup; - name = SynergyC; - refType = 4; - sourceTree = ""; - }; - 4CB43923063E4B1300969041 = { - isa = PBXFileReference; - lastKnownFileType = wrapper.framework; - name = Carbon.framework; - path = ../../../../../System/Library/Frameworks/Carbon.framework; - refType = 4; - sourceTree = ""; - }; - 4CB43930063E4B4300969041 = { - buildActionMask = 2147483647; - files = ( - 4CB43934063E4B4A00969041, - 4CDF389A063E561B007B20A1, - 4C5E87CF0648CA4B003C637B, - 4CDF389B063E561B007B20A1, - 4CDF389C063E561B007B20A1, - 4CDF389D063E561B007B20A1, - 4CDF389E063E561B007B20A1, - 4CDF389F063E561B007B20A1, - 4CDF38A0063E561B007B20A1, - 4CDF38A1063E561B007B20A1, - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43934063E4B4A00969041 = { - fileRef = 4CB43923063E4B1300969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4CB43945063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43945063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43777063E406A00969041; remoteInfo = client; }; - 4CB43946063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43777063E406A00969041; - targetProxy = 4CB43945063E4E1600969041; - }; - 4CB43947063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43947063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437B4063E443800969041; remoteInfo = arch; }; - 4CB43948063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB437B4063E443800969041; - targetProxy = 4CB43947063E4E1600969041; - }; - 4CB43949063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43949063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437D0063E44C200969041; remoteInfo = base; }; - 4CB4394A063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB437D0063E44C200969041; - targetProxy = 4CB43949063E4E1600969041; - }; - 4CB4394B063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB4394B063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43800063E45F200969041; remoteInfo = mt; }; - 4CB4394C063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43800063E45F200969041; - targetProxy = 4CB4394B063E4E1600969041; - }; - 4CB4394D063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB4394D063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43823063E46AB00969041; remoteInfo = io; }; - 4CB4394E063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43823063E46AB00969041; - targetProxy = 4CB4394D063E4E1600969041; - }; - 4CB4394F063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB4394F063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43847063E475800969041; remoteInfo = net; }; - 4CB43950063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43847063E475800969041; - targetProxy = 4CB4394F063E4E1600969041; - }; - 4CB43951063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43951063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB4386D063E47F800969041; remoteInfo = synergy; }; - 4CB43952063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB4386D063E47F800969041; - targetProxy = 4CB43951063E4E1600969041; - }; - 4CB43953063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43953063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB438B0063E488600969041; remoteInfo = platform; }; - 4CB43954063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB438B0063E488600969041; - targetProxy = 4CB43953063E4E1600969041; - }; - 4CD033E80650965F00525ED1 = { - buildPhases = ( - ); - buildSettings = { - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = ALL; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; - }; - dependencies = ( - 4CD033EA0650968500525ED1, - 4CD033EC0650968500525ED1, - ); - isa = PBXAggregateTarget; - name = ALL; - productName = ALL; - }; - 4CD033E90650968500525ED1 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CD033E90650968500525ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43913063E497700969041; remoteInfo = synergyc; }; - 4CD033EA0650968500525ED1 = { - isa = PBXTargetDependency; - target = 4CB43913063E497700969041; - targetProxy = 4CD033E90650968500525ED1; - }; - 4CD033EB0650968500525ED1 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CD033EB0650968500525ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C537F0C064E286700D3815C; remoteInfo = synergys; }; - 4CD033EC0650968500525ED1 = { +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 4C537F0D064E286700D3815C /* synergys */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = synergys; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXServerTaskBarReceiver.cpp; path = cmd/synergys/COSXServerTaskBarReceiver.cpp; sourceTree = ""; }; + 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXServerTaskBarReceiver.h; path = cmd/synergys/COSXServerTaskBarReceiver.h; sourceTree = ""; }; + 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServerTaskBarReceiver.cpp; path = cmd/synergys/CServerTaskBarReceiver.cpp; sourceTree = ""; }; + 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServerTaskBarReceiver.h; path = cmd/synergys/CServerTaskBarReceiver.h; sourceTree = ""; }; + 4C537F13064E293000D3815C /* synergys.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = synergys.cpp; path = cmd/synergys/synergys.cpp; sourceTree = ""; }; + 4C537F31064E29F800D3815C /* libserver.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libserver.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C537F32064E2A0F00D3815C /* CClientListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientListener.cpp; path = lib/server/CClientListener.cpp; sourceTree = ""; }; + 4C537F33064E2A0F00D3815C /* CClientListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientListener.h; path = lib/server/CClientListener.h; sourceTree = ""; }; + 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy.cpp; path = lib/server/CClientProxy.cpp; sourceTree = ""; }; + 4C537F35064E2A0F00D3815C /* CClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy.h; path = lib/server/CClientProxy.h; sourceTree = ""; }; + 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_0.cpp; path = lib/server/CClientProxy1_0.cpp; sourceTree = ""; }; + 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_0.h; path = lib/server/CClientProxy1_0.h; sourceTree = ""; }; + 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_1.cpp; path = lib/server/CClientProxy1_1.cpp; sourceTree = ""; }; + 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_1.h; path = lib/server/CClientProxy1_1.h; sourceTree = ""; }; + 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_2.cpp; path = lib/server/CClientProxy1_2.cpp; sourceTree = ""; }; + 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_2.h; path = lib/server/CClientProxy1_2.h; sourceTree = ""; }; + 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxyUnknown.cpp; path = lib/server/CClientProxyUnknown.cpp; sourceTree = ""; }; + 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxyUnknown.h; path = lib/server/CClientProxyUnknown.h; sourceTree = ""; }; + 4C537F3E064E2A0F00D3815C /* CConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CConfig.cpp; path = lib/server/CConfig.cpp; sourceTree = ""; }; + 4C537F3F064E2A0F00D3815C /* CConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CConfig.h; path = lib/server/CConfig.h; sourceTree = ""; }; + 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPrimaryClient.cpp; path = lib/server/CPrimaryClient.cpp; sourceTree = ""; }; + 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPrimaryClient.h; path = lib/server/CPrimaryClient.h; sourceTree = ""; }; + 4C537F42064E2A0F00D3815C /* CServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServer.cpp; path = lib/server/CServer.cpp; sourceTree = ""; }; + 4C537F43064E2A0F00D3815C /* CServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServer.h; path = lib/server/CServer.h; sourceTree = ""; }; + 4C5E868D0648C2ED003C637B /* libcommon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcommon.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C5E86920648C412003C637B /* CArch.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArch.cpp; path = lib/arch/CArch.cpp; sourceTree = ""; }; + 4C5E86930648C412003C637B /* CArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArch.h; path = lib/arch/CArch.h; sourceTree = ""; }; + 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchConsoleUnix.cpp; path = lib/arch/CArchConsoleUnix.cpp; sourceTree = ""; }; + 4C5E86950648C412003C637B /* CArchConsoleUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchConsoleUnix.h; path = lib/arch/CArchConsoleUnix.h; sourceTree = ""; }; + 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchDaemonUnix.cpp; path = lib/arch/CArchDaemonUnix.cpp; sourceTree = ""; }; + 4C5E86970648C412003C637B /* CArchDaemonUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchDaemonUnix.h; path = lib/arch/CArchDaemonUnix.h; sourceTree = ""; }; + 4C5E86980648C412003C637B /* CArchFileUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchFileUnix.cpp; path = lib/arch/CArchFileUnix.cpp; sourceTree = ""; }; + 4C5E86990648C412003C637B /* CArchFileUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchFileUnix.h; path = lib/arch/CArchFileUnix.h; sourceTree = ""; }; + 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchLogUnix.cpp; path = lib/arch/CArchLogUnix.cpp; sourceTree = ""; }; + 4C5E869B0648C412003C637B /* CArchLogUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchLogUnix.h; path = lib/arch/CArchLogUnix.h; sourceTree = ""; }; + 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchMultithreadPosix.cpp; path = lib/arch/CArchMultithreadPosix.cpp; sourceTree = ""; }; + 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchMultithreadPosix.h; path = lib/arch/CArchMultithreadPosix.h; sourceTree = ""; }; + 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchNetworkBSD.cpp; path = lib/arch/CArchNetworkBSD.cpp; sourceTree = ""; }; + 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchNetworkBSD.h; path = lib/arch/CArchNetworkBSD.h; sourceTree = ""; }; + 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchSleepUnix.cpp; path = lib/arch/CArchSleepUnix.cpp; sourceTree = ""; }; + 4C5E86A10648C412003C637B /* CArchSleepUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchSleepUnix.h; path = lib/arch/CArchSleepUnix.h; sourceTree = ""; }; + 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchStringUnix.cpp; path = lib/arch/CArchStringUnix.cpp; sourceTree = ""; }; + 4C5E86A30648C412003C637B /* CArchStringUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchStringUnix.h; path = lib/arch/CArchStringUnix.h; sourceTree = ""; }; + 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchTimeUnix.cpp; path = lib/arch/CArchTimeUnix.cpp; sourceTree = ""; }; + 4C5E86A50648C412003C637B /* CArchTimeUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchTimeUnix.h; path = lib/arch/CArchTimeUnix.h; sourceTree = ""; }; + 4C5E86A70648C412003C637B /* XArchUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = XArchUnix.cpp; path = lib/arch/XArchUnix.cpp; sourceTree = ""; }; + 4C5E86A80648C412003C637B /* XArchUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = XArchUnix.h; path = lib/arch/XArchUnix.h; sourceTree = ""; }; + 4C5E86C10648C653003C637B /* CArchDaemonNone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CArchDaemonNone.h; path = lib/arch/CArchDaemonNone.h; sourceTree = ""; }; + 4C5E86CB0648C725003C637B /* Version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Version.cpp; path = lib/common/Version.cpp; sourceTree = ""; }; + 4C5E86CD0648C7B9003C637B /* CEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CEvent.cpp; path = lib/base/CEvent.cpp; sourceTree = ""; }; + 4C5E86CE0648C7B9003C637B /* CEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CEvent.h; path = lib/base/CEvent.h; sourceTree = ""; }; + 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CEventQueue.cpp; path = lib/base/CEventQueue.cpp; sourceTree = ""; }; + 4C5E86D00648C7B9003C637B /* CEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CEventQueue.h; path = lib/base/CEventQueue.h; sourceTree = ""; }; + 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFunctionEventJob.cpp; path = lib/base/CFunctionEventJob.cpp; sourceTree = ""; }; + 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFunctionEventJob.h; path = lib/base/CFunctionEventJob.h; sourceTree = ""; }; + 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFunctionJob.cpp; path = lib/base/CFunctionJob.cpp; sourceTree = ""; }; + 4C5E86D40648C7B9003C637B /* CFunctionJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFunctionJob.h; path = lib/base/CFunctionJob.h; sourceTree = ""; }; + 4C5E86D50648C7B9003C637B /* CLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CLog.cpp; path = lib/base/CLog.cpp; sourceTree = ""; }; + 4C5E86D60648C7B9003C637B /* CLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CLog.h; path = lib/base/CLog.h; sourceTree = ""; }; + 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPriorityQueue.h; path = lib/base/CPriorityQueue.h; sourceTree = ""; }; + 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSimpleEventQueueBuffer.cpp; path = lib/base/CSimpleEventQueueBuffer.cpp; sourceTree = ""; }; + 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSimpleEventQueueBuffer.h; path = lib/base/CSimpleEventQueueBuffer.h; sourceTree = ""; }; + 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStopwatch.cpp; path = lib/base/CStopwatch.cpp; sourceTree = ""; }; + 4C5E86DB0648C7B9003C637B /* CStopwatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStopwatch.h; path = lib/base/CStopwatch.h; sourceTree = ""; }; + 4C5E86DC0648C7B9003C637B /* CString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CString.h; path = lib/base/CString.h; sourceTree = ""; }; + 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStringUtil.cpp; path = lib/base/CStringUtil.cpp; sourceTree = ""; }; + 4C5E86DE0648C7B9003C637B /* CStringUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStringUtil.h; path = lib/base/CStringUtil.h; sourceTree = ""; }; + 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CUnicode.cpp; path = lib/base/CUnicode.cpp; sourceTree = ""; }; + 4C5E86E00648C7B9003C637B /* CUnicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CUnicode.h; path = lib/base/CUnicode.h; sourceTree = ""; }; + 4C5E86E10648C7B9003C637B /* IEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventJob.h; path = lib/base/IEventJob.h; sourceTree = ""; }; + 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IEventQueue.cpp; path = lib/base/IEventQueue.cpp; sourceTree = ""; }; + 4C5E86E30648C7B9003C637B /* IEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventQueue.h; path = lib/base/IEventQueue.h; sourceTree = ""; }; + 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventQueueBuffer.h; path = lib/base/IEventQueueBuffer.h; sourceTree = ""; }; + 4C5E86E50648C7B9003C637B /* IJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJob.h; path = lib/base/IJob.h; sourceTree = ""; }; + 4C5E86E60648C7B9003C637B /* ILogOutputter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ILogOutputter.h; path = lib/base/ILogOutputter.h; sourceTree = ""; }; + 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LogOutputters.cpp; path = lib/base/LogOutputters.cpp; sourceTree = ""; }; + 4C5E86E80648C7B9003C637B /* LogOutputters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LogOutputters.h; path = lib/base/LogOutputters.h; sourceTree = ""; }; + 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMethodEventJob.h; path = lib/base/TMethodEventJob.h; sourceTree = ""; }; + 4C5E86EA0648C7B9003C637B /* TMethodJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMethodJob.h; path = lib/base/TMethodJob.h; sourceTree = ""; }; + 4C5E86EB0648C7B9003C637B /* XBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XBase.cpp; path = lib/base/XBase.cpp; sourceTree = ""; }; + 4C5E86EC0648C7B9003C637B /* XBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XBase.h; path = lib/base/XBase.h; sourceTree = ""; }; + 4C5E870D0648C809003C637B /* CCondVar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CCondVar.cpp; path = lib/mt/CCondVar.cpp; sourceTree = ""; }; + 4C5E870E0648C809003C637B /* CCondVar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCondVar.h; path = lib/mt/CCondVar.h; sourceTree = ""; }; + 4C5E870F0648C809003C637B /* CLock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CLock.cpp; path = lib/mt/CLock.cpp; sourceTree = ""; }; + 4C5E87100648C809003C637B /* CLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CLock.h; path = lib/mt/CLock.h; sourceTree = ""; }; + 4C5E87110648C809003C637B /* CMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CMutex.cpp; path = lib/mt/CMutex.cpp; sourceTree = ""; }; + 4C5E87120648C809003C637B /* CMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CMutex.h; path = lib/mt/CMutex.h; sourceTree = ""; }; + 4C5E87130648C809003C637B /* CThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CThread.cpp; path = lib/mt/CThread.cpp; sourceTree = ""; }; + 4C5E87140648C809003C637B /* CThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CThread.h; path = lib/mt/CThread.h; sourceTree = ""; }; + 4C5E87150648C809003C637B /* XMT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XMT.cpp; path = lib/mt/XMT.cpp; sourceTree = ""; }; + 4C5E87160648C809003C637B /* XMT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMT.h; path = lib/mt/XMT.h; sourceTree = ""; }; + 4C5E87170648C809003C637B /* XThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XThread.h; path = lib/mt/XThread.h; sourceTree = ""; }; + 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStreamBuffer.cpp; path = lib/io/CStreamBuffer.cpp; sourceTree = ""; }; + 4C5E87240648C83C003C637B /* CStreamBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStreamBuffer.h; path = lib/io/CStreamBuffer.h; sourceTree = ""; }; + 4C5E87250648C83C003C637B /* CStreamFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStreamFilter.cpp; path = lib/io/CStreamFilter.cpp; sourceTree = ""; }; + 4C5E87260648C83C003C637B /* CStreamFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStreamFilter.h; path = lib/io/CStreamFilter.h; sourceTree = ""; }; + 4C5E87270648C83C003C637B /* IStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IStream.cpp; path = lib/io/IStream.cpp; sourceTree = ""; }; + 4C5E87280648C83C003C637B /* IStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IStream.h; path = lib/io/IStream.h; sourceTree = ""; }; + 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IStreamFilterFactory.h; path = lib/io/IStreamFilterFactory.h; sourceTree = ""; }; + 4C5E872A0648C83C003C637B /* XIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XIO.cpp; path = lib/io/XIO.cpp; sourceTree = ""; }; + 4C5E872B0648C83C003C637B /* XIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XIO.h; path = lib/io/XIO.h; sourceTree = ""; }; + 4C5E87350648C870003C637B /* CNetworkAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CNetworkAddress.cpp; path = lib/net/CNetworkAddress.cpp; sourceTree = ""; }; + 4C5E87360648C870003C637B /* CNetworkAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CNetworkAddress.h; path = lib/net/CNetworkAddress.h; sourceTree = ""; }; + 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSocketMultiplexer.cpp; path = lib/net/CSocketMultiplexer.cpp; sourceTree = ""; }; + 4C5E87380648C870003C637B /* CSocketMultiplexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSocketMultiplexer.h; path = lib/net/CSocketMultiplexer.h; sourceTree = ""; }; + 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPListenSocket.cpp; path = lib/net/CTCPListenSocket.cpp; sourceTree = ""; }; + 4C5E873A0648C870003C637B /* CTCPListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPListenSocket.h; path = lib/net/CTCPListenSocket.h; sourceTree = ""; }; + 4C5E873B0648C870003C637B /* CTCPSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPSocket.cpp; path = lib/net/CTCPSocket.cpp; sourceTree = ""; }; + 4C5E873C0648C870003C637B /* CTCPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPSocket.h; path = lib/net/CTCPSocket.h; sourceTree = ""; }; + 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPSocketFactory.cpp; path = lib/net/CTCPSocketFactory.cpp; sourceTree = ""; }; + 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPSocketFactory.h; path = lib/net/CTCPSocketFactory.h; sourceTree = ""; }; + 4C5E873F0648C870003C637B /* IDataSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IDataSocket.cpp; path = lib/net/IDataSocket.cpp; sourceTree = ""; }; + 4C5E87400648C870003C637B /* IDataSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDataSocket.h; path = lib/net/IDataSocket.h; sourceTree = ""; }; + 4C5E87410648C870003C637B /* IListenSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IListenSocket.cpp; path = lib/net/IListenSocket.cpp; sourceTree = ""; }; + 4C5E87420648C870003C637B /* IListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IListenSocket.h; path = lib/net/IListenSocket.h; sourceTree = ""; }; + 4C5E87430648C870003C637B /* ISocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ISocket.cpp; path = lib/net/ISocket.cpp; sourceTree = ""; }; + 4C5E87440648C870003C637B /* ISocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocket.h; path = lib/net/ISocket.h; sourceTree = ""; }; + 4C5E87450648C870003C637B /* ISocketFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocketFactory.h; path = lib/net/ISocketFactory.h; sourceTree = ""; }; + 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocketMultiplexerJob.h; path = lib/net/ISocketMultiplexerJob.h; sourceTree = ""; }; + 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSocketMultiplexerMethodJob.h; path = lib/net/TSocketMultiplexerMethodJob.h; sourceTree = ""; }; + 4C5E87480648C870003C637B /* XSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XSocket.cpp; path = lib/net/XSocket.cpp; sourceTree = ""; }; + 4C5E87490648C870003C637B /* XSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XSocket.h; path = lib/net/XSocket.h; sourceTree = ""; }; + 4C5E875F0648C8BD003C637B /* CClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClipboard.cpp; path = lib/synergy/CClipboard.cpp; sourceTree = ""; }; + 4C5E87600648C8BD003C637B /* CClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClipboard.h; path = lib/synergy/CClipboard.h; sourceTree = ""; }; + 4C5E87610648C8BD003C637B /* CKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CKeyState.cpp; path = lib/synergy/CKeyState.cpp; sourceTree = ""; }; + 4C5E87620648C8BD003C637B /* CKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CKeyState.h; path = lib/synergy/CKeyState.h; sourceTree = ""; }; + 4C5E87630648C8BD003C637B /* ClipboardTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClipboardTypes.h; path = lib/synergy/ClipboardTypes.h; sourceTree = ""; }; + 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPacketStreamFilter.cpp; path = lib/synergy/CPacketStreamFilter.cpp; sourceTree = ""; }; + 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPacketStreamFilter.h; path = lib/synergy/CPacketStreamFilter.h; sourceTree = ""; }; + 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPlatformScreen.cpp; path = lib/synergy/CPlatformScreen.cpp; sourceTree = ""; }; + 4C5E87670648C8BD003C637B /* CPlatformScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPlatformScreen.h; path = lib/synergy/CPlatformScreen.h; sourceTree = ""; }; + 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CProtocolUtil.cpp; path = lib/synergy/CProtocolUtil.cpp; sourceTree = ""; }; + 4C5E87690648C8BD003C637B /* CProtocolUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CProtocolUtil.h; path = lib/synergy/CProtocolUtil.h; sourceTree = ""; }; + 4C5E876A0648C8BD003C637B /* CScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CScreen.cpp; path = lib/synergy/CScreen.cpp; sourceTree = ""; }; + 4C5E876B0648C8BD003C637B /* CScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CScreen.h; path = lib/synergy/CScreen.h; sourceTree = ""; }; + 4C5E876C0648C8BD003C637B /* IClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IClient.h; path = lib/synergy/IClient.h; sourceTree = ""; }; + 4C5E876D0648C8BD003C637B /* IClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IClipboard.cpp; path = lib/synergy/IClipboard.cpp; sourceTree = ""; }; + 4C5E876E0648C8BD003C637B /* IClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IClipboard.h; path = lib/synergy/IClipboard.h; sourceTree = ""; }; + 4C5E876F0648C8BD003C637B /* IKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IKeyState.cpp; path = lib/synergy/IKeyState.cpp; sourceTree = ""; }; + 4C5E87700648C8BD003C637B /* IKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IKeyState.h; path = lib/synergy/IKeyState.h; sourceTree = ""; }; + 4C5E87710648C8BD003C637B /* IPlatformScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPlatformScreen.h; path = lib/synergy/IPlatformScreen.h; sourceTree = ""; }; + 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IPrimaryScreen.cpp; path = lib/synergy/IPrimaryScreen.cpp; sourceTree = ""; }; + 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPrimaryScreen.h; path = lib/synergy/IPrimaryScreen.h; sourceTree = ""; }; + 4C5E87740648C8BD003C637B /* IScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IScreen.cpp; path = lib/synergy/IScreen.cpp; sourceTree = ""; }; + 4C5E87750648C8BD003C637B /* IScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IScreen.h; path = lib/synergy/IScreen.h; sourceTree = ""; }; + 4C5E87760648C8BD003C637B /* IScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IScreenSaver.h; path = lib/synergy/IScreenSaver.h; sourceTree = ""; }; + 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISecondaryScreen.h; path = lib/synergy/ISecondaryScreen.h; sourceTree = ""; }; + 4C5E87780648C8BD003C637B /* KeyTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KeyTypes.h; path = lib/synergy/KeyTypes.h; sourceTree = ""; }; + 4C5E87790648C8BD003C637B /* MouseTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MouseTypes.h; path = lib/synergy/MouseTypes.h; sourceTree = ""; }; + 4C5E877A0648C8BD003C637B /* OptionTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionTypes.h; path = lib/synergy/OptionTypes.h; sourceTree = ""; }; + 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProtocolTypes.h; path = lib/synergy/ProtocolTypes.h; sourceTree = ""; }; + 4C5E877C0648C8BD003C637B /* XScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XScreen.cpp; path = lib/synergy/XScreen.cpp; sourceTree = ""; }; + 4C5E877D0648C8BD003C637B /* XScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XScreen.h; path = lib/synergy/XScreen.h; sourceTree = ""; }; + 4C5E877E0648C8BD003C637B /* XSynergy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XSynergy.cpp; path = lib/synergy/XSynergy.cpp; sourceTree = ""; }; + 4C5E877F0648C8BD003C637B /* XSynergy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XSynergy.h; path = lib/synergy/XSynergy.h; sourceTree = ""; }; + 4C5E87A10648C913003C637B /* COSXClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboard.cpp; path = lib/platform/COSXClipboard.cpp; sourceTree = ""; }; + 4C5E87A20648C913003C637B /* COSXClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboard.h; path = lib/platform/COSXClipboard.h; sourceTree = ""; }; + 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXEventQueueBuffer.cpp; path = lib/platform/COSXEventQueueBuffer.cpp; sourceTree = ""; }; + 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXEventQueueBuffer.h; path = lib/platform/COSXEventQueueBuffer.h; sourceTree = ""; }; + 4C5E87A50648C913003C637B /* COSXKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXKeyState.cpp; path = lib/platform/COSXKeyState.cpp; sourceTree = ""; }; + 4C5E87A60648C913003C637B /* COSXKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXKeyState.h; path = lib/platform/COSXKeyState.h; sourceTree = ""; }; + 4C5E87A70648C913003C637B /* COSXScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXScreen.cpp; path = lib/platform/COSXScreen.cpp; sourceTree = ""; }; + 4C5E87A80648C913003C637B /* COSXScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXScreen.h; path = lib/platform/COSXScreen.h; sourceTree = ""; }; + 4C5E87B90648C969003C637B /* CClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClient.cpp; path = lib/client/CClient.cpp; sourceTree = ""; }; + 4C5E87BA0648C969003C637B /* CClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClient.h; path = lib/client/CClient.h; sourceTree = ""; }; + 4C5E87BB0648C969003C637B /* CServerProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServerProxy.cpp; path = lib/client/CServerProxy.cpp; sourceTree = ""; }; + 4C5E87BC0648C969003C637B /* CServerProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServerProxy.h; path = lib/client/CServerProxy.h; sourceTree = ""; }; + 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientTaskBarReceiver.cpp; path = cmd/synergyc/CClientTaskBarReceiver.cpp; sourceTree = ""; }; + 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientTaskBarReceiver.h; path = cmd/synergyc/CClientTaskBarReceiver.h; sourceTree = ""; }; + 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClientTaskBarReceiver.cpp; path = cmd/synergyc/COSXClientTaskBarReceiver.cpp; sourceTree = ""; }; + 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClientTaskBarReceiver.h; path = cmd/synergyc/COSXClientTaskBarReceiver.h; sourceTree = ""; }; + 4C5E87C50648C9D2003C637B /* synergyc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = synergyc.cpp; path = cmd/synergyc/synergyc.cpp; sourceTree = ""; }; + 4C5E87D00648CA75003C637B /* XArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XArch.cpp; path = lib/arch/XArch.cpp; sourceTree = ""; }; + 4C7D7CD9066319560097CA11 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = common.h; path = lib/common/common.h; sourceTree = ""; }; + 4CB43778063E406A00969041 /* libclient.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libclient.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB437B5063E443800969041 /* libarch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libarch.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB437D1063E44C200969041 /* libbase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbase.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43801063E45F200969041 /* libmt.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmt.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43824063E46AB00969041 /* libio.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libio.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43848063E475800969041 /* libnet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnet.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB4386E063E47F800969041 /* libsynergy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsynergy.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB438B1063E488600969041 /* libplatform.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libplatform.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43914063E497700969041 /* synergyc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = synergyc; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43923063E4B1300969041 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardAnyTextConverter.cpp; path = lib/platform/COSXClipboardAnyTextConverter.cpp; sourceTree = ""; }; + 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardAnyTextConverter.h; path = lib/platform/COSXClipboardAnyTextConverter.h; sourceTree = ""; }; + 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardTextConverter.cpp; path = lib/platform/COSXClipboardTextConverter.cpp; sourceTree = ""; }; + 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardTextConverter.h; path = lib/platform/COSXClipboardTextConverter.h; sourceTree = ""; }; + 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardUTF16Converter.cpp; path = lib/platform/COSXClipboardUTF16Converter.cpp; sourceTree = ""; }; + 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardUTF16Converter.h; path = lib/platform/COSXClipboardUTF16Converter.h; sourceTree = ""; }; + 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; + 6828A1A8081DF7AB003D9989 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 6828A1A9081DF7AB003D9989 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; + 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = OSXScreenSaverControl.h; path = lib/platform/OSXScreenSaverControl.h; sourceTree = ""; }; + 6828A237081DFAF9003D9989 /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = /System/Library/Frameworks/ScreenSaver.framework; sourceTree = ""; }; + 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_3.cpp; path = lib/server/CClientProxy1_3.cpp; sourceTree = ""; }; + 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_3.h; path = lib/server/CClientProxy1_3.h; sourceTree = ""; }; + 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchSystemUnix.cpp; path = lib/arch/CArchSystemUnix.cpp; sourceTree = ""; }; + 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchSystemUnix.h; path = lib/arch/CArchSystemUnix.h; sourceTree = ""; }; + 68871678073EBF6F00C5ABE7 /* IArchConsole.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchConsole.h; path = lib/arch/IArchConsole.h; sourceTree = ""; }; + 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchDaemon.h; path = lib/arch/IArchDaemon.h; sourceTree = ""; }; + 6887167A073EBF6F00C5ABE7 /* IArchFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchFile.h; path = lib/arch/IArchFile.h; sourceTree = ""; }; + 6887167B073EBF6F00C5ABE7 /* IArchLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchLog.h; path = lib/arch/IArchLog.h; sourceTree = ""; }; + 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchMultithread.h; path = lib/arch/IArchMultithread.h; sourceTree = ""; }; + 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchNetwork.h; path = lib/arch/IArchNetwork.h; sourceTree = ""; }; + 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchSleep.h; path = lib/arch/IArchSleep.h; sourceTree = ""; }; + 6887167F073EBF6F00C5ABE7 /* IArchString.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchString.h; path = lib/arch/IArchString.h; sourceTree = ""; }; + 68871680073EBF6F00C5ABE7 /* IArchSystem.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchSystem.h; path = lib/arch/IArchSystem.h; sourceTree = ""; }; + 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTaskBar.h; path = lib/arch/IArchTaskBar.h; sourceTree = ""; }; + 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTaskBarReceiver.h; path = lib/arch/IArchTaskBarReceiver.h; sourceTree = ""; }; + 68871683073EBF7000C5ABE7 /* IArchTime.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTime.h; path = lib/arch/IArchTime.h; sourceTree = ""; }; + 68871684073EBF7000C5ABE7 /* XArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = XArch.h; path = lib/arch/XArch.h; sourceTree = ""; }; + 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = MacOSXPrecomp.h; path = lib/common/MacOSXPrecomp.h; sourceTree = ""; }; + 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CBaseClientProxy.cpp; path = lib/server/CBaseClientProxy.cpp; sourceTree = ""; }; + 688925A809DF64B6002EB18C /* CBaseClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CBaseClientProxy.h; path = lib/server/CBaseClientProxy.h; sourceTree = ""; }; + 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchDaemonNone.cpp; path = lib/arch/CArchDaemonNone.cpp; sourceTree = ""; }; + 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchTaskBarXWindows.cpp; path = lib/arch/CArchTaskBarXWindows.cpp; sourceTree = ""; }; + 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchTaskBarXWindows.h; path = lib/arch/CArchTaskBarXWindows.h; sourceTree = ""; }; + 689D73320956490500FFAB1D /* CInputFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CInputFilter.cpp; path = lib/server/CInputFilter.cpp; sourceTree = ""; }; + 689D73330956490500FFAB1D /* CInputFilter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CInputFilter.h; path = lib/server/CInputFilter.h; sourceTree = ""; }; + 689D7337095649AF00FFAB1D /* CKeyMap.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CKeyMap.cpp; path = lib/synergy/CKeyMap.cpp; sourceTree = ""; }; + 689D7338095649AF00FFAB1D /* CKeyMap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CKeyMap.h; path = lib/synergy/CKeyMap.h; sourceTree = ""; }; + 68D87A6409A00D8800856835 /* KeyTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = KeyTypes.cpp; path = lib/synergy/KeyTypes.cpp; sourceTree = ""; }; + 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = COSXScreenSaverUtil.m; path = lib/platform/COSXScreenSaverUtil.m; sourceTree = ""; }; + 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = COSXScreenSaverUtil.h; path = lib/platform/COSXScreenSaverUtil.h; sourceTree = ""; }; + 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = COSXScreenSaver.cpp; path = lib/platform/COSXScreenSaver.cpp; sourceTree = ""; }; + 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = COSXScreenSaver.h; path = lib/platform/COSXScreenSaver.h; sourceTree = ""; }; + 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ProtocolTypes.cpp; path = lib/synergy/ProtocolTypes.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4C537F5A064E2B0700D3815C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F64064E2B4B00D3815C /* Carbon.framework in Frameworks */, + 4C537F5B064E2B4200D3815C /* libcommon.a in Frameworks */, + 4C537F5C064E2B4200D3815C /* libarch.a in Frameworks */, + 4C537F5D064E2B4200D3815C /* libbase.a in Frameworks */, + 4C537F5E064E2B4200D3815C /* libmt.a in Frameworks */, + 4C537F5F064E2B4200D3815C /* libio.a in Frameworks */, + 4C537F60064E2B4200D3815C /* libnet.a in Frameworks */, + 4C537F61064E2B4200D3815C /* libserver.a in Frameworks */, + 4C537F62064E2B4200D3815C /* libsynergy.a in Frameworks */, + 4C537F63064E2B4200D3815C /* libplatform.a in Frameworks */, + 6828A1AD081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */, + 6828A1AE081DF7AB003D9989 /* Foundation.framework in Frameworks */, + 6828A1AF081DF7AB003D9989 /* IOKit.framework in Frameworks */, + 6828A239081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */, + CAB4475F081E367700724B8D /* libclient.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43930063E4B4300969041 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CB43934063E4B4A00969041 /* Carbon.framework in Frameworks */, + 4C5E87CF0648CA4B003C637B /* libcommon.a in Frameworks */, + 6828A1AA081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */, + 6828A1AB081DF7AB003D9989 /* Foundation.framework in Frameworks */, + 6828A1AC081DF7AB003D9989 /* IOKit.framework in Frameworks */, + 6828A238081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */, + CAB44760081E368100724B8D /* libarch.a in Frameworks */, + CAB44761081E368200724B8D /* libbase.a in Frameworks */, + CAB44762081E368300724B8D /* libclient.a in Frameworks */, + CAB44764081E368700724B8D /* libio.a in Frameworks */, + CAB44765081E368800724B8D /* libmt.a in Frameworks */, + CAB44766081E368A00724B8D /* libnet.a in Frameworks */, + CAB44767081E368B00724B8D /* libplatform.a in Frameworks */, + CAB44768081E368D00724B8D /* libsynergy.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0249A662FF388D9811CA2CEA /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 6828A237081DFAF9003D9989 /* ScreenSaver.framework */, + 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */, + 6828A1A8081DF7AB003D9989 /* Foundation.framework */, + 6828A1A9081DF7AB003D9989 /* IOKit.framework */, + 4CB43923063E4B1300969041 /* Carbon.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 08FB7794FE84155DC02AAC07 /* synergy */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* Source */, + 0249A662FF388D9811CA2CEA /* External Frameworks and Libraries */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = synergy; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 4CB4378B063E432C00969041 /* Arch */, + 4CB437D2063E44E400969041 /* Base */, + 4CB43779063E40B600969041 /* Client */, + 4C5E86CA0648C6FB003C637B /* Common */, + 4CB4381E063E469300969041 /* IO */, + 4CB437FB063E45D700969041 /* MT */, + 4CB43849063E478900969041 /* Net */, + 4CB438AD063E487200969041 /* Platform */, + 4C537F2B064E29C900D3815C /* Server */, + 4CB43866063E47C800969041 /* Synergy */, + 4CB43916063E4A1A00969041 /* SynergyC */, + 4C537F07064E283300D3815C /* SynergyS */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 4CB43778063E406A00969041 /* libclient.a */, + 4C5E868D0648C2ED003C637B /* libcommon.a */, + 4CB437B5063E443800969041 /* libarch.a */, + 4CB437D1063E44C200969041 /* libbase.a */, + 4CB43801063E45F200969041 /* libmt.a */, + 4CB43824063E46AB00969041 /* libio.a */, + 4CB43848063E475800969041 /* libnet.a */, + 4C537F31064E29F800D3815C /* libserver.a */, + 4CB4386E063E47F800969041 /* libsynergy.a */, + 4CB438B1063E488600969041 /* libplatform.a */, + 4CB43914063E497700969041 /* synergyc */, + 4C537F0D064E286700D3815C /* synergys */, + ); + name = Products; + sourceTree = ""; + }; + 4C537F07064E283300D3815C /* SynergyS */ = { + isa = PBXGroup; + children = ( + 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */, + 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */, + 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */, + 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */, + 4C537F13064E293000D3815C /* synergys.cpp */, + ); + name = SynergyS; + sourceTree = ""; + }; + 4C537F2B064E29C900D3815C /* Server */ = { + isa = PBXGroup; + children = ( + 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */, + 688925A809DF64B6002EB18C /* CBaseClientProxy.h */, + 4C537F32064E2A0F00D3815C /* CClientListener.cpp */, + 4C537F33064E2A0F00D3815C /* CClientListener.h */, + 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */, + 4C537F35064E2A0F00D3815C /* CClientProxy.h */, + 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */, + 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */, + 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */, + 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */, + 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */, + 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */, + 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */, + 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */, + 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */, + 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */, + 4C537F3E064E2A0F00D3815C /* CConfig.cpp */, + 4C537F3F064E2A0F00D3815C /* CConfig.h */, + 689D73320956490500FFAB1D /* CInputFilter.cpp */, + 689D73330956490500FFAB1D /* CInputFilter.h */, + 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */, + 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */, + 4C537F42064E2A0F00D3815C /* CServer.cpp */, + 4C537F43064E2A0F00D3815C /* CServer.h */, + ); + name = Server; + sourceTree = ""; + }; + 4C5E86CA0648C6FB003C637B /* Common */ = { + isa = PBXGroup; + children = ( + 4C7D7CD9066319560097CA11 /* common.h */, + 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */, + 4C5E86CB0648C725003C637B /* Version.cpp */, + ); + name = Common; + sourceTree = ""; + }; + 4CB43779063E40B600969041 /* Client */ = { + isa = PBXGroup; + children = ( + 4C5E87B90648C969003C637B /* CClient.cpp */, + 4C5E87BA0648C969003C637B /* CClient.h */, + 4C5E87BB0648C969003C637B /* CServerProxy.cpp */, + 4C5E87BC0648C969003C637B /* CServerProxy.h */, + ); + name = Client; + sourceTree = ""; + }; + 4CB4378B063E432C00969041 /* Arch */ = { + isa = PBXGroup; + children = ( + 4C5E86920648C412003C637B /* CArch.cpp */, + 4C5E86930648C412003C637B /* CArch.h */, + 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */, + 4C5E86950648C412003C637B /* CArchConsoleUnix.h */, + 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */, + 4C5E86C10648C653003C637B /* CArchDaemonNone.h */, + 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */, + 4C5E86970648C412003C637B /* CArchDaemonUnix.h */, + 4C5E86980648C412003C637B /* CArchFileUnix.cpp */, + 4C5E86990648C412003C637B /* CArchFileUnix.h */, + 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */, + 4C5E869B0648C412003C637B /* CArchLogUnix.h */, + 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */, + 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */, + 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */, + 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */, + 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */, + 4C5E86A10648C412003C637B /* CArchSleepUnix.h */, + 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */, + 4C5E86A30648C412003C637B /* CArchStringUnix.h */, + 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */, + 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */, + 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */, + 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */, + 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */, + 4C5E86A50648C412003C637B /* CArchTimeUnix.h */, + 68871678073EBF6F00C5ABE7 /* IArchConsole.h */, + 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */, + 6887167A073EBF6F00C5ABE7 /* IArchFile.h */, + 6887167B073EBF6F00C5ABE7 /* IArchLog.h */, + 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */, + 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */, + 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */, + 6887167F073EBF6F00C5ABE7 /* IArchString.h */, + 68871680073EBF6F00C5ABE7 /* IArchSystem.h */, + 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */, + 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */, + 68871683073EBF7000C5ABE7 /* IArchTime.h */, + 68871684073EBF7000C5ABE7 /* XArch.h */, + 4C5E87D00648CA75003C637B /* XArch.cpp */, + 4C5E86A70648C412003C637B /* XArchUnix.cpp */, + 4C5E86A80648C412003C637B /* XArchUnix.h */, + ); + name = Arch; + sourceTree = ""; + }; + 4CB437D2063E44E400969041 /* Base */ = { + isa = PBXGroup; + children = ( + 4C5E86CD0648C7B9003C637B /* CEvent.cpp */, + 4C5E86CE0648C7B9003C637B /* CEvent.h */, + 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */, + 4C5E86D00648C7B9003C637B /* CEventQueue.h */, + 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */, + 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */, + 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */, + 4C5E86D40648C7B9003C637B /* CFunctionJob.h */, + 4C5E86D50648C7B9003C637B /* CLog.cpp */, + 4C5E86D60648C7B9003C637B /* CLog.h */, + 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */, + 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */, + 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */, + 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */, + 4C5E86DB0648C7B9003C637B /* CStopwatch.h */, + 4C5E86DC0648C7B9003C637B /* CString.h */, + 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */, + 4C5E86DE0648C7B9003C637B /* CStringUtil.h */, + 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */, + 4C5E86E00648C7B9003C637B /* CUnicode.h */, + 4C5E86E10648C7B9003C637B /* IEventJob.h */, + 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */, + 4C5E86E30648C7B9003C637B /* IEventQueue.h */, + 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */, + 4C5E86E50648C7B9003C637B /* IJob.h */, + 4C5E86E60648C7B9003C637B /* ILogOutputter.h */, + 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */, + 4C5E86E80648C7B9003C637B /* LogOutputters.h */, + 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */, + 4C5E86EA0648C7B9003C637B /* TMethodJob.h */, + 4C5E86EB0648C7B9003C637B /* XBase.cpp */, + 4C5E86EC0648C7B9003C637B /* XBase.h */, + ); + name = Base; + sourceTree = ""; + }; + 4CB437FB063E45D700969041 /* MT */ = { + isa = PBXGroup; + children = ( + 4C5E870D0648C809003C637B /* CCondVar.cpp */, + 4C5E870E0648C809003C637B /* CCondVar.h */, + 4C5E870F0648C809003C637B /* CLock.cpp */, + 4C5E87100648C809003C637B /* CLock.h */, + 4C5E87110648C809003C637B /* CMutex.cpp */, + 4C5E87120648C809003C637B /* CMutex.h */, + 4C5E87130648C809003C637B /* CThread.cpp */, + 4C5E87140648C809003C637B /* CThread.h */, + 4C5E87150648C809003C637B /* XMT.cpp */, + 4C5E87160648C809003C637B /* XMT.h */, + 4C5E87170648C809003C637B /* XThread.h */, + ); + name = MT; + sourceTree = ""; + }; + 4CB4381E063E469300969041 /* IO */ = { + isa = PBXGroup; + children = ( + 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */, + 4C5E87240648C83C003C637B /* CStreamBuffer.h */, + 4C5E87250648C83C003C637B /* CStreamFilter.cpp */, + 4C5E87260648C83C003C637B /* CStreamFilter.h */, + 4C5E87270648C83C003C637B /* IStream.cpp */, + 4C5E87280648C83C003C637B /* IStream.h */, + 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */, + 4C5E872A0648C83C003C637B /* XIO.cpp */, + 4C5E872B0648C83C003C637B /* XIO.h */, + ); + name = IO; + sourceTree = ""; + }; + 4CB43849063E478900969041 /* Net */ = { + isa = PBXGroup; + children = ( + 4C5E87350648C870003C637B /* CNetworkAddress.cpp */, + 4C5E87360648C870003C637B /* CNetworkAddress.h */, + 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */, + 4C5E87380648C870003C637B /* CSocketMultiplexer.h */, + 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */, + 4C5E873A0648C870003C637B /* CTCPListenSocket.h */, + 4C5E873B0648C870003C637B /* CTCPSocket.cpp */, + 4C5E873C0648C870003C637B /* CTCPSocket.h */, + 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */, + 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */, + 4C5E873F0648C870003C637B /* IDataSocket.cpp */, + 4C5E87400648C870003C637B /* IDataSocket.h */, + 4C5E87410648C870003C637B /* IListenSocket.cpp */, + 4C5E87420648C870003C637B /* IListenSocket.h */, + 4C5E87430648C870003C637B /* ISocket.cpp */, + 4C5E87440648C870003C637B /* ISocket.h */, + 4C5E87450648C870003C637B /* ISocketFactory.h */, + 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */, + 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */, + 4C5E87480648C870003C637B /* XSocket.cpp */, + 4C5E87490648C870003C637B /* XSocket.h */, + ); + name = Net; + sourceTree = ""; + }; + 4CB43866063E47C800969041 /* Synergy */ = { + isa = PBXGroup; + children = ( + 4C5E875F0648C8BD003C637B /* CClipboard.cpp */, + 4C5E87600648C8BD003C637B /* CClipboard.h */, + 689D7337095649AF00FFAB1D /* CKeyMap.cpp */, + 689D7338095649AF00FFAB1D /* CKeyMap.h */, + 4C5E87610648C8BD003C637B /* CKeyState.cpp */, + 4C5E87620648C8BD003C637B /* CKeyState.h */, + 4C5E87630648C8BD003C637B /* ClipboardTypes.h */, + 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */, + 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */, + 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */, + 4C5E87670648C8BD003C637B /* CPlatformScreen.h */, + 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */, + 4C5E87690648C8BD003C637B /* CProtocolUtil.h */, + 4C5E876A0648C8BD003C637B /* CScreen.cpp */, + 4C5E876B0648C8BD003C637B /* CScreen.h */, + 4C5E876C0648C8BD003C637B /* IClient.h */, + 4C5E876D0648C8BD003C637B /* IClipboard.cpp */, + 4C5E876E0648C8BD003C637B /* IClipboard.h */, + 4C5E876F0648C8BD003C637B /* IKeyState.cpp */, + 4C5E87700648C8BD003C637B /* IKeyState.h */, + 4C5E87710648C8BD003C637B /* IPlatformScreen.h */, + 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */, + 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */, + 4C5E87740648C8BD003C637B /* IScreen.cpp */, + 4C5E87750648C8BD003C637B /* IScreen.h */, + 4C5E87760648C8BD003C637B /* IScreenSaver.h */, + 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */, + 68D87A6409A00D8800856835 /* KeyTypes.cpp */, + 4C5E87780648C8BD003C637B /* KeyTypes.h */, + 4C5E87790648C8BD003C637B /* MouseTypes.h */, + 4C5E877A0648C8BD003C637B /* OptionTypes.h */, + 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */, + 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */, + 4C5E877C0648C8BD003C637B /* XScreen.cpp */, + 4C5E877D0648C8BD003C637B /* XScreen.h */, + 4C5E877E0648C8BD003C637B /* XSynergy.cpp */, + 4C5E877F0648C8BD003C637B /* XSynergy.h */, + ); + name = Synergy; + sourceTree = ""; + }; + 4CB438AD063E487200969041 /* Platform */ = { + isa = PBXGroup; + children = ( + 4C5E87A10648C913003C637B /* COSXClipboard.cpp */, + 4C5E87A20648C913003C637B /* COSXClipboard.h */, + 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */, + 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */, + 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */, + 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */, + 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */, + 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */, + 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */, + 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */, + 4C5E87A50648C913003C637B /* COSXKeyState.cpp */, + 4C5E87A60648C913003C637B /* COSXKeyState.h */, + 4C5E87A70648C913003C637B /* COSXScreen.cpp */, + 4C5E87A80648C913003C637B /* COSXScreen.h */, + 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */, + 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */, + 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */, + 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */, + 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */, + ); + name = Platform; + sourceTree = ""; + }; + 4CB43916063E4A1A00969041 /* SynergyC */ = { + isa = PBXGroup; + children = ( + 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */, + 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */, + 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */, + 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */, + 4C5E87C50648C9D2003C637B /* synergyc.cpp */, + ); + name = SynergyC; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 4C537F0A064E286700D3815C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F15064E293000D3815C /* COSXServerTaskBarReceiver.h in Headers */, + 4C537F17064E293000D3815C /* CServerTaskBarReceiver.h in Headers */, + 4C7D7CDA066319560097CA11 /* common.h in Headers */, + 68FBBB25089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */, + 68FBBB2B089F072D0016DF44 /* COSXScreenSaver.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F2E064E29F800D3815C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F45064E2A0F00D3815C /* CClientListener.h in Headers */, + 4C537F47064E2A0F00D3815C /* CClientProxy.h in Headers */, + 4C537F49064E2A0F00D3815C /* CClientProxy1_0.h in Headers */, + 4C537F4B064E2A0F00D3815C /* CClientProxy1_1.h in Headers */, + 4C537F4D064E2A0F00D3815C /* CClientProxy1_2.h in Headers */, + 684B63630996FB2800FE7CC7 /* CClientProxy1_3.h in Headers */, + 4C537F4F064E2A0F00D3815C /* CClientProxyUnknown.h in Headers */, + 4C537F51064E2A0F00D3815C /* CConfig.h in Headers */, + 689D73350956490500FFAB1D /* CInputFilter.h in Headers */, + 4C537F53064E2A0F00D3815C /* CPrimaryClient.h in Headers */, + 4C537F55064E2A0F00D3815C /* CServer.h in Headers */, + 4C7D7CE4066319560097CA11 /* common.h in Headers */, + 688925AA09DF64B6002EB18C /* CBaseClientProxy.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C5E868A0648C2ED003C637B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C7D7CDD066319560097CA11 /* common.h in Headers */, + 6887169B073EC03800C5ABE7 /* MacOSXPrecomp.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43775063E406A00969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87BE0648C969003C637B /* CClient.h in Headers */, + 4C5E87C00648C969003C637B /* CServerProxy.h in Headers */, + 4C7D7CE5066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437B2063E443800969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C7D7CDC066319560097CA11 /* common.h in Headers */, + 4C5E86AA0648C412003C637B /* CArch.h in Headers */, + 4C5E86AC0648C412003C637B /* CArchConsoleUnix.h in Headers */, + 4C5E86C30648C653003C637B /* CArchDaemonNone.h in Headers */, + 4C5E86AE0648C412003C637B /* CArchDaemonUnix.h in Headers */, + 4C5E86B00648C412003C637B /* CArchFileUnix.h in Headers */, + 4C5E86B20648C412003C637B /* CArchLogUnix.h in Headers */, + 4C5E86B40648C412003C637B /* CArchMultithreadPosix.h in Headers */, + 4C5E86B60648C412003C637B /* CArchNetworkBSD.h in Headers */, + 4C5E86B80648C412003C637B /* CArchSleepUnix.h in Headers */, + 4C5E86BA0648C412003C637B /* CArchStringUnix.h in Headers */, + 68871686073EBF7000C5ABE7 /* CArchSystemUnix.h in Headers */, + 68968F5F073EC484004B2F9B /* CArchTaskBarXWindows.h in Headers */, + 4C5E86BC0648C412003C637B /* CArchTimeUnix.h in Headers */, + 68871687073EBF7000C5ABE7 /* IArchConsole.h in Headers */, + 68871688073EBF7000C5ABE7 /* IArchDaemon.h in Headers */, + 68871689073EBF7000C5ABE7 /* IArchFile.h in Headers */, + 6887168A073EBF7000C5ABE7 /* IArchLog.h in Headers */, + 6887168B073EBF7000C5ABE7 /* IArchMultithread.h in Headers */, + 6887168C073EBF7000C5ABE7 /* IArchNetwork.h in Headers */, + 6887168D073EBF7000C5ABE7 /* IArchSleep.h in Headers */, + 6887168E073EBF7000C5ABE7 /* IArchString.h in Headers */, + 6887168F073EBF7000C5ABE7 /* IArchSystem.h in Headers */, + 68871690073EBF7000C5ABE7 /* IArchTaskBar.h in Headers */, + 68871691073EBF7000C5ABE7 /* IArchTaskBarReceiver.h in Headers */, + 68871692073EBF7000C5ABE7 /* IArchTime.h in Headers */, + 68871693073EBF7000C5ABE7 /* XArch.h in Headers */, + 4C5E86BF0648C412003C637B /* XArchUnix.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437CE063E44C200969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86EE0648C7B9003C637B /* CEvent.h in Headers */, + 4C5E86F00648C7B9003C637B /* CEventQueue.h in Headers */, + 4C5E86F20648C7B9003C637B /* CFunctionEventJob.h in Headers */, + 4C5E86F40648C7B9003C637B /* CFunctionJob.h in Headers */, + 4C5E86F60648C7B9003C637B /* CLog.h in Headers */, + 4C5E86F70648C7B9003C637B /* CPriorityQueue.h in Headers */, + 4C5E86F90648C7B9003C637B /* CSimpleEventQueueBuffer.h in Headers */, + 4C5E86FB0648C7B9003C637B /* CStopwatch.h in Headers */, + 4C5E86FC0648C7B9003C637B /* CString.h in Headers */, + 4C5E86FE0648C7B9003C637B /* CStringUtil.h in Headers */, + 4C5E87000648C7B9003C637B /* CUnicode.h in Headers */, + 4C5E87010648C7B9003C637B /* IEventJob.h in Headers */, + 4C5E87030648C7B9003C637B /* IEventQueue.h in Headers */, + 4C5E87040648C7B9003C637B /* IEventQueueBuffer.h in Headers */, + 4C5E87050648C7B9003C637B /* IJob.h in Headers */, + 4C5E87060648C7B9003C637B /* ILogOutputter.h in Headers */, + 4C5E87080648C7B9003C637B /* LogOutputters.h in Headers */, + 4C5E87090648C7B9003C637B /* TMethodEventJob.h in Headers */, + 4C5E870A0648C7B9003C637B /* TMethodJob.h in Headers */, + 4C5E870C0648C7B9003C637B /* XBase.h in Headers */, + 4C7D7CDE066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437FE063E45F200969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87190648C809003C637B /* CCondVar.h in Headers */, + 4C5E871B0648C809003C637B /* CLock.h in Headers */, + 4C5E871D0648C809003C637B /* CMutex.h in Headers */, + 4C5E871F0648C809003C637B /* CThread.h in Headers */, + 4C5E87210648C809003C637B /* XMT.h in Headers */, + 4C5E87220648C809003C637B /* XThread.h in Headers */, + 4C7D7CDF066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43821063E46AB00969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E872D0648C83C003C637B /* CStreamBuffer.h in Headers */, + 4C5E872F0648C83C003C637B /* CStreamFilter.h in Headers */, + 4C5E87310648C83C003C637B /* IStream.h in Headers */, + 4C5E87320648C83C003C637B /* IStreamFilterFactory.h in Headers */, + 4C5E87340648C83C003C637B /* XIO.h in Headers */, + 4C7D7CE0066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43845063E475800969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E874B0648C870003C637B /* CNetworkAddress.h in Headers */, + 4C5E874D0648C870003C637B /* CSocketMultiplexer.h in Headers */, + 4C5E874F0648C870003C637B /* CTCPListenSocket.h in Headers */, + 4C5E87510648C870003C637B /* CTCPSocket.h in Headers */, + 4C5E87530648C870003C637B /* CTCPSocketFactory.h in Headers */, + 4C5E87550648C870003C637B /* IDataSocket.h in Headers */, + 4C5E87570648C870003C637B /* IListenSocket.h in Headers */, + 4C5E87590648C870003C637B /* ISocket.h in Headers */, + 4C5E875A0648C870003C637B /* ISocketFactory.h in Headers */, + 4C5E875B0648C870003C637B /* ISocketMultiplexerJob.h in Headers */, + 4C5E875C0648C870003C637B /* TSocketMultiplexerMethodJob.h in Headers */, + 4C5E875E0648C870003C637B /* XSocket.h in Headers */, + 4C7D7CE1066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB4386B063E47F800969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87810648C8BD003C637B /* CClipboard.h in Headers */, + 689D733A095649AF00FFAB1D /* CKeyMap.h in Headers */, + 4C5E87830648C8BD003C637B /* CKeyState.h in Headers */, + 4C5E87840648C8BD003C637B /* ClipboardTypes.h in Headers */, + 4C5E87860648C8BD003C637B /* CPacketStreamFilter.h in Headers */, + 4C5E87880648C8BD003C637B /* CPlatformScreen.h in Headers */, + 4C5E878A0648C8BD003C637B /* CProtocolUtil.h in Headers */, + 4C5E878C0648C8BD003C637B /* CScreen.h in Headers */, + 4C5E878D0648C8BD003C637B /* IClient.h in Headers */, + 4C5E878F0648C8BD003C637B /* IClipboard.h in Headers */, + 4C5E87910648C8BD003C637B /* IKeyState.h in Headers */, + 4C5E87920648C8BD003C637B /* IPlatformScreen.h in Headers */, + 4C5E87940648C8BD003C637B /* IPrimaryScreen.h in Headers */, + 4C5E87960648C8BD003C637B /* IScreen.h in Headers */, + 4C5E87970648C8BD003C637B /* IScreenSaver.h in Headers */, + 4C5E87980648C8BD003C637B /* ISecondaryScreen.h in Headers */, + 4C5E87990648C8BD003C637B /* KeyTypes.h in Headers */, + 4C5E879A0648C8BD003C637B /* MouseTypes.h in Headers */, + 4C5E879B0648C8BD003C637B /* OptionTypes.h in Headers */, + 4C5E879C0648C8BD003C637B /* ProtocolTypes.h in Headers */, + 4C5E879E0648C8BD003C637B /* XScreen.h in Headers */, + 4C5E87A00648C8BD003C637B /* XSynergy.h in Headers */, + 4C7D7CE2066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB438AE063E488600969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C7D7CE3066319560097CA11 /* common.h in Headers */, + 4C5E87AE0648C913003C637B /* COSXClipboard.h in Headers */, + 4CD034960650B6F100525ED1 /* COSXClipboardAnyTextConverter.h in Headers */, + 4CD034980650B6F100525ED1 /* COSXClipboardTextConverter.h in Headers */, + 4CD0349A0650B6F100525ED1 /* COSXClipboardUTF16Converter.h in Headers */, + 4C5E87B00648C913003C637B /* COSXEventQueueBuffer.h in Headers */, + 4C5E87B20648C913003C637B /* COSXKeyState.h in Headers */, + 4C5E87B40648C913003C637B /* COSXScreen.h in Headers */, + 68FBBB2D089F07920016DF44 /* COSXScreenSaver.h in Headers */, + 68FBBB2F089F079B0016DF44 /* COSXScreenSaverUtil.h in Headers */, + 6828A1C2081DF9EB003D9989 /* OSXScreenSaverControl.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43911063E497700969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87C70648C9D2003C637B /* CClientTaskBarReceiver.h in Headers */, + 4C5E87C90648C9D2003C637B /* COSXClientTaskBarReceiver.h in Headers */, + 4C7D7CDB066319560097CA11 /* common.h in Headers */, + 68FBBB24089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */, + 68FBBB29089F072D0016DF44 /* COSXScreenSaver.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 4C537F0C064E286700D3815C /* synergys */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB14089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergys" */; + buildPhases = ( + 4C537F0A064E286700D3815C /* Headers */, + 4C537F0B064E286700D3815C /* Sources */, + 4C537F5A064E2B0700D3815C /* Frameworks */, + ); + buildRules = ( + 68FBBB53089F08940016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + PRODUCT_NAME = synergys; + }; + dependencies = ( + 4C537F1A064E298800D3815C /* PBXTargetDependency */, + 4C537F1C064E298800D3815C /* PBXTargetDependency */, + 4C537F1E064E298800D3815C /* PBXTargetDependency */, + 4C537F20064E298800D3815C /* PBXTargetDependency */, + 4C537F22064E298800D3815C /* PBXTargetDependency */, + 4C537F24064E298800D3815C /* PBXTargetDependency */, + 4C537F26064E298800D3815C /* PBXTargetDependency */, + 4C537F28064E298800D3815C /* PBXTargetDependency */, + 4C537F57064E2AA300D3815C /* PBXTargetDependency */, + ); + name = synergys; + productName = synergys; + productReference = 4C537F0D064E286700D3815C /* synergys */; + productType = "com.apple.product-type.tool"; + }; + 4C537F30064E29F800D3815C /* server */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB10089F06C20016DF44 /* Build configuration list for PBXNativeTarget "server" */; + buildPhases = ( + 4C537F2E064E29F800D3815C /* Headers */, + 4C537F2F064E29F800D3815C /* Sources */, + ); + buildRules = ( + 68FBBB4D089F087F0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = server; + }; + dependencies = ( + ); + name = server; + productName = server; + productReference = 4C537F31064E29F800D3815C /* libserver.a */; + productType = "com.apple.product-type.library.static"; + }; + 4C5E868C0648C2ED003C637B /* common */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAEC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "common" */; + buildPhases = ( + 4C5E868A0648C2ED003C637B /* Headers */, + 4C5E868B0648C2ED003C637B /* Sources */, + ); + buildRules = ( + 68FBBB37089F08160016DF44 /* PBXBuildRule */, + ); + buildSettings = { + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = common; + }; + dependencies = ( + ); + name = common; + productName = common; + productReference = 4C5E868D0648C2ED003C637B /* libcommon.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43777063E406A00969041 /* client */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB08089F06C20016DF44 /* Build configuration list for PBXNativeTarget "client" */; + buildPhases = ( + 4CB43775063E406A00969041 /* Headers */, + 4CB43776063E406A00969041 /* Sources */, + ); + buildRules = ( + 68FBBB4A089F08750016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = client; + }; + dependencies = ( + ); + name = client; + productName = client; + productReference = 4CB43778063E406A00969041 /* libclient.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB437B4063E443800969041 /* arch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAE8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "arch" */; + buildPhases = ( + 4CB437B2063E443800969041 /* Headers */, + 4CB437B3063E443800969041 /* Sources */, + ); + buildRules = ( + 68FBBB32089F07E40016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = lib/common; + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = arch; + }; + dependencies = ( + ); + name = arch; + productName = arch; + productReference = 4CB437B5063E443800969041 /* libarch.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB437D0063E44C200969041 /* base */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAF0089F06C20016DF44 /* Build configuration list for PBXNativeTarget "base" */; + buildPhases = ( + 4CB437CE063E44C200969041 /* Headers */, + 4CB437CF063E44C200969041 /* Sources */, + ); + buildRules = ( + 68FBBB3A089F08200016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = base; + }; + dependencies = ( + ); + name = base; + productName = base; + productReference = 4CB437D1063E44C200969041 /* libbase.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43800063E45F200969041 /* mt */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAF4089F06C20016DF44 /* Build configuration list for PBXNativeTarget "mt" */; + buildPhases = ( + 4CB437FE063E45F200969041 /* Headers */, + 4CB437FF063E45F200969041 /* Sources */, + ); + buildRules = ( + 68FBBB3D089F082E0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = mt; + }; + dependencies = ( + ); + name = mt; + productName = mt; + productReference = 4CB43801063E45F200969041 /* libmt.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43823063E46AB00969041 /* io */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAF8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "io" */; + buildPhases = ( + 4CB43821063E46AB00969041 /* Headers */, + 4CB43822063E46AB00969041 /* Sources */, + ); + buildRules = ( + 68FBBB3E089F084C0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = io; + }; + dependencies = ( + ); + name = io; + productName = io; + productReference = 4CB43824063E46AB00969041 /* libio.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43847063E475800969041 /* net */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAFC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "net" */; + buildPhases = ( + 4CB43845063E475800969041 /* Headers */, + 4CB43846063E475800969041 /* Sources */, + ); + buildRules = ( + 68FBBB41089F08590016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = net; + }; + dependencies = ( + ); + name = net; + productName = net; + productReference = 4CB43848063E475800969041 /* libnet.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB4386D063E47F800969041 /* synergy */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB00089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergy" */; + buildPhases = ( + 4CB4386B063E47F800969041 /* Headers */, + 4CB4386C063E47F800969041 /* Sources */, + ); + buildRules = ( + 68FBBB44089F08620016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = synergy; + }; + dependencies = ( + ); + name = synergy; + productName = synergy; + productReference = 4CB4386E063E47F800969041 /* libsynergy.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB438B0063E488600969041 /* platform */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB04089F06C20016DF44 /* Build configuration list for PBXNativeTarget "platform" */; + buildPhases = ( + 4CB438AE063E488600969041 /* Headers */, + 4CB438AF063E488600969041 /* Sources */, + ); + buildRules = ( + 68FBBB47089F086B0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/synergy, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = platform; + }; + dependencies = ( + ); + name = platform; + productName = platform; + productReference = 4CB438B1063E488600969041 /* libplatform.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43913063E497700969041 /* synergyc */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB0C089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergyc" */; + buildPhases = ( + 4CB43911063E497700969041 /* Headers */, + 4CB43912063E497700969041 /* Sources */, + 4CB43930063E4B4300969041 /* Frameworks */, + ); + buildRules = ( + 68FBBB50089F08890016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + PRODUCT_NAME = synergyc; + }; + dependencies = ( + 4CB43948063E4E1600969041 /* PBXTargetDependency */, + 4C5E86900648C32E003C637B /* PBXTargetDependency */, + 4CB4394A063E4E1600969041 /* PBXTargetDependency */, + 4CB4394C063E4E1600969041 /* PBXTargetDependency */, + 4CB4394E063E4E1600969041 /* PBXTargetDependency */, + 4CB43950063E4E1600969041 /* PBXTargetDependency */, + 4CB43952063E4E1600969041 /* PBXTargetDependency */, + 4CB43954063E4E1600969041 /* PBXTargetDependency */, + 4CB43946063E4E1600969041 /* PBXTargetDependency */, + ); + name = synergyc; + productName = synergyc; + productReference = 4CB43914063E497700969041 /* synergyc */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 68FBBB1C089F06C20016DF44 /* Build configuration list for PBXProject "synergy" */; + buildSettings = { + MACOSX_DEPLOYMENT_TARGET = 10.2; + SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; + }; + buildStyles = ( + 014CEA460018CE2711CA2923 /* Development */, + 014CEA470018CE2711CA2923 /* Deployment */, + ); + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* synergy */; + projectDirPath = ""; + targets = ( + 4CD033E80650965F00525ED1 /* ALL */, + 4CB437B4063E443800969041 /* arch */, + 4C5E868C0648C2ED003C637B /* common */, + 4CB437D0063E44C200969041 /* base */, + 4CB43800063E45F200969041 /* mt */, + 4CB43823063E46AB00969041 /* io */, + 4CB43847063E475800969041 /* net */, + 4CB4386D063E47F800969041 /* synergy */, + 4CB438B0063E488600969041 /* platform */, + 4CB43777063E406A00969041 /* client */, + 4C537F30064E29F800D3815C /* server */, + 4CB43913063E497700969041 /* synergyc */, + 4C537F0C064E286700D3815C /* synergys */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 4C537F0B064E286700D3815C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F14064E293000D3815C /* COSXServerTaskBarReceiver.cpp in Sources */, + 4C537F16064E293000D3815C /* CServerTaskBarReceiver.cpp in Sources */, + 4C537F18064E293000D3815C /* synergys.cpp in Sources */, + 68FBBB22089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */, + 68FBBB2A089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */, + 68FBBB5C089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F2F064E29F800D3815C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F44064E2A0F00D3815C /* CClientListener.cpp in Sources */, + 4C537F46064E2A0F00D3815C /* CClientProxy.cpp in Sources */, + 4C537F48064E2A0F00D3815C /* CClientProxy1_0.cpp in Sources */, + 4C537F4A064E2A0F00D3815C /* CClientProxy1_1.cpp in Sources */, + 4C537F4C064E2A0F00D3815C /* CClientProxy1_2.cpp in Sources */, + 684B63620996FB2800FE7CC7 /* CClientProxy1_3.cpp in Sources */, + 4C537F4E064E2A0F00D3815C /* CClientProxyUnknown.cpp in Sources */, + 4C537F50064E2A0F00D3815C /* CConfig.cpp in Sources */, + 689D73340956490500FFAB1D /* CInputFilter.cpp in Sources */, + 4C537F52064E2A0F00D3815C /* CPrimaryClient.cpp in Sources */, + 4C537F54064E2A0F00D3815C /* CServer.cpp in Sources */, + 688925A909DF64B6002EB18C /* CBaseClientProxy.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C5E868B0648C2ED003C637B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86CC0648C726003C637B /* Version.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43776063E406A00969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87BD0648C969003C637B /* CClient.cpp in Sources */, + 4C5E87BF0648C969003C637B /* CServerProxy.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437B3063E443800969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86A90648C412003C637B /* CArch.cpp in Sources */, + 4C5E86AB0648C412003C637B /* CArchConsoleUnix.cpp in Sources */, + 68968F5D073EC484004B2F9B /* CArchDaemonNone.cpp in Sources */, + 4C5E86AD0648C412003C637B /* CArchDaemonUnix.cpp in Sources */, + 4C5E86AF0648C412003C637B /* CArchFileUnix.cpp in Sources */, + 4C5E86B10648C412003C637B /* CArchLogUnix.cpp in Sources */, + 4C5E86B30648C412003C637B /* CArchMultithreadPosix.cpp in Sources */, + 4C5E86B50648C412003C637B /* CArchNetworkBSD.cpp in Sources */, + 4C5E86B70648C412003C637B /* CArchSleepUnix.cpp in Sources */, + 4C5E86B90648C412003C637B /* CArchStringUnix.cpp in Sources */, + 68871685073EBF7000C5ABE7 /* CArchSystemUnix.cpp in Sources */, + 68968F5E073EC484004B2F9B /* CArchTaskBarXWindows.cpp in Sources */, + 4C5E86BB0648C412003C637B /* CArchTimeUnix.cpp in Sources */, + 4C5E87D10648CA75003C637B /* XArch.cpp in Sources */, + 4C5E86BE0648C412003C637B /* XArchUnix.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437CF063E44C200969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86ED0648C7B9003C637B /* CEvent.cpp in Sources */, + 4C5E86EF0648C7B9003C637B /* CEventQueue.cpp in Sources */, + 4C5E86F10648C7B9003C637B /* CFunctionEventJob.cpp in Sources */, + 4C5E86F30648C7B9003C637B /* CFunctionJob.cpp in Sources */, + 4C5E86F50648C7B9003C637B /* CLog.cpp in Sources */, + 4C5E86F80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp in Sources */, + 4C5E86FA0648C7B9003C637B /* CStopwatch.cpp in Sources */, + 4C5E86FD0648C7B9003C637B /* CStringUtil.cpp in Sources */, + 4C5E86FF0648C7B9003C637B /* CUnicode.cpp in Sources */, + 4C5E87020648C7B9003C637B /* IEventQueue.cpp in Sources */, + 4C5E87070648C7B9003C637B /* LogOutputters.cpp in Sources */, + 4C5E870B0648C7B9003C637B /* XBase.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437FF063E45F200969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87180648C809003C637B /* CCondVar.cpp in Sources */, + 4C5E871A0648C809003C637B /* CLock.cpp in Sources */, + 4C5E871C0648C809003C637B /* CMutex.cpp in Sources */, + 4C5E871E0648C809003C637B /* CThread.cpp in Sources */, + 4C5E87200648C809003C637B /* XMT.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43822063E46AB00969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E872C0648C83C003C637B /* CStreamBuffer.cpp in Sources */, + 4C5E872E0648C83C003C637B /* CStreamFilter.cpp in Sources */, + 4C5E87300648C83C003C637B /* IStream.cpp in Sources */, + 4C5E87330648C83C003C637B /* XIO.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43846063E475800969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E874A0648C870003C637B /* CNetworkAddress.cpp in Sources */, + 4C5E874C0648C870003C637B /* CSocketMultiplexer.cpp in Sources */, + 4C5E874E0648C870003C637B /* CTCPListenSocket.cpp in Sources */, + 4C5E87500648C870003C637B /* CTCPSocket.cpp in Sources */, + 4C5E87520648C870003C637B /* CTCPSocketFactory.cpp in Sources */, + 4C5E87540648C870003C637B /* IDataSocket.cpp in Sources */, + 4C5E87560648C870003C637B /* IListenSocket.cpp in Sources */, + 4C5E87580648C870003C637B /* ISocket.cpp in Sources */, + 4C5E875D0648C870003C637B /* XSocket.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB4386C063E47F800969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87800648C8BD003C637B /* CClipboard.cpp in Sources */, + 689D7339095649AF00FFAB1D /* CKeyMap.cpp in Sources */, + 4C5E87820648C8BD003C637B /* CKeyState.cpp in Sources */, + 4C5E87850648C8BD003C637B /* CPacketStreamFilter.cpp in Sources */, + 4C5E87870648C8BD003C637B /* CPlatformScreen.cpp in Sources */, + 4C5E87890648C8BD003C637B /* CProtocolUtil.cpp in Sources */, + 4C5E878B0648C8BD003C637B /* CScreen.cpp in Sources */, + 4C5E878E0648C8BD003C637B /* IClipboard.cpp in Sources */, + 4C5E87900648C8BD003C637B /* IKeyState.cpp in Sources */, + 4C5E87930648C8BD003C637B /* IPrimaryScreen.cpp in Sources */, + 4C5E87950648C8BD003C637B /* IScreen.cpp in Sources */, + 68D87A6509A00D8800856835 /* KeyTypes.cpp in Sources */, + 68FBBB5D089F1BCE0016DF44 /* ProtocolTypes.cpp in Sources */, + 4C5E879D0648C8BD003C637B /* XScreen.cpp in Sources */, + 4C5E879F0648C8BD003C637B /* XSynergy.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB438AF063E488600969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87AD0648C913003C637B /* COSXClipboard.cpp in Sources */, + 4CD034950650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp in Sources */, + 4CD034970650B6F100525ED1 /* COSXClipboardTextConverter.cpp in Sources */, + 4CD034990650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp in Sources */, + 4C5E87AF0648C913003C637B /* COSXEventQueueBuffer.cpp in Sources */, + 4C5E87B10648C913003C637B /* COSXKeyState.cpp in Sources */, + 4C5E87B30648C913003C637B /* COSXScreen.cpp in Sources */, + 68FBBB2C089F07810016DF44 /* COSXScreenSaver.cpp in Sources */, + 68FBBB2E089F07970016DF44 /* COSXScreenSaverUtil.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43912063E497700969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87C60648C9D2003C637B /* CClientTaskBarReceiver.cpp in Sources */, + 4C5E87C80648C9D2003C637B /* COSXClientTaskBarReceiver.cpp in Sources */, + 4C5E87CA0648C9D2003C637B /* synergyc.cpp in Sources */, + 68FBBB21089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */, + 68FBBB28089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */, + 68FBBB5B089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4C537F1A064E298800D3815C /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 4C537F0C064E286700D3815C; - targetProxy = 4CD033EB0650968500525ED1; + target = 4CB437B4063E443800969041 /* arch */; + targetProxy = 4C537F19064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD0348F0650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboardAnyTextConverter.cpp; - path = lib/platform/COSXClipboardAnyTextConverter.cpp; - refType = 4; - sourceTree = ""; + 4C537F1C064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C5E868C0648C2ED003C637B /* common */; + targetProxy = 4C537F1B064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034900650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboardAnyTextConverter.h; - path = lib/platform/COSXClipboardAnyTextConverter.h; - refType = 4; - sourceTree = ""; + 4C537F1E064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB437D0063E44C200969041 /* base */; + targetProxy = 4C537F1D064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034910650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboardTextConverter.cpp; - path = lib/platform/COSXClipboardTextConverter.cpp; - refType = 4; - sourceTree = ""; + 4C537F20064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43800063E45F200969041 /* mt */; + targetProxy = 4C537F1F064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034920650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboardTextConverter.h; - path = lib/platform/COSXClipboardTextConverter.h; - refType = 4; - sourceTree = ""; + 4C537F22064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43823063E46AB00969041 /* io */; + targetProxy = 4C537F21064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034930650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboardUTF16Converter.cpp; - path = lib/platform/COSXClipboardUTF16Converter.cpp; - refType = 4; - sourceTree = ""; + 4C537F24064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43847063E475800969041 /* net */; + targetProxy = 4C537F23064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034940650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboardUTF16Converter.h; - path = lib/platform/COSXClipboardUTF16Converter.h; - refType = 4; - sourceTree = ""; + 4C537F26064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB4386D063E47F800969041 /* synergy */; + targetProxy = 4C537F25064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034950650B6F100525ED1 = { - fileRef = 4CD0348F0650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 4C537F28064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB438B0063E488600969041 /* platform */; + targetProxy = 4C537F27064E298800D3815C /* PBXContainerItemProxy */; + }; + 4C537F57064E2AA300D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C537F30064E29F800D3815C /* server */; + targetProxy = 4C537F56064E2AA300D3815C /* PBXContainerItemProxy */; + }; + 4C5E86900648C32E003C637B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C5E868C0648C2ED003C637B /* common */; + targetProxy = 4C5E868F0648C32E003C637B /* PBXContainerItemProxy */; + }; + 4CB43946063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43777063E406A00969041 /* client */; + targetProxy = 4CB43945063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43948063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB437B4063E443800969041 /* arch */; + targetProxy = 4CB43947063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB4394A063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB437D0063E44C200969041 /* base */; + targetProxy = 4CB43949063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB4394C063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43800063E45F200969041 /* mt */; + targetProxy = 4CB4394B063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB4394E063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43823063E46AB00969041 /* io */; + targetProxy = 4CB4394D063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43950063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43847063E475800969041 /* net */; + targetProxy = 4CB4394F063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43952063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB4386D063E47F800969041 /* synergy */; + targetProxy = 4CB43951063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43954063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB438B0063E488600969041 /* platform */; + targetProxy = 4CB43953063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CD033EA0650968500525ED1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43913063E497700969041 /* synergyc */; + targetProxy = 4CD033E90650968500525ED1 /* PBXContainerItemProxy */; + }; + 4CD033EC0650968500525ED1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C537F0C064E286700D3815C /* synergys */; + targetProxy = 4CD033EB0650968500525ED1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 68FBBAE9089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = lib/common; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = arch; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CD034960650B6F100525ED1 = { - fileRef = 4CD034900650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAEA089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = lib/common; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = arch; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CD034970650B6F100525ED1 = { - fileRef = 4CD034910650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAED089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = common; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CD034980650B6F100525ED1 = { - fileRef = 4CD034920650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAEE089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = common; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CD034990650B6F100525ED1 = { - fileRef = 4CD034930650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAF1089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = base; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CD0349A0650B6F100525ED1 = { - fileRef = 4CD034940650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAF2089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = base; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF3892063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libarch.a; - path = build/libarch.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3893063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libbase.a; - path = build/libbase.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3894063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libclient.a; - path = build/libclient.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3895063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libio.a; - path = build/libio.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3896063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libmt.a; - path = build/libmt.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3897063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libnet.a; - path = build/libnet.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3898063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libplatform.a; - path = build/libplatform.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3899063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libsynergy.a; - path = build/libsynergy.a; - refType = 4; - sourceTree = ""; - }; - 4CDF389A063E561B007B20A1 = { - fileRef = 4CDF3892063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAF5089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = mt; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF389B063E561B007B20A1 = { - fileRef = 4CDF3893063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAF6089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = mt; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF389C063E561B007B20A1 = { - fileRef = 4CDF3894063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAF9089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = io; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF389D063E561B007B20A1 = { - fileRef = 4CDF3895063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAFA089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = io; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF389E063E561B007B20A1 = { - fileRef = 4CDF3896063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAFD089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = net; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF389F063E561B007B20A1 = { - fileRef = 4CDF3897063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAFE089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = net; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF38A0063E561B007B20A1 = { - fileRef = 4CDF3898063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBB01089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = synergy; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF38A1063E561B007B20A1 = { - fileRef = 4CDF3899063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBB02089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = synergy; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; -//4C0 -//4C1 -//4C2 -//4C3 -//4C4 -//680 -//681 -//682 -//683 -//684 - 68871676073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchSystemUnix.cpp; - path = lib/arch/CArchSystemUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 68871677073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchSystemUnix.h; - path = lib/arch/CArchSystemUnix.h; - refType = 4; - sourceTree = ""; - }; - 68871678073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchConsole.h; - path = lib/arch/IArchConsole.h; - refType = 4; - sourceTree = ""; - }; - 68871679073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchDaemon.h; - path = lib/arch/IArchDaemon.h; - refType = 4; - sourceTree = ""; - }; - 6887167A073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchFile.h; - path = lib/arch/IArchFile.h; - refType = 4; - sourceTree = ""; - }; - 6887167B073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchLog.h; - path = lib/arch/IArchLog.h; - refType = 4; - sourceTree = ""; - }; - 6887167C073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchMultithread.h; - path = lib/arch/IArchMultithread.h; - refType = 4; - sourceTree = ""; - }; - 6887167D073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchNetwork.h; - path = lib/arch/IArchNetwork.h; - refType = 4; - sourceTree = ""; - }; - 6887167E073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchSleep.h; - path = lib/arch/IArchSleep.h; - refType = 4; - sourceTree = ""; - }; - 6887167F073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchString.h; - path = lib/arch/IArchString.h; - refType = 4; - sourceTree = ""; - }; - 68871680073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchSystem.h; - path = lib/arch/IArchSystem.h; - refType = 4; - sourceTree = ""; - }; - 68871681073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchTaskBar.h; - path = lib/arch/IArchTaskBar.h; - refType = 4; - sourceTree = ""; - }; - 68871682073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchTaskBarReceiver.h; - path = lib/arch/IArchTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 68871683073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchTime.h; - path = lib/arch/IArchTime.h; - refType = 4; - sourceTree = ""; - }; - 68871684073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XArch.h; - path = lib/arch/XArch.h; - refType = 4; - sourceTree = ""; - }; - 68871685073EBF7000C5ABE7 = { - fileRef = 68871676073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB05089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/synergy, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = platform; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 68871686073EBF7000C5ABE7 = { - fileRef = 68871677073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB06089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/synergy, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = platform; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 68871687073EBF7000C5ABE7 = { - fileRef = 68871678073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB09089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = client; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 68871688073EBF7000C5ABE7 = { - fileRef = 68871679073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB0A089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = client; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 68871689073EBF7000C5ABE7 = { - fileRef = 6887167A073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB0D089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = synergyc; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 6887168A073EBF7000C5ABE7 = { - fileRef = 6887167B073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB0E089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = synergyc; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 6887168B073EBF7000C5ABE7 = { - fileRef = 6887167C073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB11089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = server; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 6887168C073EBF7000C5ABE7 = { - fileRef = 6887167D073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB12089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = server; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 6887168D073EBF7000C5ABE7 = { - fileRef = 6887167E073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB15089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = synergys; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 6887168E073EBF7000C5ABE7 = { - fileRef = 6887167F073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB16089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = synergys; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 6887168F073EBF7000C5ABE7 = { - fileRef = 68871680073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB19089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = ALL; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 68871690073EBF7000C5ABE7 = { - fileRef = 68871681073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB1A089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = ALL; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 68871691073EBF7000C5ABE7 = { - fileRef = 68871682073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB1D089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_VERSION_i386 = 4.0; + GCC_VERSION_ppc = 3.3; + MACOSX_DEPLOYMENT_TARGET = 10.2; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.2; + SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; + SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk; + SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk; }; + name = Development; }; - 68871692073EBF7000C5ABE7 = { - fileRef = 68871683073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB1E089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_VERSION_i386 = 4.0; + GCC_VERSION_ppc = 3.3; + MACOSX_DEPLOYMENT_TARGET = 10.2; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.2; + SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; + SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk; + SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk; }; + name = Deployment; }; - 68871693073EBF7000C5ABE7 = { - fileRef = 68871684073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { - }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 68FBBAE8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "arch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAE9089F06C20016DF44 /* Development */, + 68FBBAEA089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68871699073EC02100C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = MacOSXPrecomp.h; - path = lib/common/MacOSXPrecomp.h; - refType = 4; - sourceTree = ""; + 68FBBAEC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "common" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAED089F06C20016DF44 /* Development */, + 68FBBAEE089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 6887169B073EC03800C5ABE7 = { - fileRef = 68871699073EC02100C5ABE7; - isa = PBXBuildFile; - settings = { - }; + 68FBBAF0089F06C20016DF44 /* Build configuration list for PBXNativeTarget "base" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAF1089F06C20016DF44 /* Development */, + 68FBBAF2089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5A073EC484004B2F9B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchDaemonNone.cpp; - path = lib/arch/CArchDaemonNone.cpp; - refType = 4; - sourceTree = ""; + 68FBBAF4089F06C20016DF44 /* Build configuration list for PBXNativeTarget "mt" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAF5089F06C20016DF44 /* Development */, + 68FBBAF6089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5B073EC484004B2F9B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchTaskBarXWindows.cpp; - path = lib/arch/CArchTaskBarXWindows.cpp; - refType = 4; - sourceTree = ""; + 68FBBAF8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "io" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAF9089F06C20016DF44 /* Development */, + 68FBBAFA089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5C073EC484004B2F9B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchTaskBarXWindows.h; - path = lib/arch/CArchTaskBarXWindows.h; - refType = 4; - sourceTree = ""; + 68FBBAFC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "net" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAFD089F06C20016DF44 /* Development */, + 68FBBAFE089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5D073EC484004B2F9B = { - fileRef = 68968F5A073EC484004B2F9B; - isa = PBXBuildFile; - settings = { - }; + 68FBBB00089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB01089F06C20016DF44 /* Development */, + 68FBBB02089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5E073EC484004B2F9B = { - fileRef = 68968F5B073EC484004B2F9B; - isa = PBXBuildFile; - settings = { - }; + 68FBBB04089F06C20016DF44 /* Build configuration list for PBXNativeTarget "platform" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB05089F06C20016DF44 /* Development */, + 68FBBB06089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5F073EC484004B2F9B = { - fileRef = 68968F5C073EC484004B2F9B; - isa = PBXBuildFile; - settings = { - }; + 68FBBB08089F06C20016DF44 /* Build configuration list for PBXNativeTarget "client" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB09089F06C20016DF44 /* Development */, + 68FBBB0A089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; + 68FBBB0C089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergyc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB0D089F06C20016DF44 /* Development */, + 68FBBB0E089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB10089F06C20016DF44 /* Build configuration list for PBXNativeTarget "server" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB11089F06C20016DF44 /* Development */, + 68FBBB12089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB14089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergys" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB15089F06C20016DF44 /* Development */, + 68FBBB16089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB18089F06C20016DF44 /* Build configuration list for PBXAggregateTarget "ALL" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB19089F06C20016DF44 /* Development */, + 68FBBB1A089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB1C089F06C20016DF44 /* Build configuration list for PBXProject "synergy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB1D089F06C20016DF44 /* Development */, + 68FBBB1E089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ }; - rootObject = 08FB7793FE84155DC02AAC07; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; }