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