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