mirror of
https://github.com/debauchee/barrier.git
synced 2026-05-11 00:58:14 +08:00
Restored lost files and changes in version 1.3.1 to depot.
This commit is contained in:
@@ -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; <unused>
|
||||
// xDelta; yDelta
|
||||
#define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7
|
||||
// POINT*; <unused>
|
||||
#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; <unused>
|
||||
#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<CDesk*>(vdesk);
|
||||
desk->m_threadID = GetCurrentThreadId();
|
||||
desk->m_window = NULL;
|
||||
CDesk* desk = reinterpret_cast<CDesk*>(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();
|
||||
}
|
||||
|
||||
@@ -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<CString, CDesk*> 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,9 +17,12 @@
|
||||
|
||||
#include "CKeyState.h"
|
||||
#include "CString.h"
|
||||
#include "stdvector.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
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<HKL> 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<HKL, SInt32> GroupMap;
|
||||
typedef std::map<KeyID, UINT> 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
|
||||
|
||||
@@ -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<CMSWindowsScreen>(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<CMSWindowsScreen>(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<SInt32>(lParam));
|
||||
|
||||
case SYNERGY_MSG_MOUSE_WHEEL:
|
||||
return onMouseWheel(static_cast<SInt32>(wParam));
|
||||
// XXX -- support x-axis scrolling
|
||||
return onMouseWheel(0, static_cast<SInt32>(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<KeyButton>((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<CMSWindowsScreen>(
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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<UInt32, CHotKeyItem> HotKeyMap;
|
||||
typedef std::vector<UInt32> HotKeyIDList;
|
||||
typedef std::map<CHotKeyItem, UInt32> 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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<CKeyEventInfo> CKeySequence;
|
||||
typedef std::map<KeyID, CKeySequence> CKeyIDMap;
|
||||
typedef std::map<UInt32, KeyID> CVirtualKeyMap;
|
||||
typedef std::map<UInt16, std::pair<SInt32, KeyModifierMask> > CDeadKeyMap;
|
||||
class CKeyResource;
|
||||
typedef std::vector<KeyboardLayoutRef> 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<KeyID> 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<KeyboardLayoutRef, SInt32> GroupMap;
|
||||
typedef std::map<UInt32, KeyID> 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,9 +16,21 @@
|
||||
#define COSXSCREEN_H
|
||||
|
||||
#include "CPlatformScreen.h"
|
||||
#include "stdmap.h"
|
||||
#include "stdvector.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/mach_interface.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#include <IOKit/IOMessage.h>
|
||||
|
||||
template <class T>
|
||||
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<UInt32, CHotKeyItem> HotKeyMap;
|
||||
typedef std::vector<UInt32> HotKeyIDList;
|
||||
typedef std::map<KeyModifierMask, UInt32> ModifierHotKeyMap;
|
||||
typedef std::map<CHotKeyItem, UInt32> 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<bool>* 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
|
||||
|
||||
@@ -12,45 +12,164 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "COSXScreenSaver.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
// FIXME -- implement this
|
||||
#import "COSXScreenSaver.h"
|
||||
#import "COSXScreenSaverUtil.h"
|
||||
#import "CLog.h"
|
||||
#import "IEventQueue.h"
|
||||
#import "IPrimaryScreen.h"
|
||||
#import <string.h>
|
||||
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
#define COSXSCREENSAVER_H
|
||||
|
||||
#include "IScreenSaver.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
//! 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
|
||||
|
||||
39
lib/platform/COSXScreenSaverUtil.h
Normal file
39
lib/platform/COSXScreenSaverUtil.h
Normal file
@@ -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
|
||||
81
lib/platform/COSXScreenSaverUtil.m
Normal file
81
lib/platform/COSXScreenSaverUtil.m
Normal file
@@ -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 <Foundation/NSAutoreleasePool.h>
|
||||
|
||||
//
|
||||
// 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];
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<char*>(&target), sizeof(target));
|
||||
CXWindowsUtil::appendAtomData(data, XA_STRING);
|
||||
}
|
||||
|
||||
CXWindowsUtil::convertAtomProperty(data);
|
||||
const Atom* targets = reinterpret_cast<const Atom*>(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<const CMotifClipHeader*>(data.data());
|
||||
if (data.size() >= sizeof(CMotifClipHeader) &&
|
||||
header->m_id == kMotifClipHeader) {
|
||||
if (header->m_selectionOwner == owner) {
|
||||
if (static_cast<Window>(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<const Atom*>(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<const char*>(&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<char*>(&atom), sizeof(Atom));
|
||||
atom = m_atomMultiple;
|
||||
data.append(reinterpret_cast<char*>(&atom), sizeof(Atom));
|
||||
atom = m_atomTimestamp;
|
||||
data.append(reinterpret_cast<char*>(&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<char*>(&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<const char*>(&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;
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <X11/extensions/XKBstr.h>
|
||||
# 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<int> 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<KeySym, KeyMapping> KeySymMap;
|
||||
typedef KeySymMap::const_iterator KeySymIndex;
|
||||
|
||||
// save the current keyboard mapping and note the modifiers
|
||||
void updateKeysymMap();
|
||||
typedef std::vector<KeyModifierMask> KeyModifierMaskList;
|
||||
typedef std::map<KeyModifierMask, unsigned int> KeyModifierToXMask;
|
||||
typedef std::multimap<KeyID, KeyCode> KeyToKeyCodeMap;
|
||||
typedef std::map<KeyCode, unsigned int> NonXKBModifierMap;
|
||||
typedef std::map<UInt32, XKBModifierInfo> 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
|
||||
|
||||
@@ -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 <X11/extensions/Xinerama.h>
|
||||
}
|
||||
# endif
|
||||
# if HAVE_XKB_EXTENSION
|
||||
# include <X11/XKBlib.h>
|
||||
# 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<ButtonID>(
|
||||
(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<XkbEvent*>(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<KeyID>(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<KeyID>(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<KeyID>(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));
|
||||
}
|
||||
|
||||
@@ -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<bool> CFilteredKeycodes;
|
||||
typedef std::vector<std::pair<int, unsigned int> > HotKeyList;
|
||||
typedef std::map<UInt32, HotKeyList> HotKeyMap;
|
||||
typedef std::vector<UInt32> HotKeyIDList;
|
||||
typedef std::map<CHotKeyItem, UInt32> 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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<KeySym, UInt32> CKeySymMap;
|
||||
typedef std::map<UInt32, KeySym> CUCS4Map;
|
||||
typedef std::map<KeySym, KeySyms> CKeySymsMap;
|
||||
|
||||
static CKeySymMap s_keySymToUCS4;
|
||||
static CUCS4Map s_UCS4ToKeySym;
|
||||
static CKeySymsMap s_deadKeyDecomposedKeySyms;
|
||||
static CKeySymsMap s_composeDecomposedKeySyms;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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 = \
|
||||
|
||||
36
lib/platform/OSXScreenSaverControl.h
Normal file
36
lib/platform/OSXScreenSaverControl.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// ScreenSaver.framework private API
|
||||
// Class dumping by Alex Harper http://www.ragingmenace.com/
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@protocol ScreenSaverControl
|
||||
- (double)screenSaverTimeRemaining;
|
||||
- (void)restartForUser:fp12;
|
||||
- (void)screenSaverStopNow;
|
||||
- (void)screenSaverStartNow;
|
||||
- (void)setScreenSaverCanRun:(char)fp12;
|
||||
- (BOOL)screenSaverCanRun;
|
||||
- (BOOL)screenSaverIsRunning;
|
||||
@end
|
||||
|
||||
|
||||
@interface ScreenSaverController:NSObject <ScreenSaverControl>
|
||||
|
||||
+ 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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user