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