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