/src/lib/platform/XWindowsScreen.cpp
C++ | 1849 lines | 1325 code | 203 blank | 321 comment | 329 complexity | e4b96c5c7c52985f805ed52ae56e79ba MD5 | raw file
- /*
- * synergy -- mouse and keyboard sharing utility
- * Copyright (C) 2012 Synergy Si Ltd.
- * Copyright (C) 2002 Chris Schoeneman
- *
- * This package is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * found in the file LICENSE that should have accompanied this file.
- *
- * This package is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "platform/XWindowsScreen.h"
- #include "platform/XWindowsClipboard.h"
- #include "platform/XWindowsEventQueueBuffer.h"
- #include "platform/XWindowsKeyState.h"
- #include "platform/XWindowsScreenSaver.h"
- #include "platform/XWindowsUtil.h"
- #include "synergy/Clipboard.h"
- #include "synergy/KeyMap.h"
- #include "synergy/XScreen.h"
- #include "arch/XArch.h"
- #include "arch/Arch.h"
- #include "base/Log.h"
- #include "base/Stopwatch.h"
- #include "base/String.h"
- #include "base/IEventQueue.h"
- #include "base/TMethodEventJob.h"
- #include <cstring>
- #include <cstdlib>
- #if X_DISPLAY_MISSING
- # error X11 is required to build synergy
- #else
- # include <X11/X.h>
- # include <X11/Xutil.h>
- # define XK_MISCELLANY
- # define XK_XKB_KEYS
- # include <X11/keysymdef.h>
- # if HAVE_X11_EXTENSIONS_DPMS_H
- extern "C" {
- # include <X11/extensions/dpms.h>
- }
- # endif
- # if HAVE_X11_EXTENSIONS_XTEST_H
- # include <X11/extensions/XTest.h>
- # else
- # error The XTest extension is required to build synergy
- # endif
- # if HAVE_X11_EXTENSIONS_XINERAMA_H
- // Xinerama.h may lack extern "C" for inclusion by C++
- extern "C" {
- # include <X11/extensions/Xinerama.h>
- }
- # endif
- # if HAVE_X11_EXTENSIONS_XRANDR_H
- # include <X11/extensions/Xrandr.h>
- # endif
- # if HAVE_XKB_EXTENSION
- # include <X11/XKBlib.h>
- # endif
- # ifdef HAVE_XI2
- # include <X11/extensions/XInput2.h>
- # endif
- #endif
- static int xi_opcode;
- //
- // XWindowsScreen
- //
- // NOTE -- the X display is shared among several objects but is owned
- // by the XWindowsScreen. Xlib is not reentrant so we must ensure
- // that no two objects can simultaneously call Xlib with the display.
- // this is easy since we only make X11 calls from the main thread.
- // we must also ensure that these objects do not use the display in
- // their destructors or, if they do, we can tell them not to. This
- // is to handle unexpected disconnection of the X display, when any
- // call on the display is invalid. In that situation we discard the
- // display and the X11 event queue buffer, ignore any calls that try
- // to use the display, and wait to be destroyed.
- XWindowsScreen* XWindowsScreen::s_screen = NULL;
- XWindowsScreen::XWindowsScreen(
- const char* displayName,
- bool isPrimary,
- bool disableXInitThreads,
- int mouseScrollDelta,
- IEventQueue* events) :
- m_isPrimary(isPrimary),
- m_mouseScrollDelta(mouseScrollDelta),
- m_display(NULL),
- m_root(None),
- m_window(None),
- m_isOnScreen(m_isPrimary),
- m_x(0), m_y(0),
- m_w(0), m_h(0),
- m_xCenter(0), m_yCenter(0),
- m_xCursor(0), m_yCursor(0),
- m_keyState(NULL),
- m_lastFocus(None),
- m_lastFocusRevert(RevertToNone),
- m_im(NULL),
- m_ic(NULL),
- m_lastKeycode(0),
- m_sequenceNumber(0),
- m_screensaver(NULL),
- m_screensaverNotify(false),
- m_xtestIsXineramaUnaware(true),
- m_preserveFocus(false),
- m_xkb(false),
- m_xi2detected(false),
- m_xrandr(false),
- m_events(events),
- PlatformScreen(events)
- {
- assert(s_screen == NULL);
- if (mouseScrollDelta==0) m_mouseScrollDelta=120;
- s_screen = this;
-
- if (!disableXInitThreads) {
- // initializes Xlib support for concurrent threads.
- if (XInitThreads() == 0)
- throw XArch("XInitThreads() returned zero");
- } else {
- LOG((CLOG_DEBUG "skipping XInitThreads()"));
- }
- // set the X I/O error handler so we catch the display disconnecting
- XSetIOErrorHandler(&XWindowsScreen::ioErrorHandler);
- try {
- m_display = openDisplay(displayName);
- m_root = DefaultRootWindow(m_display);
- saveShape();
- m_window = openWindow();
- m_screensaver = new XWindowsScreenSaver(m_display,
- m_window, getEventTarget(), events);
- m_keyState = new XWindowsKeyState(m_display, m_xkb, events, m_keyMap);
- 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));
- }
- catch (...) {
- if (m_display != NULL) {
- XCloseDisplay(m_display);
- }
- throw;
- }
- // primary/secondary screen only initialization
- if (m_isPrimary) {
- // start watching for events on other windows
- selectEvents(m_root);
- m_xi2detected = detectXI2();
- if (m_xi2detected) {
- #ifdef HAVE_XI2
- selectXIRawMotion();
- #endif
- } else
- {
- // start watching for events on other windows
- selectEvents(m_root);
- }
- // prepare to use input methods
- openIM();
- }
- else {
- // become impervious to server grabs
- XTestGrabControl(m_display, True);
- }
- // initialize the clipboards
- for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
- m_clipboard[id] = new XWindowsClipboard(m_display, m_window, id);
- }
- // install event handlers
- m_events->adoptHandler(Event::kSystem, m_events->getSystemTarget(),
- new TMethodEventJob<XWindowsScreen>(this,
- &XWindowsScreen::handleSystemEvent));
- // install the platform event queue
- m_events->adoptBuffer(new XWindowsEventQueueBuffer(
- m_display, m_window, m_events));
- }
- XWindowsScreen::~XWindowsScreen()
- {
- assert(s_screen != NULL);
- assert(m_display != NULL);
- m_events->adoptBuffer(NULL);
- m_events->removeHandler(Event::kSystem, m_events->getSystemTarget());
- for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
- delete m_clipboard[id];
- }
- delete m_keyState;
- delete m_screensaver;
- m_keyState = NULL;
- m_screensaver = NULL;
- if (m_display != NULL) {
- // FIXME -- is it safe to clean up the IC and IM without a display?
- if (m_ic != NULL) {
- XDestroyIC(m_ic);
- }
- if (m_im != NULL) {
- XCloseIM(m_im);
- }
- XDestroyWindow(m_display, m_window);
- XCloseDisplay(m_display);
- }
- XSetIOErrorHandler(NULL);
- s_screen = NULL;
- }
- void
- XWindowsScreen::enable()
- {
- if (!m_isPrimary) {
- // get the keyboard control state
- 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);
- // raise and show the window
- // FIXME -- take focus?
- XMapRaised(m_display, m_window);
- // warp the mouse to the cursor center
- fakeMouseMove(m_xCenter, m_yCenter);
- }
- }
- void
- XWindowsScreen::disable()
- {
- // release input context focus
- if (m_ic != NULL) {
- XUnsetICFocus(m_ic);
- }
- // unmap the hider/grab window. this also ungrabs the mouse and
- // keyboard if they're grabbed.
- XUnmapWindow(m_display, m_window);
- // restore auto-repeat state
- if (!m_isPrimary && m_autoRepeat) {
- //XAutoRepeatOn(m_display);
- }
- }
- void
- XWindowsScreen::enter()
- {
- screensaver(false);
- // release input context focus
- if (m_ic != NULL) {
- XUnsetICFocus(m_ic);
- }
- // 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
- XWindowsUtil::ErrorLock lock(m_display);
- XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime);
- }
- #if HAVE_X11_EXTENSIONS_DPMS_H
- // Force the DPMS to turn screen back on since we don't
- // actually cause physical hardware input to trigger it
- int dummy;
- CARD16 powerlevel;
- BOOL enabled;
- if (DPMSQueryExtension(m_display, &dummy, &dummy) &&
- DPMSCapable(m_display) &&
- DPMSInfo(m_display, &powerlevel, &enabled))
- {
- if (enabled && powerlevel != DPMSModeOn)
- DPMSForceLevel(m_display, DPMSModeOn);
- }
- #endif
-
- // 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.
- XSetInputFocus(m_display, PointerRoot, PointerRoot, CurrentTime);
- */
- if (!m_isPrimary) {
- // get the keyboard control state
- 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
- // those keys.
- //XAutoRepeatOff(m_display);
- }
- // now on screen
- m_isOnScreen = true;
- }
- bool
- XWindowsScreen::leave()
- {
- if (!m_isPrimary) {
- // restore the previous keyboard auto-repeat state. if the user
- // changed the auto-repeat configuration while on the client then
- // that state is lost. that's because we can't get notified by
- // the X server when the auto-repeat configuration is changed so
- // we can't track the desired configuration.
- if (m_autoRepeat) {
- //XAutoRepeatOn(m_display);
- }
- // move hider window under the cursor center
- XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
- }
- // raise and show the window
- XMapRaised(m_display, m_window);
- // grab the mouse and keyboard, if primary and possible
- if (m_isPrimary && !grabMouseAndKeyboard()) {
- XUnmapWindow(m_display, m_window);
- return false;
- }
- // save current focus
- XGetInputFocus(m_display, &m_lastFocus, &m_lastFocusRevert);
- // take focus
- if (m_isPrimary || !m_preserveFocus) {
- XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
- }
- // now warp the mouse. we warp after showing the window so we're
- // guaranteed to get the mouse leave event and to prevent the
- // keyboard focus from changing under point-to-focus policies.
- if (m_isPrimary) {
- warpCursor(m_xCenter, m_yCenter);
- }
- else {
- fakeMouseMove(m_xCenter, m_yCenter);
- }
- // set input context focus to our window
- if (m_ic != NULL) {
- XmbResetIC(m_ic);
- XSetICFocus(m_ic);
- m_filtered.clear();
- }
- // now off screen
- m_isOnScreen = false;
- return true;
- }
- bool
- XWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard)
- {
- // fail if we don't have the requested clipboard
- if (m_clipboard[id] == NULL) {
- return false;
- }
- // get the actual time. ICCCM does not allow CurrentTime.
- Time timestamp = XWindowsUtil::getCurrentTime(
- m_display, m_clipboard[id]->getWindow());
- if (clipboard != NULL) {
- // save clipboard data
- return Clipboard::copy(m_clipboard[id], clipboard, timestamp);
- }
- else {
- // assert clipboard ownership
- if (!m_clipboard[id]->open(timestamp)) {
- return false;
- }
- m_clipboard[id]->empty();
- m_clipboard[id]->close();
- return true;
- }
- }
- void
- XWindowsScreen::checkClipboards()
- {
- // do nothing, we're always up to date
- }
- void
- XWindowsScreen::openScreensaver(bool notify)
- {
- m_screensaverNotify = notify;
- if (!m_screensaverNotify) {
- m_screensaver->disable();
- }
- }
- void
- XWindowsScreen::closeScreensaver()
- {
- if (!m_screensaverNotify) {
- m_screensaver->enable();
- }
- }
- void
- XWindowsScreen::screensaver(bool activate)
- {
- if (activate) {
- m_screensaver->activate();
- }
- else {
- m_screensaver->deactivate();
- }
- }
- void
- XWindowsScreen::resetOptions()
- {
- m_xtestIsXineramaUnaware = true;
- m_preserveFocus = false;
- }
- void
- XWindowsScreen::setOptions(const OptionsList& options)
- {
- for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
- if (options[i] == kOptionXTestXineramaUnaware) {
- m_xtestIsXineramaUnaware = (options[i + 1] != 0);
- LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false"));
- }
- else if (options[i] == kOptionScreenPreserveFocus) {
- m_preserveFocus = (options[i + 1] != 0);
- LOG((CLOG_DEBUG1 "Preserve Focus = %s", m_preserveFocus ? "true" : "false"));
- }
- }
- }
- void
- XWindowsScreen::setSequenceNumber(UInt32 seqNum)
- {
- m_sequenceNumber = seqNum;
- }
- bool
- XWindowsScreen::isPrimary() const
- {
- return m_isPrimary;
- }
- void*
- XWindowsScreen::getEventTarget() const
- {
- return const_cast<XWindowsScreen*>(this);
- }
- bool
- XWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const
- {
- assert(clipboard != NULL);
- // fail if we don't have the requested clipboard
- if (m_clipboard[id] == NULL) {
- return false;
- }
- // get the actual time. ICCCM does not allow CurrentTime.
- Time timestamp = XWindowsUtil::getCurrentTime(
- m_display, m_clipboard[id]->getWindow());
- // copy the clipboard
- return Clipboard::copy(clipboard, m_clipboard[id], timestamp);
- }
- void
- XWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
- {
- x = m_x;
- y = m_y;
- w = m_w;
- h = m_h;
- }
- void
- XWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
- {
- Window root, window;
- int mx, my, xWindow, yWindow;
- unsigned int mask;
- if (XQueryPointer(m_display, m_root, &root, &window,
- &mx, &my, &xWindow, &yWindow, &mask)) {
- x = mx;
- y = my;
- }
- else {
- x = m_xCenter;
- y = m_yCenter;
- }
- }
- void
- XWindowsScreen::reconfigure(UInt32)
- {
- // do nothing
- }
- void
- XWindowsScreen::warpCursor(SInt32 x, SInt32 y)
- {
- // warp mouse
- warpCursorNoFlush(x, y);
- // remove all input events before and including warp
- XEvent event;
- while (XCheckMaskEvent(m_display, PointerMotionMask |
- ButtonPressMask | ButtonReleaseMask |
- KeyPressMask | KeyReleaseMask |
- KeymapStateMask,
- &event)) {
- // do nothing
- }
- // save position as last position
- m_xCursor = x;
- m_yCursor = y;
- }
- UInt32
- XWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
- {
- // only allow certain modifiers
- if ((mask & ~(KeyModifierShift | KeyModifierControl |
- KeyModifierAlt | KeyModifierSuper)) != 0) {
- LOG((CLOG_DEBUG "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_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
- return 0;
- }
- XWindowsKeyState::KeycodeList keycodes;
- m_keyState->mapKeyToKeycodes(key, keycodes);
- if (key != kKeyNone && keycodes.empty()) {
- // can't map key
- LOG((CLOG_DEBUG "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;
- {
- XWindowsUtil::ErrorLock 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[HotKeyItem(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 (XWindowsKeyState::KeycodeList::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[HotKeyItem(*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(HotKeyItem(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)", synergy::KeyMap::formatKey(key, mask).c_str(), key, mask));
- return 0;
- }
-
- LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", synergy::KeyMap::formatKey(key, mask).c_str(), key, mask, id));
- return id;
- }
- void
- XWindowsScreen::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;
- {
- XWindowsUtil::ErrorLock 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(HotKeyItem(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
- XWindowsScreen::fakeInputBegin()
- {
- // FIXME -- not implemented
- }
- void
- XWindowsScreen::fakeInputEnd()
- {
- // FIXME -- not implemented
- }
- SInt32
- XWindowsScreen::getJumpZoneSize() const
- {
- return 1;
- }
- bool
- XWindowsScreen::isAnyMouseButtonDown(UInt32& buttonID) const
- {
- // query the pointer to get the button state
- Window root, window;
- int xRoot, yRoot, xWindow, yWindow;
- unsigned int state;
- if (XQueryPointer(m_display, m_root, &root, &window,
- &xRoot, &yRoot, &xWindow, &yWindow, &state)) {
- return ((state & (Button1Mask | Button2Mask | Button3Mask |
- Button4Mask | Button5Mask)) != 0);
- }
- return false;
- }
- void
- XWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
- {
- x = m_xCenter;
- y = m_yCenter;
- }
- void
- XWindowsScreen::fakeMouseButton(ButtonID button, bool press)
- {
- const unsigned int xButton = mapButtonToX(button);
- if (xButton != 0) {
- XTestFakeButtonEvent(m_display, xButton,
- press ? True : False, CurrentTime);
- XFlush(m_display);
- }
- }
- void
- XWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y)
- {
- if (m_xinerama && m_xtestIsXineramaUnaware) {
- XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
- }
- else {
- XTestFakeMotionEvent(m_display, DefaultScreen(m_display),
- x, y, CurrentTime);
- }
- XFlush(m_display);
- }
- void
- XWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
- {
- // FIXME -- ignore xinerama for now
- if (false && m_xinerama && m_xtestIsXineramaUnaware) {
- // XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
- }
- else {
- XTestFakeRelativeMotionEvent(m_display, dx, dy, CurrentTime);
- }
- XFlush(m_display);
- }
- void
- XWindowsScreen::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>(
- (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 (yDelta >= 0) {
- keycode = XKeysymToKeycode(m_display, XK_Page_Up);
- }
- else {
- keycode = XKeysymToKeycode(m_display, XK_Page_Down);
- }
- if (keycode != 0) {
- XTestFakeKeyEvent(m_display, keycode, True, CurrentTime);
- XTestFakeKeyEvent(m_display, keycode, False, CurrentTime);
- }
- return;
- }
- // now use absolute value of delta
- if (yDelta < 0) {
- yDelta = -yDelta;
- }
- if (yDelta < m_mouseScrollDelta) {
- LOG((CLOG_WARN "Wheel scroll delta (%d) smaller than threshold (%d)", yDelta, m_mouseScrollDelta));
- }
- // send as many clicks as necessary
- for (; yDelta >= m_mouseScrollDelta; yDelta -= m_mouseScrollDelta) {
- XTestFakeButtonEvent(m_display, xButton, True, CurrentTime);
- XTestFakeButtonEvent(m_display, xButton, False, CurrentTime);
- }
- XFlush(m_display);
- }
- Display*
- XWindowsScreen::openDisplay(const char* displayName)
- {
- // get the DISPLAY
- if (displayName == NULL) {
- displayName = getenv("DISPLAY");
- if (displayName == NULL) {
- displayName = ":0.0";
- }
- }
- // open the display
- LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", displayName));
- Display* display = XOpenDisplay(displayName);
- if (display == NULL) {
- throw XScreenUnavailable(60.0);
- }
- // verify the availability of the XTest extension
- if (!m_isPrimary) {
- int majorOpcode, firstEvent, firstError;
- if (!XQueryExtension(display, XTestExtensionName,
- &majorOpcode, &firstEvent, &firstError)) {
- LOG((CLOG_ERR "XTEST extension not available"));
- XCloseDisplay(display);
- throw XScreenOpenFailure();
- }
- }
- #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
- #if HAVE_X11_EXTENSIONS_XRANDR_H
- // query for XRandR extension
- int dummyError;
- m_xrandr = XRRQueryExtension(display, &m_xrandrEventBase, &dummyError);
- if (m_xrandr) {
- // enable XRRScreenChangeNotifyEvent
- XRRSelectInput(display, DefaultRootWindow(display), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask);
- }
- #endif
- return display;
- }
- void
- XWindowsScreen::saveShape()
- {
- // get shape of default screen
- m_x = 0;
- m_y = 0;
- m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display));
- m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display));
- #if HAVE_X11_EXTENSIONS_XRANDR_H
- if (m_xrandr){
- int numSizes;
- XRRScreenSize* xrrs;
- Rotation rotation;
- xrrs = XRRSizes(m_display, DefaultScreen(m_display), &numSizes);
- XRRRotations(m_display, DefaultScreen(m_display), &rotation);
- if (xrrs != NULL) {
- if (rotation & (RR_Rotate_90|RR_Rotate_270) ){
- m_w = xrrs->height;
- m_h = xrrs->width;
- }
- }
- }
- #endif
- // get center of default screen
- m_xCenter = m_x + (m_w >> 1);
- m_yCenter = m_y + (m_h >> 1);
- // check if xinerama is enabled and there is more than one screen.
- // get center of first Xinerama screen. Xinerama appears to have
- // a bug when XWarpPointer() is used in combination with
- // XGrabPointer(). in that case, the warp is successful but the
- // next pointer motion warps the pointer again, apparently to
- // constrain it to some unknown region, possibly the region from
- // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over
- // all physical screens. this warp only seems to happen if the
- // pointer wasn't in that region before the XWarpPointer(). the
- // second (unexpected) warp causes synergy to think the pointer
- // has been moved when it hasn't. to work around the problem,
- // we warp the pointer to the center of the first physical
- // screen instead of the logical screen.
- m_xinerama = false;
- #if HAVE_X11_EXTENSIONS_XINERAMA_H
- int eventBase, errorBase;
- if (XineramaQueryExtension(m_display, &eventBase, &errorBase) &&
- XineramaIsActive(m_display)) {
- int numScreens;
- XineramaScreenInfo* screens;
- screens = XineramaQueryScreens(m_display, &numScreens);
- if (screens != NULL) {
- if (numScreens > 1) {
- m_xinerama = true;
- m_xCenter = screens[0].x_org + (screens[0].width >> 1);
- m_yCenter = screens[0].y_org + (screens[0].height >> 1);
- }
- XFree(screens);
- }
- }
- #endif
- }
- Window
- XWindowsScreen::openWindow() const
- {
- // default window attributes. we don't want the window manager
- // messing with our window and we don't want the cursor to be
- // visible inside the window.
- XSetWindowAttributes attr;
- attr.do_not_propagate_mask = 0;
- attr.override_redirect = True;
- attr.cursor = createBlankCursor();
- // adjust attributes and get size and shape
- SInt32 x, y, w, h;
- if (m_isPrimary) {
- // grab window attributes. this window is used to capture user
- // input when the user is focused on another client. it covers
- // the whole screen.
- attr.event_mask = PointerMotionMask |
- ButtonPressMask | ButtonReleaseMask |
- KeyPressMask | KeyReleaseMask |
- KeymapStateMask | PropertyChangeMask;
- x = m_x;
- y = m_y;
- w = m_w;
- h = m_h;
- }
- else {
- // cursor hider window attributes. this window is used to hide the
- // cursor when it's not on the screen. the window is hidden as soon
- // as the cursor enters the screen or the display's real mouse is
- // moved. we'll reposition the window as necessary so its
- // position here doesn't matter. it only needs to be 1x1 because
- // it only needs to contain the cursor's hotspot.
- attr.event_mask = LeaveWindowMask;
- x = 0;
- y = 0;
- w = 1;
- h = 1;
- }
- // create and return the window
- Window window = XCreateWindow(m_display, m_root, x, y, w, h, 0, 0,
- InputOnly, CopyFromParent,
- CWDontPropagate | CWEventMask |
- CWOverrideRedirect | CWCursor,
- &attr);
- if (window == None) {
- throw XScreenOpenFailure();
- }
- return window;
- }
- void
- XWindowsScreen::openIM()
- {
- // open the input methods
- XIM im = XOpenIM(m_display, NULL, NULL, NULL);
- if (im == NULL) {
- LOG((CLOG_INFO "no support for IM"));
- return;
- }
- // find the appropriate style. synergy supports XIMPreeditNothing
- // only at the moment.
- XIMStyles* styles;
- if (XGetIMValues(im, XNQueryInputStyle, &styles, NULL) != NULL ||
- styles == NULL) {
- LOG((CLOG_WARN "cannot get IM styles"));
- XCloseIM(im);
- return;
- }
- XIMStyle style = 0;
- for (unsigned short i = 0; i < styles->count_styles; ++i) {
- style = styles->supported_styles[i];
- if ((style & XIMPreeditNothing) != 0) {
- if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) {
- break;
- }
- }
- }
- XFree(styles);
- if (style == 0) {
- LOG((CLOG_INFO "no supported IM styles"));
- XCloseIM(im);
- return;
- }
- // create an input context for the style and tell it about our window
- XIC ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, m_window, NULL);
- if (ic == NULL) {
- LOG((CLOG_WARN "cannot create IC"));
- XCloseIM(im);
- return;
- }
- // find out the events we must select for and do so
- unsigned long mask;
- if (XGetICValues(ic, XNFilterEvents, &mask, NULL) != NULL) {
- LOG((CLOG_WARN "cannot get IC filter events"));
- XDestroyIC(ic);
- XCloseIM(im);
- return;
- }
- // we have IM
- m_im = im;
- m_ic = ic;
- m_lastKeycode = 0;
- // select events on our window that IM requires
- XWindowAttributes attr;
- XGetWindowAttributes(m_display, m_window, &attr);
- XSelectInput(m_display, m_window, attr.your_event_mask | mask);
- }
- void
- XWindowsScreen::sendEvent(Event::Type type, void* data)
- {
- m_events->addEvent(Event(type, getEventTarget(), data));
- }
- void
- XWindowsScreen::sendClipboardEvent(Event::Type type, ClipboardID id)
- {
- ClipboardInfo* info = (ClipboardInfo*)malloc(sizeof(ClipboardInfo));
- info->m_id = id;
- info->m_sequenceNumber = m_sequenceNumber;
- sendEvent(type, info);
- }
- IKeyState*
- XWindowsScreen::getKeyState() const
- {
- return m_keyState;
- }
- Bool
- XWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
- {
- KeyEventFilter* filter = reinterpret_cast<KeyEventFilter*>(arg);
- return (xevent->type == filter->m_event &&
- xevent->xkey.window == filter->m_window &&
- xevent->xkey.time == filter->m_time &&
- xevent->xkey.keycode == filter->m_keycode) ? True : False;
- }
- void
- XWindowsScreen::handleSystemEvent(const Event& event, void*)
- {
- XEvent* xevent = reinterpret_cast<XEvent*>(event.getData());
- assert(xevent != NULL);
- // update key state
- bool isRepeat = false;
- if (m_isPrimary) {
- if (xevent->type == KeyRelease) {
- // check if this is a key repeat by getting the next
- // KeyPress event that has the same key and time as
- // this release event, if any. first prepare the
- // filter info.
- KeyEventFilter filter;
- filter.m_event = KeyPress;
- filter.m_window = xevent->xkey.window;
- filter.m_time = xevent->xkey.time;
- filter.m_keycode = xevent->xkey.keycode;
- XEvent xevent2;
- isRepeat = (XCheckIfEvent(m_display, &xevent2,
- &XWindowsScreen::findKeyEvent,
- (XPointer)&filter) == True);
- }
- if (xevent->type == KeyPress || xevent->type == KeyRelease) {
- 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);
- }
- }
- // let input methods try to handle event first
- if (m_ic != NULL) {
- // XFilterEvent() may eat the event and generate a new KeyPress
- // event with a keycode of 0 because there isn't an actual key
- // associated with the keysym. but the KeyRelease may pass
- // through XFilterEvent() and keep its keycode. this means
- // there's a mismatch between KeyPress and KeyRelease keycodes.
- // since we use the keycode on the client to detect when a key
- // is released this won't do. so we remember the keycode on
- // the most recent KeyPress (and clear it on a matching
- // KeyRelease) so we have a keycode for a synthesized KeyPress.
- if (xevent->type == KeyPress && xevent->xkey.keycode != 0) {
- m_lastKeycode = xevent->xkey.keycode;
- }
- else if (xevent->type == KeyRelease &&
- xevent->xkey.keycode == m_lastKeycode) {
- m_lastKeycode = 0;
- }
- // now filter the event
- if (XFilterEvent(xevent, DefaultRootWindow(m_display))) {
- if (xevent->type == KeyPress) {
- // add filtered presses to the filtered list
- m_filtered.insert(m_lastKeycode);
- }
- return;
- }
- // discard matching key releases for key presses that were
- // filtered and remove them from our filtered list.
- else if (xevent->type == KeyRelease &&
- m_filtered.count(xevent->xkey.keycode) > 0) {
- m_filtered.erase(xevent->xkey.keycode);
- return;
- }
- }
- // let screen saver have a go
- if (m_screensaver->handleXEvent(xevent)) {
- // screen saver handled it
- return;
- }
- #ifdef HAVE_XI2
- if (m_xi2detected) {
- // Process RawMotion
- XGenericEventCookie *cookie = (XGenericEventCookie*)&xevent->xcookie;
- if (XGetEventData(m_display, cookie) &&
- cookie->type == GenericEvent &&
- cookie->extension == xi_opcode) {
- if (cookie->evtype == XI_RawMotion) {
- // Get current pointer's position
- Window root, child;
- XMotionEvent xmotion;
- xmotion.type = MotionNotify;
- xmotion.send_event = False; // Raw motion
- xmotion.display = m_display;
- xmotion.window = m_window;
- /* xmotion's time, state and is_hint are not used */
- unsigned int msk;
- xmotion.same_screen = XQueryPointer(
- m_display, m_root, &xmotion.root, &xmotion.subwindow,
- &xmotion.x_root,
- &xmotion.y_root,
- &xmotion.x,
- &xmotion.y,
- &msk);
- onMouseMove(xmotion);
- XFreeEventData(m_display, cookie);
- return;
- }
- XFreeEventData(m_display, cookie);
- }
- }
- #endif
- // handle the event ourself
- switch (xevent->type) {
- case CreateNotify:
- if (m_isPrimary) {
- // select events on new window
- selectEvents(xevent->xcreatewindow.window);
- }
- break;
- case MappingNotify:
- refreshKeyboard(xevent);
- break;
- case LeaveNotify:
- if (!m_isPrimary) {
- // mouse moved out of hider window somehow. hide the window.
- XUnmapWindow(m_display, m_window);
- }
- break;
- case SelectionClear:
- {
- // we just lost the selection. that means someone else
- // grabbed the selection so this screen is now the
- // selection owner. report that to the receiver.
- ClipboardID id = getClipboardID(xevent->xselectionclear.selection);
- if (id != kClipboardEnd) {
- m_clipboard[id]->lost(xevent->xselectionclear.time);
- sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), id);
- return;
- }
- }
- break;
- case SelectionNotify:
- // notification of selection transferred. we shouldn't
- // get this here because we handle them in the selection
- // retrieval methods. we'll just delete the property
- // with the data (satisfying the usual ICCCM protocol).
- if (xevent->xselection.property != None) {
- XDeleteProperty(m_display,
- xevent->xselection.requestor,
- xevent->xselection.property);
- }
- break;
- case SelectionRequest:
- {
- // somebody is asking for clipboard data
- ClipboardID id = getClipboardID(
- xevent->xselectionrequest.selection);
- if (id != kClipboardEnd) {
- m_clipboard[id]->addRequest(
- xevent->xselectionrequest.owner,
- xevent->xselectionrequest.requestor,
- xevent->xselectionrequest.target,
- xevent->xselectionrequest.time,
- xevent->xselectionrequest.property);
- return;
- }
- }
- break;
- case PropertyNotify:
- // property delete may be part of a selection conversion
- if (xevent->xproperty.state == PropertyDelete) {
- processClipboardRequest(xevent->xproperty.window,
- xevent->xproperty.time,
- xevent->xproperty.atom);
- }
- break;
- case DestroyNotify:
- // looks like one of the windows that requested a clipboard
- // transfer has gone bye-bye.
- destroyClipboardRequest(xevent->xdestroywindow.window);
- break;
- case KeyPress:
- if (m_isPrimary) {
- onKeyPress(xevent->xkey);
- }
- return;
- case KeyRelease:
- if (m_isPrimary) {
- onKeyRelease(xevent->xkey, isRepeat);
- }
- return;
- case ButtonPress:
- if (m_isPrimary) {
- onMousePress(xevent->xbutton);
- }
- return;
- case ButtonRelease:
- if (m_isPrimary) {
- onMouseRelease(xevent->xbutton);
- }
- return;
- case MotionNotify:
- if (m_isPrimary) {
- onMouseMove(xevent->xmotion);
- }
- 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
- #if HAVE_X11_EXTENSIONS_XRANDR_H
- if (m_xrandr) {
- if (xevent->type == m_xrandrEventBase + RRScreenChangeNotify
- || xevent->type == m_xrandrEventBase + RRNotify
- && reinterpret_cast<XRRNotifyEvent *>(xevent)->subtype == RRNotify_CrtcChange) {
- LOG((CLOG_INFO "XRRScreenChangeNotifyEvent or RRNotify_CrtcChange received"));
- // we're required to call back into XLib so XLib can update its internal state
- XRRUpdateConfiguration(xevent);
- // requery/recalculate the screen shape
- saveShape();
- // we need to resize m_window, otherwise we'll get a weird problem where moving
- // off the server onto the client causes the pointer to warp to the
- // center of the server (so you can't move the pointer off the server)
- if (m_isPrimary) {
- XMoveWindow(m_display, m_window, m_x, m_y);
- XResizeWindow(m_display, m_window, m_w, m_h);
- }
- }
- }
- #endif
- break;
- }
- }
- void
- XWindowsScreen::onKeyPress(XKeyEvent& xkey)
- {
- LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xkey.keycode, xkey.state));
- const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
- KeyID key = mapKeyFromX(&xkey);
- if (key != kKeyNone) {
- // check for ctrl+alt+del emulation
- if ((key == kKeyPause || key == kKeyBreak) &&
- (mask & (KeyModifierControl | KeyModifierAlt)) ==
- (KeyModifierControl | KeyModifierAlt)) {
- // pretend it's ctrl+alt+del
- LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
- key = kKeyDelete;
- }
- // get which button. see call to XFilterEvent() in onEvent()
- // for more info.
- bool isFake = false;
- KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
- if (keycode == 0) {
- isFake = true;
- keycode = static_cast<KeyButton>(m_lastKeycode);
- if (keycode == 0) {
- // no keycode
- return;
- }
- }
- // handle key
- m_keyState->sendKeyEvent(getEventTarget(),
- true, false, key, mask, 1, keycode);
- // do fake release if this is a fake press
- if (isFake) {
- m_keyState->sendKeyEvent(getEventTarget(),
- false, false, key, mask, 1, keycode);
- }
- }
- }
- void
- XWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat)
- {
- const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
- KeyID key = mapKeyFromX(&xkey);
- if (key != kKeyNone) {
- // check for ctrl+alt+del emulation
- if ((key == kKeyPause || key == kKeyBreak) &&
- (mask & (KeyModifierControl | KeyModifierAlt)) ==
- (KeyModifierControl | KeyModifierAlt)) {
- // pretend it's ctrl+alt+del and ignore autorepeat
- LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
- key = kKeyDelete;
- isRepeat = false;
- }
- KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
- if (!isRepeat) {
- // no press event follows so it's a plain release
- LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state));
- m_keyState->sendKeyEvent(getEventTarget(),
- false, false, key, mask, 1, keycode);
- }
- else {
- // found a press event following so it's a repeat.
- // we could attempt to count the already queued
- // repeats but we'll just send a repeat of 1.
- // note that we discard the press event.
- LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state));
- m_keyState->sendKeyEvent(getEventTarget(),
- false, true, key, mask, 1, keycode);
- }
- }
- }
- bool
- XWindowsScreen::onHotKey(XKeyEvent& xkey, bool isRepeat)
- {
- // find the hot key id
- HotKeyToIDMap::const_iterator i =
- m_hotKeyToIDMap.find(HotKeyItem(xkey.keycode, xkey.state));
- if (i == m_hotKeyToIDMap.end()) {
- return false;
- }
- // find what kind of event
- Event::Type type;
- if (xkey.type == KeyPress) {
- type = m_events->forIPrimaryScreen().hotKeyDown();
- }
- else if (xkey.type == KeyRelease) {
- type = m_events->forIPrimaryScreen().hotKeyUp();
- }
- else {
- return false;
- }
- // generate event (ignore key repeats)
- if (!isRepeat) {
- m_events->addEvent(Event(type, getEventTarget(),
- HotKeyInfo::alloc(i->second)));
- }
- return true;
- }
- void
- XWindowsScreen::onMousePress(const XButtonEvent& xbutton)
- {
- LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button));
- ButtonID button = mapButtonFromX(&xbutton);
- KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
- if (button != kButtonNone) {
- sendEvent(m_events->forIPrimaryScreen().buttonDown(), ButtonInfo::alloc(button, mask));
- }
- }
- void
- XWindowsScreen::onMouseRelease(const XButtonEvent& xbutton)
- {
- LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button));
- ButtonID button = mapButtonFromX(&xbutton);
- KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
- if (button != kButtonNone) {
- sendEvent(m_events->forIPrimaryScreen().buttonUp(), ButtonInfo::alloc(button, mask));
- }
- else if (xbutton.button == 4) {
- // wheel forward (away from user)
- sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(0, 120));
- }
- else if (xbutton.button == 5) {
- // wheel backward (toward user)
- sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(0, -120));
- }
- // XXX -- support x-axis scrolling
- }
- void
- XWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
- {
- LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xmotion.x_root, xmotion.y_root));
- // compute motion delta (relative to the last known
- // mouse position)
- SInt32 x = xmotion.x_root - m_xCursor;
- SInt32 y = xmotion.y_root - m_yCursor;
- // save position to compute delta of next motion
- m_xCursor = xmotion.x_root;
- m_yCursor = xmotion.y_root;
- if (xmotion.send_event) {
- // we warped the mouse. discard events until we
- // find the matching sent event. see
- // warpCursorNoFlush() for where the events are
- // sent. we discard the matching sent event and
- // can be sure we've skipped the warp event.
- XEvent xevent;
- char cntr = 0;
- do {
- XMaskEvent(m_display, PointerMotionMask, &xevent);
- if (cntr++ > 10) {
- LOG((CLOG_WARN "too many discarded events! %d", cntr));
- break;
- }
- } while (!xevent.xany.send_event);
- cntr = 0;
- }
- else if (m_isOnScreen) {
- // motion on primary screen
- sendEvent(m_events->forIPrimaryScreen().motionOnPrimary(),
- MotionInfo::alloc(m_xCursor, m_yCursor));
- }
- else {
- // motion on secondary screen. warp mouse back to
- // center.
- //
- // my lombard (powerbook g3) running linux and
- // using the adbmouse driver has two problems:
- // first, the driver only sends motions of +/-2
- // pixels and, second, it seems to discard some
- // physical input after a warp. the former isn't a
- // big deal (we're just limited to every other
- // pixel) but the latter is a PITA. to work around
- // it we only warp when the mouse has moved more
- // than s_size pixels from the center.
- static const SInt32 s_size = 32;
- if (xmotion.x_root - m_xCenter < -s_size ||
- xmotion.x_root - m_xCenter > s_size ||
- xmotion.y_root - m_yCenter < -s_size ||
- xmotion.y_root - m_yCenter > s_size) {
- warpCursorNoFlush(m_xCenter, m_yCenter);
- }
- // send event if mouse moved. do this after warping
- // back to center in case the motion takes us onto
- // the primary screen. if we sent the event first
- // in that case then the warp would happen after
- // warping to the primary screen's enter position,
- // effectively overriding it.
- if (x != 0 || y != 0) {
- sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y));
- }
- }
- }
- Cursor
- XWindowsScreen::createBlankCursor() const
- {
- // this seems just a bit more complicated than really necessary
- // get the closet cursor size to 1x1
- unsigned int w, h;
- XQueryBestCursor(m_display, m_root, 1, 1, &w, &h);
- // make bitmap data for cursor of closet size. since the cursor
- // is blank we can use the same bitmap for shape and mask: all
- // zeros.
- const int size = ((w + 7) >> 3) * h;
- char* data = new char[size];
- memset(data, 0, size);
- // make bitmap
- Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h);
- // need an arbitrary color for the cursor
- XColor color;
- color.pixel = 0;
- color.red = color.green = color.blue = 0;
- color.flags = DoRed | DoGreen | DoBlue;
- // make cursor from bitmap
- Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap,
- &color, &color, 0, 0);
- // don't need bitmap or the data anymore
- delete[] data;
- XFreePixmap(m_display, bitmap);
- return cursor;
- }
- ClipboardID
- XWindowsScreen::getClipboardID(Atom selection) const
- {
- for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
- if (m_clipboard[id] != NULL &&
- m_clipboard[id]->getSelection() == selection) {
- return id;
- }
- }
- return kClipboardEnd;
- }
- void
- XWindowsScreen::processClipboardRequest(Window requestor,
- Time time, Atom property)
- {
- // check every clipboard until one returns success
- for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
- if (m_clipboard[id] != NULL &&
- m_clipboard[id]->processRequest(requestor, time, property)) {
- break;
- }
- }
- }
- void
- XWindowsScreen::destroyClipboardRequest(Window requestor)
- {
- // check every clipboard until one returns success
- for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
- if (m_clipboard[id] != NULL &&
- m_clipboard[id]->destroyRequest(requestor)) {
- break;
- }
- }
- }
- void
- XWindowsScreen::onError()
- {
- // prevent further access to the X display
- m_events->adoptBuffer(NULL);
- m_screensaver->destroy();
- m_screensaver = NULL;
- m_display = NULL;
- // notify of failure
- sendEvent(m_events->forIScreen().error(), NULL);
- // FIXME -- should ensure that we ignore operations that involve
- // m_display from now on. however, Xlib will simply exit the
- // application in response to the X I/O error so there's no
- // point in trying to really handle the error. if we did want
- // to handle the error, it'd probably be easiest to delegate to
- // one of two objects. one object would take the implementation
- // from this class. the other object would be stub methods that
- // don't use X11. on error, we'd switch to the latter.
- }
- int
- XWindowsScreen::ioErrorHandler(Display*)
- {
- // the display has disconnected, probably because X is shutting
- // down. X forces us to exit at this point which is annoying.
- // we'll pretend as if we won't exit so we try to make sure we
- // don't access the display anymore.
- LOG((CLOG_CRIT "X display has unexpectedly disconnected"));
- s_screen->onError();
- return 0;
- }
- void
- XWindowsScreen::selectEvents(Window w) const
- {
- // ignore errors while we adjust event masks. windows could be
- // destroyed at any time after the XQueryTree() in doSelectEvents()
- // so we must ignore BadWindow errors.
- XWindowsUtil::ErrorLock lock(m_display);
- // adjust event masks
- doSelectEvents(w);
- }
- void
- XWindowsScreen::doSelectEvents(Window w) const
- {
- // we want to track the mouse everywhere on the display. to achieve
- // that we select PointerMotionMask on every window. we also select
- // SubstructureNotifyMask in order to get CreateNotify events so we
- // select events on new windows too.
- // we don't want to adjust our grab window
- if (w == m_window) {
- return;
- }
- // X11 has a design flaw. If *no* client selected PointerMotionMask for
- // a window, motion events will be delivered to that window's parent.
- // If *any* client, not necessarily the owner, selects PointerMotionMask
- // on such a window, X will stop propagating motion events to its
- // parent. This breaks applications that rely on event propagation
- // behavior.
- //
- // Avoid selecting PointerMotionMask unless some other client selected
- // it already.
- long mask = SubstructureNotifyMask;
- XWindowAttributes attr;
- XGetWindowAttributes(m_display, w, &attr);
- if ((attr.all_event_masks & PointerMotionMask) == PointerMotionMask) {
- mask |= PointerMotionMask;
- }
- // select events of interest. do this before querying the tree so
- // we'll get notifications of children created after the XQueryTree()
- // so we won't miss them.
- XSelectInput(m_display, w, mask);
- // recurse on child windows
- Window rw, pw, *cw;
- unsigned int nc;
- if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) {
- for (unsigned int i = 0; i < nc; ++i) {
- doSelectEvents(cw[i]);
- }
- XFree(cw);
- }
- }
- KeyID
- XWindowsScreen::mapKeyFromX(XKeyEvent* event) const
- {
- // convert to a keysym
- KeySym keysym;
- if (event->type == KeyPress && m_ic != NULL) {
- // do multibyte lookup. can only call XmbLookupString with a
- // key press event and a valid XIC so we checked those above.
- char scratch[32];
- int n = sizeof(scratch) / sizeof(scratch[0]);
- char* buffer = scratch;
- int status;
- n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
- if (status == XBufferOverflow) {
- // not enough space. grow buffer and try again.
- buffer = new char[n];
- n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
- delete[] buffer;
- }
- // see what we got. since we don't care about the string
- // we'll just look for a keysym.
- switch (status) {
- default:
- case XLookupNone:
- case XLookupChars:
- keysym = 0;
- break;