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