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