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