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