]> icculus.org git repositories - mikachu/openbox.git/blob - src/client.cc
configure request optimizations
[mikachu/openbox.git] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "client.hh"
8 #include "frame.hh"
9 #include "screen.hh"
10 #include "openbox.hh"
11 #include "otk/display.hh"
12 #include "otk/property.hh"
13
14 extern "C" {
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17
18 #include <assert.h>
19
20 #include "gettext.h"
21 #define _(str) gettext(str)
22 }
23
24 namespace ob {
25
26 OBClient::OBClient(int screen, Window window)
27   : otk::OtkEventHandler(),
28     OBWidget(OBWidget::Type_Client),
29     frame(0), _screen(screen), _window(window)
30 {
31   assert(screen >= 0);
32   assert(window);
33
34   ignore_unmaps = 0;
35   
36   // update EVERYTHING the first time!!
37
38   // the state is kinda assumed to be normal. is this right? XXX
39   _wmstate = NormalState; _iconic = false;
40   // no default decors or functions, each has to be enabled
41   _decorations = _functions = 0;
42   // start unfocused
43   _focused = false;
44   
45   getArea();
46   getDesktop();
47   // XXX: updateTransientFor();
48   getType();
49
50   // set the decorations and functions
51   switch (_type) {
52   case Type_Normal:
53     // normal windows retain all of the possible decorations and
54     // functionality
55     _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
56                    Decor_Iconify | Decor_Maximize;
57     _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
58
59   case Type_Dialog:
60     // dialogs cannot be maximized
61     _decorations &= ~Decor_Maximize;
62     _functions &= ~Func_Maximize;
63     break;
64
65   case Type_Menu:
66   case Type_Toolbar:
67   case Type_Utility:
68     // these windows get less functionality
69     _decorations &= ~(Decor_Iconify | Decor_Handle);
70     _functions &= ~(Func_Iconify | Func_Resize);
71     break;
72
73   case Type_Desktop:
74   case Type_Dock:
75   case Type_Splash:
76     // none of these windows are manipulated by the window manager
77     _decorations = 0;
78     _functions = 0;
79     break;
80   }
81   
82   getMwmHints(); // this fucks (in good ways) with the decors and functions
83   getState();
84   getShaped();
85
86   updateProtocols();
87   updateNormalHints();
88   updateWMHints();
89   updateTitle();
90   updateIconTitle();
91   updateClass();
92
93   calcLayer();
94   changeState();
95 }
96
97
98 OBClient::~OBClient()
99 {
100   const otk::OBProperty *property = Openbox::instance->property();
101
102   if (Openbox::instance->state() != Openbox::State_Exiting) {
103     // these values should not be persisted across a window unmapping/mapping
104     property->erase(_window, otk::OBProperty::net_wm_desktop);
105     property->erase(_window, otk::OBProperty::net_wm_state);
106   }
107 }
108
109
110 void OBClient::getDesktop()
111 {
112   const otk::OBProperty *property = Openbox::instance->property();
113
114   // defaults to the current desktop
115   _desktop = 0; // XXX: change this to the current desktop!
116
117   property->get(_window, otk::OBProperty::net_wm_desktop,
118                 otk::OBProperty::Atom_Cardinal,
119                 &_desktop);
120 }
121
122
123 void OBClient::getType()
124 {
125   const otk::OBProperty *property = Openbox::instance->property();
126
127   _type = (WindowType) -1;
128   
129   unsigned long *val;
130   unsigned long num = (unsigned) -1;
131   if (property->get(_window, otk::OBProperty::net_wm_window_type,
132                     otk::OBProperty::Atom_Atom,
133                     &num, &val)) {
134     // use the first value that we know about in the array
135     for (unsigned long i = 0; i < num; ++i) {
136       if (val[i] ==
137           property->atom(otk::OBProperty::net_wm_window_type_desktop))
138         _type = Type_Desktop;
139       else if (val[i] ==
140                property->atom(otk::OBProperty::net_wm_window_type_dock))
141         _type = Type_Dock;
142       else if (val[i] ==
143                property->atom(otk::OBProperty::net_wm_window_type_toolbar))
144         _type = Type_Toolbar;
145       else if (val[i] ==
146                property->atom(otk::OBProperty::net_wm_window_type_menu))
147         _type = Type_Menu;
148       else if (val[i] ==
149                property->atom(otk::OBProperty::net_wm_window_type_utility))
150         _type = Type_Utility;
151       else if (val[i] ==
152                property->atom(otk::OBProperty::net_wm_window_type_splash))
153         _type = Type_Splash;
154       else if (val[i] ==
155                property->atom(otk::OBProperty::net_wm_window_type_dialog))
156         _type = Type_Dialog;
157       else if (val[i] ==
158                property->atom(otk::OBProperty::net_wm_window_type_normal))
159         _type = Type_Normal;
160 //      else if (val[i] ==
161 //               property->atom(otk::OBProperty::kde_net_wm_window_type_override))
162 //        mwm_decorations = 0; // prevent this window from getting any decor
163       // XXX: make this work again
164     }
165     delete val;
166   }
167     
168   if (_type == (WindowType) -1) {
169     /*
170      * the window type hint was not set, which means we either classify ourself
171      * as a normal window or a dialog, depending on if we are a transient.
172      */
173     // XXX: make this code work!
174     //if (isTransient())
175     //  _type = Type_Dialog;
176     //else
177       _type = Type_Normal;
178   }
179 }
180
181
182 void OBClient::getMwmHints()
183 {
184   const otk::OBProperty *property = Openbox::instance->property();
185
186   unsigned long num;
187   MwmHints *hints;
188
189   num = MwmHints::elements;
190   if (!property->get(_window, otk::OBProperty::motif_wm_hints,
191                      otk::OBProperty::motif_wm_hints, &num,
192                      (unsigned long **)&hints))
193     return;
194   
195   if (num < MwmHints::elements) {
196     delete [] hints;
197     return;
198   }
199
200   // retrieved the hints
201   // Mwm Hints are applied subtractively to what has already been chosen for
202   // decor and functionality
203
204   if (hints->flags & MwmFlag_Decorations) {
205     if (! (hints->decorations & MwmDecor_All)) {
206       if (! (hints->decorations & MwmDecor_Border))
207         _decorations &= ~Decor_Border;
208       if (! (hints->decorations & MwmDecor_Handle))
209         _decorations &= ~Decor_Handle;
210       if (! (hints->decorations & MwmDecor_Title))
211         _decorations &= ~Decor_Titlebar;
212       if (! (hints->decorations & MwmDecor_Iconify))
213         _decorations &= ~Decor_Iconify;
214       if (! (hints->decorations & MwmDecor_Maximize))
215         _decorations &= ~Decor_Maximize;
216     }
217   }
218
219   if (hints->flags & MwmFlag_Functions) {
220     if (! (hints->functions & MwmFunc_All)) {
221       if (! (hints->functions & MwmFunc_Resize))
222         _functions &= ~Func_Resize;
223       if (! (hints->functions & MwmFunc_Move))
224         _functions &= ~Func_Move;
225       if (! (hints->functions & MwmFunc_Iconify))
226         _functions &= ~Func_Iconify;
227       if (! (hints->functions & MwmFunc_Maximize))
228         _functions &= ~Func_Maximize;
229       //if (! (hints->functions & MwmFunc_Close))
230       //  _functions &= ~Func_Close;
231     }
232   }
233   delete [] hints;
234 }
235
236
237 void OBClient::getArea()
238 {
239   XWindowAttributes wattrib;
240   Status ret;
241   
242   ret = XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib);
243   assert(ret != BadWindow);
244
245   _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
246   _border_width = wattrib.border_width;
247 }
248
249
250 void OBClient::getState()
251 {
252   const otk::OBProperty *property = Openbox::instance->property();
253
254   _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
255     _skip_taskbar = _skip_pager = false;
256   
257   unsigned long *state;
258   unsigned long num = (unsigned) -1;
259   
260   if (property->get(_window, otk::OBProperty::net_wm_state,
261                     otk::OBProperty::Atom_Atom, &num, &state)) {
262     for (unsigned long i = 0; i < num; ++i) {
263       if (state[i] == property->atom(otk::OBProperty::net_wm_state_modal))
264         _modal = true;
265       else if (state[i] ==
266                property->atom(otk::OBProperty::net_wm_state_shaded))
267         _shaded = true;
268       else if (state[i] ==
269                property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
270         _skip_taskbar = true;
271       else if (state[i] ==
272                property->atom(otk::OBProperty::net_wm_state_skip_pager))
273         _skip_pager = true;
274       else if (state[i] ==
275                property->atom(otk::OBProperty::net_wm_state_fullscreen))
276         _fullscreen = true;
277       else if (state[i] ==
278                property->atom(otk::OBProperty::net_wm_state_maximized_vert))
279         _max_vert = true;
280       else if (state[i] ==
281                property->atom(otk::OBProperty::net_wm_state_maximized_horz))
282         _max_horz = true;
283       else if (state[i] ==
284                property->atom(otk::OBProperty::net_wm_state_above))
285         _above = true;
286       else if (state[i] ==
287                property->atom(otk::OBProperty::net_wm_state_below))
288         _below = true;
289     }
290
291     delete [] state;
292   }
293 }
294
295
296 void OBClient::getShaped()
297 {
298   _shaped = false;
299 #ifdef   SHAPE
300   if (otk::OBDisplay::shape()) {
301     int foo;
302     unsigned int ufoo;
303     int s;
304
305     XShapeSelectInput(otk::OBDisplay::display, _window, ShapeNotifyMask);
306
307     XShapeQueryExtents(otk::OBDisplay::display, _window, &s, &foo,
308                        &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
309     _shaped = (s != 0);
310   }
311 #endif // SHAPE
312 }
313
314
315 void OBClient::calcLayer() {
316   if (_iconic) _layer = OBScreen::Layer_Icon;
317   else if (_type == Type_Desktop) _layer = OBScreen::Layer_Desktop;
318   else if (_type == Type_Dock) _layer = OBScreen::Layer_Top;
319   else if (_fullscreen) _layer = OBScreen::Layer_Fullscreen;
320   else if (_above) _layer = OBScreen::Layer_Above;
321   else if (_below) _layer = OBScreen::Layer_Below;
322   else _layer = OBScreen::Layer_Normal;
323 }
324
325
326 void OBClient::updateProtocols()
327 {
328   const otk::OBProperty *property = Openbox::instance->property();
329
330   Atom *proto;
331   int num_return = 0;
332
333   _focus_notify = false;
334   _decorations &= ~Decor_Close;
335   _functions &= ~Func_Close;
336
337   if (XGetWMProtocols(otk::OBDisplay::display, _window, &proto, &num_return)) {
338     for (int i = 0; i < num_return; ++i) {
339       if (proto[i] == property->atom(otk::OBProperty::wm_delete_window)) {
340         _decorations |= Decor_Close;
341         _functions |= Func_Close;
342         if (frame)
343           frame->adjustSize(); // update the decorations
344       } else if (proto[i] == property->atom(otk::OBProperty::wm_take_focus))
345         // if this protocol is requested, then the window will be notified
346         // by the window manager whenever it receives focus
347         _focus_notify = true;
348     }
349     XFree(proto);
350   }
351 }
352
353
354 void OBClient::updateNormalHints()
355 {
356   XSizeHints size;
357   long ret;
358   int oldgravity = _gravity;
359
360   // defaults
361   _gravity = NorthWestGravity;
362   _size_inc.setPoint(1, 1);
363   _base_size.setPoint(0, 0);
364   _min_size.setPoint(0, 0);
365   _max_size.setPoint(INT_MAX, INT_MAX);
366
367   // XXX: might want to cancel any interactive resizing of the window at this
368   // point..
369
370   // get the hints from the window
371   if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
372     _positioned = (size.flags & (PPosition|USPosition));
373
374     if (size.flags & PWinGravity)
375       _gravity = size.win_gravity;
376
377     if (size.flags & PMinSize)
378       _min_size.setPoint(size.min_width, size.min_height);
379     
380     if (size.flags & PMaxSize)
381       _max_size.setPoint(size.max_width, size.max_height);
382     
383     if (size.flags & PBaseSize)
384       _base_size.setPoint(size.base_width, size.base_height);
385     
386     if (size.flags & PResizeInc)
387       _size_inc.setPoint(size.width_inc, size.height_inc);
388   }
389
390   // if the client has a frame, i.e. has already been mapped and is
391   // changing its gravity
392   if (frame && _gravity != oldgravity) {
393     // move our idea of the client's position based on its new gravity
394     int x, y;
395     frame->frameGravity(x, y);
396     _area.setPos(x, y);
397   }
398 }
399
400
401 void OBClient::updateWMHints()
402 {
403   XWMHints *hints;
404
405   // assume a window takes input if it doesnt specify
406   _can_focus = true;
407   _urgent = false;
408   
409   if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
410     if (hints->flags & InputHint)
411       _can_focus = hints->input;
412
413     if (hints->flags & XUrgencyHint)
414       _urgent = true;
415
416     if (hints->flags & WindowGroupHint) {
417       if (hints->window_group != _group) {
418         // XXX: remove from the old group if there was one
419         _group = hints->window_group;
420         // XXX: do stuff with the group
421       }
422     } else // no group!
423       _group = None;
424
425     XFree(hints);
426   }
427 }
428
429
430 void OBClient::updateTitle()
431 {
432   const otk::OBProperty *property = Openbox::instance->property();
433
434   _title = "";
435   
436   // try netwm
437   if (! property->get(_window, otk::OBProperty::net_wm_name,
438                       otk::OBProperty::utf8, &_title)) {
439     // try old x stuff
440     property->get(_window, otk::OBProperty::wm_name,
441                   otk::OBProperty::ascii, &_title);
442   }
443
444   if (_title.empty())
445     _title = _("Unnamed Window");
446
447   if (frame)
448     frame->setTitle(_title);
449 }
450
451
452 void OBClient::updateIconTitle()
453 {
454   const otk::OBProperty *property = Openbox::instance->property();
455
456   _icon_title = "";
457   
458   // try netwm
459   if (! property->get(_window, otk::OBProperty::net_wm_icon_name,
460                       otk::OBProperty::utf8, &_icon_title)) {
461     // try old x stuff
462     property->get(_window, otk::OBProperty::wm_icon_name,
463                   otk::OBProperty::ascii, &_icon_title);
464   }
465
466   if (_title.empty())
467     _icon_title = _("Unnamed Window");
468 }
469
470
471 void OBClient::updateClass()
472 {
473   const otk::OBProperty *property = Openbox::instance->property();
474
475   // set the defaults
476   _app_name = _app_class = "";
477
478   otk::OBProperty::StringVect v;
479   unsigned long num = 2;
480
481   if (! property->get(_window, otk::OBProperty::wm_class,
482                       otk::OBProperty::ascii, &num, &v))
483     return;
484
485   if (num > 0) _app_name = v[0];
486   if (num > 1) _app_class = v[1];
487 }
488
489
490 void OBClient::propertyHandler(const XPropertyEvent &e)
491 {
492   otk::OtkEventHandler::propertyHandler(e);
493   
494   const otk::OBProperty *property = Openbox::instance->property();
495
496   // compress changes to a single property into a single change
497   XEvent ce;
498   while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
499     // XXX: it would be nice to compress ALL changes to a property, not just
500     //      changes in a row without other props between.
501     if (ce.xproperty.atom != e.atom) {
502       XPutBackEvent(otk::OBDisplay::display, &ce);
503       break;
504     }
505   }
506
507   if (e.atom == XA_WM_NORMAL_HINTS)
508     updateNormalHints();
509   else if (e.atom == XA_WM_HINTS)
510     updateWMHints();
511   else if (e.atom == property->atom(otk::OBProperty::net_wm_name) ||
512            e.atom == property->atom(otk::OBProperty::wm_name))
513     updateTitle();
514   else if (e.atom == property->atom(otk::OBProperty::net_wm_icon_name) ||
515            e.atom == property->atom(otk::OBProperty::wm_icon_name))
516     updateIconTitle();
517   else if (e.atom == property->atom(otk::OBProperty::wm_class))
518     updateClass();
519   else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
520     updateProtocols();
521   // XXX: transient for hint
522   // XXX: strut hint
523 }
524
525
526 void OBClient::setWMState(long state)
527 {
528   if (state == _wmstate) return; // no change
529   
530   switch (state) {
531   case IconicState:
532     // XXX: cause it to iconify
533     break;
534   case NormalState:
535     // XXX: cause it to uniconify
536     break;
537   }
538   _wmstate = state;
539 }
540
541
542 void OBClient::setDesktop(long target)
543 {
544   printf("Setting desktop %ld\n", target);
545   assert(target >= 0 || target == (signed)0xffffffff);
546   //assert(target == 0xffffffff || target < MAX);
547
548   // XXX: move the window to the new desktop (and set root property)
549   _desktop = target;
550 }
551
552
553 void OBClient::setState(StateAction action, long data1, long data2)
554 {
555   const otk::OBProperty *property = Openbox::instance->property();
556   bool restack = false, shadestate = _shaded;
557
558   if (!(action == State_Add || action == State_Remove ||
559         action == State_Toggle))
560     return; // an invalid action was passed to the client message, ignore it
561
562   for (int i = 0; i < 2; ++i) {
563     Atom state = i == 0 ? data1 : data2;
564     
565     if (! state) continue;
566
567     // if toggling, then pick whether we're adding or removing
568     if (action == State_Toggle) {
569       if (state == property->atom(otk::OBProperty::net_wm_state_modal))
570         action = _modal ? State_Remove : State_Add;
571       else if (state ==
572                property->atom(otk::OBProperty::net_wm_state_maximized_vert))
573         action = _max_vert ? State_Remove : State_Add;
574       else if (state ==
575                property->atom(otk::OBProperty::net_wm_state_maximized_horz))
576         action = _max_horz ? State_Remove : State_Add;
577       else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
578         action = _shaded ? State_Remove : State_Add;
579       else if (state ==
580                property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
581         action = _skip_taskbar ? State_Remove : State_Add;
582       else if (state ==
583                property->atom(otk::OBProperty::net_wm_state_skip_pager))
584         action = _skip_pager ? State_Remove : State_Add;
585       else if (state ==
586                property->atom(otk::OBProperty::net_wm_state_fullscreen))
587         action = _fullscreen ? State_Remove : State_Add;
588       else if (state == property->atom(otk::OBProperty::net_wm_state_above))
589         action = _above ? State_Remove : State_Add;
590       else if (state == property->atom(otk::OBProperty::net_wm_state_below))
591         action = _below ? State_Remove : State_Add;
592     }
593     
594     if (action == State_Add) {
595       if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
596         if (_modal) continue;
597         _modal = true;
598         // XXX: give it focus if another window has focus that shouldnt now
599       } else if (state ==
600                  property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
601         if (_max_vert) continue;
602         _max_vert = true;
603         // XXX: resize the window etc
604       } else if (state ==
605                  property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
606         if (_max_horz) continue;
607         _max_horz = true;
608         // XXX: resize the window etc
609       } else if (state ==
610                  property->atom(otk::OBProperty::net_wm_state_shaded)) {
611         if (_shaded) continue;
612         // shade when we're all thru here
613         shadestate = true;
614       } else if (state ==
615                  property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
616         _skip_taskbar = true;
617       } else if (state ==
618                  property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
619         _skip_pager = true;
620       } else if (state ==
621                  property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
622         if (_fullscreen) continue;
623         _fullscreen = true;
624         restack = false;
625       } else if (state ==
626                  property->atom(otk::OBProperty::net_wm_state_above)) {
627         if (_above) continue;
628         _above = true;
629         restack = true;
630       } else if (state ==
631                  property->atom(otk::OBProperty::net_wm_state_below)) {
632         if (_below) continue;
633         _below = true;
634         restack = true;
635       }
636
637     } else { // action == State_Remove
638       if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
639         if (!_modal) continue;
640         _modal = false;
641       } else if (state ==
642                  property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
643         if (!_max_vert) continue;
644         _max_vert = false;
645         // XXX: resize the window etc
646       } else if (state ==
647                  property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
648         if (!_max_horz) continue;
649         _max_horz = false;
650         // XXX: resize the window etc
651       } else if (state ==
652                  property->atom(otk::OBProperty::net_wm_state_shaded)) {
653         if (!_shaded) continue;
654         // unshade when we're all thru here
655         shadestate = false;
656       } else if (state ==
657                  property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
658         _skip_taskbar = false;
659       } else if (state ==
660                  property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
661         _skip_pager = false;
662       } else if (state ==
663                  property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
664         if (!_fullscreen) continue;
665         _fullscreen = false;
666         restack = true;
667       } else if (state ==
668                  property->atom(otk::OBProperty::net_wm_state_above)) {
669         if (!_above) continue;
670         _above = false;
671         restack = true;
672       } else if (state ==
673                  property->atom(otk::OBProperty::net_wm_state_below)) {
674         if (!_below) continue;
675         _below = false;
676         restack = true;
677       }
678     }
679   }
680   if (shadestate != _shaded)
681     shade(shadestate);
682   if (restack) {
683     calcLayer();
684     Openbox::instance->screen(_screen)->restack(true, this); // raise
685   }
686 }
687
688
689 void OBClient::toggleClientBorder(bool addborder)
690 {
691   // adjust our idea of where the client is, based on its border. When the
692   // border is removed, the client should now be considered to be in a
693   // different position.
694   // when re-adding the border to the client, the same operation needs to be
695   // reversed.
696   int x = _area.x(), y = _area.y();
697   switch(_gravity) {
698   case NorthWestGravity:
699   case WestGravity:
700   case SouthWestGravity:
701     break;
702   case NorthEastGravity:
703   case EastGravity:
704   case SouthEastGravity:
705     if (addborder) x -= _border_width * 2;
706     else           x += _border_width * 2;
707     break;
708   }
709   switch(_gravity) {
710   case NorthWestGravity:
711   case NorthGravity:
712   case NorthEastGravity:
713     break;
714   case SouthWestGravity:
715   case SouthGravity:
716   case SouthEastGravity:
717     if (addborder) y -= _border_width * 2;
718     else           y += _border_width * 2;
719     break;
720   default:
721     // no change for StaticGravity etc.
722     break;
723   }
724   _area.setPos(x, y);
725
726   if (addborder) {
727     XSetWindowBorderWidth(otk::OBDisplay::display, _window, _border_width);
728
729     // move the client so it is back it the right spot _with_ its border!
730     XMoveWindow(otk::OBDisplay::display, _window, x, y);
731   } else
732     XSetWindowBorderWidth(otk::OBDisplay::display, _window, 0);
733 }
734
735
736 void OBClient::clientMessageHandler(const XClientMessageEvent &e)
737 {
738   otk::OtkEventHandler::clientMessageHandler(e);
739   
740   if (e.format != 32) return;
741
742   const otk::OBProperty *property = Openbox::instance->property();
743   
744   if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) {
745     // compress changes into a single change
746     bool compress = false;
747     XEvent ce;
748     while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
749       // XXX: it would be nice to compress ALL messages of a type, not just
750       //      messages in a row without other message types between.
751       if (ce.xclient.message_type != e.message_type) {
752         XPutBackEvent(otk::OBDisplay::display, &ce);
753         break;
754       }
755       compress = true;
756     }
757     if (compress)
758       setWMState(ce.xclient.data.l[0]); // use the found event
759     else
760       setWMState(e.data.l[0]); // use the original event
761   } else if (e.message_type ==
762              property->atom(otk::OBProperty::net_wm_desktop)) {
763     // compress changes into a single change 
764     bool compress = false;
765     XEvent ce;
766     while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
767       // XXX: it would be nice to compress ALL messages of a type, not just
768       //      messages in a row without other message types between.
769       if (ce.xclient.message_type != e.message_type) {
770         XPutBackEvent(otk::OBDisplay::display, &ce);
771         break;
772       }
773       compress = true;
774     }
775     if (compress)
776       setDesktop(e.data.l[0]); // use the found event
777     else
778       setDesktop(e.data.l[0]); // use the original event
779   } else if (e.message_type == property->atom(otk::OBProperty::net_wm_state)) {
780     // can't compress these
781     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
782   } else if (e.message_type ==
783              property->atom(otk::OBProperty::net_close_window)) {
784     close();
785   } else if (e.message_type ==
786              property->atom(otk::OBProperty::net_active_window)) {
787     focus();
788     Openbox::instance->screen(_screen)->restack(true, this); // raise
789   } else {
790   }
791 }
792
793
794 #if defined(SHAPE)
795 void OBClient::shapeHandler(const XShapeEvent &e)
796 {
797   otk::OtkEventHandler::shapeHandler(e);
798   
799   _shaped = e.shaped;
800   frame->adjustShape();
801 }
802 #endif
803
804
805 void OBClient::resize(Corner anchor, int w, int h, int x, int y)
806 {
807   w -= _base_size.x(); 
808   h -= _base_size.y();
809
810   // for interactive resizing. have to move half an increment in each
811   // direction.
812   w += _size_inc.x() / 2;
813   h += _size_inc.y() / 2;
814
815   // is the window resizable? if it is not, then don't check its sizes, the
816   // client can do what it wants and the user can't change it anyhow
817   if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
818     // smaller than min size or bigger than max size?
819     if (w < _min_size.x()) w = _min_size.x();
820     else if (w > _max_size.x()) w = _max_size.x();
821     if (h < _min_size.y()) h = _min_size.y();
822     else if (h > _max_size.y()) h = _max_size.y();
823   }
824
825   // keep to the increments
826   w /= _size_inc.x();
827   h /= _size_inc.y();
828
829   // store the logical size
830   _logical_size.setPoint(w, h);
831
832   w *= _size_inc.x();
833   h *= _size_inc.y();
834
835   w += _base_size.x();
836   h += _base_size.y();
837
838   if (x == INT_MIN || y == INT_MIN) {
839     x = _area.x();
840     y = _area.y();
841     switch (anchor) {
842     case TopLeft:
843       break;
844     case TopRight:
845       x -= w - _area.width();
846       break;
847     case BottomLeft:
848       y -= h - _area.height();
849       break;
850     case BottomRight:
851       x -= w - _area.width();
852       y -= h - _area.height();
853       break;
854     }
855   }
856
857   _area.setSize(w, h);
858
859   XResizeWindow(otk::OBDisplay::display, _window, w, h);
860
861   // resize the frame to match the request
862   frame->adjustSize();
863   move(x, y);
864 }
865
866
867 void OBClient::move(int x, int y)
868 {
869   _area.setPos(x, y);
870
871   // move the frame to be in the requested position
872   frame->adjustPosition();
873 }
874
875
876 void OBClient::close()
877 {
878   XEvent ce;
879   const otk::OBProperty *property = Openbox::instance->property();
880
881   if (!(_functions & Func_Close)) return;
882
883   // XXX: itd be cool to do timeouts and shit here for killing the client's
884   //      process off
885
886   ce.xclient.type = ClientMessage;
887   ce.xclient.message_type =  property->atom(otk::OBProperty::wm_protocols);
888   ce.xclient.display = otk::OBDisplay::display;
889   ce.xclient.window = _window;
890   ce.xclient.format = 32;
891   ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_delete_window);
892   ce.xclient.data.l[1] = CurrentTime;
893   ce.xclient.data.l[2] = 0l;
894   ce.xclient.data.l[3] = 0l;
895   ce.xclient.data.l[4] = 0l;
896   XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
897 }
898
899
900 void OBClient::changeState()
901 {
902   const otk::OBProperty *property = Openbox::instance->property();
903
904   unsigned long state[2];
905   state[0] = _wmstate;
906   state[1] = None;
907   property->set(_window, otk::OBProperty::wm_state, otk::OBProperty::wm_state,
908                 state, 2);
909   
910   Atom netstate[10];
911   int num = 0;
912   if (_modal)
913     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_modal);
914   if (_shaded)
915     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_shaded);
916   if (_iconic)
917     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_hidden);
918   if (_skip_taskbar)
919     netstate[num++] =
920       property->atom(otk::OBProperty::net_wm_state_skip_taskbar);
921   if (_skip_pager)
922     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_skip_pager);
923   if (_fullscreen)
924     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_fullscreen);
925   if (_max_vert)
926     netstate[num++] =
927       property->atom(otk::OBProperty::net_wm_state_maximized_vert);
928   if (_max_horz)
929     netstate[num++] =
930       property->atom(otk::OBProperty::net_wm_state_maximized_horz);
931   if (_above)
932     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_above);
933   if (_below)
934     netstate[num++] = property->atom(otk::OBProperty::net_wm_state_below);
935   property->set(_window, otk::OBProperty::net_wm_state,
936                 otk::OBProperty::Atom_Atom, netstate, num);
937   
938 }
939
940 void OBClient::shade(bool shade)
941 {
942   if (shade == _shaded) return; // already done
943
944   _wmstate = shade ? IconicState : NormalState;
945   _shaded = shade;
946   changeState();
947   frame->adjustSize();
948 }
949
950
951 bool OBClient::focus()
952 {
953   if (!(_can_focus || _focus_notify) || _focused) return false;
954
955   if (_can_focus)
956     XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
957
958   if (_focus_notify) {
959     XEvent ce;
960     const otk::OBProperty *property = Openbox::instance->property();
961     
962     ce.xclient.type = ClientMessage;
963     ce.xclient.message_type =  property->atom(otk::OBProperty::wm_protocols);
964     ce.xclient.display = otk::OBDisplay::display;
965     ce.xclient.window = _window;
966     ce.xclient.format = 32;
967     ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_take_focus);
968     ce.xclient.data.l[1] = Openbox::instance->lastTime();
969     ce.xclient.data.l[2] = 0l;
970     ce.xclient.data.l[3] = 0l;
971     ce.xclient.data.l[4] = 0l;
972     XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
973   }
974
975   return true;
976 }
977
978
979 void OBClient::unfocus()
980 {
981   if (!_focused) return;
982
983   assert(Openbox::instance->focusedClient() == this);
984   Openbox::instance->setFocusedClient(0);
985 }
986
987
988 void OBClient::focusHandler(const XFocusChangeEvent &e)
989 {
990 #ifdef    DEBUG
991   printf("FocusIn for 0x%lx\n", e.window);
992 #endif // DEBUG
993   
994   OtkEventHandler::focusHandler(e);
995
996   frame->focus();
997   _focused = true;
998
999   Openbox::instance->setFocusedClient(this);
1000 }
1001
1002
1003 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
1004 {
1005 #ifdef    DEBUG
1006   printf("FocusOut for 0x%lx\n", e.window);
1007 #endif // DEBUG
1008   
1009   OtkEventHandler::unfocusHandler(e);
1010
1011   frame->unfocus();
1012   _focused = false;
1013
1014   if (Openbox::instance->focusedClient() == this) {
1015     printf("UNFOCUSED!\n");
1016     Openbox::instance->setFocusedClient(this);
1017   }
1018 }
1019
1020
1021 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
1022 {
1023 #ifdef    DEBUG
1024   printf("ConfigureRequest for 0x%lx\n", e.window);
1025 #endif // DEBUG
1026   
1027   OtkEventHandler::configureRequestHandler(e);
1028
1029   // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1030
1031   if (e.value_mask & CWBorderWidth)
1032     _border_width = e.border_width;
1033
1034   // resize, then move, as specified in the EWMH section 7.7
1035   if (e.value_mask & (CWWidth | CWHeight)) {
1036     int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1037     int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1038
1039     Corner corner;
1040     switch (_gravity) {
1041     case NorthEastGravity:
1042     case EastGravity:
1043       corner = TopRight;
1044       break;
1045     case SouthWestGravity:
1046     case SouthGravity:
1047       corner = BottomLeft;
1048       break;
1049     case SouthEastGravity:
1050       corner = BottomRight;
1051       break;
1052     default:     // NorthWest, Static, etc
1053       corner = TopLeft;
1054     }
1055
1056     // if moving AND resizing ...
1057     if (e.value_mask & (CWX | CWY)) {
1058       int x = (e.value_mask & CWX) ? e.x : _area.x();
1059       int y = (e.value_mask & CWY) ? e.y : _area.y();
1060       resize(corner, w, h, x, y);
1061     } else // if JUST resizing...
1062       resize(corner, w, h);
1063   } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1064     int x = (e.value_mask & CWX) ? e.x : _area.x();
1065     int y = (e.value_mask & CWY) ? e.y : _area.y();
1066     move(x, y);
1067   }
1068
1069   if (e.value_mask & CWStackMode) {
1070     switch (e.detail) {
1071     case Below:
1072     case BottomIf:
1073       // XXX: lower the window
1074       break;
1075
1076     case Above:
1077     case TopIf:
1078     default:
1079       // XXX: raise the window
1080       break;
1081     }
1082   }
1083 }
1084
1085
1086 void OBClient::unmapHandler(const XUnmapEvent &e)
1087 {
1088 #ifdef    DEBUG
1089   printf("UnmapNotify for 0x%lx\n", e.window);
1090 #endif // DEBUG
1091
1092   if (ignore_unmaps) {
1093     ignore_unmaps--;
1094     return;
1095   }
1096   
1097   OtkEventHandler::unmapHandler(e);
1098
1099   // this deletes us etc
1100   Openbox::instance->screen(_screen)->unmanageWindow(this);
1101 }
1102
1103
1104 void OBClient::destroyHandler(const XDestroyWindowEvent &e)
1105 {
1106 #ifdef    DEBUG
1107   printf("DestroyNotify for 0x%lx\n", e.window);
1108 #endif // DEBUG
1109
1110   OtkEventHandler::destroyHandler(e);
1111
1112   // this deletes us etc
1113   Openbox::instance->screen(_screen)->unmanageWindow(this);
1114 }
1115
1116
1117 void OBClient::reparentHandler(const XReparentEvent &e)
1118 {
1119   // this is when the client is first taken captive in the frame
1120   if (e.parent == frame->plate()) return;
1121
1122 #ifdef    DEBUG
1123   printf("ReparentNotify for 0x%lx\n", e.window);
1124 #endif // DEBUG
1125
1126   OtkEventHandler::reparentHandler(e);
1127
1128   /*
1129     This event is quite rare and is usually handled in unmapHandler.
1130     However, if the window is unmapped when the reparent event occurs,
1131     the window manager never sees it because an unmap event is not sent
1132     to an already unmapped window.
1133   */
1134
1135   // this deletes us etc
1136   Openbox::instance->screen(_screen)->unmanageWindow(this);
1137 }
1138
1139 }