]> icculus.org git repositories - dana/openbox.git/blob - util/epist/screen.cc
Added titlebar layout documentation
[dana/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     // disconnect the child from epist's session and the tty
572     if (setsid() == -1) {
573       cout << "warning: could not start a new process group\n";
574       perror("setsid");
575     }
576
577     // make the command run on the correct screen
578     if (putenv(const_cast<char*>(_info->displayString().c_str()))) {
579       cout << "warning: couldn't set environment variable 'DISPLAY'\n";
580       perror("putenv()");
581     }
582     execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL);
583     exit(-1);
584   } else if (pid == -1) {
585     cout << _epist->getApplicationName() <<
586       ": Could not fork a process for executing a command\n";
587   }
588 }
589
590
591 void screen::cycleWindow(unsigned int state, const bool forward,
592                          const int increment, const bool allscreens,
593                          const bool alldesktops, const bool sameclass,
594                          const string &cn)
595 {
596   assert(_managed);
597   assert(increment > 0);
598
599   if (_clients.empty()) return;
600
601   string classname(cn);
602   if (sameclass && classname.empty() && _active != _clients.end())
603     classname = (*_active)->appClass();
604
605   WindowList::const_iterator target = _active,
606     begin = _clients.begin(),
607     end = _clients.end();
608
609   XWindow *t = 0;
610   
611   for (int x = 0; x < increment; ++x) {
612     while (1) {
613       if (forward) {
614         if (target == end) {
615           target = begin;
616         } else {
617           ++target;
618         }
619       } else {
620         if (target == begin)
621           target = end;
622         --target;
623       }
624
625       // must be no window to focus
626       if (target == _active)
627         return;
628
629       // start back at the beginning of the loop
630       if (target == end)
631         continue;
632
633       // determine if this window is invalid for cycling to
634       t = *target;
635       if (t->iconic()) continue;
636       if (! allscreens && t->getScreen() != this) continue;
637       if (! alldesktops && ! (t->desktop() == _active_desktop ||
638                               t->desktop() == 0xffffffff)) continue;
639       if (sameclass && ! classname.empty() &&
640           t->appClass() != classname) continue;
641       if (! t->canFocus()) continue;
642
643       // found a good window so break out of the while, and perhaps continue
644       // with the for loop
645       break;
646     }
647   }
648
649   // phew. we found the window, so focus it.
650   if (_stacked_cycling && state) {
651     if (!_cycling) {
652       // grab modifiers so we can intercept KeyReleases from them
653       grabModifiers();
654       _cycling = true;
655     }
656
657     // if the window is on another desktop, we can't use XSetInputFocus, since
658     // it doesn't imply a workspace change.
659     if (t->desktop() == _active_desktop || t->desktop() == 0xffffffff)
660       t->focus(false); // focus, but don't raise
661     else
662       t->focus(); // change workspace and focus
663   }  
664   else {
665     t->focus();
666   }
667 }
668
669
670 void screen::cycleWorkspace(const bool forward, const int increment,
671                             const bool loop) const {
672   assert(_managed);
673   assert(increment > 0);
674
675   unsigned int destination = _active_desktop;
676
677   for (int x = 0; x < increment; ++x) {
678     if (forward) {
679       if (destination < _num_desktops - 1)
680         ++destination;
681       else if (loop)
682         destination = 0;
683     } else {
684       if (destination > 0)
685         --destination;
686       else if (loop)
687         destination = _num_desktops - 1;
688     }
689   }
690
691   if (destination != _active_desktop) 
692     changeWorkspace(destination);
693 }
694
695
696 void screen::changeWorkspace(const int num) const {
697   assert(_managed);
698
699   _xatom->sendClientMessage(_root, XAtom::net_current_desktop, _root, num);
700 }
701
702 void screen::changeWorkspaceVert(const int num) const {
703   assert(_managed);
704   int width = 0;
705   int num_desktops = (signed)_num_desktops;
706   int active_desktop = (signed)_active_desktop;
707   int wnum = 0;
708
709   _config->getValue(Config::workspaceColumns, width);
710
711   if (width > num_desktops || width <= 0)
712     return;
713
714   // a cookie to the person that makes this pretty
715   if (num < 0) {
716     wnum = active_desktop - width;
717     if (wnum < 0) {
718       wnum = num_desktops/width * width + active_desktop;
719       if (wnum >= num_desktops)
720         wnum = num_desktops - 1;
721     }
722   }
723   else {
724     wnum = active_desktop + width;
725     if (wnum >= num_desktops) {
726       wnum = (active_desktop + width) % num_desktops - 1;
727       if (wnum < 0)
728         wnum = 0;
729     }
730   }
731   changeWorkspace(wnum);
732 }
733
734 void screen::changeWorkspaceHorz(const int num) const {
735   assert(_managed);
736   int width = 0;
737   int num_desktops = (signed)_num_desktops;
738   int active_desktop = (signed)_active_desktop;
739   int wnum = 0;
740
741   _config->getValue(Config::workspaceColumns, width);
742
743   if (width > num_desktops || width <= 0)
744     return;
745
746   if (num < 0) {
747     if (active_desktop % width != 0)
748       changeWorkspace(active_desktop - 1);
749     else {
750       wnum = active_desktop + width - 1;
751       if (wnum >= num_desktops)
752         wnum = num_desktops - 1;
753     }
754   }
755   else {
756     if (active_desktop % width != width - 1) {
757       wnum = active_desktop + 1;
758       if (wnum >= num_desktops)
759         wnum = num_desktops / width * width;
760     }
761     else
762       wnum = active_desktop - width + 1;
763   }
764   changeWorkspace(wnum);
765 }
766
767 void screen::grabKey(const KeyCode keyCode, const int modifierMask) const {
768
769   Display *display = _epist->getXDisplay();
770   int numlockMask, scrolllockMask;
771
772   _epist->getLockModifiers(numlockMask, scrolllockMask);
773
774   XGrabKey(display, keyCode, modifierMask,
775            _root, True, GrabModeAsync, GrabModeAsync);
776   XGrabKey(display, keyCode, 
777            modifierMask|LockMask,
778            _root, True, GrabModeAsync, GrabModeAsync);
779   XGrabKey(display, keyCode, 
780            modifierMask|scrolllockMask,
781            _root, True, GrabModeAsync, GrabModeAsync);
782   XGrabKey(display, keyCode, 
783            modifierMask|numlockMask,
784            _root, True, GrabModeAsync, GrabModeAsync);
785     
786   XGrabKey(display, keyCode, 
787            modifierMask|LockMask|scrolllockMask,
788            _root, True, GrabModeAsync, GrabModeAsync);
789   XGrabKey(display, keyCode, 
790            modifierMask|scrolllockMask|numlockMask,
791            _root, True, GrabModeAsync, GrabModeAsync);
792   XGrabKey(display, keyCode, 
793            modifierMask|numlockMask|LockMask,
794            _root, True, GrabModeAsync, GrabModeAsync);
795     
796   XGrabKey(display, keyCode, 
797            modifierMask|numlockMask|LockMask|scrolllockMask,
798            _root, True, GrabModeAsync, GrabModeAsync);
799 }
800
801 void screen::ungrabKey(const KeyCode keyCode, const int modifierMask) const {
802
803   Display *display = _epist->getXDisplay();
804   int numlockMask, scrolllockMask;
805
806   _epist->getLockModifiers(numlockMask, scrolllockMask);
807
808   XUngrabKey(display, keyCode, modifierMask, _root);
809   XUngrabKey(display, keyCode, modifierMask|LockMask, _root);
810   XUngrabKey(display, keyCode, modifierMask|scrolllockMask, _root);
811   XUngrabKey(display, keyCode, modifierMask|numlockMask, _root);
812   XUngrabKey(display, keyCode, modifierMask|LockMask|scrolllockMask, _root);
813   XUngrabKey(display, keyCode, modifierMask|scrolllockMask|numlockMask, _root);
814   XUngrabKey(display, keyCode, modifierMask|numlockMask|LockMask, _root);
815   XUngrabKey(display, keyCode, modifierMask|numlockMask|LockMask|
816              scrolllockMask, _root);
817 }
818
819
820 void screen::grabModifiers() const {
821   Display *display = _epist->getXDisplay();
822
823   XGrabKeyboard(display, rootWindow(), True, GrabModeAsync,
824                 GrabModeAsync, CurrentTime);
825 }
826
827
828 void screen::ungrabModifiers() const {
829   Display *display = _epist->getXDisplay();
830
831   XUngrabKeyboard(display, CurrentTime);
832 }
833
834
835 bool screen::nothingIsPressed(void) const
836 {
837   char keys[32];
838   XQueryKeymap(_epist->getXDisplay(), keys);
839
840   for (int i = 0; i < 32; ++i) {
841     if (keys[i] != 0)
842       return false;
843   }
844
845   return true;
846 }