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