]> icculus.org git repositories - dana/openbox.git/blob - src/client.cc
dont shade without a titlebar.
[dana/openbox.git] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "client.hh"
8 #include "frame.hh"
9 #include "screen.hh"
10 #include "openbox.hh"
11 #include "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
14
15 extern "C" {
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
19
20 #include <assert.h>
21
22 #include "gettext.h"
23 #define _(str) gettext(str)
24 }
25
26 #include <algorithm>
27
28 namespace ob {
29
30 Client::Client(int screen, Window window)
31   : otk::EventHandler(),
32     WidgetBase(WidgetBase::Type_Client),
33     frame(0), _screen(screen), _window(window)
34 {
35   assert(screen >= 0);
36   assert(window);
37
38   ignore_unmaps = 0;
39   
40   // update EVERYTHING the first time!!
41
42   // we default to NormalState, visible
43   _wmstate = NormalState;
44   // start unfocused
45   _focused = false;
46   // not a transient by default of course
47   _transient_for = 0;
48   // pick a layer to start from
49   _layer = Layer_Normal;
50   // default to not urgent
51   _urgent = false;
52   // not positioned unless specified
53   _positioned = false;
54   // nothing is disabled unless specified
55   _disabled_decorations = 0;
56   
57   getArea();
58   getDesktop();
59
60   updateTransientFor();
61   getType();
62   getMwmHints();
63
64   getState();
65   getShaped();
66
67   updateProtocols();
68
69   getGravity();        // get the attribute gravity
70   updateNormalHints(); // this may override the attribute gravity
71
72   // got the type, the mwmhints, the protocols, and the normal hints (min/max
73   // sizes), so we're ready to set up
74   // the decorations/functions
75   setupDecorAndFunctions();
76   
77   // also get the initial_state and set _iconic if we aren't "starting"
78   // when we're "starting" that means we should use whatever state was already
79   // on the window over the initial map state, because it was already mapped
80   updateWMHints(openbox->state() != Openbox::State_Starting);
81   updateTitle();
82   updateIconTitle();
83   updateClass();
84   updateStrut();
85
86   // this makes sure that these windows appear on all desktops
87   if (/*_type == Type_Dock ||*/ _type == Type_Desktop)
88     _desktop = 0xffffffff;
89   
90   // set the desktop hint, to make sure that it always exists, and to reflect
91   // any changes we've made here
92   otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
93                      otk::Property::atoms.cardinal, (unsigned)_desktop);
94   
95   changeState();
96 }
97
98
99 Client::~Client()
100 {
101   // clean up childrens' references
102   while (!_transients.empty()) {
103     _transients.front()->_transient_for = 0;
104     _transients.pop_front();
105   }
106   
107   // clean up parents reference to this
108   if (_transient_for)
109     _transient_for->_transients.remove(this); // remove from old parent
110   
111   if (openbox->state() != Openbox::State_Exiting) {
112     // these values should not be persisted across a window unmapping/mapping
113     otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
114     otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
115   } else {
116     // if we're left in an iconic state, the client wont be mapped. this is
117     // bad, since we will no longer be managing the window on restart
118     if (_iconic)
119       XMapWindow(**otk::display, _window);
120   }
121 }
122
123
124 bool Client::validate() const
125 {
126   XSync(**otk::display, false); // get all events on the server
127
128   XEvent e;
129   if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &e) ||
130       XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &e)) {
131     XPutBackEvent(**otk::display, &e);
132     return false;
133   }
134
135   return true;
136 }
137
138
139 void Client::getGravity()
140 {
141   XWindowAttributes wattrib;
142   Status ret;
143
144   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
145   assert(ret != BadWindow);
146   _gravity = wattrib.win_gravity;
147 }
148
149
150 void Client::getDesktop()
151 {
152   // defaults to the current desktop
153   _desktop = openbox->screen(_screen)->desktop();
154
155   if (otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
156                          otk::Property::atoms.cardinal,
157                          (long unsigned*)&_desktop)) {
158 #ifdef DEBUG
159 //    printf("Window requested desktop: %ld\n", _desktop);
160 #endif
161   }
162 }
163
164
165 void Client::getType()
166 {
167   _type = (WindowType) -1;
168   
169   unsigned long *val;
170   unsigned long num = (unsigned) -1;
171   if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
172                          otk::Property::atoms.atom, &num, &val)) {
173     // use the first value that we know about in the array
174     for (unsigned long i = 0; i < num; ++i) {
175       if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
176         _type = Type_Desktop;
177       else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
178         _type = Type_Dock;
179       else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
180         _type = Type_Toolbar;
181       else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
182         _type = Type_Menu;
183       else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
184         _type = Type_Utility;
185       else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
186         _type = Type_Splash;
187       else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
188         _type = Type_Dialog;
189       else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
190         _type = Type_Normal;
191 //    XXX: make this work again
192 //    else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
193 //      mwm_decorations = 0; // prevent this window from getting any decor
194       if (_type != (WindowType) -1)
195         break; // grab the first known type
196     }
197     delete val;
198   }
199     
200   if (_type == (WindowType) -1) {
201     /*
202      * the window type hint was not set, which means we either classify ourself
203      * as a normal window or a dialog, depending on if we are a transient.
204      */
205     if (_transient_for)
206       _type = Type_Dialog;
207     else
208       _type = Type_Normal;
209   }
210 }
211
212
213 void Client::setupDecorAndFunctions()
214 {
215   // start with everything (cept fullscreen)
216   _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
217     Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
218   _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
219     Func_Shade;
220   if (_delete_window) {
221     _decorations |= Decor_Close;
222     _functions |= Func_Close;
223   }
224
225   if (!(_min_size.x() < _max_size.x() || _min_size.y() < _max_size.y())) {
226     _decorations &= ~(Decor_Maximize | Decor_Handle);
227     _functions &= ~(Func_Resize | Func_Maximize);
228   }
229   
230   switch (_type) {
231   case Type_Normal:
232     // normal windows retain all of the possible decorations and
233     // functionality, and are the only windows that you can fullscreen
234     _functions |= Func_Fullscreen;
235
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 x = _area.x(), y = _area.y();
907   switch(_gravity) {
908   default:
909   case NorthWestGravity:
910   case WestGravity:
911   case SouthWestGravity:
912     break;
913   case NorthEastGravity:
914   case EastGravity:
915   case SouthEastGravity:
916     if (addborder) x -= _border_width * 2;
917     else           x += _border_width * 2;
918     break;
919   case NorthGravity:
920   case SouthGravity:
921   case CenterGravity:
922   case ForgetGravity:
923   case StaticGravity:
924     if (addborder) x -= _border_width;
925     else           x += _border_width;
926     break;
927   }
928   switch(_gravity) {
929   default:
930   case NorthWestGravity:
931   case NorthGravity:
932   case NorthEastGravity:
933     break;
934   case SouthWestGravity:
935   case SouthGravity:
936   case SouthEastGravity:
937     if (addborder) y -= _border_width * 2;
938     else           y += _border_width * 2;
939     break;
940   case WestGravity:
941   case EastGravity:
942   case CenterGravity:
943   case ForgetGravity:
944   case StaticGravity:
945     if (addborder) y -= _border_width;
946     else           y += _border_width;
947     break;
948   }
949   _area.setPos(x, y);
950
951   if (addborder) {
952     XSetWindowBorderWidth(**otk::display, _window, _border_width);
953
954     // move the client so it is back it the right spot _with_ its border!
955     XMoveWindow(**otk::display, _window, x, y);
956   } else
957     XSetWindowBorderWidth(**otk::display, _window, 0);
958 }
959
960
961 void Client::clientMessageHandler(const XClientMessageEvent &e)
962 {
963   otk::EventHandler::clientMessageHandler(e);
964   
965   // validate cuz we query stuff off the client here
966   if (!validate()) return;
967   
968   if (e.format != 32) return;
969
970   if (e.message_type == otk::Property::atoms.wm_change_state) {
971     // compress changes into a single change
972     bool compress = false;
973     XEvent ce;
974     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
975       // XXX: it would be nice to compress ALL messages of a type, not just
976       //      messages in a row without other message types between.
977       if (ce.xclient.message_type != e.message_type) {
978         XPutBackEvent(**otk::display, &ce);
979         break;
980       }
981       compress = true;
982     }
983     if (compress)
984       setWMState(ce.xclient.data.l[0]); // use the found event
985     else
986       setWMState(e.data.l[0]); // use the original event
987   } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
988     // compress changes into a single change 
989     bool compress = false;
990     XEvent ce;
991     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
992       // XXX: it would be nice to compress ALL messages of a type, not just
993       //      messages in a row without other message types between.
994       if (ce.xclient.message_type != e.message_type) {
995         XPutBackEvent(**otk::display, &ce);
996         break;
997       }
998       compress = true;
999     }
1000     if (compress)
1001       setDesktop(e.data.l[0]); // use the found event
1002     else
1003       setDesktop(e.data.l[0]); // use the original event
1004   } else if (e.message_type == otk::Property::atoms.net_wm_state) {
1005     // can't compress these
1006 #ifdef DEBUG
1007     printf("net_wm_state %s %ld %ld for 0x%lx\n",
1008            (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
1009             e.data.l[0] == 2 ? "Toggle" : "INVALID"),
1010            e.data.l[1], e.data.l[2], _window);
1011 #endif
1012     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
1013   } else if (e.message_type == otk::Property::atoms.net_close_window) {
1014 #ifdef DEBUG
1015     printf("net_close_window for 0x%lx\n", _window);
1016 #endif
1017     close();
1018   } else if (e.message_type == otk::Property::atoms.net_active_window) {
1019 #ifdef DEBUG
1020     printf("net_active_window for 0x%lx\n", _window);
1021 #endif
1022     if (_iconic)
1023       setDesktop(openbox->screen(_screen)->desktop());
1024     if (_shaded)
1025       shade(false);
1026     // XXX: deiconify
1027     focus();
1028     openbox->screen(_screen)->raiseWindow(this);
1029   }
1030 }
1031
1032
1033 #if defined(SHAPE)
1034 void Client::shapeHandler(const XShapeEvent &e)
1035 {
1036   otk::EventHandler::shapeHandler(e);
1037
1038   if (e.kind == ShapeBounding) {
1039     _shaped = e.shaped;
1040     frame->adjustShape();
1041   }
1042 }
1043 #endif
1044
1045
1046 void Client::resize(Corner anchor, int w, int h)
1047 {
1048   if (!(_functions & Func_Resize)) return;
1049   internal_resize(anchor, w, h);
1050 }
1051
1052
1053 void Client::internal_resize(Corner anchor, int w, int h, bool user,
1054                              int x, int y)
1055 {
1056   w -= _base_size.x(); 
1057   h -= _base_size.y();
1058
1059   // for interactive resizing. have to move half an increment in each
1060   // direction.
1061   w += _size_inc.x() / 2;
1062   h += _size_inc.y() / 2;
1063
1064   if (user) {
1065     // if this is a user-requested resize, then check against min/max sizes
1066     // and aspect ratios
1067
1068     // smaller than min size or bigger than max size?
1069     if (w < _min_size.x()) w = _min_size.x();
1070     else if (w > _max_size.x()) w = _max_size.x();
1071     if (h < _min_size.y()) h = _min_size.y();
1072     else if (h > _max_size.y()) h = _max_size.y();
1073
1074     // adjust the height ot match the width for the aspect ratios
1075     if (_min_ratio)
1076       if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
1077     if (_max_ratio)
1078       if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
1079   }
1080
1081   // keep to the increments
1082   w /= _size_inc.x();
1083   h /= _size_inc.y();
1084
1085   // you cannot resize to nothing
1086   if (w < 1) w = 1;
1087   if (h < 1) h = 1;
1088   
1089   // store the logical size
1090   _logical_size.setPoint(w, h);
1091
1092   w *= _size_inc.x();
1093   h *= _size_inc.y();
1094
1095   w += _base_size.x();
1096   h += _base_size.y();
1097
1098   if (x == INT_MIN || y == INT_MIN) {
1099     x = _area.x();
1100     y = _area.y();
1101     switch (anchor) {
1102     case TopLeft:
1103       break;
1104     case TopRight:
1105       x -= w - _area.width();
1106       break;
1107     case BottomLeft:
1108       y -= h - _area.height();
1109       break;
1110     case BottomRight:
1111       x -= w - _area.width();
1112       y -= h - _area.height();
1113       break;
1114     }
1115   }
1116
1117   _area.setSize(w, h);
1118
1119   XResizeWindow(**otk::display, _window, w, h);
1120
1121   // resize the frame to match the request
1122   frame->adjustSize();
1123   internal_move(x, y);
1124 }
1125
1126
1127 void Client::move(int x, int y)
1128 {
1129   if (!(_functions & Func_Move)) return;
1130   internal_move(x, y);
1131 }
1132
1133
1134 void Client::internal_move(int x, int y)
1135 {
1136   _area.setPos(x, y);
1137
1138   // move the frame to be in the requested position
1139   if (frame) { // this can be called while mapping, before frame exists
1140     frame->adjustPosition();
1141
1142     // send synthetic configure notify (we don't need to if we aren't mapped
1143     // yet)
1144     XEvent event;
1145     event.type = ConfigureNotify;
1146     event.xconfigure.display = **otk::display;
1147     event.xconfigure.event = _window;
1148     event.xconfigure.window = _window;
1149     
1150     // root window coords with border in mind
1151     event.xconfigure.x = x - _border_width + frame->size().left;
1152     event.xconfigure.y = y - _border_width + frame->size().top;
1153     
1154     event.xconfigure.width = _area.width();
1155     event.xconfigure.height = _area.height();
1156     event.xconfigure.border_width = _border_width;
1157     event.xconfigure.above = frame->plate();
1158     event.xconfigure.override_redirect = False;
1159     XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1160                StructureNotifyMask, &event);
1161 #if 0//def DEBUG
1162     printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1163            event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
1164            event.xconfigure.height, event.xconfigure.window);
1165 #endif
1166   }
1167 }
1168
1169
1170 void Client::close()
1171 {
1172   XEvent ce;
1173
1174   if (!(_functions & Func_Close)) return;
1175
1176   // XXX: itd be cool to do timeouts and shit here for killing the client's
1177   //      process off
1178   // like... if the window is around after 5 seconds, then the close button
1179   // turns a nice red, and if this function is called again, the client is
1180   // explicitly killed.
1181
1182   ce.xclient.type = ClientMessage;
1183   ce.xclient.message_type =  otk::Property::atoms.wm_protocols;
1184   ce.xclient.display = **otk::display;
1185   ce.xclient.window = _window;
1186   ce.xclient.format = 32;
1187   ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1188   ce.xclient.data.l[1] = CurrentTime;
1189   ce.xclient.data.l[2] = 0l;
1190   ce.xclient.data.l[3] = 0l;
1191   ce.xclient.data.l[4] = 0l;
1192   XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1193 }
1194
1195
1196 void Client::changeState()
1197 {
1198   unsigned long state[2];
1199   state[0] = _wmstate;
1200   state[1] = None;
1201   otk::Property::set(_window, otk::Property::atoms.wm_state,
1202                      otk::Property::atoms.wm_state, state, 2);
1203   
1204   Atom netstate[10];
1205   int num = 0;
1206   if (_modal)
1207     netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1208   if (_shaded)
1209     netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1210   if (_iconic)
1211     netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1212   if (_skip_taskbar)
1213     netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1214   if (_skip_pager)
1215     netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1216   if (_fullscreen)
1217     netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1218   if (_max_vert)
1219     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1220   if (_max_horz)
1221     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1222   if (_above)
1223     netstate[num++] = otk::Property::atoms.net_wm_state_above;
1224   if (_below)
1225     netstate[num++] = otk::Property::atoms.net_wm_state_below;
1226   otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1227                      otk::Property::atoms.atom, netstate, num);
1228
1229   calcLayer();
1230
1231   if (frame)
1232     frame->adjustState();
1233 }
1234
1235
1236 void Client::changeAllowedActions(void)
1237 {
1238   Atom actions[9];
1239   int num = 0;
1240
1241   actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1242
1243   if (_functions & Func_Shade)
1244     actions[num++] = otk::Property::atoms.net_wm_action_shade;
1245   if (_functions & Func_Close)
1246     actions[num++] = otk::Property::atoms.net_wm_action_close;
1247   if (_functions & Func_Move)
1248     actions[num++] = otk::Property::atoms.net_wm_action_move;
1249   if (_functions & Func_Iconify)
1250     actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1251   if (_functions & Func_Resize)
1252     actions[num++] = otk::Property::atoms.net_wm_action_resize;
1253   if (_functions & Func_Fullscreen)
1254     actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1255   if (_functions & Func_Maximize) {
1256     actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1257     actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1258   }
1259
1260   otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1261                      otk::Property::atoms.atom, actions, num);
1262 }
1263
1264
1265 void Client::applyStartupState()
1266 {
1267   // these are in a carefully crafted order..
1268
1269   if (_iconic) {
1270     _iconic = false;
1271     setDesktop(ICONIC_DESKTOP);
1272   }
1273   if (_fullscreen) {
1274     _fullscreen = false;
1275     fullscreen(true);
1276   }
1277   if (_shaded) {
1278     _shaded = false;
1279     shade(true);
1280   }
1281   if (_urgent)
1282     fireUrgent();
1283   
1284   if (_max_vert); // XXX: incomplete
1285   if (_max_horz); // XXX: incomplete
1286
1287   if (_skip_taskbar); // nothing to do for this
1288   if (_skip_pager);   // nothing to do for this
1289   if (_modal);        // nothing to do for this
1290   if (_above);        // nothing to do for this
1291   if (_below);        // nothing to do for this
1292 }
1293
1294
1295 void Client::fireUrgent()
1296 {
1297   // call the python UrgentWindow callbacks
1298   EventData data(_screen, this, EventAction::UrgentWindow, 0);
1299   openbox->bindings()->fireEvent(&data);
1300 }
1301
1302
1303 void Client::shade(bool shade)
1304 {
1305   if (!(_functions & Func_Shade) || // can't
1306       _shaded == shade) return;     // already done
1307
1308   // when we're iconic, don't change the wmstate
1309   if (!_iconic)
1310     _wmstate = shade ? IconicState : NormalState;
1311   _shaded = shade;
1312   changeState();
1313   frame->adjustSize();
1314 }
1315
1316
1317 void Client::fullscreen(bool fs)
1318 {
1319   static FunctionFlags saved_func;
1320   static DecorationFlags saved_decor;
1321   static otk::Rect saved_area;
1322   static otk::Point saved_logical_size;
1323
1324   if (!(_functions & Func_Fullscreen) || // can't
1325       _fullscreen == fs) return;         // already done
1326
1327   _fullscreen = fs;
1328   changeState(); // change the state hints on the client
1329
1330   if (fs) {
1331     // save the functions and remove them
1332     saved_func = _functions;
1333     _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1334     // save the decorations and remove them
1335     saved_decor = _decorations;
1336     _decorations = 0;
1337     // save the area and adjust it (we don't call internal resize here for
1338     // constraints on the size, etc, we just make it fullscreen).
1339     saved_area = _area;
1340     const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1341     _area.setRect(0, 0, info->width(), info->height());
1342     saved_logical_size = _logical_size;
1343     _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
1344                            (info->height() - _base_size.y()) / _size_inc.y());
1345   } else {
1346     _functions = saved_func;
1347     _decorations = saved_decor;
1348     _area = saved_area;
1349     _logical_size = saved_logical_size;
1350   }
1351   
1352   changeAllowedActions();  // based on the new _functions
1353   
1354   frame->adjustSize();     // drop/replace the decor's and resize
1355   frame->adjustPosition(); // get (back) in position!
1356
1357   // raise (back) into our stacking layer
1358   openbox->screen(_screen)->raiseWindow(this);
1359
1360   // try focus us when we go into fullscreen mode
1361   if (fs) focus();
1362 }
1363
1364
1365 void Client::disableDecorations(DecorationFlags flags)
1366 {
1367   _disabled_decorations = flags;
1368   setupDecorAndFunctions();
1369 }
1370
1371
1372 bool Client::focus()
1373 {
1374   // won't try focus if the client doesn't want it, or if the window isn't
1375   // visible on the screen
1376   if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1377
1378   if (_focused) return true;
1379
1380   // do a check to see if the window has already been unmapped or destroyed
1381   // do this intelligently while watching out for unmaps we've generated
1382   // (ignore_unmaps > 0)
1383   XEvent ev;
1384   if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1385     XPutBackEvent(**otk::display, &ev);
1386     return false;
1387   }
1388   while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1389     if (ignore_unmaps) {
1390       unmapHandler(ev.xunmap);
1391     } else {
1392       XPutBackEvent(**otk::display, &ev);
1393       return false;
1394     }
1395   }
1396
1397   if (_can_focus)
1398     XSetInputFocus(**otk::display, _window,
1399                    RevertToNone, CurrentTime);
1400
1401   if (_focus_notify) {
1402     XEvent ce;
1403     ce.xclient.type = ClientMessage;
1404     ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1405     ce.xclient.display = **otk::display;
1406     ce.xclient.window = _window;
1407     ce.xclient.format = 32;
1408     ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1409     ce.xclient.data.l[1] = openbox->lastTime();
1410     ce.xclient.data.l[2] = 0l;
1411     ce.xclient.data.l[3] = 0l;
1412     ce.xclient.data.l[4] = 0l;
1413     XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1414   }
1415
1416   XSync(**otk::display, False);
1417   return true;
1418 }
1419
1420
1421 void Client::unfocus() const
1422 {
1423   if (!_focused) return;
1424
1425   assert(openbox->focusedClient() == this);
1426   openbox->setFocusedClient(0);
1427 }
1428
1429
1430 void Client::focusHandler(const XFocusChangeEvent &e)
1431 {
1432 #ifdef    DEBUG
1433 //  printf("FocusIn for 0x%lx\n", e.window);
1434 #endif // DEBUG
1435   
1436   otk::EventHandler::focusHandler(e);
1437
1438   frame->focus();
1439   _focused = true;
1440
1441   openbox->setFocusedClient(this);
1442 }
1443
1444
1445 void Client::unfocusHandler(const XFocusChangeEvent &e)
1446 {
1447 #ifdef    DEBUG
1448 //  printf("FocusOut for 0x%lx\n", e.window);
1449 #endif // DEBUG
1450   
1451   otk::EventHandler::unfocusHandler(e);
1452
1453   frame->unfocus();
1454   _focused = false;
1455
1456   if (openbox->focusedClient() == this)
1457     openbox->setFocusedClient(0);
1458 }
1459
1460
1461 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1462 {
1463 #ifdef    DEBUG
1464   printf("ConfigureRequest for 0x%lx\n", e.window);
1465 #endif // DEBUG
1466   
1467   otk::EventHandler::configureRequestHandler(e);
1468
1469   // if we are iconic (or shaded (fvwm does this)) ignore the event
1470   if (_iconic || _shaded) return;
1471
1472   if (e.value_mask & CWBorderWidth)
1473     _border_width = e.border_width;
1474
1475   // resize, then move, as specified in the EWMH section 7.7
1476   if (e.value_mask & (CWWidth | CWHeight)) {
1477     int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1478     int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1479
1480     Corner corner;
1481     switch (_gravity) {
1482     case NorthEastGravity:
1483     case EastGravity:
1484       corner = TopRight;
1485       break;
1486     case SouthWestGravity:
1487     case SouthGravity:
1488       corner = BottomLeft;
1489       break;
1490     case SouthEastGravity:
1491       corner = BottomRight;
1492       break;
1493     default:     // NorthWest, Static, etc
1494       corner = TopLeft;
1495     }
1496
1497     // if moving AND resizing ...
1498     if (e.value_mask & (CWX | CWY)) {
1499       int x = (e.value_mask & CWX) ? e.x : _area.x();
1500       int y = (e.value_mask & CWY) ? e.y : _area.y();
1501       internal_resize(corner, w, h, false, x, y);
1502     } else // if JUST resizing...
1503       internal_resize(corner, w, h, false);
1504   } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1505     int x = (e.value_mask & CWX) ? e.x : _area.x();
1506     int y = (e.value_mask & CWY) ? e.y : _area.y();
1507     internal_move(x, y);
1508   }
1509
1510   if (e.value_mask & CWStackMode) {
1511     switch (e.detail) {
1512     case Below:
1513     case BottomIf:
1514       openbox->screen(_screen)->lowerWindow(this);
1515       break;
1516
1517     case Above:
1518     case TopIf:
1519     default:
1520       openbox->screen(_screen)->raiseWindow(this);
1521       break;
1522     }
1523   }
1524 }
1525
1526
1527 void Client::unmapHandler(const XUnmapEvent &e)
1528 {
1529   if (ignore_unmaps) {
1530 #ifdef    DEBUG
1531 //  printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1532 #endif // DEBUG
1533     ignore_unmaps--;
1534     return;
1535   }
1536   
1537 #ifdef    DEBUG
1538   printf("UnmapNotify for 0x%lx\n", e.window);
1539 #endif // DEBUG
1540
1541   otk::EventHandler::unmapHandler(e);
1542
1543   // this deletes us etc
1544   openbox->screen(_screen)->unmanageWindow(this);
1545 }
1546
1547
1548 void Client::destroyHandler(const XDestroyWindowEvent &e)
1549 {
1550 #ifdef    DEBUG
1551   printf("DestroyNotify for 0x%lx\n", e.window);
1552 #endif // DEBUG
1553
1554   otk::EventHandler::destroyHandler(e);
1555
1556   // this deletes us etc
1557   openbox->screen(_screen)->unmanageWindow(this);
1558 }
1559
1560
1561 void Client::reparentHandler(const XReparentEvent &e)
1562 {
1563   // this is when the client is first taken captive in the frame
1564   if (e.parent == frame->plate()) return;
1565
1566 #ifdef    DEBUG
1567   printf("ReparentNotify for 0x%lx\n", e.window);
1568 #endif // DEBUG
1569
1570   otk::EventHandler::reparentHandler(e);
1571
1572   /*
1573     This event is quite rare and is usually handled in unmapHandler.
1574     However, if the window is unmapped when the reparent event occurs,
1575     the window manager never sees it because an unmap event is not sent
1576     to an already unmapped window.
1577   */
1578
1579   // we don't want the reparent event, put it back on the stack for the X
1580   // server to deal with after we unmanage the window
1581   XEvent ev;
1582   ev.xreparent = e;
1583   XPutBackEvent(**otk::display, &ev);
1584   
1585   // this deletes us etc
1586   openbox->screen(_screen)->unmanageWindow(this);
1587 }
1588
1589 void Client::mapRequestHandler(const XMapRequestEvent &e)
1590 {
1591 #ifdef    DEBUG
1592   printf("MapRequest for already managed 0x%lx\n", e.window);
1593 #endif // DEBUG
1594
1595   assert(_iconic); // we shouldn't be able to get this unless we're iconic
1596
1597   // move to the current desktop (uniconify)
1598   setDesktop(openbox->screen(_screen)->desktop());
1599   // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1600 }
1601
1602 }