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