]> icculus.org git repositories - mikachu/openbox.git/blob - src/screen.cc
dont hide windows when switching to the current workspace
[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
143
144 OBScreen::~OBScreen()
145 {
146   if (! _managed) return;
147
148   XSelectInput(otk::OBDisplay::display, _info->rootWindow(), NoEventMask);
149   
150   // unmanage all windows
151   while (!clients.empty())
152     unmanageWindow(clients.front());
153
154   XDestroyWindow(otk::OBDisplay::display, _focuswindow);
155   XDestroyWindow(otk::OBDisplay::display, _supportwindow);
156
157   delete _image_control;
158 }
159
160
161 void OBScreen::manageExisting()
162 {
163   unsigned int i, j, nchild;
164   Window r, p, *children;
165   XQueryTree(otk::OBDisplay::display, _info->rootWindow(), &r, &p,
166              &children, &nchild);
167
168   // preen the window list of all icon windows... for better dockapp support
169   for (i = 0; i < nchild; i++) {
170     if (children[i] == None) continue;
171
172     XWMHints *wmhints = XGetWMHints(otk::OBDisplay::display,
173                                     children[i]);
174
175     if (wmhints) {
176       if ((wmhints->flags & IconWindowHint) &&
177           (wmhints->icon_window != children[i])) {
178         for (j = 0; j < nchild; j++) {
179           if (children[j] == wmhints->icon_window) {
180             children[j] = None;
181             break;
182           }
183         }
184       }
185
186       XFree(wmhints);
187     }
188   }
189
190   // manage shown windows
191   for (i = 0; i < nchild; ++i) {
192     if (children[i] == None)
193       continue;
194
195     XWindowAttributes attrib;
196     if (XGetWindowAttributes(otk::OBDisplay::display, children[i], &attrib)) {
197       if (attrib.override_redirect) continue;
198
199       if (attrib.map_state != IsUnmapped) {
200         manageWindow(children[i]);
201       }
202     }
203   }
204
205   XFree(children);
206 }
207
208
209 void OBScreen::updateStrut()
210 {
211   _strut.left = _strut.right = _strut.top = _strut.bottom = 0;
212
213   OBClient::List::iterator it, end = clients.end();
214   for (it = clients.begin(); it != end; ++it) {
215     const otk::Strut &s = (*it)->strut();
216     _strut.left = std::max(_strut.left, s.left);
217     _strut.right = std::max(_strut.right, s.right);
218     _strut.top = std::max(_strut.top, s.top);
219     _strut.bottom = std::max(_strut.bottom, s.bottom);
220   }
221   calcArea();
222 }
223
224
225 void OBScreen::calcArea()
226 {
227   otk::Rect old_area = _area;
228
229 /*
230 #ifdef    XINERAMA
231   // reset to the full areas
232   if (isXineramaActive())
233     xineramaUsableArea = getXineramaAreas();
234 #endif // XINERAMA
235 */
236   
237   _area.setRect(_strut.left, _strut.top,
238                 _info->width() - (_strut.left + _strut.right),
239                 _info->height() - (_strut.top + _strut.bottom));
240
241 /*
242 #ifdef    XINERAMA
243   if (isXineramaActive()) {
244     // keep each of the ximerama-defined areas inside the strut
245     RectList::iterator xit, xend = xineramaUsableArea.end();
246     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
247       if (xit->x() < usableArea.x()) {
248         xit->setX(usableArea.x());
249         xit->setWidth(xit->width() - usableArea.x());
250       }
251       if (xit->y() < usableArea.y()) {
252         xit->setY(usableArea.y());
253         xit->setHeight(xit->height() - usableArea.y());
254       }
255       if (xit->x() + xit->width() > usableArea.width())
256         xit->setWidth(usableArea.width() - xit->x());
257       if (xit->y() + xit->height() > usableArea.height())
258         xit->setHeight(usableArea.height() - xit->y());
259     }
260   }
261 #endif // XINERAMA
262 */
263   
264   if (old_area != _area)
265     // XXX: re-maximize windows
266
267   changeWorkArea();
268 }
269
270
271 void OBScreen::changeSupportedAtoms()
272 {
273   // create the netwm support window
274   _supportwindow = XCreateSimpleWindow(otk::OBDisplay::display,
275                                        _info->rootWindow(),
276                                        0, 0, 1, 1, 0, 0, 0);
277   assert(_supportwindow != None);
278
279   // set supporting window
280   Openbox::instance->property()->set(_info->rootWindow(),
281                                      otk::OBProperty::net_supporting_wm_check,
282                                      otk::OBProperty::Atom_Window,
283                                      _supportwindow);
284
285   //set properties on the supporting window
286   Openbox::instance->property()->set(_supportwindow,
287                                      otk::OBProperty::net_wm_name,
288                                      otk::OBProperty::utf8,
289                                      "Openbox");
290   Openbox::instance->property()->set(_supportwindow,
291                                      otk::OBProperty::net_supporting_wm_check,
292                                      otk::OBProperty::Atom_Window,
293                                      _supportwindow);
294
295   
296   Atom supported[] = {
297       otk::OBProperty::net_current_desktop,
298       otk::OBProperty::net_number_of_desktops,
299       otk::OBProperty::net_desktop_geometry,
300       otk::OBProperty::net_desktop_viewport,
301       otk::OBProperty::net_active_window,
302       otk::OBProperty::net_workarea,
303       otk::OBProperty::net_client_list,
304       otk::OBProperty::net_client_list_stacking,
305       otk::OBProperty::net_desktop_names,
306       otk::OBProperty::net_close_window,
307       otk::OBProperty::net_wm_name,
308       otk::OBProperty::net_wm_visible_name,
309       otk::OBProperty::net_wm_icon_name,
310       otk::OBProperty::net_wm_visible_icon_name,
311 /*
312       otk::OBProperty::net_wm_desktop,
313 */
314       otk::OBProperty::net_wm_strut,
315       otk::OBProperty::net_wm_window_type,
316       otk::OBProperty::net_wm_window_type_desktop,
317       otk::OBProperty::net_wm_window_type_dock,
318       otk::OBProperty::net_wm_window_type_toolbar,
319       otk::OBProperty::net_wm_window_type_menu,
320       otk::OBProperty::net_wm_window_type_utility,
321       otk::OBProperty::net_wm_window_type_splash,
322       otk::OBProperty::net_wm_window_type_dialog,
323       otk::OBProperty::net_wm_window_type_normal,
324 /*
325       otk::OBProperty::net_wm_moveresize,
326       otk::OBProperty::net_wm_moveresize_size_topleft,
327       otk::OBProperty::net_wm_moveresize_size_topright,
328       otk::OBProperty::net_wm_moveresize_size_bottomleft,
329       otk::OBProperty::net_wm_moveresize_size_bottomright,
330       otk::OBProperty::net_wm_moveresize_move,
331 */
332 /*
333       otk::OBProperty::net_wm_allowed_actions,
334       otk::OBProperty::net_wm_action_move,
335       otk::OBProperty::net_wm_action_resize,
336       otk::OBProperty::net_wm_action_shade,
337       otk::OBProperty::net_wm_action_maximize_horz,
338       otk::OBProperty::net_wm_action_maximize_vert,
339       otk::OBProperty::net_wm_action_change_desktop,
340       otk::OBProperty::net_wm_action_close,
341 */
342       otk::OBProperty::net_wm_state,
343       otk::OBProperty::net_wm_state_modal,
344       otk::OBProperty::net_wm_state_maximized_vert,
345       otk::OBProperty::net_wm_state_maximized_horz,
346       otk::OBProperty::net_wm_state_shaded,
347       otk::OBProperty::net_wm_state_skip_taskbar,
348       otk::OBProperty::net_wm_state_skip_pager,
349       otk::OBProperty::net_wm_state_hidden,
350       otk::OBProperty::net_wm_state_fullscreen,
351       otk::OBProperty::net_wm_state_above,
352       otk::OBProperty::net_wm_state_below,
353     };
354   const int num_supported = sizeof(supported)/sizeof(Atom);
355
356   // convert to the atom values
357   for (int i = 0; i < num_supported; ++i)
358     supported[i] =
359       Openbox::instance->property()->atom((otk::OBProperty::Atoms)supported[i]);
360   
361   Openbox::instance->property()->set(_info->rootWindow(),
362                                      otk::OBProperty::net_supported,
363                                      otk::OBProperty::Atom_Atom,
364                                      supported, num_supported);
365 }
366
367
368 void OBScreen::changeClientList()
369 {
370   Window *windows;
371   unsigned int size = clients.size();
372
373   // create an array of the window ids
374   if (size > 0) {
375     Window *win_it;
376     
377     windows = new Window[size];
378     win_it = windows;
379     OBClient::List::const_iterator it = clients.begin();
380     const OBClient::List::const_iterator end = clients.end();
381     for (; it != end; ++it, ++win_it)
382       *win_it = (*it)->window();
383   } else
384     windows = (Window*) 0;
385
386   Openbox::instance->property()->set(_info->rootWindow(),
387                                      otk::OBProperty::net_client_list,
388                                      otk::OBProperty::Atom_Window,
389                                      windows, size);
390
391   if (size)
392     delete [] windows;
393
394   changeStackingList();
395 }
396
397
398 void OBScreen::changeStackingList()
399 {
400   Window *windows;
401   unsigned int size = _stacking.size();
402
403   assert(size == clients.size()); // just making sure.. :)
404
405   
406   // create an array of the window ids
407   if (size > 0) {
408     Window *win_it;
409     
410     windows = new Window[size];
411     win_it = windows;
412     OBClient::List::const_iterator it = _stacking.begin();
413     const OBClient::List::const_iterator end = _stacking.end();
414     for (; it != end; ++it, ++win_it)
415       *win_it = (*it)->window();
416   } else
417     windows = (Window*) 0;
418
419   Openbox::instance->property()->set(_info->rootWindow(),
420                                      otk::OBProperty::net_client_list_stacking,
421                                      otk::OBProperty::Atom_Window,
422                                      windows, size);
423
424   if (size)
425     delete [] windows;
426 }
427
428
429 void OBScreen::changeWorkArea() {
430   unsigned long *dims = new unsigned long[4 * _num_desktops];
431   for (long i = 0; i < _num_desktops; ++i) {
432     // XXX: this could be different for each workspace
433     dims[(i * 4) + 0] = _area.x();
434     dims[(i * 4) + 1] = _area.y();
435     dims[(i * 4) + 2] = _area.width();
436     dims[(i * 4) + 3] = _area.height();
437   }
438   Openbox::instance->property()->set(_info->rootWindow(),
439                                      otk::OBProperty::net_workarea,
440                                      otk::OBProperty::Atom_Cardinal,
441                                      dims, 4 * _num_desktops);
442   delete [] dims;
443 }
444
445
446 void OBScreen::manageWindow(Window window)
447 {
448   OBClient *client = 0;
449   XWMHints *wmhint;
450   XSetWindowAttributes attrib_set;
451
452   // is the window a docking app
453   if ((wmhint = XGetWMHints(otk::OBDisplay::display, window))) {
454     if ((wmhint->flags & StateHint) &&
455         wmhint->initial_state == WithdrawnState) {
456       //slit->addClient(w); // XXX: make dock apps work!
457       XFree(wmhint);
458       return;
459     }
460     XFree(wmhint);
461   }
462
463   otk::OBDisplay::grab();
464
465   // choose the events we want to receive on the CLIENT window
466   attrib_set.event_mask = OBClient::event_mask;
467   attrib_set.do_not_propagate_mask = OBClient::no_propagate_mask;
468   XChangeWindowAttributes(otk::OBDisplay::display, window,
469                           CWEventMask|CWDontPropagate, &attrib_set);
470
471   // create the OBClient class, which gets all of the hints on the window
472   client = new OBClient(_number, window);
473   // register for events
474   Openbox::instance->registerHandler(window, client);
475   // add to the wm's map
476   Openbox::instance->addClient(window, client);
477
478   // we dont want a border on the client
479   client->toggleClientBorder(false);
480   
481   // specify that if we exit, the window should not be destroyed and should be
482   // reparented back to root automatically
483   XChangeSaveSet(otk::OBDisplay::display, window, SetModeInsert);
484
485   if (!client->positionRequested()) {
486     // XXX: position the window intelligenty
487   }
488
489   // create the decoration frame for the client window
490   client->frame = new OBFrame(client, &_style);
491
492   // add to the wm's map
493   Openbox::instance->addClient(client->frame->window(), client);
494   Openbox::instance->addClient(client->frame->plate(), client);
495   Openbox::instance->addClient(client->frame->titlebar(), client);
496   Openbox::instance->addClient(client->frame->label(), client);
497   Openbox::instance->addClient(client->frame->button_max(), client);
498   Openbox::instance->addClient(client->frame->button_iconify(), client);
499   Openbox::instance->addClient(client->frame->button_stick(), client);
500   Openbox::instance->addClient(client->frame->button_close(), client);
501   Openbox::instance->addClient(client->frame->handle(), client);
502   Openbox::instance->addClient(client->frame->grip_left(), client);
503   Openbox::instance->addClient(client->frame->grip_right(), client);
504
505   bool shown = false;
506   
507   // if on the current desktop.. (or all desktops)
508   if (client->desktop() == _desktop ||
509       client->desktop() == (signed)0xffffffff) {
510     shown = true;
511     client->frame->show();
512   }
513  
514   // XXX: handle any requested states such as maximized
515
516   otk::OBDisplay::ungrab();
517
518   // add to the screen's list
519   clients.push_back(client);
520   // this puts into the stacking order, then raises it
521   _stacking.push_back(client);
522   restack(true, client);
523   // update the root properties
524   changeClientList();
525
526   Openbox::instance->bindings()->grabButtons(true, client);
527
528   if (shown) {
529     // XXX: make this optional or more intelligent
530     if (client->normal())
531       client->focus();
532   }
533
534   // call the python NEWWINDOW binding
535   EventData *data = new_event_data(window, EventNewWindow, 0);
536   Openbox::instance->bindings()->fireEvent(data);
537   Py_DECREF((PyObject*)data);
538
539   printf("Managed window 0x%lx\n", window);
540 }
541
542
543 void OBScreen::unmanageWindow(OBClient *client)
544 {
545   OBFrame *frame = client->frame;
546
547   // call the python CLOSEWINDOW binding 
548   EventData *data = new_event_data(client->window(), EventCloseWindow, 0);
549   Openbox::instance->bindings()->fireEvent(data);
550   Py_DECREF((PyObject*)data);
551
552   Openbox::instance->bindings()->grabButtons(false, client);
553
554   // remove from the stacking order
555   _stacking.remove(client);
556   
557   // pass around focus if this window was focused XXX do this better!
558   if (Openbox::instance->focusedClient() == client) {
559     OBClient *newfocus = 0;
560     OBClient::List::iterator it, end = _stacking.end();
561     for (it = _stacking.begin(); it != end; ++it)
562       if ((*it)->desktop() == _desktop && (*it)->normal() && (*it)->focus()) {
563         newfocus = *it;
564         break;
565       }
566     if (!newfocus)
567       client->unfocus();
568   }
569
570   // remove from the wm's map
571   Openbox::instance->removeClient(client->window());
572   Openbox::instance->removeClient(frame->window());
573   Openbox::instance->removeClient(frame->plate());
574   Openbox::instance->removeClient(frame->titlebar());
575   Openbox::instance->removeClient(frame->label());
576   Openbox::instance->removeClient(frame->button_max());
577   Openbox::instance->removeClient(frame->button_iconify());
578   Openbox::instance->removeClient(frame->button_stick());
579   Openbox::instance->removeClient(frame->button_close());
580   Openbox::instance->removeClient(frame->handle());
581   Openbox::instance->removeClient(frame->grip_left());
582   Openbox::instance->removeClient(frame->grip_right());
583   // unregister for handling events
584   Openbox::instance->clearHandler(client->window());
585   
586   // remove the window from our save set
587   XChangeSaveSet(otk::OBDisplay::display, client->window(), SetModeDelete);
588
589   // we dont want events no more
590   XSelectInput(otk::OBDisplay::display, client->window(), NoEventMask);
591
592   frame->hide();
593
594   // give the client its border back
595   client->toggleClientBorder(true);
596
597   delete client->frame;
598   client->frame = 0;
599
600   // remove from the screen's list
601   clients.remove(client);
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
663 void OBScreen::changeNumDesktops(long num)
664 {
665   assert(num > 0);
666   
667   if (!(num > 0)) return;
668
669   // XXX: move windows on desktops that will no longer exist!
670   
671   _num_desktops = num;
672   Openbox::instance->property()->set(_info->rootWindow(),
673                                      otk::OBProperty::net_number_of_desktops,
674                                      otk::OBProperty::Atom_Cardinal,
675                                      _num_desktops);
676
677   // set the viewport hint
678   unsigned long *viewport = new unsigned long[_num_desktops * 2];
679   memset(viewport, 0, sizeof(unsigned long) * _num_desktops * 2);
680   Openbox::instance->property()->set(_info->rootWindow(),
681                                      otk::OBProperty::net_desktop_viewport,
682                                      otk::OBProperty::Atom_Cardinal,
683                                      viewport, _num_desktops * 2);
684   delete [] viewport;
685
686   // update the work area hint
687   changeWorkArea();
688 }
689
690
691 void OBScreen::updateDesktopNames()
692 {
693   const otk::OBProperty *property = Openbox::instance->property();
694
695   unsigned long num = (unsigned) -1;
696   
697   if (!property->get(_info->rootWindow(),
698                      otk::OBProperty::net_desktop_names,
699                      otk::OBProperty::utf8, &num, &_desktop_names))
700     _desktop_names.clear();
701   while ((long)_desktop_names.size() < _num_desktops)
702     _desktop_names.push_back("Unnamed");
703 }
704
705
706 void OBScreen::setDesktopName(long i, const std::string &name)
707 {
708   assert(i >= 0);
709
710   if (i >= _num_desktops) return;
711
712   const otk::OBProperty *property = Openbox::instance->property();
713   
714   otk::OBProperty::StringVect newnames = _desktop_names;
715   newnames[i] = name;
716   property->set(_info->rootWindow(), otk::OBProperty::net_desktop_names,
717                 otk::OBProperty::utf8, newnames);
718 }
719
720
721 void OBScreen::propertyHandler(const XPropertyEvent &e)
722 {
723   otk::OtkEventHandler::propertyHandler(e);
724
725   const otk::OBProperty *property = Openbox::instance->property();
726
727   // compress changes to a single property into a single change
728   XEvent ce;
729   while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
730     // XXX: it would be nice to compress ALL changes to a property, not just
731     //      changes in a row without other props between.
732     if (ce.xproperty.atom != e.atom) {
733       XPutBackEvent(otk::OBDisplay::display, &ce);
734       break;
735     }
736   }
737
738   if (e.atom == property->atom(otk::OBProperty::net_desktop_names)) 
739     updateDesktopNames();
740 }
741
742
743 void OBScreen::clientMessageHandler(const XClientMessageEvent &e)
744 {
745   otk::OtkEventHandler::clientMessageHandler(e);
746
747   if (e.format != 32) return;
748
749   const otk::OBProperty *property = Openbox::instance->property();
750
751   if (e.message_type == property->atom(otk::OBProperty::net_current_desktop)) {
752     changeDesktop(e.data.l[0]);
753   } else if (e.message_type ==
754              property->atom(otk::OBProperty::net_number_of_desktops)) {
755     changeNumDesktops(e.data.l[0]);
756   }
757   // XXX: so many client messages to handle here! ..or not.. they go to clients
758 }
759
760
761 void OBScreen::mapRequestHandler(const XMapRequestEvent &e)
762 {
763   otk::OtkEventHandler::mapRequestHandler(e);
764
765 #ifdef    DEBUG
766   printf("MapRequest for 0x%lx\n", e.window);
767 #endif // DEBUG
768
769   /*
770     MapRequest events come here even after the window exists instead of going
771     right to the client window, because of how they are sent and their struct
772     layout.
773   */
774   OBClient *c = Openbox::instance->findClient(e.window);
775
776   if (c) {
777     if (c->shaded())
778       c->shade(false);
779     // XXX: uniconify the window
780     c->focus();
781   } else
782     manageWindow(e.window);
783 }
784 }