]> icculus.org git repositories - mikachu/openbox.git/blob - src/client.cc
add an UrgentNotify callback type. fire it when a window becomes "urgent"
[mikachu/openbox.git] / src / client.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 #include "client.hh"
8 #include "frame.hh"
9 #include "screen.hh"
10 #include "openbox.hh"
11 #include "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
14
15 extern "C" {
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
19
20 #include <assert.h>
21
22 #include "gettext.h"
23 #define _(str) gettext(str)
24 }
25
26 namespace ob {
27
28 Client::Client(int screen, Window window)
29   : otk::EventHandler(),
30     WidgetBase(WidgetBase::Type_Client),
31     frame(0), _screen(screen), _window(window)
32 {
33   assert(screen >= 0);
34   assert(window);
35
36   ignore_unmaps = 0;
37   
38   // update EVERYTHING the first time!!
39
40   // we default to NormalState, visible
41   _wmstate = NormalState;
42   // start unfocused
43   _focused = false;
44   // not a transient by default of course
45   _transient_for = 0;
46   // pick a layer to start from
47   _layer = Layer_Normal;
48   // default to not urgent
49   _urgent = false;
50   
51   getArea();
52   getDesktop();
53
54   updateTransientFor();
55   getType();
56   getMwmHints();
57
58   getState();
59   getShaped();
60
61   updateProtocols();
62
63   // got the type, the mwmhints, and the protocols, so we're ready to set up
64   // the decorations/functions
65   setupDecorAndFunctions();
66   
67   getGravity();        // get the attribute gravity
68   updateNormalHints(); // this may override the attribute gravity
69   // also get the initial_state and set _iconic if we aren't "starting"
70   // when we're "starting" that means we should use whatever state was already
71   // on the window over the initial map state, because it was already mapped
72   updateWMHints(openbox->state() != Openbox::State_Starting);
73   updateTitle();
74   updateIconTitle();
75   updateClass();
76   updateStrut();
77
78   // this makes sure that these windows appear on all desktops
79   if (_type == Type_Dock || _type == Type_Desktop)
80     _desktop = 0xffffffff;
81   
82   // set the desktop hint, to make sure that it always exists, and to reflect
83   // any changes we've made here
84   otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
85                      otk::Property::atoms.cardinal, (unsigned)_desktop);
86   
87   changeState();
88 }
89
90
91 Client::~Client()
92 {
93   // clean up childrens' references
94   while (!_transients.empty()) {
95     _transients.front()->_transient_for = 0;
96     _transients.pop_front();
97   }
98   
99   // clean up parents reference to this
100   if (_transient_for)
101     _transient_for->_transients.remove(this); // remove from old parent
102   
103   if (openbox->state() != Openbox::State_Exiting) {
104     // these values should not be persisted across a window unmapping/mapping
105     otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
106     otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
107   } else {
108     // if we're left in an iconic state, the client wont be mapped. this is
109     // bad, since we will no longer be managing the window on restart
110     if (_iconic)
111       XMapWindow(**otk::display, _window);
112   }
113 }
114
115
116 void Client::getGravity()
117 {
118   XWindowAttributes wattrib;
119   Status ret;
120
121   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
122   assert(ret != BadWindow);
123   _gravity = wattrib.win_gravity;
124 }
125
126
127 void Client::getDesktop()
128 {
129   // defaults to the current desktop
130   _desktop = openbox->screen(_screen)->desktop();
131
132   otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
133                      otk::Property::atoms.cardinal,
134                      (long unsigned*)&_desktop);
135 }
136
137
138 void Client::getType()
139 {
140   _type = (WindowType) -1;
141   
142   unsigned long *val;
143   unsigned long num = (unsigned) -1;
144   if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
145                          otk::Property::atoms.atom, &num, &val)) {
146     // use the first value that we know about in the array
147     for (unsigned long i = 0; i < num; ++i) {
148       if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
149         _type = Type_Desktop;
150       else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
151         _type = Type_Dock;
152       else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
153         _type = Type_Toolbar;
154       else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
155         _type = Type_Menu;
156       else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
157         _type = Type_Utility;
158       else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
159         _type = Type_Splash;
160       else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
161         _type = Type_Dialog;
162       else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
163         _type = Type_Normal;
164 //    XXX: make this work again
165 //    else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
166 //      mwm_decorations = 0; // prevent this window from getting any decor
167       if (_type != (WindowType) -1)
168         break; // grab the first known type
169     }
170     delete val;
171   }
172     
173   if (_type == (WindowType) -1) {
174     /*
175      * the window type hint was not set, which means we either classify ourself
176      * as a normal window or a dialog, depending on if we are a transient.
177      */
178     if (_transient_for)
179       _type = Type_Dialog;
180     else
181       _type = Type_Normal;
182   }
183 }
184
185
186 void Client::setupDecorAndFunctions()
187 {
188   // start with everything (cept fullscreen)
189   _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
190     Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
191   _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
192     Func_Shade;
193   if (_delete_window) {
194     _decorations |= Decor_Close;
195     _functions |= Func_Close;
196   }
197   
198   switch (_type) {
199   case Type_Normal:
200     // normal windows retain all of the possible decorations and
201     // functionality, and are the only windows that you can fullscreen
202     _functions |= Func_Fullscreen;
203
204   case Type_Dialog:
205     // dialogs cannot be maximized
206     _decorations &= ~Decor_Maximize;
207     _functions &= ~Func_Maximize;
208     break;
209
210   case Type_Menu:
211   case Type_Toolbar:
212   case Type_Utility:
213     // these windows get less functionality
214     _decorations &= ~(Decor_Iconify | Decor_Handle);
215     _functions &= ~(Func_Iconify | Func_Resize);
216     break;
217
218   case Type_Desktop:
219   case Type_Dock:
220   case Type_Splash:
221     // none of these windows are manipulated by the window manager
222     _decorations = 0;
223     _functions = 0;
224     break;
225   }
226
227   // Mwm Hints are applied subtractively to what has already been chosen for
228   // decor and functionality
229   if (_mwmhints.flags & MwmFlag_Decorations) {
230     if (! (_mwmhints.decorations & MwmDecor_All)) {
231       if (! (_mwmhints.decorations & MwmDecor_Border))
232         _decorations &= ~Decor_Border;
233       if (! (_mwmhints.decorations & MwmDecor_Handle))
234         _decorations &= ~Decor_Handle;
235       if (! (_mwmhints.decorations & MwmDecor_Title)) {
236         _decorations &= ~Decor_Titlebar;
237         // if we don't have a titlebar, then we cannot shade!
238         _functions &= ~Func_Shade;
239       }
240       if (! (_mwmhints.decorations & MwmDecor_Iconify))
241         _decorations &= ~Decor_Iconify;
242       if (! (_mwmhints.decorations & MwmDecor_Maximize))
243         _decorations &= ~Decor_Maximize;
244     }
245   }
246
247   if (_mwmhints.flags & MwmFlag_Functions) {
248     if (! (_mwmhints.functions & MwmFunc_All)) {
249       if (! (_mwmhints.functions & MwmFunc_Resize))
250         _functions &= ~Func_Resize;
251       if (! (_mwmhints.functions & MwmFunc_Move))
252         _functions &= ~Func_Move;
253       if (! (_mwmhints.functions & MwmFunc_Iconify))
254         _functions &= ~Func_Iconify;
255       if (! (_mwmhints.functions & MwmFunc_Maximize))
256         _functions &= ~Func_Maximize;
257       // dont let mwm hints kill the close button
258       //if (! (_mwmhints.functions & MwmFunc_Close))
259       //  _functions &= ~Func_Close;
260     }
261   }
262
263   changeAllowedActions();
264 }
265
266
267 void Client::getMwmHints()
268 {
269   unsigned long num = MwmHints::elements;
270   unsigned long *hints;
271
272   _mwmhints.flags = 0; // default to none
273   
274   if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
275                           otk::Property::atoms.motif_wm_hints, &num,
276                           (unsigned long **)&hints))
277     return;
278   
279   if (num >= MwmHints::elements) {
280     // retrieved the hints
281     _mwmhints.flags = hints[0];
282     _mwmhints.functions = hints[1];
283     _mwmhints.decorations = hints[2];
284   }
285
286   delete [] hints;
287 }
288
289
290 void Client::getArea()
291 {
292   XWindowAttributes wattrib;
293   Status ret;
294   
295   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
296   assert(ret != BadWindow);
297
298   _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
299   _border_width = wattrib.border_width;
300 }
301
302
303 void Client::getState()
304 {
305   _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
306     _iconic = _skip_taskbar = _skip_pager = false;
307   
308   unsigned long *state;
309   unsigned long num = (unsigned) -1;
310   
311   if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
312                          otk::Property::atoms.atom, &num, &state)) {
313     for (unsigned long i = 0; i < num; ++i) {
314       if (state[i] == otk::Property::atoms.net_wm_state_modal)
315         _modal = true;
316       else if (state[i] == otk::Property::atoms.net_wm_state_shaded)
317         _shaded = true;
318       else if (state[i] == otk::Property::atoms.net_wm_state_hidden)
319         _iconic = true;
320       else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
321         _skip_taskbar = true;
322       else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
323         _skip_pager = true;
324       else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
325         _fullscreen = true;
326       else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
327         _max_vert = true;
328       else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
329         _max_horz = true;
330       else if (state[i] == otk::Property::atoms.net_wm_state_above)
331         _above = true;
332       else if (state[i] == otk::Property::atoms.net_wm_state_below)
333         _below = true;
334     }
335
336     delete [] state;
337   }
338 }
339
340
341 void Client::getShaped()
342 {
343   _shaped = false;
344 #ifdef   SHAPE
345   if (otk::display->shape()) {
346     int foo;
347     unsigned int ufoo;
348     int s;
349
350     XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
351
352     XShapeQueryExtents(**otk::display, _window, &s, &foo,
353                        &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
354     _shaped = (s != 0);
355   }
356 #endif // SHAPE
357 }
358
359
360 void Client::calcLayer() {
361   StackLayer l;
362
363   if (_iconic) l = Layer_Icon;
364   else if (_fullscreen) l = Layer_Fullscreen;
365   else if (_type == Type_Desktop) l = Layer_Desktop;
366   else if (_type == Type_Dock) {
367     if (!_below) l = Layer_Top;
368     else l = Layer_Normal;
369   }
370   else if (_above) l = Layer_Above;
371   else if (_below) l = Layer_Below;
372   else l = Layer_Normal;
373
374   if (l != _layer) {
375     _layer = l;
376     if (frame) {
377       /*
378         if we don't have a frame, then we aren't mapped yet (and this would
379         SIGSEGV :)
380       */
381       openbox->screen(_screen)->raiseWindow(this);
382     }
383   }
384 }
385
386
387 void Client::updateProtocols()
388 {
389   Atom *proto;
390   int num_return = 0;
391
392   _focus_notify = false;
393   _delete_window = false;
394
395   if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
396     for (int i = 0; i < num_return; ++i) {
397       if (proto[i] == otk::Property::atoms.wm_delete_window) {
398         // this means we can request the window to close
399         _delete_window = true;
400       } else if (proto[i] == otk::Property::atoms.wm_take_focus)
401         // if this protocol is requested, then the window will be notified
402         // by the window manager whenever it receives focus
403         _focus_notify = true;
404     }
405     XFree(proto);
406   }
407 }
408
409
410 void Client::updateNormalHints()
411 {
412   XSizeHints size;
413   long ret;
414   int oldgravity = _gravity;
415
416   // defaults
417   _size_inc.setPoint(1, 1);
418   _base_size.setPoint(0, 0);
419   _min_size.setPoint(0, 0);
420   _max_size.setPoint(INT_MAX, INT_MAX);
421
422   // XXX: might want to cancel any interactive resizing of the window at this
423   // point..
424
425   // get the hints from the window
426   if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
427     _positioned = (size.flags & (PPosition|USPosition));
428
429     if (size.flags & PWinGravity) {
430       _gravity = size.win_gravity;
431       
432       // if the client has a frame, i.e. has already been mapped and is
433       // changing its gravity
434       if (frame && _gravity != oldgravity) {
435         // move our idea of the client's position based on its new gravity
436         int x, y;
437         frame->frameGravity(x, y);
438         _area.setPos(x, y);
439       }
440     }
441
442     if (size.flags & PMinSize)
443       _min_size.setPoint(size.min_width, size.min_height);
444     
445     if (size.flags & PMaxSize)
446       _max_size.setPoint(size.max_width, size.max_height);
447     
448     if (size.flags & PBaseSize)
449       _base_size.setPoint(size.base_width, size.base_height);
450     
451     if (size.flags & PResizeInc)
452       _size_inc.setPoint(size.width_inc, size.height_inc);
453   }
454 }
455
456
457 void Client::updateWMHints(bool initstate)
458 {
459   XWMHints *hints;
460
461   // assume a window takes input if it doesnt specify
462   _can_focus = true;
463   bool ur = false;
464   
465   if ((hints = XGetWMHints(**otk::display, _window)) != NULL) {
466     if (hints->flags & InputHint)
467       _can_focus = hints->input;
468
469     // only do this when initstate is true!
470     if (initstate && (hints->flags & StateHint))
471       _iconic = hints->initial_state == IconicState;
472
473     if (hints->flags & XUrgencyHint)
474       ur = true;
475
476     if (hints->flags & WindowGroupHint) {
477       if (hints->window_group != _group) {
478         // XXX: remove from the old group if there was one
479         _group = hints->window_group;
480         // XXX: do stuff with the group
481       }
482     } else // no group!
483       _group = None;
484
485     XFree(hints);
486   }
487
488   if (ur != _urgent) {
489     _urgent = ur;
490     // fire the urgent callback if we're mapped, otherwise, wait until after
491     // we're mapped
492     if (_urgent && frame)
493       fireUrgent();
494   }
495 }
496
497
498 void Client::updateTitle()
499 {
500   _title = "";
501   
502   // try netwm
503   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_name,
504                           otk::Property::utf8, &_title)) {
505     // try old x stuff
506     otk::Property::get(_window, otk::Property::atoms.wm_name,
507                        otk::Property::ascii, &_title);
508   }
509
510   if (_title.empty())
511     _title = _("Unnamed Window");
512
513   if (frame)
514     frame->setTitle(_title);
515 }
516
517
518 void Client::updateIconTitle()
519 {
520   _icon_title = "";
521   
522   // try netwm
523   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon_name,
524                           otk::Property::utf8, &_icon_title)) {
525     // try old x stuff
526     otk::Property::get(_window, otk::Property::atoms.wm_icon_name,
527                        otk::Property::ascii, &_icon_title);
528   }
529
530   if (_title.empty())
531     _icon_title = _("Unnamed Window");
532 }
533
534
535 void Client::updateClass()
536 {
537   // set the defaults
538   _app_name = _app_class = _role = "";
539
540   otk::Property::StringVect v;
541   unsigned long num = 2;
542
543   if (otk::Property::get(_window, otk::Property::atoms.wm_class,
544                          otk::Property::ascii, &num, &v)) {
545     if (num > 0) _app_name = v[0].c_str();
546     if (num > 1) _app_class = v[1].c_str();
547   }
548
549   v.clear();
550   num = 1;
551   if (otk::Property::get(_window, otk::Property::atoms.wm_window_role,
552                          otk::Property::ascii, &num, &v)) {
553     if (num > 0) _role = v[0].c_str();
554   }
555 }
556
557
558 void Client::updateStrut()
559 {
560   unsigned long num = 4;
561   unsigned long *data;
562   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_strut,
563                           otk::Property::atoms.cardinal, &num, &data))
564     return;
565
566   if (num == 4) {
567     _strut.left = data[0];
568     _strut.right = data[1];
569     _strut.top = data[2];
570     _strut.bottom = data[3];
571     
572     openbox->screen(_screen)->updateStrut();
573   }
574
575   delete [] data;
576 }
577
578
579 void Client::updateTransientFor()
580 {
581   Window t = 0;
582   Client *c = 0;
583
584   if (XGetTransientForHint(**otk::display, _window, &t) &&
585       t != _window) { // cant be transient to itself!
586     c = openbox->findClient(t);
587     assert(c != this); // if this happens then we need to check for it
588
589     if (!c /*XXX: && _group*/) {
590       // not transient to a client, see if it is transient for a group
591       if (//t == _group->leader() ||
592         t == None ||
593         t == otk::display->screenInfo(_screen)->rootWindow()) {
594         // window is a transient for its group!
595         // XXX: for now this is treated as non-transient.
596         //      this needs to be fixed!
597       }
598     }
599   }
600
601   // if anything has changed...
602   if (c != _transient_for) {
603     if (_transient_for)
604       _transient_for->_transients.remove(this); // remove from old parent
605     _transient_for = c;
606     if (_transient_for)
607       _transient_for->_transients.push_back(this); // add to new parent
608
609     // XXX: change decor status?
610   }
611 }
612
613
614 void Client::propertyHandler(const XPropertyEvent &e)
615 {
616   otk::EventHandler::propertyHandler(e);
617   
618   // compress changes to a single property into a single change
619   XEvent ce;
620   while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
621     // XXX: it would be nice to compress ALL changes to a property, not just
622     //      changes in a row without other props between.
623     if (ce.xproperty.atom != e.atom) {
624       XPutBackEvent(**otk::display, &ce);
625       break;
626     }
627   }
628
629   if (e.atom == XA_WM_NORMAL_HINTS)
630     updateNormalHints();
631   else if (e.atom == XA_WM_HINTS)
632     updateWMHints();
633   else if (e.atom == XA_WM_TRANSIENT_FOR) {
634     updateTransientFor();
635     getType();
636     calcLayer(); // type may have changed, so update the layer
637     setupDecorAndFunctions();
638     frame->adjustSize(); // this updates the frame for any new decor settings
639   }
640   else if (e.atom == otk::Property::atoms.net_wm_name ||
641            e.atom == otk::Property::atoms.wm_name)
642     updateTitle();
643   else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
644            e.atom == otk::Property::atoms.wm_icon_name)
645     updateIconTitle();
646   else if (e.atom == otk::Property::atoms.wm_class)
647     updateClass();
648   else if (e.atom == otk::Property::atoms.wm_protocols) {
649     updateProtocols();
650     setupDecorAndFunctions();
651     frame->adjustSize(); // update the decorations
652   }
653   else if (e.atom == otk::Property::atoms.net_wm_strut)
654     updateStrut();
655 }
656
657
658 void Client::setWMState(long state)
659 {
660   if (state == _wmstate) return; // no change
661   
662   switch (state) {
663   case IconicState:
664     setDesktop(ICONIC_DESKTOP);
665     break;
666   case NormalState:
667     setDesktop(openbox->screen(_screen)->desktop());
668     break;
669   }
670 }
671
672
673 void Client::setDesktop(long target)
674 {
675   if (target == _desktop) return;
676   
677   printf("Setting desktop %ld\n", target);
678
679   if (!(target >= 0 || target == (signed)0xffffffff ||
680         target == ICONIC_DESKTOP))
681     return;
682   
683   _desktop = target;
684
685   // set the desktop hint, but not if we're iconifying
686   if (_desktop != ICONIC_DESKTOP)
687     otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
688                        otk::Property::atoms.cardinal, (unsigned)_desktop);
689   
690   // 'move' the window to the new desktop
691   if (_desktop == openbox->screen(_screen)->desktop() ||
692       _desktop == (signed)0xffffffff)
693     frame->show();
694   else
695     frame->hide();
696
697   // Handle Iconic state. Iconic state is maintained by the client being a
698   // member of the ICONIC_DESKTOP, so this is where we make iconifying and
699   // uniconifying happen.
700   bool i = _desktop == ICONIC_DESKTOP;
701   if (i != _iconic) { // has the state changed?
702     _iconic = i;
703     if (_iconic) {
704       _wmstate = IconicState;
705       ignore_unmaps++;
706       // we unmap the client itself so that we can get MapRequest events, and
707       // because the ICCCM tells us to!
708       XUnmapWindow(**otk::display, _window);
709     } else {
710       _wmstate = NormalState;
711       XMapWindow(**otk::display, _window);
712     }
713     changeState();
714   }
715   
716   frame->adjustState();
717 }
718
719
720 void Client::setState(StateAction action, long data1, long data2)
721 {
722   bool shadestate = _shaded;
723   bool fsstate = _fullscreen;
724
725   if (!(action == State_Add || action == State_Remove ||
726         action == State_Toggle))
727     return; // an invalid action was passed to the client message, ignore it
728
729   for (int i = 0; i < 2; ++i) {
730     Atom state = i == 0 ? data1 : data2;
731     
732     if (! state) continue;
733
734     // if toggling, then pick whether we're adding or removing
735     if (action == State_Toggle) {
736       if (state == otk::Property::atoms.net_wm_state_modal)
737         action = _modal ? State_Remove : State_Add;
738       else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
739         action = _max_vert ? State_Remove : State_Add;
740       else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
741         action = _max_horz ? State_Remove : State_Add;
742       else if (state == otk::Property::atoms.net_wm_state_shaded)
743         action = _shaded ? State_Remove : State_Add;
744       else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
745         action = _skip_taskbar ? State_Remove : State_Add;
746       else if (state == otk::Property::atoms.net_wm_state_skip_pager)
747         action = _skip_pager ? State_Remove : State_Add;
748       else if (state == otk::Property::atoms.net_wm_state_fullscreen)
749         action = _fullscreen ? State_Remove : State_Add;
750       else if (state == otk::Property::atoms.net_wm_state_above)
751         action = _above ? State_Remove : State_Add;
752       else if (state == otk::Property::atoms.net_wm_state_below)
753         action = _below ? State_Remove : State_Add;
754     }
755     
756     if (action == State_Add) {
757       if (state == otk::Property::atoms.net_wm_state_modal) {
758         if (_modal) continue;
759         _modal = true;
760         // XXX: give it focus if another window has focus that shouldnt now
761       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
762         if (_max_vert) continue;
763         _max_vert = true;
764         // XXX: resize the window etc
765       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
766         if (_max_horz) continue;
767         _max_horz = true;
768         // XXX: resize the window etc
769       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
770         shadestate = true;
771       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
772         _skip_taskbar = true;
773       } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
774         _skip_pager = true;
775       } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
776         fsstate = true;
777       } else if (state == otk::Property::atoms.net_wm_state_above) {
778         if (_above) continue;
779         _above = true;
780       } else if (state == otk::Property::atoms.net_wm_state_below) {
781         if (_below) continue;
782         _below = true;
783       }
784
785     } else { // action == State_Remove
786       if (state == otk::Property::atoms.net_wm_state_modal) {
787         if (!_modal) continue;
788         _modal = false;
789       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
790         if (!_max_vert) continue;
791         _max_vert = false;
792         // XXX: resize the window etc
793       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
794         if (!_max_horz) continue;
795         _max_horz = false;
796         // XXX: resize the window etc
797       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
798         shadestate = false;
799       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
800         _skip_taskbar = false;
801       } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
802         _skip_pager = false;
803       } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
804         fsstate = false;
805       } else if (state == otk::Property::atoms.net_wm_state_above) {
806         if (!_above) continue;
807         _above = false;
808       } else if (state == otk::Property::atoms.net_wm_state_below) {
809         if (!_below) continue;
810         _below = false;
811       }
812     }
813   }
814   // change fullscreen state before shading, as it will affect if the window
815   // can shade or not
816   if (fsstate != _fullscreen)
817     fullscreen(fsstate);
818   if (shadestate != _shaded)
819     shade(shadestate);
820   calcLayer();
821 }
822
823
824 void Client::toggleClientBorder(bool addborder)
825 {
826   // adjust our idea of where the client is, based on its border. When the
827   // border is removed, the client should now be considered to be in a
828   // different position.
829   // when re-adding the border to the client, the same operation needs to be
830   // reversed.
831   int x = _area.x(), y = _area.y();
832   switch(_gravity) {
833   default:
834   case NorthWestGravity:
835   case WestGravity:
836   case SouthWestGravity:
837     break;
838   case NorthEastGravity:
839   case EastGravity:
840   case SouthEastGravity:
841     if (addborder) x -= _border_width * 2;
842     else           x += _border_width * 2;
843     break;
844   case NorthGravity:
845   case SouthGravity:
846   case CenterGravity:
847   case ForgetGravity:
848   case StaticGravity:
849     if (addborder) x -= _border_width;
850     else           x += _border_width;
851     break;
852   }
853   switch(_gravity) {
854   default:
855   case NorthWestGravity:
856   case NorthGravity:
857   case NorthEastGravity:
858     break;
859   case SouthWestGravity:
860   case SouthGravity:
861   case SouthEastGravity:
862     if (addborder) y -= _border_width * 2;
863     else           y += _border_width * 2;
864     break;
865   case WestGravity:
866   case EastGravity:
867   case CenterGravity:
868   case ForgetGravity:
869   case StaticGravity:
870     if (addborder) y -= _border_width;
871     else           y += _border_width;
872     break;
873   }
874   _area.setPos(x, y);
875
876   if (addborder) {
877     XSetWindowBorderWidth(**otk::display, _window, _border_width);
878
879     // move the client so it is back it the right spot _with_ its border!
880     XMoveWindow(**otk::display, _window, x, y);
881   } else
882     XSetWindowBorderWidth(**otk::display, _window, 0);
883 }
884
885
886 void Client::clientMessageHandler(const XClientMessageEvent &e)
887 {
888   otk::EventHandler::clientMessageHandler(e);
889   
890   if (e.format != 32) return;
891
892   if (e.message_type == otk::Property::atoms.wm_change_state) {
893     // compress changes into a single change
894     bool compress = false;
895     XEvent ce;
896     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
897       // XXX: it would be nice to compress ALL messages of a type, not just
898       //      messages in a row without other message types between.
899       if (ce.xclient.message_type != e.message_type) {
900         XPutBackEvent(**otk::display, &ce);
901         break;
902       }
903       compress = true;
904     }
905     if (compress)
906       setWMState(ce.xclient.data.l[0]); // use the found event
907     else
908       setWMState(e.data.l[0]); // use the original event
909   } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
910     // compress changes into a single change 
911     bool compress = false;
912     XEvent ce;
913     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
914       // XXX: it would be nice to compress ALL messages of a type, not just
915       //      messages in a row without other message types between.
916       if (ce.xclient.message_type != e.message_type) {
917         XPutBackEvent(**otk::display, &ce);
918         break;
919       }
920       compress = true;
921     }
922     if (compress)
923       setDesktop(e.data.l[0]); // use the found event
924     else
925       setDesktop(e.data.l[0]); // use the original event
926   } else if (e.message_type == otk::Property::atoms.net_wm_state) {
927     // can't compress these
928 #ifdef DEBUG
929     printf("net_wm_state %s %ld %ld for 0x%lx\n",
930            (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
931             e.data.l[0] == 2 ? "Toggle" : "INVALID"),
932            e.data.l[1], e.data.l[2], _window);
933 #endif
934     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
935   } else if (e.message_type == otk::Property::atoms.net_close_window) {
936 #ifdef DEBUG
937     printf("net_close_window for 0x%lx\n", _window);
938 #endif
939     close();
940   } else if (e.message_type == otk::Property::atoms.net_active_window) {
941 #ifdef DEBUG
942     printf("net_active_window for 0x%lx\n", _window);
943 #endif
944     if (_iconic)
945       setDesktop(openbox->screen(_screen)->desktop());
946     if (_shaded)
947       shade(false);
948     // XXX: deiconify
949     focus();
950     openbox->screen(_screen)->raiseWindow(this);
951   }
952 }
953
954
955 #if defined(SHAPE)
956 void Client::shapeHandler(const XShapeEvent &e)
957 {
958   otk::EventHandler::shapeHandler(e);
959
960   if (e.kind == ShapeBounding) {
961     _shaped = e.shaped;
962     frame->adjustShape();
963   }
964 }
965 #endif
966
967
968 void Client::resize(Corner anchor, int w, int h)
969 {
970   if (!(_functions & Func_Resize)) return;
971   internal_resize(anchor, w, h);
972 }
973
974
975 void Client::internal_resize(Corner anchor, int w, int h, int x, int y)
976 {
977   w -= _base_size.x(); 
978   h -= _base_size.y();
979
980   // for interactive resizing. have to move half an increment in each
981   // direction.
982   w += _size_inc.x() / 2;
983   h += _size_inc.y() / 2;
984
985   // is the window resizable? if it is not, then don't check its sizes, the
986   // client can do what it wants and the user can't change it anyhow
987   if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
988     // smaller than min size or bigger than max size?
989     if (w < _min_size.x()) w = _min_size.x();
990     else if (w > _max_size.x()) w = _max_size.x();
991     if (h < _min_size.y()) h = _min_size.y();
992     else if (h > _max_size.y()) h = _max_size.y();
993   }
994
995   // keep to the increments
996   w /= _size_inc.x();
997   h /= _size_inc.y();
998
999   // you cannot resize to nothing
1000   if (w < 1) w = 1;
1001   if (h < 1) h = 1;
1002   
1003   // store the logical size
1004   _logical_size.setPoint(w, h);
1005
1006   w *= _size_inc.x();
1007   h *= _size_inc.y();
1008
1009   w += _base_size.x();
1010   h += _base_size.y();
1011
1012   if (x == INT_MIN || y == INT_MIN) {
1013     x = _area.x();
1014     y = _area.y();
1015     switch (anchor) {
1016     case TopLeft:
1017       break;
1018     case TopRight:
1019       x -= w - _area.width();
1020       break;
1021     case BottomLeft:
1022       y -= h - _area.height();
1023       break;
1024     case BottomRight:
1025       x -= w - _area.width();
1026       y -= h - _area.height();
1027       break;
1028     }
1029   }
1030
1031   _area.setSize(w, h);
1032
1033   XResizeWindow(**otk::display, _window, w, h);
1034
1035   // resize the frame to match the request
1036   frame->adjustSize();
1037   internal_move(x, y);
1038 }
1039
1040
1041 void Client::move(int x, int y)
1042 {
1043   if (!(_functions & Func_Move)) return;
1044   internal_move(x, y);
1045 }
1046
1047
1048 void Client::internal_move(int x, int y)
1049 {
1050   _area.setPos(x, y);
1051
1052   // move the frame to be in the requested position
1053   if (frame) { // this can be called while mapping, before frame exists
1054     frame->adjustPosition();
1055
1056     // send synthetic configure notify (we don't need to if we aren't mapped
1057     // yet)
1058     XEvent event;
1059     event.type = ConfigureNotify;
1060     event.xconfigure.display = **otk::display;
1061     event.xconfigure.event = _window;
1062     event.xconfigure.window = _window;
1063     event.xconfigure.x = x;
1064     event.xconfigure.y = y;
1065     event.xconfigure.width = _area.width();
1066     event.xconfigure.height = _area.height();
1067     event.xconfigure.border_width = _border_width;
1068     event.xconfigure.above = frame->window();
1069     event.xconfigure.override_redirect = False;
1070     XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1071                StructureNotifyMask, &event);
1072   }
1073 }
1074
1075
1076 void Client::close()
1077 {
1078   XEvent ce;
1079
1080   if (!(_functions & Func_Close)) return;
1081
1082   // XXX: itd be cool to do timeouts and shit here for killing the client's
1083   //      process off
1084   // like... if the window is around after 5 seconds, then the close button
1085   // turns a nice red, and if this function is called again, the client is
1086   // explicitly killed.
1087
1088   ce.xclient.type = ClientMessage;
1089   ce.xclient.message_type =  otk::Property::atoms.wm_protocols;
1090   ce.xclient.display = **otk::display;
1091   ce.xclient.window = _window;
1092   ce.xclient.format = 32;
1093   ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1094   ce.xclient.data.l[1] = CurrentTime;
1095   ce.xclient.data.l[2] = 0l;
1096   ce.xclient.data.l[3] = 0l;
1097   ce.xclient.data.l[4] = 0l;
1098   XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1099 }
1100
1101
1102 void Client::changeState()
1103 {
1104   unsigned long state[2];
1105   state[0] = _wmstate;
1106   state[1] = None;
1107   otk::Property::set(_window, otk::Property::atoms.wm_state,
1108                      otk::Property::atoms.wm_state, state, 2);
1109   
1110   Atom netstate[10];
1111   int num = 0;
1112   if (_modal)
1113     netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1114   if (_shaded)
1115     netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1116   if (_iconic)
1117     netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1118   if (_skip_taskbar)
1119     netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1120   if (_skip_pager)
1121     netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1122   if (_fullscreen)
1123     netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1124   if (_max_vert)
1125     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1126   if (_max_horz)
1127     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1128   if (_above)
1129     netstate[num++] = otk::Property::atoms.net_wm_state_above;
1130   if (_below)
1131     netstate[num++] = otk::Property::atoms.net_wm_state_below;
1132   otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1133                      otk::Property::atoms.atom, netstate, num);
1134
1135   calcLayer();
1136
1137   if (frame)
1138     frame->adjustState();
1139 }
1140
1141
1142 void Client::changeAllowedActions(void)
1143 {
1144   Atom actions[9];
1145   int num = 0;
1146
1147   actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1148
1149   if (_functions & Func_Shade)
1150     actions[num++] = otk::Property::atoms.net_wm_action_shade;
1151   if (_functions & Func_Close)
1152     actions[num++] = otk::Property::atoms.net_wm_action_close;
1153   if (_functions & Func_Move)
1154     actions[num++] = otk::Property::atoms.net_wm_action_move;
1155   if (_functions & Func_Iconify)
1156     actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1157   if (_functions & Func_Resize)
1158     actions[num++] = otk::Property::atoms.net_wm_action_resize;
1159   if (_functions & Func_Fullscreen)
1160     actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1161   if (_functions & Func_Maximize) {
1162     actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1163     actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1164   }
1165
1166   otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1167                      otk::Property::atoms.atom, actions, num);
1168 }
1169
1170
1171 void Client::applyStartupState()
1172 {
1173   // these are in a carefully crafted order..
1174
1175   if (_iconic) {
1176     printf("MAP ICONIC\n");
1177     _iconic = false;
1178     setDesktop(ICONIC_DESKTOP);
1179   }
1180   if (_fullscreen) {
1181     _fullscreen = false;
1182     fullscreen(true);
1183   }
1184   if (_shaded) {
1185     _shaded = false;
1186     shade(true);
1187   }
1188   if (_urgent)
1189     fireUrgent();
1190   
1191   if (_max_vert); // XXX: incomplete
1192   if (_max_horz); // XXX: incomplete
1193
1194   if (_skip_taskbar); // nothing to do for this
1195   if (_skip_pager);   // nothing to do for this
1196   if (_modal);        // nothing to do for this
1197   if (_above);        // nothing to do for this
1198   if (_below);        // nothing to do for this
1199 }
1200
1201
1202 void Client::fireUrgent()
1203 {
1204   // call the python UrgentNotify callbacks
1205   EventData data(_screen, this, EventUrgentNotify, 0);
1206   openbox->bindings()->fireEvent(&data);
1207 }
1208
1209
1210 void Client::shade(bool shade)
1211 {
1212   if (!(_functions & Func_Shade) || // can't
1213       _shaded == shade) return;     // already done
1214
1215   // when we're iconic, don't change the wmstate
1216   if (!_iconic)
1217     _wmstate = shade ? IconicState : NormalState;
1218   _shaded = shade;
1219   changeState();
1220   frame->adjustSize();
1221 }
1222
1223
1224 void Client::fullscreen(bool fs)
1225 {
1226   static FunctionFlags saved_func;
1227   static DecorationFlags saved_decor;
1228   static otk::Rect saved_area;
1229   static otk::Point saved_logical_size;
1230
1231   if (!(_functions & Func_Fullscreen) || // can't
1232       _fullscreen == fs) return;         // already done
1233
1234   _fullscreen = fs;
1235   changeState(); // change the state hints on the client
1236
1237   if (fs) {
1238     // save the functions and remove them
1239     saved_func = _functions;
1240     _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1241     // save the decorations and remove them
1242     saved_decor = _decorations;
1243     _decorations = 0;
1244     // save the area and adjust it (we don't call internal resize here for
1245     // constraints on the size, etc, we just make it fullscreen).
1246     saved_area = _area;
1247     const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1248     _area.setRect(0, 0, info->width(), info->height());
1249     saved_logical_size = _logical_size;
1250     _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
1251                            (info->height() - _base_size.y()) / _size_inc.y());
1252   } else {
1253     _functions = saved_func;
1254     _decorations = saved_decor;
1255     _area = saved_area;
1256     _logical_size = saved_logical_size;
1257   }
1258   
1259   changeAllowedActions();  // based on the new _functions
1260   
1261   frame->adjustSize();     // drop/replace the decor's and resize
1262   frame->adjustPosition(); // get (back) in position!
1263
1264   // raise (back) into our stacking layer
1265   openbox->screen(_screen)->raiseWindow(this);
1266
1267   // try focus us when we go into fullscreen mode
1268   if (fs) focus();
1269 }
1270
1271
1272 bool Client::focus()
1273 {
1274   // won't try focus if the client doesn't want it, or if the window isn't
1275   // visible on the screen
1276   if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1277
1278   if (_focused) return true;
1279
1280   // do a check to see if the window has already been unmapped or destroyed
1281   // do this intelligently while watching out for unmaps we've generated
1282   // (ignore_unmaps > 0)
1283   XEvent ev;
1284   if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1285     XPutBackEvent(**otk::display, &ev);
1286     return false;
1287   }
1288   while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1289     if (ignore_unmaps) {
1290       unmapHandler(ev.xunmap);
1291     } else {
1292       XPutBackEvent(**otk::display, &ev);
1293       return false;
1294     }
1295   }
1296
1297   if (_can_focus)
1298     XSetInputFocus(**otk::display, _window,
1299                    RevertToNone, CurrentTime);
1300
1301   if (_focus_notify) {
1302     XEvent ce;
1303     ce.xclient.type = ClientMessage;
1304     ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1305     ce.xclient.display = **otk::display;
1306     ce.xclient.window = _window;
1307     ce.xclient.format = 32;
1308     ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1309     ce.xclient.data.l[1] = openbox->lastTime();
1310     ce.xclient.data.l[2] = 0l;
1311     ce.xclient.data.l[3] = 0l;
1312     ce.xclient.data.l[4] = 0l;
1313     XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1314   }
1315
1316   return true;
1317 }
1318
1319
1320 void Client::unfocus() const
1321 {
1322   if (!_focused) return;
1323
1324   assert(openbox->focusedClient() == this);
1325   openbox->setFocusedClient(0);
1326 }
1327
1328
1329 void Client::focusHandler(const XFocusChangeEvent &e)
1330 {
1331 #ifdef    DEBUG
1332 //  printf("FocusIn for 0x%lx\n", e.window);
1333 #endif // DEBUG
1334   
1335   otk::EventHandler::focusHandler(e);
1336
1337   frame->focus();
1338   _focused = true;
1339
1340   openbox->setFocusedClient(this);
1341 }
1342
1343
1344 void Client::unfocusHandler(const XFocusChangeEvent &e)
1345 {
1346 #ifdef    DEBUG
1347 //  printf("FocusOut for 0x%lx\n", e.window);
1348 #endif // DEBUG
1349   
1350   otk::EventHandler::unfocusHandler(e);
1351
1352   frame->unfocus();
1353   _focused = false;
1354
1355   if (openbox->focusedClient() == this)
1356     openbox->setFocusedClient(0);
1357 }
1358
1359
1360 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1361 {
1362 #ifdef    DEBUG
1363   printf("ConfigureRequest for 0x%lx\n", e.window);
1364 #endif // DEBUG
1365   
1366   otk::EventHandler::configureRequestHandler(e);
1367
1368   // if we are iconic (or shaded (fvwm does this)) ignore the event
1369   if (_iconic || _shaded) return;
1370
1371   if (e.value_mask & CWBorderWidth)
1372     _border_width = e.border_width;
1373
1374   // resize, then move, as specified in the EWMH section 7.7
1375   if (e.value_mask & (CWWidth | CWHeight)) {
1376     int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1377     int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1378
1379     Corner corner;
1380     switch (_gravity) {
1381     case NorthEastGravity:
1382     case EastGravity:
1383       corner = TopRight;
1384       break;
1385     case SouthWestGravity:
1386     case SouthGravity:
1387       corner = BottomLeft;
1388       break;
1389     case SouthEastGravity:
1390       corner = BottomRight;
1391       break;
1392     default:     // NorthWest, Static, etc
1393       corner = TopLeft;
1394     }
1395
1396     // if moving AND resizing ...
1397     if (e.value_mask & (CWX | CWY)) {
1398       int x = (e.value_mask & CWX) ? e.x : _area.x();
1399       int y = (e.value_mask & CWY) ? e.y : _area.y();
1400       internal_resize(corner, w, h, x, y);
1401     } else // if JUST resizing...
1402       internal_resize(corner, w, h);
1403   } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1404     int x = (e.value_mask & CWX) ? e.x : _area.x();
1405     int y = (e.value_mask & CWY) ? e.y : _area.y();
1406     internal_move(x, y);
1407   }
1408
1409   if (e.value_mask & CWStackMode) {
1410     switch (e.detail) {
1411     case Below:
1412     case BottomIf:
1413       openbox->screen(_screen)->lowerWindow(this);
1414       break;
1415
1416     case Above:
1417     case TopIf:
1418     default:
1419       openbox->screen(_screen)->raiseWindow(this);
1420       break;
1421     }
1422   }
1423 }
1424
1425
1426 void Client::unmapHandler(const XUnmapEvent &e)
1427 {
1428   if (ignore_unmaps) {
1429 #ifdef    DEBUG
1430     printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1431 #endif // DEBUG
1432     ignore_unmaps--;
1433     return;
1434   }
1435   
1436 #ifdef    DEBUG
1437   printf("UnmapNotify for 0x%lx\n", e.window);
1438 #endif // DEBUG
1439
1440   otk::EventHandler::unmapHandler(e);
1441
1442   // this deletes us etc
1443   openbox->screen(_screen)->unmanageWindow(this);
1444 }
1445
1446
1447 void Client::destroyHandler(const XDestroyWindowEvent &e)
1448 {
1449 #ifdef    DEBUG
1450   printf("DestroyNotify for 0x%lx\n", e.window);
1451 #endif // DEBUG
1452
1453   otk::EventHandler::destroyHandler(e);
1454
1455   // this deletes us etc
1456   openbox->screen(_screen)->unmanageWindow(this);
1457 }
1458
1459
1460 void Client::reparentHandler(const XReparentEvent &e)
1461 {
1462   // this is when the client is first taken captive in the frame
1463   if (e.parent == frame->plate()) return;
1464
1465 #ifdef    DEBUG
1466   printf("ReparentNotify for 0x%lx\n", e.window);
1467 #endif // DEBUG
1468
1469   otk::EventHandler::reparentHandler(e);
1470
1471   /*
1472     This event is quite rare and is usually handled in unmapHandler.
1473     However, if the window is unmapped when the reparent event occurs,
1474     the window manager never sees it because an unmap event is not sent
1475     to an already unmapped window.
1476   */
1477
1478   // we don't want the reparent event, put it back on the stack for the X
1479   // server to deal with after we unmanage the window
1480   XEvent ev;
1481   ev.xreparent = e;
1482   XPutBackEvent(**otk::display, &ev);
1483   
1484   // this deletes us etc
1485   openbox->screen(_screen)->unmanageWindow(this);
1486 }
1487
1488 void Client::mapRequestHandler(const XMapRequestEvent &e)
1489 {
1490 #ifdef    DEBUG
1491   printf("MapRequest for already managed 0x%lx\n", e.window);
1492 #endif // DEBUG
1493
1494   assert(_iconic); // we shouldn't be able to get this unless we're iconic
1495
1496   // move to the current desktop (uniconify)
1497   setDesktop(openbox->screen(_screen)->desktop());
1498   // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1499 }
1500
1501 }