]> icculus.org git repositories - dana/openbox.git/blob - src/client.cc
changeState after a setState request
[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   changeState(); // change the hint to relect these changes
897 }
898
899
900 void Client::toggleClientBorder(bool addborder)
901 {
902   // adjust our idea of where the client is, based on its border. When the
903   // border is removed, the client should now be considered to be in a
904   // different position.
905   // when re-adding the border to the client, the same operation needs to be
906   // reversed.
907   int oldx = _area.x(), oldy = _area.y();
908   int x = oldx, y = oldy;
909   switch(_gravity) {
910   default:
911   case NorthWestGravity:
912   case WestGravity:
913   case SouthWestGravity:
914     break;
915   case NorthEastGravity:
916   case EastGravity:
917   case SouthEastGravity:
918     if (addborder) x -= _border_width * 2;
919     else           x += _border_width * 2;
920     break;
921   case NorthGravity:
922   case SouthGravity:
923   case CenterGravity:
924   case ForgetGravity:
925   case StaticGravity:
926     if (addborder) x -= _border_width;
927     else           x += _border_width;
928     break;
929   }
930   switch(_gravity) {
931   default:
932   case NorthWestGravity:
933   case NorthGravity:
934   case NorthEastGravity:
935     break;
936   case SouthWestGravity:
937   case SouthGravity:
938   case SouthEastGravity:
939     if (addborder) y -= _border_width * 2;
940     else           y += _border_width * 2;
941     break;
942   case WestGravity:
943   case EastGravity:
944   case CenterGravity:
945   case ForgetGravity:
946   case StaticGravity:
947     if (addborder) y -= _border_width;
948     else           y += _border_width;
949     break;
950   }
951   _area.setPos(x, y);
952
953   if (addborder) {
954     XSetWindowBorderWidth(**otk::display, _window, _border_width);
955
956     // move the client so it is back it the right spot _with_ its border!
957     if (x != oldx || y != oldy)
958       XMoveWindow(**otk::display, _window, x, y);
959   } else
960     XSetWindowBorderWidth(**otk::display, _window, 0);
961 }
962
963
964 void Client::clientMessageHandler(const XClientMessageEvent &e)
965 {
966   otk::EventHandler::clientMessageHandler(e);
967   
968   // validate cuz we query stuff off the client here
969   if (!validate()) return;
970   
971   if (e.format != 32) return;
972
973   if (e.message_type == otk::Property::atoms.wm_change_state) {
974     // compress changes into a single change
975     bool compress = false;
976     XEvent ce;
977     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
978       // XXX: it would be nice to compress ALL messages of a type, not just
979       //      messages in a row without other message types between.
980       if (ce.xclient.message_type != e.message_type) {
981         XPutBackEvent(**otk::display, &ce);
982         break;
983       }
984       compress = true;
985     }
986     if (compress)
987       setWMState(ce.xclient.data.l[0]); // use the found event
988     else
989       setWMState(e.data.l[0]); // use the original event
990   } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
991     // compress changes into a single change 
992     bool compress = false;
993     XEvent ce;
994     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
995       // XXX: it would be nice to compress ALL messages of a type, not just
996       //      messages in a row without other message types between.
997       if (ce.xclient.message_type != e.message_type) {
998         XPutBackEvent(**otk::display, &ce);
999         break;
1000       }
1001       compress = true;
1002     }
1003     if (compress)
1004       setDesktop(e.data.l[0]); // use the found event
1005     else
1006       setDesktop(e.data.l[0]); // use the original event
1007   } else if (e.message_type == otk::Property::atoms.net_wm_state) {
1008     // can't compress these
1009 #ifdef DEBUG
1010     printf("net_wm_state %s %ld %ld for 0x%lx\n",
1011            (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
1012             e.data.l[0] == 2 ? "Toggle" : "INVALID"),
1013            e.data.l[1], e.data.l[2], _window);
1014 #endif
1015     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
1016   } else if (e.message_type == otk::Property::atoms.net_close_window) {
1017 #ifdef DEBUG
1018     printf("net_close_window for 0x%lx\n", _window);
1019 #endif
1020     close();
1021   } else if (e.message_type == otk::Property::atoms.net_active_window) {
1022 #ifdef DEBUG
1023     printf("net_active_window for 0x%lx\n", _window);
1024 #endif
1025     if (_iconic)
1026       setDesktop(openbox->screen(_screen)->desktop());
1027     if (_shaded)
1028       shade(false);
1029     // XXX: deiconify
1030     focus();
1031     openbox->screen(_screen)->raiseWindow(this);
1032   }
1033 }
1034
1035
1036 #if defined(SHAPE)
1037 void Client::shapeHandler(const XShapeEvent &e)
1038 {
1039   otk::EventHandler::shapeHandler(e);
1040
1041   if (e.kind == ShapeBounding) {
1042     _shaped = e.shaped;
1043     frame->adjustShape();
1044   }
1045 }
1046 #endif
1047
1048
1049 void Client::resize(Corner anchor, int w, int h)
1050 {
1051   if (!(_functions & Func_Resize)) return;
1052   internal_resize(anchor, w, h);
1053 }
1054
1055
1056 void Client::internal_resize(Corner anchor, int w, int h, bool user,
1057                              int x, int y)
1058 {
1059   w -= _base_size.x(); 
1060   h -= _base_size.y();
1061
1062   // for interactive resizing. have to move half an increment in each
1063   // direction.
1064   w += _size_inc.x() / 2;
1065   h += _size_inc.y() / 2;
1066
1067   if (user) {
1068     // if this is a user-requested resize, then check against min/max sizes
1069     // and aspect ratios
1070
1071     // smaller than min size or bigger than max size?
1072     if (w < _min_size.x()) w = _min_size.x();
1073     else if (w > _max_size.x()) w = _max_size.x();
1074     if (h < _min_size.y()) h = _min_size.y();
1075     else if (h > _max_size.y()) h = _max_size.y();
1076
1077     // adjust the height ot match the width for the aspect ratios
1078     if (_min_ratio)
1079       if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
1080     if (_max_ratio)
1081       if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
1082   }
1083
1084   // keep to the increments
1085   w /= _size_inc.x();
1086   h /= _size_inc.y();
1087
1088   // you cannot resize to nothing
1089   if (w < 1) w = 1;
1090   if (h < 1) h = 1;
1091   
1092   // store the logical size
1093   _logical_size.setPoint(w, h);
1094
1095   w *= _size_inc.x();
1096   h *= _size_inc.y();
1097
1098   w += _base_size.x();
1099   h += _base_size.y();
1100
1101   if (x == INT_MIN || y == INT_MIN) {
1102     x = _area.x();
1103     y = _area.y();
1104     switch (anchor) {
1105     case TopLeft:
1106       break;
1107     case TopRight:
1108       x -= w - _area.width();
1109       break;
1110     case BottomLeft:
1111       y -= h - _area.height();
1112       break;
1113     case BottomRight:
1114       x -= w - _area.width();
1115       y -= h - _area.height();
1116       break;
1117     }
1118   }
1119
1120   _area.setSize(w, h);
1121
1122   XResizeWindow(**otk::display, _window, w, h);
1123
1124   // resize the frame to match the request
1125   frame->adjustSize();
1126   internal_move(x, y);
1127 }
1128
1129
1130 void Client::move(int x, int y)
1131 {
1132   if (!(_functions & Func_Move)) return;
1133   internal_move(x, y);
1134 }
1135
1136
1137 void Client::internal_move(int x, int y)
1138 {
1139   _area.setPos(x, y);
1140
1141   // move the frame to be in the requested position
1142   if (frame) { // this can be called while mapping, before frame exists
1143     frame->adjustPosition();
1144
1145     // send synthetic configure notify (we don't need to if we aren't mapped
1146     // yet)
1147     XEvent event;
1148     event.type = ConfigureNotify;
1149     event.xconfigure.display = **otk::display;
1150     event.xconfigure.event = _window;
1151     event.xconfigure.window = _window;
1152     
1153     // root window coords with border in mind
1154     event.xconfigure.x = x - _border_width + frame->size().left;
1155     event.xconfigure.y = y - _border_width + frame->size().top;
1156     
1157     event.xconfigure.width = _area.width();
1158     event.xconfigure.height = _area.height();
1159     event.xconfigure.border_width = _border_width;
1160     event.xconfigure.above = frame->plate();
1161     event.xconfigure.override_redirect = False;
1162     XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1163                StructureNotifyMask, &event);
1164 #if 0//def DEBUG
1165     printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1166            event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
1167            event.xconfigure.height, event.xconfigure.window);
1168 #endif
1169   }
1170 }
1171
1172
1173 void Client::close()
1174 {
1175   XEvent ce;
1176
1177   if (!(_functions & Func_Close)) return;
1178
1179   // XXX: itd be cool to do timeouts and shit here for killing the client's
1180   //      process off
1181   // like... if the window is around after 5 seconds, then the close button
1182   // turns a nice red, and if this function is called again, the client is
1183   // explicitly killed.
1184
1185   ce.xclient.type = ClientMessage;
1186   ce.xclient.message_type =  otk::Property::atoms.wm_protocols;
1187   ce.xclient.display = **otk::display;
1188   ce.xclient.window = _window;
1189   ce.xclient.format = 32;
1190   ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1191   ce.xclient.data.l[1] = CurrentTime;
1192   ce.xclient.data.l[2] = 0l;
1193   ce.xclient.data.l[3] = 0l;
1194   ce.xclient.data.l[4] = 0l;
1195   XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1196 }
1197
1198
1199 void Client::changeState()
1200 {
1201   unsigned long state[2];
1202   state[0] = _wmstate;
1203   state[1] = None;
1204   otk::Property::set(_window, otk::Property::atoms.wm_state,
1205                      otk::Property::atoms.wm_state, state, 2);
1206   
1207   Atom netstate[10];
1208   int num = 0;
1209   if (_modal)
1210     netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1211   if (_shaded)
1212     netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1213   if (_iconic)
1214     netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1215   if (_skip_taskbar)
1216     netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1217   if (_skip_pager)
1218     netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1219   if (_fullscreen)
1220     netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1221   if (_max_vert)
1222     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1223   if (_max_horz)
1224     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1225   if (_above)
1226     netstate[num++] = otk::Property::atoms.net_wm_state_above;
1227   if (_below)
1228     netstate[num++] = otk::Property::atoms.net_wm_state_below;
1229   otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1230                      otk::Property::atoms.atom, netstate, num);
1231
1232   calcLayer();
1233
1234   if (frame)
1235     frame->adjustState();
1236 }
1237
1238
1239 void Client::changeAllowedActions(void)
1240 {
1241   Atom actions[9];
1242   int num = 0;
1243
1244   actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1245
1246   if (_functions & Func_Shade)
1247     actions[num++] = otk::Property::atoms.net_wm_action_shade;
1248   if (_functions & Func_Close)
1249     actions[num++] = otk::Property::atoms.net_wm_action_close;
1250   if (_functions & Func_Move)
1251     actions[num++] = otk::Property::atoms.net_wm_action_move;
1252   if (_functions & Func_Iconify)
1253     actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1254   if (_functions & Func_Resize)
1255     actions[num++] = otk::Property::atoms.net_wm_action_resize;
1256   if (_functions & Func_Fullscreen)
1257     actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1258   if (_functions & Func_Maximize) {
1259     actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1260     actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1261   }
1262
1263   otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1264                      otk::Property::atoms.atom, actions, num);
1265 }
1266
1267
1268 void Client::applyStartupState()
1269 {
1270   // these are in a carefully crafted order..
1271
1272   if (_iconic) {
1273     _iconic = false;
1274     setDesktop(ICONIC_DESKTOP);
1275   }
1276   if (_fullscreen) {
1277     _fullscreen = false;
1278     fullscreen(true);
1279   }
1280   if (_shaded) {
1281     _shaded = false;
1282     shade(true);
1283   }
1284   if (_urgent)
1285     fireUrgent();
1286   
1287   if (_max_vert); // XXX: incomplete
1288   if (_max_horz); // XXX: incomplete
1289
1290   if (_skip_taskbar); // nothing to do for this
1291   if (_skip_pager);   // nothing to do for this
1292   if (_modal);        // nothing to do for this
1293   if (_above);        // nothing to do for this
1294   if (_below);        // nothing to do for this
1295 }
1296
1297
1298 void Client::fireUrgent()
1299 {
1300   // call the python UrgentWindow callbacks
1301   EventData data(_screen, this, EventAction::UrgentWindow, 0);
1302   openbox->bindings()->fireEvent(&data);
1303 }
1304
1305
1306 void Client::shade(bool shade)
1307 {
1308   if (!(_functions & Func_Shade) || // can't
1309       _shaded == shade) return;     // already done
1310
1311   // when we're iconic, don't change the wmstate
1312   if (!_iconic)
1313     _wmstate = shade ? IconicState : NormalState;
1314   _shaded = shade;
1315   changeState();
1316   frame->adjustSize();
1317 }
1318
1319
1320 void Client::fullscreen(bool fs)
1321 {
1322   static FunctionFlags saved_func;
1323   static DecorationFlags saved_decor;
1324   static otk::Rect saved_area;
1325   static otk::Point saved_logical_size;
1326
1327   if (!(_functions & Func_Fullscreen) || // can't
1328       _fullscreen == fs) return;         // already done
1329
1330   _fullscreen = fs;
1331   changeState(); // change the state hints on the client
1332
1333   if (fs) {
1334     // save the functions and remove them
1335     saved_func = _functions;
1336     _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1337     // save the decorations and remove them
1338     saved_decor = _decorations;
1339     _decorations = 0;
1340     // save the area and adjust it (we don't call internal resize here for
1341     // constraints on the size, etc, we just make it fullscreen).
1342     saved_area = _area;
1343     const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1344     _area.setRect(0, 0, info->width(), info->height());
1345     saved_logical_size = _logical_size;
1346     _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
1347                            (info->height() - _base_size.y()) / _size_inc.y());
1348   } else {
1349     _functions = saved_func;
1350     _decorations = saved_decor;
1351     _area = saved_area;
1352     _logical_size = saved_logical_size;
1353   }
1354   
1355   changeAllowedActions();  // based on the new _functions
1356   
1357   frame->adjustSize();     // drop/replace the decor's and resize
1358   frame->adjustPosition(); // get (back) in position!
1359
1360   // raise (back) into our stacking layer
1361   openbox->screen(_screen)->raiseWindow(this);
1362
1363   // try focus us when we go into fullscreen mode
1364   if (fs) focus();
1365 }
1366
1367
1368 void Client::disableDecorations(DecorationFlags flags)
1369 {
1370   _disabled_decorations = flags;
1371   setupDecorAndFunctions();
1372 }
1373
1374
1375 bool Client::focus()
1376 {
1377   // won't try focus if the client doesn't want it, or if the window isn't
1378   // visible on the screen
1379   if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1380
1381   if (_focused) return true;
1382
1383   // do a check to see if the window has already been unmapped or destroyed
1384   // do this intelligently while watching out for unmaps we've generated
1385   // (ignore_unmaps > 0)
1386   XEvent ev;
1387   if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1388     XPutBackEvent(**otk::display, &ev);
1389     return false;
1390   }
1391   while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1392     if (ignore_unmaps) {
1393       unmapHandler(ev.xunmap);
1394     } else {
1395       XPutBackEvent(**otk::display, &ev);
1396       return false;
1397     }
1398   }
1399
1400   if (_can_focus)
1401     XSetInputFocus(**otk::display, _window,
1402                    RevertToNone, CurrentTime);
1403
1404   if (_focus_notify) {
1405     XEvent ce;
1406     ce.xclient.type = ClientMessage;
1407     ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1408     ce.xclient.display = **otk::display;
1409     ce.xclient.window = _window;
1410     ce.xclient.format = 32;
1411     ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1412     ce.xclient.data.l[1] = openbox->lastTime();
1413     ce.xclient.data.l[2] = 0l;
1414     ce.xclient.data.l[3] = 0l;
1415     ce.xclient.data.l[4] = 0l;
1416     XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1417   }
1418
1419   XSync(**otk::display, False);
1420   return true;
1421 }
1422
1423
1424 void Client::unfocus() const
1425 {
1426   if (!_focused) return;
1427
1428   assert(openbox->focusedClient() == this);
1429   openbox->setFocusedClient(0);
1430 }
1431
1432
1433 void Client::focusHandler(const XFocusChangeEvent &e)
1434 {
1435 #ifdef    DEBUG
1436 //  printf("FocusIn for 0x%lx\n", e.window);
1437 #endif // DEBUG
1438   
1439   otk::EventHandler::focusHandler(e);
1440
1441   frame->focus();
1442   _focused = true;
1443
1444   openbox->setFocusedClient(this);
1445 }
1446
1447
1448 void Client::unfocusHandler(const XFocusChangeEvent &e)
1449 {
1450 #ifdef    DEBUG
1451 //  printf("FocusOut for 0x%lx\n", e.window);
1452 #endif // DEBUG
1453   
1454   otk::EventHandler::unfocusHandler(e);
1455
1456   frame->unfocus();
1457   _focused = false;
1458
1459   if (openbox->focusedClient() == this)
1460     openbox->setFocusedClient(0);
1461 }
1462
1463
1464 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1465 {
1466 #ifdef    DEBUG
1467   printf("ConfigureRequest for 0x%lx\n", e.window);
1468 #endif // DEBUG
1469   
1470   otk::EventHandler::configureRequestHandler(e);
1471
1472   // if we are iconic (or shaded (fvwm does this)) ignore the event
1473   if (_iconic || _shaded) return;
1474
1475   if (e.value_mask & CWBorderWidth)
1476     _border_width = e.border_width;
1477
1478   // resize, then move, as specified in the EWMH section 7.7
1479   if (e.value_mask & (CWWidth | CWHeight)) {
1480     int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1481     int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1482
1483     Corner corner;
1484     switch (_gravity) {
1485     case NorthEastGravity:
1486     case EastGravity:
1487       corner = TopRight;
1488       break;
1489     case SouthWestGravity:
1490     case SouthGravity:
1491       corner = BottomLeft;
1492       break;
1493     case SouthEastGravity:
1494       corner = BottomRight;
1495       break;
1496     default:     // NorthWest, Static, etc
1497       corner = TopLeft;
1498     }
1499
1500     // if moving AND resizing ...
1501     if (e.value_mask & (CWX | CWY)) {
1502       int x = (e.value_mask & CWX) ? e.x : _area.x();
1503       int y = (e.value_mask & CWY) ? e.y : _area.y();
1504       internal_resize(corner, w, h, false, x, y);
1505     } else // if JUST resizing...
1506       internal_resize(corner, w, h, false);
1507   } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1508     int x = (e.value_mask & CWX) ? e.x : _area.x();
1509     int y = (e.value_mask & CWY) ? e.y : _area.y();
1510     internal_move(x, y);
1511   }
1512
1513   if (e.value_mask & CWStackMode) {
1514     switch (e.detail) {
1515     case Below:
1516     case BottomIf:
1517       openbox->screen(_screen)->lowerWindow(this);
1518       break;
1519
1520     case Above:
1521     case TopIf:
1522     default:
1523       openbox->screen(_screen)->raiseWindow(this);
1524       break;
1525     }
1526   }
1527 }
1528
1529
1530 void Client::unmapHandler(const XUnmapEvent &e)
1531 {
1532   if (ignore_unmaps) {
1533 #ifdef    DEBUG
1534 //  printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1535 #endif // DEBUG
1536     ignore_unmaps--;
1537     return;
1538   }
1539   
1540 #ifdef    DEBUG
1541   printf("UnmapNotify for 0x%lx\n", e.window);
1542 #endif // DEBUG
1543
1544   otk::EventHandler::unmapHandler(e);
1545
1546   // this deletes us etc
1547   openbox->screen(_screen)->unmanageWindow(this);
1548 }
1549
1550
1551 void Client::destroyHandler(const XDestroyWindowEvent &e)
1552 {
1553 #ifdef    DEBUG
1554   printf("DestroyNotify for 0x%lx\n", e.window);
1555 #endif // DEBUG
1556
1557   otk::EventHandler::destroyHandler(e);
1558
1559   // this deletes us etc
1560   openbox->screen(_screen)->unmanageWindow(this);
1561 }
1562
1563
1564 void Client::reparentHandler(const XReparentEvent &e)
1565 {
1566   // this is when the client is first taken captive in the frame
1567   if (e.parent == frame->plate()) return;
1568
1569 #ifdef    DEBUG
1570   printf("ReparentNotify for 0x%lx\n", e.window);
1571 #endif // DEBUG
1572
1573   otk::EventHandler::reparentHandler(e);
1574
1575   /*
1576     This event is quite rare and is usually handled in unmapHandler.
1577     However, if the window is unmapped when the reparent event occurs,
1578     the window manager never sees it because an unmap event is not sent
1579     to an already unmapped window.
1580   */
1581
1582   // we don't want the reparent event, put it back on the stack for the X
1583   // server to deal with after we unmanage the window
1584   XEvent ev;
1585   ev.xreparent = e;
1586   XPutBackEvent(**otk::display, &ev);
1587   
1588   // this deletes us etc
1589   openbox->screen(_screen)->unmanageWindow(this);
1590 }
1591
1592 void Client::mapRequestHandler(const XMapRequestEvent &e)
1593 {
1594 #ifdef    DEBUG
1595   printf("MapRequest for already managed 0x%lx\n", e.window);
1596 #endif // DEBUG
1597
1598   assert(_iconic); // we shouldn't be able to get this unless we're iconic
1599
1600   // move to the current desktop (uniconify)
1601   setDesktop(openbox->screen(_screen)->desktop());
1602   // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1603 }
1604
1605 }