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