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