/* * Function for initialising the whole shebang. */ void xautolock_initDiy (Display* d) { int s; queue.display = d; queue.tail = 0; queue.head = 0; for (s = -1; ++s < ScreenCount (d); ) { Window root = RootWindowOfScreen (ScreenOfDisplay (d, s)); addToQueue (root); #if 0 selectEvents (root, True); #endif } }
static void processQueue (time_t age) { if (queue.head) { time_t now = time (0); xautolock_item current = queue.head; while (current && current->creationtime + age < now) { selectEvents (current->window, False); queue.head = current->next; free (current); current = queue.head; } if (!queue.head) queue.tail = 0; } }
void CXWindowsPrimaryScreen::createWindow() { assert(m_window == None); // get size of screen SInt32 x, y, w, h; m_screen->getShape(x, y, w, h); // grab window attributes. this window is used to capture user // input when the user is focused on another client. don't let // the window manager mess with it. XSetWindowAttributes attr; attr.event_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | KeymapStateMask | PropertyChangeMask; attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = m_screen->getBlankCursor(); { // create the grab window CDisplayLock display(m_screen); m_window = XCreateWindow(display, m_screen->getRoot(), x, y, w, h, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); if (m_window == None) { throw XScreenOpenFailure(); } LOG((CLOG_DEBUG "window is 0x%08x", m_window)); // start watching for events on other windows selectEvents(display, m_screen->getRoot()); } // tell generic screen about the window m_screen->setWindow(m_window); }
bool CXWindowsPrimaryScreen::onEvent(CEvent* event) { assert(event != NULL); XEvent& xevent = event->m_event; // let input methods try to handle event first if (m_ic != NULL) { // XFilterEvent() may eat the event and generate a new KeyPress // event with a keycode of 0 because there isn't an actual key // associated with the keysym. but the KeyRelease may pass // through XFilterEvent() and keep its keycode. this means // there's a mismatch between KeyPress and KeyRelease keycodes. // since we use the keycode on the client to detect when a key // is released this won't do. so we remember the keycode on // the most recent KeyPress (and clear it on a matching // KeyRelease) so we have a keycode for a synthesized KeyPress. if (xevent.type == KeyPress && xevent.xkey.keycode != 0) { m_lastKeycode = xevent.xkey.keycode; } else if (xevent.type == KeyRelease && xevent.xkey.keycode == m_lastKeycode) { m_lastKeycode = 0; } // now filter the event if (XFilterEvent(&xevent, None)) { return true; } } // handle event switch (xevent.type) { case CreateNotify: { // select events on new window CDisplayLock display(m_screen); selectEvents(display, xevent.xcreatewindow.window); } return true; case MappingNotify: // keyboard mapping changed updateKeys(); return true; case KeyPress: { LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { // check for ctrl+alt+del emulation if ((key == kKeyPause || key == kKeyBreak) && (mask & (KeyModifierControl | KeyModifierAlt)) == (KeyModifierControl | KeyModifierAlt)) { // pretend it's ctrl+alt+del LOG((CLOG_DEBUG "emulate ctrl+alt+del")); key = kKeyDelete; } // get which button. see call to XFilterEvent() above // for more info. KeyCode keycode = xevent.xkey.keycode; if (keycode == 0) { keycode = m_lastKeycode; } // handle key m_receiver->onKeyDown(key, mask, static_cast<KeyButton>(keycode)); if (key == kKeyCapsLock && m_capsLockHalfDuplex) { m_receiver->onKeyUp(key, mask | KeyModifierCapsLock, static_cast<KeyButton>(keycode)); } else if (key == kKeyNumLock && m_numLockHalfDuplex) { m_receiver->onKeyUp(key, mask | KeyModifierNumLock, static_cast<KeyButton>(keycode)); } } } return true; case KeyRelease: { const KeyModifierMask mask = mapModifier(xevent.xkey.state); KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { // check if this is a key repeat by getting the next // KeyPress event that has the same key and time as // this release event, if any. first prepare the // filter info. CKeyEventInfo filter; filter.m_event = KeyPress; filter.m_window = xevent.xkey.window; filter.m_time = xevent.xkey.time; filter.m_keycode = xevent.xkey.keycode; // now check for event bool hasPress; { XEvent xevent2; CDisplayLock display(m_screen); hasPress = (XCheckIfEvent(display, &xevent2, &CXWindowsPrimaryScreen::findKeyEvent, (XPointer)&filter) == True); } // check for ctrl+alt+del emulation if ((key == kKeyPause || key == kKeyBreak) && (mask & (KeyModifierControl | KeyModifierAlt)) == (KeyModifierControl | KeyModifierAlt)) { // pretend it's ctrl+alt+del and ignore autorepeat LOG((CLOG_DEBUG "emulate ctrl+alt+del")); key = kKeyDelete; hasPress = false; } if (!hasPress) { // no press event follows so it's a plain release LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == kKeyCapsLock && m_capsLockHalfDuplex) { m_receiver->onKeyDown(key, mask, static_cast<KeyButton>(xevent.xkey.keycode)); } else if (key == kKeyNumLock && m_numLockHalfDuplex) { m_receiver->onKeyDown(key, mask, static_cast<KeyButton>(xevent.xkey.keycode)); } m_receiver->onKeyUp(key, mask, static_cast<KeyButton>(xevent.xkey.keycode)); } else { // found a press event following so it's a repeat. // we could attempt to count the already queued // repeats but we'll just send a repeat of 1. // note that we discard the press event. LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); m_receiver->onKeyRepeat(key, mask, 1, static_cast<KeyButton>(xevent.xkey.keycode)); } } } return true; case ButtonPress: { LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_receiver->onMouseDown(button); } } return true; case ButtonRelease: { LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_receiver->onMouseUp(button); } else if (xevent.xbutton.button == 4) { // wheel forward (away from user) m_receiver->onMouseWheel(120); } else if (xevent.xbutton.button == 5) { // wheel backward (toward user) m_receiver->onMouseWheel(-120); } } return true; case MotionNotify: { LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); // compute motion delta (relative to the last known // mouse position) SInt32 x = xevent.xmotion.x_root - m_x; SInt32 y = xevent.xmotion.y_root - m_y; // save position to compute delta of next motion m_x = xevent.xmotion.x_root; m_y = xevent.xmotion.y_root; if (xevent.xmotion.send_event) { // we warped the mouse. discard events until we // find the matching sent event. see // warpCursorNoFlush() for where the events are // sent. we discard the matching sent event and // can be sure we've skipped the warp event. CDisplayLock display(m_screen); do { XMaskEvent(display, PointerMotionMask, &xevent); } while (!xevent.xmotion.send_event); } else if (!isActive()) { // motion on primary screen m_receiver->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to // center. // // my lombard (powerbook g3) running linux and // using the adbmouse driver has two problems: // first, the driver only sends motions of +/-2 // pixels and, second, it seems to discard some // physical input after a warp. the former isn't a // big deal (we're just limited to every other // pixel) but the latter is a PITA. to work around // it we only warp when the mouse has moved more // than s_size pixels from the center. static const SInt32 s_size = 32; if (xevent.xmotion.x_root - m_xCenter < -s_size || xevent.xmotion.x_root - m_xCenter > s_size || xevent.xmotion.y_root - m_yCenter < -s_size || xevent.xmotion.y_root - m_yCenter > s_size) { CDisplayLock display(m_screen); warpCursorNoFlush(display, m_xCenter, m_yCenter); } // send event if mouse moved. do this after warping // back to center in case the motion takes us onto // the primary screen. if we sent the event first // in that case then the warp would happen after // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { m_receiver->onMouseMoveSecondary(x, y); } } } return true; } return false; }
/* * Function for selecting all interesting events on a given * (tree of) window(s). */ static void selectEvents (Window window, Bool substructureOnly) { Window root; /* root window of the window */ Window parent; /* parent of the window */ Window* children; /* children of the window */ unsigned nofChildren = 0; /* number of children */ unsigned i; /* loop counter */ XWindowAttributes attribs; /* attributes of the window */ if( xautolock_ignoreWindow( window )) return; /* * Start by querying the server about the root and parent windows. */ if (!XQueryTree (queue.display, window, &root, &parent, &children, &nofChildren)) { return; } if (nofChildren) (void) XFree ((char*) children); /* * Build the appropriate event mask. The basic idea is that we don't * want to interfere with the normal event propagation mechanism if * we don't have to. * * On the root window, we need to ask for both substructureNotify * and KeyPress events. On all other windows, we always need * substructureNotify, but only need Keypress if some other client * also asked for them, or if they are not being propagated up the * window tree. */ #if 0 if (substructureOnly) { (void) XSelectInput (queue.display, window, SubstructureNotifyMask); } else { if (parent == None) /* the *real* rootwindow */ { attribs.all_event_masks = attribs.do_not_propagate_mask = KeyPressMask; } else if (!XGetWindowAttributes (queue.display, window, &attribs)) #else { if (!XGetWindowAttributes (queue.display, window, &attribs)) #endif { return; } #if 0 (void) XSelectInput (queue.display, window, SubstructureNotifyMask | ( ( attribs.all_event_masks | attribs.do_not_propagate_mask) & KeyPressMask)); #else { int mask = SubstructureNotifyMask | attribs.your_event_mask; if( !substructureOnly ) { mask |= ( ( attribs.all_event_masks | attribs.do_not_propagate_mask) & KeyPressMask ); } (void) XSelectInput (queue.display, window, mask ); } #endif } /* * Now ask for the list of children again, since it might have changed * in between the last time and us selecting SubstructureNotifyMask. * * There is a (very small) chance that we might process a subtree twice: * child windows that have been created after our XSelectinput() has * been processed but before we get to the XQueryTree() bit will be * in this situation. This is harmless. It could be avoided by using * XGrabServer(), but that'd be an impolite thing to do, and since it * isn't required... */ if (!XQueryTree (queue.display, window, &root, &parent, &children, &nofChildren)) { return; } /* * Now do the same thing for all children. */ for (i = 0; i < nofChildren; ++i) { selectEvents (children[i], substructureOnly); } if (nofChildren) (void) XFree ((char*) children); } #if 0 /* * Function for processing any events that have come in since * last time. It is crucial that this function does not block * in case nothing interesting happened. */ void processEvents (void) { while (XPending (queue.display)) { XEvent event; if (XCheckMaskEvent (queue.display, SubstructureNotifyMask, &event)) { if (event.type == CreateNotify) { addToQueue (event.xcreatewindow.window); } } else { (void) XNextEvent (queue.display, &event); } /* * Reset the triggers if and only if the event is a * KeyPress event *and* was not generated by XSendEvent(). */ if ( event.type == KeyPress && !event.xany.send_event) { resetTriggers (); } } /* * Check the window queue for entries that are older than * CREATION_DELAY seconds. */ processQueue ((time_t) CREATION_DELAY); } #else void xautolock_processEvent( XEvent* event ) { if (event->type == CreateNotify) { addToQueue (event->xcreatewindow.window); } /* * Reset the triggers if and only if the event is a * KeyPress event *and* was not generated by XSendEvent(). */ if ( event->type == KeyPress && !event->xany.send_event) { xautolock_resetTriggers (); } } void xautolock_processQueue() { /* * Check the window queue for entries that are older than * CREATION_DELAY seconds. */ processQueue ((time_t) CREATION_DELAY); }