]> icculus.org git repositories - dana/openbox.git/blob - src/screen.cc
set the root window, to a color for now
[dana/openbox.git] / src / screen.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 extern "C" {
8 #ifdef    HAVE_STDIO_H
9 #  include <stdio.h>
10 #endif // HAVE_STDIO_H
11
12 #ifdef    HAVE_STRING_H
13 #  include <string.h>
14 #endif // HAVE_STRING_H
15
16 #ifdef    HAVE_UNISTD_H
17 #  include <sys/types.h>
18 #  include <unistd.h>
19 #endif // HAVE_UNISTD_H
20
21 #include "gettext.h"
22 #define _(str) gettext(str)
23 }
24
25 #include "screen.hh"
26 #include "client.hh"
27 #include "openbox.hh"
28 #include "frame.hh"
29 #include "bindings.hh"
30 #include "python.hh"
31 #include "otk/display.hh"
32 #include "otk/property.hh"
33
34 #include <vector>
35 #include <algorithm>
36
37 static bool running;
38 static int anotherWMRunning(Display *display, XErrorEvent *) {
39   printf(_("Another window manager already running on display %s.\n"),
40          DisplayString(display));
41   running = true;
42   return -1;
43 }
44
45
46 namespace ob {
47
48
49 Screen::Screen(int screen)
50   : WidgetBase(WidgetBase::Type_Root),
51     _number(screen),
52     _style(screen, "")
53 {
54   assert(screen >= 0); assert(screen < ScreenCount(**otk::display));
55   _info = otk::display->screenInfo(screen);
56
57   ::running = false;
58   XErrorHandler old = XSetErrorHandler(::anotherWMRunning);
59   XSelectInput(**otk::display, _info->rootWindow(),
60                Screen::event_mask);
61   XSync(**otk::display, false);
62   XSetErrorHandler(old);
63
64   _managed = !::running;
65   if (! _managed) return; // was unable to manage the screen
66
67   printf(_("Managing screen %d: visual 0x%lx, depth %d\n"),
68          _number, XVisualIDFromVisual(_info->visual()), _info->depth());
69
70   otk::Property::set(_info->rootWindow(), otk::Property::atoms.openbox_pid,
71                      otk::Property::atoms.cardinal, (unsigned long) getpid());
72
73   // set the mouse cursor for the root window (the default cursor)
74   XDefineCursor(**otk::display, _info->rootWindow(),
75                 openbox->cursors().session);
76
77   // XXX: initialize the screen's style
78   /*
79   otk::ustring stylepath;
80   python_get_string("theme", &stylepath);
81   otk::Configuration sconfig(false);
82   sconfig.setFile(otk::expandTilde(stylepath.c_str()));
83   if (!sconfig.load()) {
84     sconfig.setFile(otk::expandTilde(DEFAULTSTYLE));
85     if (!sconfig.load()) {
86       printf(_("Unable to load default style: %s. Aborting.\n"), DEFAULTSTYLE);
87       ::exit(1);
88     }
89   }
90   _style.load(sconfig);
91   */
92   otk::display->renderControl(_number)->drawRoot(*_style.rootColor());
93
94   // set up notification of netwm support
95   changeSupportedAtoms();
96
97   // Set the netwm properties for geometry
98   unsigned long geometry[] = { _info->width(),
99                                _info->height() };
100   otk::Property::set(_info->rootWindow(),
101                      otk::Property::atoms.net_desktop_geometry,
102                      otk::Property::atoms.cardinal, geometry, 2);
103
104   // Set the net_desktop_names property
105   std::vector<otk::ustring> names;
106   python_get_stringlist("desktop_names", &names);
107   otk::Property::set(_info->rootWindow(),
108                      otk::Property::atoms.net_desktop_names,
109                      otk::Property::utf8, names);
110   // the above set() will cause the updateDesktopNames to fire right away so
111   // we have a list of desktop names
112
113   if (!python_get_long("number_of_desktops", &_num_desktops))
114     _num_desktops = 1;
115   changeNumDesktops(_num_desktops); // set the hint
116
117   _desktop = 0;
118   changeDesktop(0); // set the hint
119
120   // create the window which gets focus when no clients get it
121   XSetWindowAttributes attr;
122   attr.override_redirect = true;
123   _focuswindow = XCreateWindow(**otk::display, _info->rootWindow(),
124                                -100, -100, 1, 1, 0, 0, InputOnly,
125                                _info->visual(), CWOverrideRedirect, &attr);
126   XMapRaised(**otk::display, _focuswindow);
127   
128   // these may be further updated if any pre-existing windows are found in
129   // the manageExising() function
130   changeClientList();  // initialize the client lists, which will be empty
131   calcArea();          // initialize the available working area
132
133   // register this class as the event handler for the root window
134   openbox->registerHandler(_info->rootWindow(), this);
135
136   // call the python Startup callbacks
137   EventData data(_number, 0, EventShutdown, 0);
138   openbox->bindings()->fireEvent(&data);
139 }
140
141
142 Screen::~Screen()
143 {
144   if (! _managed) return;
145
146   XSelectInput(**otk::display, _info->rootWindow(), NoEventMask);
147   
148   // unmanage all windows
149   while (!clients.empty())
150     unmanageWindow(clients.front());
151
152   // call the python Shutdown callbacks
153   EventData data(_number, 0, EventShutdown, 0);
154   openbox->bindings()->fireEvent(&data);
155
156   XDestroyWindow(**otk::display, _focuswindow);
157   XDestroyWindow(**otk::display, _supportwindow);
158 }
159
160
161 void Screen::manageExisting()
162 {
163   unsigned int i, j, nchild;
164   Window r, p, *children;
165   XQueryTree(**otk::display, _info->rootWindow(), &r, &p,
166              &children, &nchild);
167
168   // preen the window list of all icon windows... for better dockapp support
169   for (i = 0; i < nchild; i++) {
170     if (children[i] == None) continue;
171
172     XWMHints *wmhints = XGetWMHints(**otk::display,
173                                     children[i]);
174
175     if (wmhints) {
176       if ((wmhints->flags & IconWindowHint) &&
177           (wmhints->icon_window != children[i])) {
178         for (j = 0; j < nchild; j++) {
179           if (children[j] == wmhints->icon_window) {
180             children[j] = None;
181             break;
182           }
183         }
184       }
185
186       XFree(wmhints);
187     }
188   }
189
190   // manage shown windows
191   for (i = 0; i < nchild; ++i) {
192     if (children[i] == None)
193       continue;
194
195     XWindowAttributes attrib;
196     if (XGetWindowAttributes(**otk::display, children[i], &attrib)) {
197       if (attrib.override_redirect) continue;
198
199       if (attrib.map_state != IsUnmapped) {
200         manageWindow(children[i]);
201       }
202     }
203   }
204
205   XFree(children);
206 }
207
208
209 void Screen::updateStrut()
210 {
211   _strut.left = _strut.right = _strut.top = _strut.bottom = 0;
212
213   Client::List::iterator it, end = clients.end();
214   for (it = clients.begin(); it != end; ++it) {
215     const otk::Strut &s = (*it)->strut();
216     _strut.left = std::max(_strut.left, s.left);
217     _strut.right = std::max(_strut.right, s.right);
218     _strut.top = std::max(_strut.top, s.top);
219     _strut.bottom = std::max(_strut.bottom, s.bottom);
220   }
221   calcArea();
222 }
223
224
225 void Screen::calcArea()
226 {
227   otk::Rect old_area = _area;
228
229 /*
230 #ifdef    XINERAMA
231   // reset to the full areas
232   if (isXineramaActive())
233     xineramaUsableArea = getXineramaAreas();
234 #endif // XINERAMA
235 */
236   
237   _area.setRect(_strut.left, _strut.top,
238                 _info->width() - (_strut.left + _strut.right),
239                 _info->height() - (_strut.top + _strut.bottom));
240
241 /*
242 #ifdef    XINERAMA
243   if (isXineramaActive()) {
244     // keep each of the ximerama-defined areas inside the strut
245     RectList::iterator xit, xend = xineramaUsableArea.end();
246     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
247       if (xit->x() < usableArea.x()) {
248         xit->setX(usableArea.x());
249         xit->setWidth(xit->width() - usableArea.x());
250       }
251       if (xit->y() < usableArea.y()) {
252         xit->setY(usableArea.y());
253         xit->setHeight(xit->height() - usableArea.y());
254       }
255       if (xit->x() + xit->width() > usableArea.width())
256         xit->setWidth(usableArea.width() - xit->x());
257       if (xit->y() + xit->height() > usableArea.height())
258         xit->setHeight(usableArea.height() - xit->y());
259     }
260   }
261 #endif // XINERAMA
262 */
263   
264   if (old_area != _area)
265     // XXX: re-maximize windows
266
267   changeWorkArea();
268 }
269
270
271 void Screen::changeSupportedAtoms()
272 {
273   // create the netwm support window
274   _supportwindow = XCreateSimpleWindow(**otk::display,
275                                        _info->rootWindow(),
276                                        0, 0, 1, 1, 0, 0, 0);
277
278   // set supporting window
279   otk::Property::set(_info->rootWindow(),
280                      otk::Property::atoms.net_supporting_wm_check,
281                      otk::Property::atoms.window, _supportwindow);
282
283   //set properties on the supporting window
284   otk::Property::set(_supportwindow, otk::Property::atoms.net_wm_name,
285                      otk::Property::utf8, "Openbox");
286   otk::Property::set(_supportwindow,
287                      otk::Property::atoms.net_supporting_wm_check,
288                      otk::Property::atoms.window, _supportwindow);
289
290   
291   Atom supported[] = {
292     otk::Property::atoms.net_current_desktop,
293     otk::Property::atoms.net_number_of_desktops,
294     otk::Property::atoms.net_desktop_geometry,
295     otk::Property::atoms.net_desktop_viewport,
296     otk::Property::atoms.net_active_window,
297     otk::Property::atoms.net_workarea,
298     otk::Property::atoms.net_client_list,
299     otk::Property::atoms.net_client_list_stacking,
300     otk::Property::atoms.net_desktop_names,
301     otk::Property::atoms.net_close_window,
302     otk::Property::atoms.net_wm_name,
303     otk::Property::atoms.net_wm_visible_name,
304     otk::Property::atoms.net_wm_icon_name,
305     otk::Property::atoms.net_wm_visible_icon_name,
306 /*
307     otk::Property::atoms.net_wm_desktop,
308 */
309     otk::Property::atoms.net_wm_strut,
310     otk::Property::atoms.net_wm_window_type,
311     otk::Property::atoms.net_wm_window_type_desktop,
312     otk::Property::atoms.net_wm_window_type_dock,
313     otk::Property::atoms.net_wm_window_type_toolbar,
314     otk::Property::atoms.net_wm_window_type_menu,
315     otk::Property::atoms.net_wm_window_type_utility,
316     otk::Property::atoms.net_wm_window_type_splash,
317     otk::Property::atoms.net_wm_window_type_dialog,
318     otk::Property::atoms.net_wm_window_type_normal,
319 /*
320     otk::Property::atoms.net_wm_moveresize,
321     otk::Property::atoms.net_wm_moveresize_size_topleft,
322     otk::Property::atoms.net_wm_moveresize_size_topright,
323     otk::Property::atoms.net_wm_moveresize_size_bottomleft,
324     otk::Property::atoms.net_wm_moveresize_size_bottomright,
325     otk::Property::atoms.net_wm_moveresize_move,
326 */
327     otk::Property::atoms.net_wm_allowed_actions,
328     otk::Property::atoms.net_wm_action_move,
329     otk::Property::atoms.net_wm_action_resize,
330     otk::Property::atoms.net_wm_action_minimize,
331     otk::Property::atoms.net_wm_action_shade,
332 /*    otk::Property::atoms.net_wm_action_stick,*/
333     otk::Property::atoms.net_wm_action_maximize_horz,
334     otk::Property::atoms.net_wm_action_maximize_vert,
335     otk::Property::atoms.net_wm_action_fullscreen,
336     otk::Property::atoms.net_wm_action_change_desktop,
337     otk::Property::atoms.net_wm_action_close,
338
339     otk::Property::atoms.net_wm_state,
340     otk::Property::atoms.net_wm_state_modal,
341     otk::Property::atoms.net_wm_state_maximized_vert,
342     otk::Property::atoms.net_wm_state_maximized_horz,
343     otk::Property::atoms.net_wm_state_shaded,
344     otk::Property::atoms.net_wm_state_skip_taskbar,
345     otk::Property::atoms.net_wm_state_skip_pager,
346     otk::Property::atoms.net_wm_state_hidden,
347     otk::Property::atoms.net_wm_state_fullscreen,
348     otk::Property::atoms.net_wm_state_above,
349     otk::Property::atoms.net_wm_state_below,
350   };
351   const int num_supported = sizeof(supported)/sizeof(Atom);
352
353   otk::Property::set(_info->rootWindow(), otk::Property::atoms.net_supported,
354                      otk::Property::atoms.atom, supported, num_supported);
355 }
356
357
358 void Screen::changeClientList()
359 {
360   Window *windows;
361   unsigned int size = clients.size();
362
363   // create an array of the window ids
364   if (size > 0) {
365     Window *win_it;
366     
367     windows = new Window[size];
368     win_it = windows;
369     Client::List::const_iterator it = clients.begin();
370     const Client::List::const_iterator end = clients.end();
371     for (; it != end; ++it, ++win_it)
372       *win_it = (*it)->window();
373   } else
374     windows = (Window*) 0;
375
376   otk::Property::set(_info->rootWindow(), otk::Property::atoms.net_client_list,
377                      otk::Property::atoms.window, windows, size);
378
379   if (size)
380     delete [] windows;
381
382   changeStackingList();
383 }
384
385
386 void Screen::changeStackingList()
387 {
388   Window *windows;
389   unsigned int size = _stacking.size();
390
391   assert(size == clients.size()); // just making sure.. :)
392
393   
394   // create an array of the window ids (from bottom to top, reverse order!)
395   if (size > 0) {
396     Window *win_it;
397     
398     windows = new Window[size];
399     win_it = windows;
400     Client::List::const_reverse_iterator it = _stacking.rbegin();
401     const Client::List::const_reverse_iterator end = _stacking.rend();
402     for (; it != end; ++it, ++win_it)
403       *win_it = (*it)->window();
404   } else
405     windows = (Window*) 0;
406
407   otk::Property::set(_info->rootWindow(),
408                      otk::Property::atoms.net_client_list_stacking,
409                      otk::Property::atoms.window, windows, size);
410
411   if (size)
412     delete [] windows;
413 }
414
415
416 void Screen::changeWorkArea() {
417   unsigned long *dims = new unsigned long[4 * _num_desktops];
418   for (long i = 0; i < _num_desktops; ++i) {
419     // XXX: this could be different for each workspace
420     dims[(i * 4) + 0] = _area.x();
421     dims[(i * 4) + 1] = _area.y();
422     dims[(i * 4) + 2] = _area.width();
423     dims[(i * 4) + 3] = _area.height();
424   }
425   otk::Property::set(_info->rootWindow(), otk::Property::atoms.net_workarea,
426                      otk::Property::atoms.cardinal, dims, 4 * _num_desktops);
427   delete [] dims;
428 }
429
430
431 void Screen::manageWindow(Window window)
432 {
433   Client *client = 0;
434   XWMHints *wmhint;
435   XSetWindowAttributes attrib_set;
436
437   otk::display->grab();
438
439   // is the window a docking app
440   if ((wmhint = XGetWMHints(**otk::display, window))) {
441     if ((wmhint->flags & StateHint) &&
442         wmhint->initial_state == WithdrawnState) {
443       //slit->addClient(w); // XXX: make dock apps work!
444       otk::display->ungrab();
445
446       XFree(wmhint);
447       return;
448     }
449     XFree(wmhint);
450   }
451
452   // choose the events we want to receive on the CLIENT window
453   attrib_set.event_mask = Client::event_mask;
454   attrib_set.do_not_propagate_mask = Client::no_propagate_mask;
455   XChangeWindowAttributes(**otk::display, window,
456                           CWEventMask|CWDontPropagate, &attrib_set);
457
458   // create the Client class, which gets all of the hints on the window
459   client = new Client(_number, window);
460   // register for events
461   openbox->registerHandler(window, client);
462   // add to the wm's map
463   openbox->addClient(window, client);
464
465   // we dont want a border on the client
466   client->toggleClientBorder(false);
467   
468   // specify that if we exit, the window should not be destroyed and should be
469   // reparented back to root automatically
470   XChangeSaveSet(**otk::display, window, SetModeInsert);
471
472   // create the decoration frame for the client window
473   client->frame = new Frame(client, &_style);
474
475   // add to the wm's map
476   openbox->addClient(client->frame->window(), client);
477   openbox->addClient(client->frame->plate(), client);
478   openbox->addClient(client->frame->titlebar(), client);
479   openbox->addClient(client->frame->label(), client);
480   openbox->addClient(client->frame->button_max(), client);
481   openbox->addClient(client->frame->button_iconify(), client);
482   openbox->addClient(client->frame->button_alldesk(), client);
483   openbox->addClient(client->frame->button_close(), client);
484   openbox->addClient(client->frame->handle(), client);
485   openbox->addClient(client->frame->grip_left(), client);
486   openbox->addClient(client->frame->grip_right(), client);
487
488   // reparent the client to the frame
489   client->frame->grabClient();
490
491   if (!(openbox->state() == Openbox::State_Starting ||
492         client->positionRequested())) {
493     // position the window intelligenty .. hopefully :)
494     // call the python PLACEWINDOW binding
495     EventData data(_number, client, EventPlaceWindow, 0);
496     openbox->bindings()->fireEvent(&data);
497   }
498
499   // if on the current desktop.. (or all desktops)
500   if (client->desktop() == _desktop ||
501       client->desktop() == (signed)0xffffffff) {
502     client->frame->show();
503   }
504
505   client->applyStartupState();
506
507   otk::display->ungrab();
508
509   // add to the screen's list
510   clients.push_back(client);
511   // this puts into the stacking order, then raises it
512   _stacking.push_back(client);
513   raiseWindow(client);
514   // update the root properties
515   changeClientList();
516
517   openbox->bindings()->grabButtons(true, client);
518
519   // call the python NEWWINDOW binding
520   EventData data(_number, client, EventNewWindow, 0);
521   openbox->bindings()->fireEvent(&data);
522
523 #ifdef DEBUG
524   printf("Managed window 0x%lx\n", window);
525 #endif
526 }
527
528
529 void Screen::unmanageWindow(Client *client)
530 {
531   Frame *frame = client->frame;
532
533   // call the python CLOSEWINDOW binding 
534   EventData data(_number, client, EventCloseWindow, 0);
535   openbox->bindings()->fireEvent(&data);
536
537   openbox->bindings()->grabButtons(false, client);
538
539   // remove from the wm's map
540   openbox->removeClient(client->window());
541   openbox->removeClient(frame->window());
542   openbox->removeClient(frame->plate());
543   openbox->removeClient(frame->titlebar());
544   openbox->removeClient(frame->label());
545   openbox->removeClient(frame->button_max());
546   openbox->removeClient(frame->button_iconify());
547   openbox->removeClient(frame->button_alldesk());
548   openbox->removeClient(frame->button_close());
549   openbox->removeClient(frame->handle());
550   openbox->removeClient(frame->grip_left());
551   openbox->removeClient(frame->grip_right());
552   // unregister for handling events
553   openbox->clearHandler(client->window());
554   
555   // remove the window from our save set
556   XChangeSaveSet(**otk::display, client->window(), SetModeDelete);
557
558   // we dont want events no more
559   XSelectInput(**otk::display, client->window(), NoEventMask);
560
561   frame->hide();
562
563   // give the client its border back
564   client->toggleClientBorder(true);
565
566   // reparent the window out of the frame
567   frame->releaseClient();
568
569   delete client->frame;
570   client->frame = 0;
571
572   // remove from the stacking order
573   _stacking.remove(client);
574
575   // remove from the screen's list
576   clients.remove(client);
577
578   // unfocus the client (calls the focus callbacks)
579   client->unfocus();
580
581 #ifdef DEBUG
582   printf("Unmanaged window 0x%lx\n", client->window());
583 #endif
584   
585   delete client;
586
587   // update the root properties
588   changeClientList();
589 }
590
591 void Screen::lowerWindow(Client *client)
592 {
593   Window wins[2];  // only ever restack 2 windows.
594
595   assert(!_stacking.empty()); // this would be bad
596
597   Client::List::iterator it = --_stacking.end();
598   const Client::List::iterator end = _stacking.begin();
599
600   for (; it != end && (*it)->layer() < client->layer(); --it);
601   if (*it == client) return;          // already the bottom, return
602
603   wins[0] = (*it)->frame->window();
604   wins[1] = client->frame->window();
605
606   _stacking.remove(client);
607   _stacking.insert(++it, client);
608
609   XRestackWindows(**otk::display, wins, 2);
610   changeStackingList();
611 }
612
613 void Screen::raiseWindow(Client *client)
614 {
615   Window wins[2];  // only ever restack 2 windows.
616
617   assert(!_stacking.empty()); // this would be bad
618
619   // remove the client before looking so we can't run into ourselves
620   _stacking.remove(client);
621   
622   Client::List::iterator it = _stacking.begin();
623   const Client::List::iterator end = _stacking.end();
624
625   // the stacking list is from highest to lowest
626   for (; it != end && (*it)->layer() > client->layer(); ++it);
627
628   /*
629     if our new position is the top, we want to stack under the _focuswindow
630     otherwise, we want to stack under the previous window in the stack.
631   */
632   wins[0] = (it == _stacking.begin() ? _focuswindow :
633              ((*(--Client::List::const_iterator(it)))->frame->window()));
634   wins[1] = client->frame->window();
635
636   _stacking.insert(it, client);
637
638   XRestackWindows(**otk::display, wins, 2);
639   changeStackingList();
640 }
641
642 void Screen::changeDesktop(long desktop)
643 {
644   if (!(desktop >= 0 && desktop < _num_desktops)) return;
645
646   printf("Moving to desktop %ld\n", desktop);
647   
648   long old = _desktop;
649   
650   _desktop = desktop;
651   otk::Property::set(_info->rootWindow(),
652                      otk::Property::atoms.net_current_desktop,
653                      otk::Property::atoms.cardinal, _desktop);
654
655   if (old == _desktop) return;
656
657   Client::List::iterator it, end = clients.end();
658   for (it = clients.begin(); it != end; ++it) {
659     if ((*it)->desktop() == old) {
660       (*it)->frame->hide();
661     } else if ((*it)->desktop() == _desktop) {
662       (*it)->frame->show();
663     }
664   }
665
666   // force the callbacks to fire
667   if (!openbox->focusedClient())
668     openbox->setFocusedClient(0);
669 }
670
671 void Screen::changeNumDesktops(long num)
672 {
673   assert(num > 0);
674   
675   if (!(num > 0)) return;
676
677   // XXX: move windows on desktops that will no longer exist!
678   
679   _num_desktops = num;
680   otk::Property::set(_info->rootWindow(),
681                      otk::Property::atoms.net_number_of_desktops,
682                      otk::Property::atoms.cardinal, _num_desktops);
683
684   // set the viewport hint
685   unsigned long *viewport = new unsigned long[_num_desktops * 2];
686   memset(viewport, 0, sizeof(unsigned long) * _num_desktops * 2);
687   otk::Property::set(_info->rootWindow(),
688                      otk::Property::atoms.net_desktop_viewport,
689                      otk::Property::atoms.cardinal,
690                      viewport, _num_desktops * 2);
691   delete [] viewport;
692
693   // update the work area hint
694   changeWorkArea();
695 }
696
697
698 void Screen::updateDesktopNames()
699 {
700   unsigned long num = (unsigned) -1;
701   
702   if (!otk::Property::get(_info->rootWindow(),
703                           otk::Property::atoms.net_desktop_names,
704                           otk::Property::utf8, &num, &_desktop_names))
705     _desktop_names.clear();
706   while ((long)_desktop_names.size() < _num_desktops)
707     _desktop_names.push_back("Unnamed");
708 }
709
710
711 void Screen::setDesktopName(long i, const otk::ustring &name)
712 {
713   assert(i >= 0);
714
715   if (i >= _num_desktops) return;
716
717   otk::Property::StringVect newnames = _desktop_names;
718   newnames[i] = name;
719   otk::Property::set(_info->rootWindow(),
720                      otk::Property::atoms.net_desktop_names,
721                      otk::Property::utf8, newnames);
722 }
723
724
725 void Screen::propertyHandler(const XPropertyEvent &e)
726 {
727   otk::EventHandler::propertyHandler(e);
728
729   // compress changes to a single property into a single change
730   XEvent ce;
731   while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
732     // XXX: it would be nice to compress ALL changes to a property, not just
733     //      changes in a row without other props between.
734     if (ce.xproperty.atom != e.atom) {
735       XPutBackEvent(**otk::display, &ce);
736       break;
737     }
738   }
739
740   if (e.atom == otk::Property::atoms.net_desktop_names)
741     updateDesktopNames();
742 }
743
744
745 void Screen::clientMessageHandler(const XClientMessageEvent &e)
746 {
747   otk::EventHandler::clientMessageHandler(e);
748
749   if (e.format != 32) return;
750
751   if (e.message_type == otk::Property::atoms.net_current_desktop) {
752     changeDesktop(e.data.l[0]);
753   } else if (e.message_type == otk::Property::atoms.net_number_of_desktops) {
754     changeNumDesktops(e.data.l[0]);
755   }
756   // XXX: so many client messages to handle here! ..or not.. they go to clients
757 }
758
759
760 void Screen::mapRequestHandler(const XMapRequestEvent &e)
761 {
762   otk::EventHandler::mapRequestHandler(e);
763
764 #ifdef    DEBUG
765   printf("MapRequest for 0x%lx\n", e.window);
766 #endif // DEBUG
767
768   /*
769     MapRequest events come here even after the window exists instead of going
770     right to the client window, because of how they are sent and their struct
771     layout.
772   */
773   Client *c = openbox->findClient(e.window);
774
775   if (c) {
776     // send a net_active_window message
777     XEvent ce;
778     ce.xclient.type = ClientMessage;
779     ce.xclient.message_type = otk::Property::atoms.net_active_window;
780     ce.xclient.display = **otk::display;
781     ce.xclient.window = c->window();
782     ce.xclient.format = 32;
783     ce.xclient.data.l[0] = 0l;
784     ce.xclient.data.l[1] = 0l;
785     ce.xclient.data.l[2] = 0l;
786     ce.xclient.data.l[3] = 0l;
787     ce.xclient.data.l[4] = 0l;
788     XSendEvent(**otk::display, _info->rootWindow(), false,
789                SubstructureRedirectMask | SubstructureNotifyMask,
790                &ce);
791   } else
792     manageWindow(e.window);
793 }
794 }