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