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