]> icculus.org git repositories - mikachu/openbox.git/blob - util/epist/screen.cc
remove some unneeded std::'s
[mikachu/openbox.git] / util / epist / screen.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2 // screen.cc for Epistophy - 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 #ifdef    HAVE_CONFIG_H
24 #  include "../../config.h"
25 #endif // HAVE_CONFIG_H
26
27 extern "C" {
28 #ifdef    HAVE_STDIO_H
29 #  include <stdio.h>
30 #endif // HAVE_STDIO_H
31
32 #ifdef    HAVE_UNISTD_H
33 #  include <sys/types.h>
34 #  include <unistd.h>
35 #endif // HAVE_UNISTD_H
36 }
37
38 #include <iostream>
39 #include <string>
40
41 using std::cout;
42 using std::endl;
43 using std::hex;
44 using std::dec;
45 using std::string;
46
47 #include "../../src/BaseDisplay.hh"
48 #include "../../src/XAtom.hh"
49 #include "screen.hh"
50 #include "epist.hh"
51
52
53 screen::screen(epist *epist, int number) 
54   : _clients(epist->clientsList()),
55     _active(epist->activeWindow()) {
56   _epist = epist;
57   _xatom = _epist->xatom();
58   _last_active = _clients.end();
59   _number = number;
60   _info = _epist->getScreenInfo(_number);
61   _root = _info->getRootWindow();
62   
63   cout << "root window on screen " << _number << ": 0x" << hex << _root << 
64     dec << endl;
65   
66   // find a window manager supporting NETWM, waiting for it to load if we must
67   int count = 20;  // try for 20 seconds
68   _managed = false;
69   while (! (_epist->doShutdown() || _managed || count <= 0)) {
70     if (! (_managed = findSupportingWM()))
71       usleep(1000);
72     --count;
73   }
74   if (_managed)
75     cout << "Found compatible window manager '" << _wm_name << "' for screen "
76       << _number << ".\n";
77   else {
78     cout << "Unable to find a compatible window manager for screen " <<
79       _number << ".\n";
80     return;
81   }
82  
83   XSelectInput(_epist->getXDisplay(), _root, PropertyChangeMask);
84 }
85
86 screen::~screen() {
87   if (_managed)
88     XSelectInput(_epist->getXDisplay(), _root, None);
89 }
90
91
92 bool screen::findSupportingWM() {
93   Window support_win;
94   if (! _xatom->getValue(_root, XAtom::net_supporting_wm_check, XAtom::window,
95                          support_win) || support_win == None)
96     return false;
97
98   string title;
99   _xatom->getValue(support_win, XAtom::net_wm_name, XAtom::utf8, title);
100   _wm_name = title;
101   return true;
102 }
103
104
105 XWindow *screen::findWindow(const XEvent &e) const {
106   assert(_managed);
107
108   WindowList::const_iterator it, end = _clients.end();
109   for (it = _clients.begin(); it != end; ++it)
110     if (**it == e.xany.window)
111       break;
112   if(it == end)
113     return 0;
114   return *it;
115 }
116
117
118 void screen::processEvent(const XEvent &e) {
119   assert(_managed);
120   assert(e.xany.window == _root);
121
122   switch (e.type) {
123   case PropertyNotify:
124     // root window
125     if (e.xproperty.atom == _xatom->getAtom(XAtom::net_number_of_desktops))
126       updateNumDesktops();
127     if (e.xproperty.atom == _xatom->getAtom(XAtom::net_current_desktop))
128       updateActiveDesktop();
129     if (e.xproperty.atom == _xatom->getAtom(XAtom::net_active_window))
130       updateActiveWindow();
131     if (e.xproperty.atom == _xatom->getAtom(XAtom::net_client_list)) {
132       // catch any window unmaps first
133       XEvent ev;
134       if (XCheckTypedWindowEvent(_epist->getXDisplay(), e.xany.window,
135                                  DestroyNotify, &ev) ||
136           XCheckTypedWindowEvent(_epist->getXDisplay(), e.xany.window,
137                                  UnmapNotify, &ev)) {
138         processEvent(ev);
139       }
140
141       updateClientList();
142     }
143     break;
144   case KeyPress:
145     handleKeypress(e);
146     break;
147   }
148 }
149
150 void screen::handleKeypress(const XEvent &e) {
151   int scrolllockMask, numlockMask;
152
153   ActionList::const_iterator it = _epist->actions().begin();
154   ActionList::const_iterator end = _epist->actions().end();
155
156   _epist->getLockModifiers(numlockMask, scrolllockMask);
157   
158   for (; it != end; ++it) {
159     unsigned int state = e.xkey.state & ~(LockMask|scrolllockMask|numlockMask);
160     
161     if (e.xkey.keycode == it->keycode() &&
162         state == it->modifierMask()) {
163       switch (it->type()) {
164       case Action::nextScreen:
165         _epist->cycleScreen(_number, true);
166         return;
167
168       case Action::prevScreen:
169         _epist->cycleScreen(_number, false);
170         return;
171
172       case Action::nextWorkspace:
173         cycleWorkspace(true);
174         return;
175
176       case Action::prevWorkspace:
177         cycleWorkspace(false);
178         return;
179
180       case Action::nextWindow:
181         cycleWindow(true);
182         return;
183
184       case Action::prevWindow:
185         cycleWindow(false);
186         return;
187
188       case Action::nextWindowOnAllWorkspaces:
189         cycleWindow(true, false, true);
190         return;
191
192       case Action::prevWindowOnAllWorkspaces:
193         cycleWindow(false, false, true);
194         return;
195
196       case Action::nextWindowOnAllScreens:
197         cycleWindow(true, true);
198         return;
199
200       case Action::prevWindowOnAllScreens:
201         cycleWindow(false, true);
202         return;
203
204       case Action::nextWindowOfClass:
205         cycleWindow(true, false, false, true, it->string());
206         return;
207
208       case Action::prevWindowOfClass:
209         cycleWindow(false, false, false, true, it->string());
210         return;
211
212       case Action::nextWindowOfClassOnAllWorkspaces:
213         cycleWindow(true, false, true, true, it->string());
214         return;
215
216       case Action::prevWindowOfClassOnAllWorkspaces:
217         cycleWindow(false, false, true, true, it->string());
218         return;
219
220       case Action::changeWorkspace:
221         changeWorkspace(it->number());
222         return;
223
224       case Action::execute:
225         execCommand(it->string());
226         return;
227
228       default:
229         break;
230       }
231
232       // these actions require an active window
233       if (_active != _clients.end()) {
234         XWindow *window = *_active;
235
236         switch (it->type()) {
237         case Action::iconify:
238           window->iconify();
239           return;
240
241         case Action::close:
242           window->close();
243           return;
244
245         case Action::raise:
246           window->raise();
247           return;
248
249         case Action::lower:
250           window->lower();
251           return;
252
253         case Action::sendToWorkspace:
254           window->sendTo(it->number());
255           return;
256
257         case Action::toggleomnipresent:
258           if (window->desktop() == 0xffffffff)
259             window->sendTo(_active_desktop);
260           else
261             window->sendTo(0xffffffff);
262           return;
263
264         case Action::moveWindowUp:
265           window->move(window->x(), window->y() - it->number());
266           return;
267       
268         case Action::moveWindowDown:
269           window->move(window->x(), window->y() + it->number());
270           return;
271       
272         case Action::moveWindowLeft:
273           window->move(window->x() - it->number(), window->y());
274           return;
275       
276         case Action::moveWindowRight:
277           window->move(window->x() + it->number(), window->y());
278           return;
279       
280         case Action::resizeWindowWidth:
281           window->resize(window->width() + it->number(), window->height());
282           return;
283       
284         case Action::resizeWindowHeight:
285           window->resize(window->width(), window->height() + it->number());
286           return;
287       
288         case Action::toggleshade:
289           window->shade(! window->shaded());
290           return;
291       
292         case Action::toggleMaximizeHorizontal:
293           window->toggleMaximize(XWindow::Max_Horz);
294           return;
295       
296         case Action::toggleMaximizeVertical:
297           window->toggleMaximize(XWindow::Max_Vert);
298           return;
299       
300         case Action::toggleMaximizeFull:
301           window->toggleMaximize(XWindow::Max_Full);
302           return;
303       
304         default:
305           assert(false);  // unhandled action type!
306           break;
307         }
308       }
309     }
310   }
311 }
312
313 // do we want to add this window to our list?
314 bool screen::doAddWindow(Window window) const {
315   assert(_managed);
316
317   Atom type;
318   if (! _xatom->getValue(window, XAtom::net_wm_window_type, XAtom::atom,
319                          type))
320     return True;
321
322   if (type == _xatom->getAtom(XAtom::net_wm_window_type_dock) ||
323       type == _xatom->getAtom(XAtom::net_wm_window_type_menu))
324     return False;
325
326   return True;
327 }
328
329
330 void screen::updateEverything() {
331   updateNumDesktops();
332   updateActiveDesktop();
333   updateClientList();
334   updateActiveWindow();
335 }
336
337
338 void screen::updateNumDesktops() {
339   assert(_managed);
340
341   if (! _xatom->getValue(_root, XAtom::net_number_of_desktops, XAtom::cardinal,
342                          (unsigned long)_num_desktops))
343     _num_desktops = 1;  // assume that there is at least 1 desktop!
344 }
345
346
347 void screen::updateActiveDesktop() {
348   assert(_managed);
349
350   if (! _xatom->getValue(_root, XAtom::net_current_desktop, XAtom::cardinal,
351                          (unsigned long)_active_desktop))
352     _active_desktop = 0;  // there must be at least one desktop, and it must
353                           // be the current one
354 }
355
356
357 void screen::updateClientList() {
358   assert(_managed);
359
360   WindowList::iterator insert_point = _active;
361   if (insert_point != _clients.end())
362     ++insert_point; // get to the item client the focused client
363   
364   // get the client list from the root window
365   Window *rootclients = 0;
366   unsigned long num = (unsigned) -1;
367   if (! _xatom->getValue(_root, XAtom::net_client_list, XAtom::window, num,
368                          &rootclients))
369     num = 0;
370
371   WindowList::iterator it;
372   const WindowList::iterator end = _clients.end();
373   unsigned long i;
374   
375   // insert new clients after the active window
376   for (i = 0; i < num; ++i) {
377     for (it = _clients.begin(); it != end; ++it)
378       if (**it == rootclients[i])
379         break;
380     if (it == end) {  // didn't already exist
381       if (doAddWindow(rootclients[i])) {
382 //        cout << "Added window: 0x" << hex << rootclients[i] << dec << endl;
383         _clients.insert(insert_point, new XWindow(_epist, this,
384                                                   rootclients[i]));
385       }
386     }
387   }
388
389   // remove clients that no longer exist (that belong to this screen)
390   for (it = _clients.begin(); it != end;) {
391     WindowList::iterator it2 = it;
392     ++it;
393
394     // is on another screen?
395     if ((*it2)->getScreen() != this)
396       continue;
397
398     for (i = 0; i < num; ++i)
399       if (**it2 == rootclients[i])
400         break;
401     if (i == num)  { // no longer exists
402 //      cout << "Removed window: 0x" << hex << (*it2)->window() << dec << endl;
403       // watch for the active and last-active window
404       if (it2 == _active)
405         _active = _clients.end();
406       if (it2 == _last_active)
407         _last_active = _clients.end();
408       delete *it2;
409       _clients.erase(it2);
410     }
411   }
412
413   if (rootclients) delete [] rootclients;
414 }
415
416
417 const XWindow *screen::lastActiveWindow() const {
418   if (_last_active != _clients.end())
419     return *_last_active;
420
421   // find a window if one exists
422   WindowList::const_iterator it, end = _clients.end();
423   for (it = _clients.begin(); it != end; ++it)
424     if ((*it)->getScreen() == this)
425       return *it;
426
427   // no windows on this screen
428   return 0;
429 }
430
431
432 void screen::updateActiveWindow() {
433   assert(_managed);
434
435   Window a = None;
436   _xatom->getValue(_root, XAtom::net_active_window, XAtom::window, a);
437   
438   WindowList::iterator it, end = _clients.end();
439   for (it = _clients.begin(); it != end; ++it) {
440     if (**it == a) {
441       if ((*it)->getScreen() != this)
442         return;
443       break;
444     }
445   }
446   _active = it;
447   _last_active = it;
448
449 /*  cout << "Active window is now: ";
450   if (_active == _clients.end()) cout << "None\n";
451   else cout << "0x" << hex << (*_active)->window() << dec << endl;
452 */
453 }
454
455
456 void screen::execCommand(const string &cmd) const {
457   pid_t pid;
458   if ((pid = fork()) == 0) {
459     extern char **environ;
460
461     char *const argv[] = {
462       "sh",
463       "-c",
464       const_cast<char *>(cmd.c_str()),
465       0
466     };
467     // make the command run on the correct screen
468     if (putenv(const_cast<char*>(_info->displayString().c_str()))) {
469       cout << "warning: couldn't set environment variable 'DISPLAY'\n";
470       perror("putenv()");
471     }
472     execve("/bin/sh", argv, environ);
473     exit(127);
474   } else if (pid == -1) {
475     cout << _epist->getApplicationName() <<
476       ": Could not fork a process for executing a command\n";
477   }
478 }
479
480
481 void screen::cycleWindow(const bool forward, const bool allscreens,
482                          const bool alldesktops, const bool sameclass,
483                          const string &cn) const {
484   assert(_managed);
485
486   if (_clients.empty()) return;
487   
488   string classname(cn);
489   if (sameclass && classname.empty() && _active != _clients.end())
490     classname = (*_active)->appClass();
491
492   WindowList::const_iterator target = _active,
493                              first = _active,
494                              begin = _clients.begin(),
495                              end = _clients.end();
496  
497   do {
498     if (forward) {
499       if (target == end) {
500         target = begin;
501       } else {
502         ++target;
503         if (target == end)
504           target = begin;
505       }
506     } else {
507       if (target == begin)
508         target = end;
509       --target;
510     }
511
512     // must be no window to focus
513     if (target == first)
514       return;
515   } while ((*target)->iconic() ||
516            (! allscreens && (*target)->getScreen() != this) ||
517            (! alldesktops && (*target)->desktop() != _active_desktop) ||
518            (sameclass && ! classname.empty() &&
519             (*target)->appClass() != classname));
520   
521   if (target != _clients.end())
522     (*target)->focus();
523 }
524
525
526 void screen::cycleWorkspace(const bool forward, const bool loop) const {
527   assert(_managed);
528
529   unsigned int destination = _active_desktop;
530
531   if (forward) {
532     if (destination < _num_desktops - 1)
533       ++destination;
534     else if (loop)
535       destination = 0;
536   } else {
537     if (destination > 0)
538       --destination;
539     else if (loop)
540       destination = _num_desktops - 1;
541   }
542
543   if (destination != _active_desktop) 
544     changeWorkspace(destination);
545 }
546
547
548 void screen::changeWorkspace(const int num) const {
549   assert(_managed);
550
551   _xatom->sendClientMessage(_root, XAtom::net_current_desktop, _root, num);
552 }
553
554 void screen::grabKey(const KeyCode keyCode, const int modifierMask) const {
555
556   Display *display = _epist->getXDisplay();
557   int numlockMask, scrolllockMask;
558
559   _epist->getLockModifiers(numlockMask, scrolllockMask);
560
561   XGrabKey(display, keyCode, modifierMask,
562            _root, True, GrabModeAsync, GrabModeAsync);
563   XGrabKey(display, keyCode, 
564            modifierMask|LockMask,
565            _root, True, GrabModeAsync, GrabModeAsync);
566   XGrabKey(display, keyCode, 
567            modifierMask|scrolllockMask,
568            _root, True, GrabModeAsync, GrabModeAsync);
569   XGrabKey(display, keyCode, 
570            modifierMask|numlockMask,
571            _root, True, GrabModeAsync, GrabModeAsync);
572     
573   XGrabKey(display, keyCode, 
574            modifierMask|LockMask|scrolllockMask,
575            _root, True, GrabModeAsync, GrabModeAsync);
576   XGrabKey(display, keyCode, 
577            modifierMask|scrolllockMask|numlockMask,
578            _root, True, GrabModeAsync, GrabModeAsync);
579   XGrabKey(display, keyCode, 
580            modifierMask|numlockMask|LockMask,
581            _root, True, GrabModeAsync, GrabModeAsync);
582     
583   XGrabKey(display, keyCode, 
584            modifierMask|numlockMask|LockMask|scrolllockMask,
585            _root, True, GrabModeAsync, GrabModeAsync);
586 }