call the place window routine after the frame is created
[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 /*
332     otk::Property::atoms.net_wm_allowed_actions,
333     otk::Property::atoms.net_wm_action_move,
334     otk::Property::atoms.net_wm_action_resize,
335     otk::Property::atoms.net_wm_action_shade,
336     otk::Property::atoms.net_wm_action_maximize_horz,
337     otk::Property::atoms.net_wm_action_maximize_vert,
338     otk::Property::atoms.net_wm_action_change_desktop,
339     otk::Property::atoms.net_wm_action_close,
340 */
341     otk::Property::atoms.net_wm_state,
342     otk::Property::atoms.net_wm_state_modal,
343     otk::Property::atoms.net_wm_state_maximized_vert,
344     otk::Property::atoms.net_wm_state_maximized_horz,
345     otk::Property::atoms.net_wm_state_shaded,
346     otk::Property::atoms.net_wm_state_skip_taskbar,
347     otk::Property::atoms.net_wm_state_skip_pager,
348     otk::Property::atoms.net_wm_state_hidden,
349     otk::Property::atoms.net_wm_state_fullscreen,
350     otk::Property::atoms.net_wm_state_above,
351     otk::Property::atoms.net_wm_state_below,
352   };
353   const int num_supported = sizeof(supported)/sizeof(Atom);
354
355   otk::Property::set(_info->rootWindow(), otk::Property::atoms.net_supported,
356                      otk::Property::atoms.atom, supported, num_supported);
357 }
358
359
360 void Screen::changeClientList()
361 {
362   Window *windows;
363   unsigned int size = clients.size();
364
365   // create an array of the window ids
366   if (size > 0) {
367     Window *win_it;
368     
369     windows = new Window[size];
370     win_it = windows;
371     Client::List::const_iterator it = clients.begin();
372     const Client::List::const_iterator end = clients.end();
373     for (; it != end; ++it, ++win_it)
374       *win_it = (*it)->window();
375   } else
376     windows = (Window*) 0;
377
378   otk::Property::set(_info->rootWindow(), otk::Property::atoms.net_client_list,
379                      otk::Property::atoms.window, windows, size);
380
381   if (size)
382     delete [] windows;
383
384   changeStackingList();
385 }
386
387
388 void Screen::changeStackingList()
389 {
390   Window *windows;
391   unsigned int size = _stacking.size();
392
393   assert(size == clients.size()); // just making sure.. :)
394
395   
396   // create an array of the window ids (from bottom to top, reverse order!)
397   if (size > 0) {
398     Window *win_it;
399     
400     windows = new Window[size];
401     win_it = windows;
402     Client::List::const_reverse_iterator it = _stacking.rbegin();
403     const Client::List::const_reverse_iterator end = _stacking.rend();
404     for (; it != end; ++it, ++win_it)
405       *win_it = (*it)->window();
406   } else
407     windows = (Window*) 0;
408
409   otk::Property::set(_info->rootWindow(),
410                      otk::Property::atoms.net_client_list_stacking,
411                      otk::Property::atoms.window, windows, size);
412
413   if (size)
414     delete [] windows;
415 }
416
417
418 void Screen::changeWorkArea() {
419   unsigned long *dims = new unsigned long[4 * _num_desktops];
420   for (long i = 0; i < _num_desktops; ++i) {
421     // XXX: this could be different for each workspace
422     dims[(i * 4) + 0] = _area.x();
423     dims[(i * 4) + 1] = _area.y();
424     dims[(i * 4) + 2] = _area.width();
425     dims[(i * 4) + 3] = _area.height();
426   }
427   otk::Property::set(_info->rootWindow(), otk::Property::atoms.net_workarea,
428                      otk::Property::atoms.cardinal, dims, 4 * _num_desktops);
429   delete [] dims;
430 }
431
432
433 void Screen::manageWindow(Window window)
434 {
435   Client *client = 0;
436   XWMHints *wmhint;
437   XSetWindowAttributes attrib_set;
438
439   otk::display->grab();
440
441   // is the window a docking app
442   if ((wmhint = XGetWMHints(**otk::display, window))) {
443     if ((wmhint->flags & StateHint) &&
444         wmhint->initial_state == WithdrawnState) {
445       //slit->addClient(w); // XXX: make dock apps work!
446       otk::display->ungrab();
447
448       XFree(wmhint);
449       return;
450     }
451     XFree(wmhint);
452   }
453
454   // choose the events we want to receive on the CLIENT window
455   attrib_set.event_mask = Client::event_mask;
456   attrib_set.do_not_propagate_mask = Client::no_propagate_mask;
457   XChangeWindowAttributes(**otk::display, window,
458                           CWEventMask|CWDontPropagate, &attrib_set);
459
460   // create the Client class, which gets all of the hints on the window
461   client = new Client(_number, window);
462   // register for events
463   openbox->registerHandler(window, client);
464   // add to the wm's map
465   openbox->addClient(window, client);
466
467   // we dont want a border on the client
468   client->toggleClientBorder(false);
469   
470   // specify that if we exit, the window should not be destroyed and should be
471   // reparented back to root automatically
472   XChangeSaveSet(**otk::display, window, SetModeInsert);
473
474   // create the decoration frame for the client window
475   client->frame = new Frame(client, &_style);
476
477   if (!(openbox->state() == Openbox::State_Starting ||
478         client->positionRequested())) {
479     // position the window intelligenty .. hopefully :)
480     // call the python PLACEWINDOW binding
481     EventData data(_number, client, EventPlaceWindow, 0);
482     openbox->bindings()->fireEvent(&data);
483   }
484
485   // add to the wm's map
486   openbox->addClient(client->frame->window(), client);
487   openbox->addClient(client->frame->plate(), client);
488   openbox->addClient(client->frame->titlebar(), client);
489   openbox->addClient(client->frame->label(), client);
490   openbox->addClient(client->frame->button_max(), client);
491   openbox->addClient(client->frame->button_iconify(), client);
492   openbox->addClient(client->frame->button_stick(), client);
493   openbox->addClient(client->frame->button_close(), client);
494   openbox->addClient(client->frame->handle(), client);
495   openbox->addClient(client->frame->grip_left(), client);
496   openbox->addClient(client->frame->grip_right(), client);
497
498   // reparent the client to the frame
499   client->frame->grabClient();
500
501   // if on the current desktop.. (or all desktops)
502   if (client->desktop() == _desktop ||
503       client->desktop() == (signed)0xffffffff) {
504     client->frame->show();
505   }
506  
507   // XXX: handle any requested states such as maximized
508
509   otk::display->ungrab();
510
511   // add to the screen's list
512   clients.push_back(client);
513   // this puts into the stacking order, then raises it
514   _stacking.push_back(client);
515   raiseWindow(client);
516   // update the root properties
517   changeClientList();
518
519   openbox->bindings()->grabButtons(true, client);
520
521   // call the python NEWWINDOW binding
522   EventData data(_number, client, EventNewWindow, 0);
523   openbox->bindings()->fireEvent(&data);
524
525 #ifdef DEBUG
526   printf("Managed window 0x%lx\n", window);
527 #endif
528 }
529
530
531 void Screen::unmanageWindow(Client *client)
532 {
533   Frame *frame = client->frame;
534
535   // call the python CLOSEWINDOW binding 
536   EventData data(_number, client, EventCloseWindow, 0);
537   openbox->bindings()->fireEvent(&data);
538
539   openbox->bindings()->grabButtons(false, client);
540
541   // remove from the wm's map
542   openbox->removeClient(client->window());
543   openbox->removeClient(frame->window());
544   openbox->removeClient(frame->plate());
545   openbox->removeClient(frame->titlebar());
546   openbox->removeClient(frame->label());
547   openbox->removeClient(frame->button_max());
548   openbox->removeClient(frame->button_iconify());
549   openbox->removeClient(frame->button_stick());
550   openbox->removeClient(frame->button_close());
551   openbox->removeClient(frame->handle());
552   openbox->removeClient(frame->grip_left());
553   openbox->removeClient(frame->grip_right());
554   // unregister for handling events
555   openbox->clearHandler(client->window());
556   
557   // remove the window from our save set
558   XChangeSaveSet(**otk::display, client->window(), SetModeDelete);
559
560   // we dont want events no more
561   XSelectInput(**otk::display, client->window(), NoEventMask);
562
563   frame->hide();
564
565   // give the client its border back
566   client->toggleClientBorder(true);
567
568   // reparent the window out of the frame
569   frame->releaseClient();
570
571   delete client->frame;
572   client->frame = 0;
573
574   // remove from the stacking order
575   _stacking.remove(client);
576
577   // remove from the screen's list
578   clients.remove(client);
579
580   // unfocus the client (calls the focus callbacks)
581   client->unfocus();
582
583 #ifdef DEBUG
584   printf("Unmanaged window 0x%lx\n", client->window());
585 #endif
586   
587   delete client;
588
589   // update the root properties
590   changeClientList();
591 }
592
593 void Screen::lowerWindow(Client *client)
594 {
595   Window wins[2];  // only ever restack 2 windows.
596
597   assert(!_stacking.empty()); // this would be bad
598
599   Client::List::iterator it = --_stacking.end();
600   Client::List::const_iterator end = _stacking.begin();
601
602   for (; it != end && (*it)->layer() < client->layer(); --it);
603   if (*it == client) return;          // already the bottom, return
604
605   wins[0] = (*it)->frame->window();
606   wins[1] = client->frame->window();
607
608   _stacking.remove(client);
609   _stacking.insert(++it, client);
610
611   XRestackWindows(**otk::display, wins, 2);
612   changeStackingList();
613 }
614
615 void Screen::raiseWindow(Client *client)
616 {
617   Window wins[2];  // only ever restack 2 windows.
618
619   assert(!_stacking.empty()); // this would be bad
620
621   // remove the client before looking so we can't run into ourselves
622   _stacking.remove(client);
623   
624   Client::List::iterator it = _stacking.begin();
625   Client::List::const_iterator end = _stacking.end();
626
627   // the stacking list is from highest to lowest
628   for (; it != end && (*it)->layer() > client->layer(); ++it);
629
630   /*
631     if our new position is the top, we want to stack under the _focuswindow
632     otherwise, we want to stack under the previous window in the stack.
633   */
634   wins[0] = (it == _stacking.begin() ? _focuswindow :
635              ((*(--Client::List::const_iterator(it)))->frame->window()));
636   wins[1] = client->frame->window();
637
638   _stacking.insert(it, client);
639
640   XRestackWindows(**otk::display, wins, 2);
641   changeStackingList();
642 }
643
644 void Screen::changeDesktop(long desktop)
645 {
646   if (!(desktop >= 0 && desktop < _num_desktops)) return;
647
648   printf("Moving to desktop %ld\n", desktop);
649   
650   long old = _desktop;
651   
652   _desktop = desktop;
653   otk::Property::set(_info->rootWindow(),
654                      otk::Property::atoms.net_current_desktop,
655                      otk::Property::atoms.cardinal, _desktop);
656
657   if (old == _desktop) return;
658
659   Client::List::iterator it, end = clients.end();
660   for (it = clients.begin(); it != end; ++it) {
661     if ((*it)->desktop() == old) {
662       (*it)->frame->hide();
663     } else if ((*it)->desktop() == _desktop) {
664       (*it)->frame->show();
665     }
666   }
667
668   // force the callbacks to fire
669   if (!openbox->focusedClient())
670     openbox->setFocusedClient(0);
671 }
672
673 void Screen::changeNumDesktops(long num)
674 {
675   assert(num > 0);
676   
677   if (!(num > 0)) return;
678
679   // XXX: move windows on desktops that will no longer exist!
680   
681   _num_desktops = num;
682   otk::Property::set(_info->rootWindow(),
683                      otk::Property::atoms.net_number_of_desktops,
684                      otk::Property::atoms.cardinal, _num_desktops);
685
686   // set the viewport hint
687   unsigned long *viewport = new unsigned long[_num_desktops * 2];
688   memset(viewport, 0, sizeof(unsigned long) * _num_desktops * 2);
689   otk::Property::set(_info->rootWindow(),
690                      otk::Property::atoms.net_desktop_viewport,
691                      otk::Property::atoms.cardinal,
692                      viewport, _num_desktops * 2);
693   delete [] viewport;
694
695   // update the work area hint
696   changeWorkArea();
697 }
698
699
700 void Screen::updateDesktopNames()
701 {
702   unsigned long num = (unsigned) -1;
703   
704   if (!otk::Property::get(_info->rootWindow(),
705                           otk::Property::atoms.net_desktop_names,
706                           otk::Property::utf8, &num, &_desktop_names))
707     _desktop_names.clear();
708   while ((long)_desktop_names.size() < _num_desktops)
709     _desktop_names.push_back("Unnamed");
710 }
711
712
713 void Screen::setDesktopName(long i, const otk::ustring &name)
714 {
715   assert(i >= 0);
716
717   if (i >= _num_desktops) return;
718
719   otk::Property::StringVect newnames = _desktop_names;
720   newnames[i] = name;
721   otk::Property::set(_info->rootWindow(),
722                      otk::Property::atoms.net_desktop_names,
723                      otk::Property::utf8, newnames);
724 }
725
726
727 void Screen::propertyHandler(const XPropertyEvent &e)
728 {
729   otk::EventHandler::propertyHandler(e);
730
731   // compress changes to a single property into a single change
732   XEvent ce;
733   while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
734     // XXX: it would be nice to compress ALL changes to a property, not just
735     //      changes in a row without other props between.
736     if (ce.xproperty.atom != e.atom) {
737       XPutBackEvent(**otk::display, &ce);
738       break;
739     }
740   }
741
742   if (e.atom == otk::Property::atoms.net_desktop_names)
743     updateDesktopNames();
744 }
745
746
747 void Screen::clientMessageHandler(const XClientMessageEvent &e)
748 {
749   otk::EventHandler::clientMessageHandler(e);
750
751   if (e.format != 32) return;
752
753   if (e.message_type == otk::Property::atoms.net_current_desktop) {
754     changeDesktop(e.data.l[0]);
755   } else if (e.message_type == otk::Property::atoms.net_number_of_desktops) {
756     changeNumDesktops(e.data.l[0]);
757   }
758   // XXX: so many client messages to handle here! ..or not.. they go to clients
759 }
760
761
762 void Screen::mapRequestHandler(const XMapRequestEvent &e)
763 {
764   otk::EventHandler::mapRequestHandler(e);
765
766 #ifdef    DEBUG
767   printf("MapRequest for 0x%lx\n", e.window);
768 #endif // DEBUG
769
770   /*
771     MapRequest events come here even after the window exists instead of going
772     right to the client window, because of how they are sent and their struct
773     layout.
774   */
775   Client *c = openbox->findClient(e.window);
776
777   if (c) {
778     // send a net_active_window message
779     XEvent ce;
780     ce.xclient.type = ClientMessage;
781     ce.xclient.message_type = otk::Property::atoms.net_active_window;
782     ce.xclient.display = **otk::display;
783     ce.xclient.window = c->window();
784     ce.xclient.format = 32;
785     ce.xclient.data.l[0] = 0l;
786     ce.xclient.data.l[1] = 0l;
787     ce.xclient.data.l[2] = 0l;
788     ce.xclient.data.l[3] = 0l;
789     ce.xclient.data.l[4] = 0l;
790     XSendEvent(**otk::display, _info->rootWindow(), false,
791                SubstructureRedirectMask | SubstructureNotifyMask,
792                &ce);
793   } else
794     manageWindow(e.window);
795 }
796 }