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