]> icculus.org git repositories - mikachu/openbox.git/blob - util/epist/screen.cc
call the window's processEvent() directly instead of the screen's, since it didn...
[mikachu/openbox.git] / util / epist / screen.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // screen.cc for Epistrophy - a key handler for NETWM/EWMH window managers.
3 // Copyright (c) 2002 - 2002 Ben Jansens <ben at orodu.net>
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
22
23 /* A few comments about stacked cycling:
24  * When stacked cycling is turned on, the focused window is always at the top
25  * (front) of the list (_clients), EXCEPT for when we are in cycling mode.
26  * (_cycling is true) If we were to add the focused window to the top of the
27  * stack while we were cycling, we would end in a deadlock between 2 windows.
28  * When the modifiers are released, the window that has focus (but it's not
29  * at the top of the stack, since we are cycling) is placed at the top of the
30  * stack and raised.
31  * Hooray and Bummy. - Marius
32  */
33
34 #ifdef    HAVE_CONFIG_H
35 #  include "../../config.h"
36 #endif // HAVE_CONFIG_H
37
38 extern "C" {
39 #ifdef    HAVE_STDIO_H
40 #  include <stdio.h>
41 #endif // HAVE_STDIO_H
42
43 #ifdef    HAVE_UNISTD_H
44 #  include <sys/types.h>
45 #  include <unistd.h>
46 #endif // HAVE_UNISTD_H
47
48 #include <X11/keysym.h>
49 }
50
51 #include <iostream>
52 #include <string>
53
54 using std::cout;
55 using std::endl;
56 using std::hex;
57 using std::dec;
58 using std::string;
59
60 #include "../../src/BaseDisplay.hh"
61 #include "../../src/XAtom.hh"
62 #include "screen.hh"
63 #include "epist.hh"
64 #include "config.hh"
65
66 screen::screen(epist *epist, int number) 
67   : _clients(epist->clientsList()), _active(epist->activeWindow()),
68     _config(epist->getConfig()), _grabbed(true), _cycling(false),
69     _stacked_cycling(false)
70 {
71   _epist = epist;
72   _xatom = _epist->xatom();
73   _last_active = _clients.end();
74   _number = number;
75   _info = _epist->getScreenInfo(_number);
76   _root = _info->getRootWindow();
77
78   _config->getValue(Config::stackedCycling, _stacked_cycling);
79
80   // find a window manager supporting NETWM, waiting for it to load if we must
81   int count = 20;  // try for 20 seconds
82   _managed = false;
83   while (! (_epist->doShutdown() || _managed || count <= 0)) {
84     if (! (_managed = findSupportingWM()))
85       sleep(1);
86     --count;
87   }
88   if (_managed)
89     cout << "Found compatible window manager '" << _wm_name << "' for screen "
90          << _number << ".\n";
91   else {
92     cout << "Unable to find a compatible window manager for screen " <<
93       _number << ".\n";
94     return;
95   }
96  
97   XSelectInput(_epist->getXDisplay(), _root, PropertyChangeMask);
98 }
99
100 screen::~screen() {
101   if (_managed)
102     XSelectInput(_epist->getXDisplay(), _root, None);
103 }
104
105
106 bool screen::findSupportingWM() {
107   Window support_win;
108   if (! _xatom->getValue(_root, XAtom::net_supporting_wm_check, XAtom::window,
109                          support_win) || support_win == None)
110     return false;
111
112   string title;
113   _xatom->getValue(support_win, XAtom::net_wm_name, XAtom::utf8, title);
114   _wm_name = title;
115   return true;
116 }
117
118
119 XWindow *screen::findWindow(const XEvent &e) const {
120   assert(_managed);
121
122   WindowList::const_iterator it, end = _clients.end();
123   for (it = _clients.begin(); it != end; ++it)
124     if (**it == e.xany.window)
125       break;
126   if(it == end)
127     return 0;
128   return *it;
129 }
130
131
132 void screen::processEvent(const XEvent &e) {
133   assert(_managed);
134   assert(e.xany.window == _root);
135
136   switch (e.type) {
137   case PropertyNotify:
138     // root window
139     if (e.xproperty.atom == _xatom->getAtom(XAtom::net_number_of_desktops))
140       updateNumDesktops();
141     else if (e.xproperty.atom == _xatom->getAtom(XAtom::net_current_desktop))
142       updateActiveDesktop();
143     else if (e.xproperty.atom == _xatom->getAtom(XAtom::net_active_window))
144       updateActiveWindow();
145     else if (e.xproperty.atom == _xatom->getAtom(XAtom::net_client_list)) {
146       // catch any window unmaps first
147       XEvent ev;
148       if (XCheckTypedWindowEvent(_epist->getXDisplay(), e.xany.window,
149                                  DestroyNotify, &ev) ||
150           XCheckTypedWindowEvent(_epist->getXDisplay(), e.xany.window,
151                                  UnmapNotify, &ev)) {
152
153         XWindow *win = _epist->findWindow(e.xany.window);
154         if (win) win->processEvent(ev);
155       }
156
157       updateClientList();
158     }
159     break;
160   case KeyPress:
161     handleKeypress(e);
162     break;
163
164   case KeyRelease:
165     handleKeyrelease(e);
166     break;
167
168   default:
169     break;
170   }
171 }
172
173 void screen::handleKeypress(const XEvent &e) {
174   int scrolllockMask, numlockMask;
175   _epist->getLockModifiers(numlockMask, scrolllockMask);
176   
177   // Mask out the lock modifiers. We want our keys to always work
178   // This should be made an option
179   unsigned int state = e.xkey.state & ~(LockMask|scrolllockMask|numlockMask);
180   keytree &ktree = _epist->getKeyTree();
181   const Action *it = ktree.getAction(e, state, this);
182
183   if (!it)
184     return;
185
186   switch (it->type()) {
187   case Action::nextScreen:
188     _epist->cycleScreen(_number, true);
189     return;
190
191   case Action::prevScreen:
192     _epist->cycleScreen(_number, false);
193     return;
194
195   case Action::nextWorkspace:
196     cycleWorkspace(true, it->number() != 0 ? it->number(): 1);
197     return;
198
199   case Action::prevWorkspace:
200     cycleWorkspace(false, it->number() != 0 ? it->number(): 1);
201     return;
202
203   case Action::nextWindow:
204     
205     cycleWindow(state, true, it->number() != 0 ? it->number(): 1);
206     return;
207
208   case Action::prevWindow:
209     cycleWindow(state, false, it->number() != 0 ? it->number(): 1);
210     return;
211
212   case Action::nextWindowOnAllWorkspaces:
213     cycleWindow(state, true, it->number() != 0 ? it->number(): 1,  false, true);
214     return;
215
216   case Action::prevWindowOnAllWorkspaces:
217     cycleWindow(state, false, it->number() != 0 ? it->number(): 1, false, true);
218     return;
219
220   case Action::nextWindowOnAllScreens:
221     cycleWindow(state, true, it->number() != 0 ? it->number(): 1, true);
222     return;
223
224   case Action::prevWindowOnAllScreens:
225     cycleWindow(state, false, it->number() != 0 ? it->number(): 1, true);
226     return;
227
228   case Action::nextWindowOfClass:
229     cycleWindow(state, true, it->number() != 0 ? it->number(): 1,
230                 false, false, true, it->string());
231     return;
232
233   case Action::prevWindowOfClass:
234     cycleWindow(state, false, it->number() != 0 ? it->number(): 1,
235                 false, false, true, it->string());
236     return;
237       
238   case Action::nextWindowOfClassOnAllWorkspaces:
239     cycleWindow(state, true, it->number() != 0 ? it->number(): 1,
240                 false, true, true, it->string());
241     return;
242       
243   case Action::prevWindowOfClassOnAllWorkspaces:
244     cycleWindow(state, false, it->number() != 0 ? it->number(): 1,
245                 false, true, true, it->string());
246     return;
247
248   case Action::changeWorkspace:
249     changeWorkspace(it->number());
250     return;
251
252   case Action::upWorkspace:
253     changeWorkspaceVert(-1);
254     return;
255
256   case Action::downWorkspace:
257     changeWorkspaceVert(1);
258     return;
259
260   case Action::leftWorkspace:
261     changeWorkspaceHorz(-1);
262     return;
263
264   case Action::rightWorkspace:
265     changeWorkspaceHorz(1);
266     return;
267
268   case Action::execute:
269     execCommand(it->string());
270     return;
271
272   case Action::showRootMenu:
273     _xatom->sendClientMessage(rootWindow(), XAtom::openbox_show_root_menu,
274                               None);
275     return;
276
277   case Action::showWorkspaceMenu:
278     _xatom->sendClientMessage(rootWindow(), XAtom::openbox_show_workspace_menu,
279                               None);
280     return;
281
282   case Action::toggleGrabs: {
283     if (_grabbed) {
284       ktree.ungrabDefaults(this);
285       _grabbed = false;
286     } else {
287       ktree.grabDefaults(this);
288       _grabbed = true;
289     }
290     return;
291   }
292
293   default:
294     break;
295   }
296
297   // these actions require an active window
298   if (_active != _clients.end()) {
299     XWindow *window = *_active;
300       
301     switch (it->type()) {
302     case Action::iconify:
303       window->iconify();
304       return;
305
306     case Action::close:
307       window->close();
308       return;
309
310     case Action::raise:
311       window->raise();
312       return;
313
314     case Action::lower:
315       window->lower();
316       return;
317
318     case Action::sendToWorkspace:
319       window->sendTo(it->number());
320       return;
321
322     case Action::toggleOmnipresent:
323       if (window->desktop() == 0xffffffff)
324         window->sendTo(_active_desktop);
325       else
326         window->sendTo(0xffffffff);
327       return;
328
329     case Action::moveWindowUp:
330       window->move(window->x(), window->y() -
331                    (it->number() != 0 ? it->number(): 1));
332       return;
333       
334     case Action::moveWindowDown:
335       window->move(window->x(), window->y() +
336                    (it->number() != 0 ? it->number(): 1));
337       return;
338       
339     case Action::moveWindowLeft:
340       window->move(window->x() - (it->number() != 0 ? it->number(): 1),
341                    window->y());
342       return;
343       
344     case Action::moveWindowRight:
345       window->move(window->x() + (it->number() != 0 ? it->number(): 1),
346                    window->y());
347       return;
348       
349     case Action::resizeWindowWidth:
350       window->resizeRel(it->number(), 0);
351       return;
352       
353     case Action::resizeWindowHeight:
354       window->resizeRel(0, it->number());
355       return;
356       
357     case Action::toggleShade:
358       window->shade(! window->shaded());
359       return;
360       
361     case Action::toggleMaximizeHorizontal:
362       window->toggleMaximize(XWindow::Max_Horz);
363       return;
364       
365     case Action::toggleMaximizeVertical:
366       window->toggleMaximize(XWindow::Max_Vert);
367       return;
368       
369     case Action::toggleMaximizeFull:
370       window->toggleMaximize(XWindow::Max_Full);
371       return;
372
373     case Action::toggleDecorations:
374       window->decorate(! window->decorated());
375       return;
376       
377     default:
378       assert(false);  // unhandled action type!
379       break;
380     }
381   }
382 }
383
384
385 void screen::handleKeyrelease(const XEvent &) {
386   // the only keyrelease event we care about (for now) is when we do stacked
387   // cycling and the modifier is released
388   if (_stacked_cycling && _cycling && nothingIsPressed()) {
389     // all modifiers have been released. ungrab the keyboard, move the
390     // focused window to the top of the Z-order and raise it
391     ungrabModifiers();
392
393     if (_active != _clients.end()) {
394       XWindow *w = *_active;
395       bool e = _last_active == _active;
396       _clients.remove(w);
397       _clients.push_front(w);
398       _active = _clients.begin();
399       if (e) _last_active = _active;
400       w->raise();
401     }
402
403     _cycling = false;
404   }
405 }
406
407
408 // do we want to add this window to our list?
409 bool screen::doAddWindow(Window window) const {
410   assert(_managed);
411
412   Atom type;
413   if (! _xatom->getValue(window, XAtom::net_wm_window_type, XAtom::atom,
414                          type))
415     return True;
416
417   if (type == _xatom->getAtom(XAtom::net_wm_window_type_dock) ||
418       type == _xatom->getAtom(XAtom::net_wm_window_type_menu))
419     return False;
420
421   return True;
422 }
423
424
425 void screen::updateEverything() {
426   updateNumDesktops();
427   updateActiveDesktop();
428   updateClientList();
429   updateActiveWindow();
430 }
431
432
433 void screen::updateNumDesktops() {
434   assert(_managed);
435
436   if (! _xatom->getValue(_root, XAtom::net_number_of_desktops, XAtom::cardinal,
437                          (unsigned long)_num_desktops))
438     _num_desktops = 1;  // assume that there is at least 1 desktop!
439 }
440
441
442 void screen::updateActiveDesktop() {
443   assert(_managed);
444
445   if (! _xatom->getValue(_root, XAtom::net_current_desktop, XAtom::cardinal,
446                          (unsigned long)_active_desktop))
447     _active_desktop = 0;  // there must be at least one desktop, and it must
448                           // be the current one
449 }
450
451
452 void screen::updateClientList() {
453   assert(_managed);
454
455   WindowList::iterator insert_point = _active;
456   if (insert_point != _clients.end())
457     ++insert_point; // get to the item client the focused client
458   
459   // get the client list from the root window
460   Window *rootclients = 0;
461   unsigned long num = (unsigned) -1;
462   if (! _xatom->getValue(_root, XAtom::net_client_list, XAtom::window, num,
463                          &rootclients))
464     num = 0;
465
466   WindowList::iterator it;
467   const WindowList::iterator end = _clients.end();
468   unsigned long i;
469   
470   for (i = 0; i < num; ++i) {
471     for (it = _clients.begin(); it != end; ++it)
472       if (**it == rootclients[i])
473         break;
474     if (it == end) {  // didn't already exist
475       if (doAddWindow(rootclients[i])) {
476 //        cout << "Added window: 0x" << hex << rootclients[i] << dec << endl;
477         // insert new clients after the active window
478         _clients.insert(insert_point, new XWindow(_epist, this,
479                                                   rootclients[i]));
480       }
481     }
482   }
483
484   // remove clients that no longer exist (that belong to this screen)
485   for (it = _clients.begin(); it != end;) {
486     WindowList::iterator it2 = it;
487     ++it;
488
489     // is on another screen?
490     if ((*it2)->getScreen() != this)
491       continue;
492
493     for (i = 0; i < num; ++i)
494       if (**it2 == rootclients[i])
495         break;
496     if (i == num)  { // no longer exists
497       //      cout << "Removed window: 0x" << hex << (*it2)->window() << dec << endl;
498       // watch for the active and last-active window
499       if (it2 == _active)
500         _active = _clients.end();
501       if (it2 == _last_active)
502         _last_active = _clients.end();
503       delete *it2;
504       _clients.erase(it2);
505     }
506   }
507
508   if (rootclients) delete [] rootclients;
509 }
510
511
512 const XWindow *screen::lastActiveWindow() const {
513   if (_last_active != _clients.end())
514     return *_last_active;
515
516   // find a window if one exists
517   WindowList::const_iterator it, end = _clients.end();
518   for (it = _clients.begin(); it != end; ++it)
519     if ((*it)->getScreen() == this && ! (*it)->iconic() &&
520         (*it)->canFocus() &&
521         ((*it)->desktop() == 0xffffffff ||
522          (*it)->desktop() == _active_desktop))
523       return *it;
524
525   // no windows on this screen
526   return 0;
527 }
528
529
530 void screen::updateActiveWindow() {
531   assert(_managed);
532
533   Window a = None;
534   _xatom->getValue(_root, XAtom::net_active_window, XAtom::window, a);
535   
536   WindowList::iterator it, end = _clients.end();
537   for (it = _clients.begin(); it != end; ++it) {
538     if (**it == a) {
539       if ((*it)->getScreen() != this)
540         return;
541       break;
542     }
543   }
544
545   _active = it;
546
547   if (_active != end) {
548     /* if we're not cycling and a window gets focus, add it to the top of the
549      * cycle stack.
550      */
551     if (_stacked_cycling && !_cycling) {
552       XWindow *win = *_active;
553       _clients.remove(win);
554       _clients.push_front(win);
555       _active = _clients.begin();
556
557       _last_active = _active;
558     }
559   }
560
561   /*  cout << "Active window is now: ";
562       if (_active == _clients.end()) cout << "None\n";
563       else cout << "0x" << hex << (*_active)->window() << dec << endl;
564   */
565 }
566
567
568 void screen::execCommand(const string &cmd) const {
569   pid_t pid;
570   if ((pid = fork()) == 0) {
571     // make the command run on the correct screen
572     if (putenv(const_cast<char*>(_info->displayString().c_str()))) {
573       cout << "warning: couldn't set environment variable 'DISPLAY'\n";
574       perror("putenv()");
575     }
576     execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL);
577     exit(-1);
578   } else if (pid == -1) {
579     cout << _epist->getApplicationName() <<
580       ": Could not fork a process for executing a command\n";
581   }
582 }
583
584
585 void screen::cycleWindow(unsigned int state, const bool forward,
586                          const int increment, const bool allscreens,
587                          const bool alldesktops, const bool sameclass,
588                          const string &cn)
589 {
590   assert(_managed);
591   assert(increment > 0);
592
593   if (_clients.empty()) return;
594
595   string classname(cn);
596   if (sameclass && classname.empty() && _active != _clients.end())
597     classname = (*_active)->appClass();
598
599   WindowList::const_iterator target = _active,
600     begin = _clients.begin(),
601     end = _clients.end();
602
603   XWindow *t = 0;
604   
605   for (int x = 0; x < increment; ++x) {
606     while (1) {
607       if (forward) {
608         if (target == end) {
609           target = begin;
610         } else {
611           ++target;
612         }
613       } else {
614         if (target == begin)
615           target = end;
616         --target;
617       }
618
619       // must be no window to focus
620       if (target == _active)
621         return;
622
623       // start back at the beginning of the loop
624       if (target == end)
625         continue;
626
627       // determine if this window is invalid for cycling to
628       t = *target;
629       if (t->iconic()) continue;
630       if (! allscreens && t->getScreen() != this) continue;
631       if (! alldesktops && ! (t->desktop() == _active_desktop ||
632                               t->desktop() == 0xffffffff)) continue;
633       if (sameclass && ! classname.empty() &&
634           t->appClass() != classname) continue;
635       if (! t->canFocus()) continue;
636
637       // found a good window so break out of the while, and perhaps continue
638       // with the for loop
639       break;
640     }
641   }
642
643   // phew. we found the window, so focus it.
644   if (_stacked_cycling && state) {
645     if (!_cycling) {
646       // grab modifiers so we can intercept KeyReleases from them
647       grabModifiers();
648       _cycling = true;
649     }
650
651     // if the window is on another desktop, we can't use XSetInputFocus, since
652     // it doesn't imply a workspace change.
653     if (t->desktop() == _active_desktop || t->desktop() == 0xffffffff)
654       t->focus(false); // focus, but don't raise
655     else
656       t->focus(); // change workspace and focus
657   }  
658   else {
659     t->focus();
660   }
661 }
662
663
664 void screen::cycleWorkspace(const bool forward, const int increment,
665                             const bool loop) const {
666   assert(_managed);
667   assert(increment > 0);
668
669   unsigned int destination = _active_desktop;
670
671   for (int x = 0; x < increment; ++x) {
672     if (forward) {
673       if (destination < _num_desktops - 1)
674         ++destination;
675       else if (loop)
676         destination = 0;
677     } else {
678       if (destination > 0)
679         --destination;
680       else if (loop)
681         destination = _num_desktops - 1;
682     }
683   }
684
685   if (destination != _active_desktop) 
686     changeWorkspace(destination);
687 }
688
689
690 void screen::changeWorkspace(const int num) const {
691   assert(_managed);
692
693   _xatom->sendClientMessage(_root, XAtom::net_current_desktop, _root, num);
694 }
695
696 void screen::changeWorkspaceVert(const int num) const {
697   assert(_managed);
698   int width = 0;
699   int num_desktops = (signed)_num_desktops;
700   int active_desktop = (signed)_active_desktop;
701   int wnum = 0;
702
703   _config->getValue(Config::workspaceColumns, width);
704
705   if (width > num_desktops || width <= 0)
706     return;
707
708   // a cookie to the person that makes this pretty
709   if (num < 0) {
710     wnum = active_desktop - width;
711     if (wnum < 0) {
712       wnum = num_desktops/width * width + active_desktop;
713       if (wnum >= num_desktops)
714         wnum = num_desktops - 1;
715     }
716   }
717   else {
718     wnum = active_desktop + width;
719     if (wnum >= num_desktops) {
720       wnum = (active_desktop + width) % num_desktops - 1;
721       if (wnum < 0)
722         wnum = 0;
723     }
724   }
725   changeWorkspace(wnum);
726 }
727
728 void screen::changeWorkspaceHorz(const int num) const {
729   assert(_managed);
730   int width = 0;
731   int num_desktops = (signed)_num_desktops;
732   int active_desktop = (signed)_active_desktop;
733   int wnum = 0;
734
735   _config->getValue(Config::workspaceColumns, width);
736
737   if (width > num_desktops || width <= 0)
738     return;
739
740   if (num < 0) {
741     if (active_desktop % width != 0)
742       changeWorkspace(active_desktop - 1);
743     else {
744       wnum = active_desktop + width - 1;
745       if (wnum >= num_desktops)
746         wnum = num_desktops - 1;
747     }
748   }
749   else {
750     if (active_desktop % width != width - 1) {
751       wnum = active_desktop + 1;
752       if (wnum >= num_desktops)
753         wnum = num_desktops / width * width;
754     }
755     else
756       wnum = active_desktop - width + 1;
757   }
758   changeWorkspace(wnum);
759 }
760
761 void screen::grabKey(const KeyCode keyCode, const int modifierMask) const {
762
763   Display *display = _epist->getXDisplay();
764   int numlockMask, scrolllockMask;
765
766   _epist->getLockModifiers(numlockMask, scrolllockMask);
767
768   XGrabKey(display, keyCode, modifierMask,
769            _root, True, GrabModeAsync, GrabModeAsync);
770   XGrabKey(display, keyCode, 
771            modifierMask|LockMask,
772            _root, True, GrabModeAsync, GrabModeAsync);
773   XGrabKey(display, keyCode, 
774            modifierMask|scrolllockMask,
775            _root, True, GrabModeAsync, GrabModeAsync);
776   XGrabKey(display, keyCode, 
777            modifierMask|numlockMask,
778            _root, True, GrabModeAsync, GrabModeAsync);
779     
780   XGrabKey(display, keyCode, 
781            modifierMask|LockMask|scrolllockMask,
782            _root, True, GrabModeAsync, GrabModeAsync);
783   XGrabKey(display, keyCode, 
784            modifierMask|scrolllockMask|numlockMask,
785            _root, True, GrabModeAsync, GrabModeAsync);
786   XGrabKey(display, keyCode, 
787            modifierMask|numlockMask|LockMask,
788            _root, True, GrabModeAsync, GrabModeAsync);
789     
790   XGrabKey(display, keyCode, 
791            modifierMask|numlockMask|LockMask|scrolllockMask,
792            _root, True, GrabModeAsync, GrabModeAsync);
793 }
794
795 void screen::ungrabKey(const KeyCode keyCode, const int modifierMask) const {
796
797   Display *display = _epist->getXDisplay();
798   int numlockMask, scrolllockMask;
799
800   _epist->getLockModifiers(numlockMask, scrolllockMask);
801
802   XUngrabKey(display, keyCode, modifierMask, _root);
803   XUngrabKey(display, keyCode, modifierMask|LockMask, _root);
804   XUngrabKey(display, keyCode, modifierMask|scrolllockMask, _root);
805   XUngrabKey(display, keyCode, modifierMask|numlockMask, _root);
806   XUngrabKey(display, keyCode, modifierMask|LockMask|scrolllockMask, _root);
807   XUngrabKey(display, keyCode, modifierMask|scrolllockMask|numlockMask, _root);
808   XUngrabKey(display, keyCode, modifierMask|numlockMask|LockMask, _root);
809   XUngrabKey(display, keyCode, modifierMask|numlockMask|LockMask|
810              scrolllockMask, _root);
811 }
812
813
814 void screen::grabModifiers() const {
815   Display *display = _epist->getXDisplay();
816
817   XGrabKeyboard(display, rootWindow(), True, GrabModeAsync,
818                 GrabModeAsync, CurrentTime);
819 }
820
821
822 void screen::ungrabModifiers() const {
823   Display *display = _epist->getXDisplay();
824
825   XUngrabKeyboard(display, CurrentTime);
826 }
827
828
829 bool screen::nothingIsPressed(void) const
830 {
831   char keys[32];
832   XQueryKeymap(_epist->getXDisplay(), keys);
833
834   for (int i = 0; i < 32; ++i) {
835     if (keys[i] != 0)
836       return false;
837   }
838
839   return true;
840 }