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