]> icculus.org git repositories - mikachu/openbox.git/blob - src/client.cc
handle configure requests
[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 "bbscreen.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     _screen(screen), _window(window)
29 {
30   assert(screen >= 0);
31   assert(window);
32
33   Openbox::instance->registerHandler(_window, this);
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;
41   // no default decors or functions, each has to be enabled
42   _decorations = _functions = 0;
43   
44   getArea();
45   getDesktop();
46   getType();
47
48   // set the decorations and functions
49   switch (_type) {
50   case Type_Normal:
51     // normal windows retain all of the possible decorations and
52     // functionality
53     _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
54                    Decor_Iconify | Decor_Maximize;
55     _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
56
57   case Type_Dialog:
58     // dialogs cannot be maximized
59     _decorations &= ~Decor_Maximize;
60     _functions &= ~Func_Maximize;
61     break;
62
63   case Type_Menu:
64   case Type_Toolbar:
65   case Type_Utility:
66     // these windows get less functionality
67     _decorations &= ~(Decor_Iconify | Decor_Handle);
68     _functions &= ~(Func_Iconify | Func_Resize);
69     break;
70
71   case Type_Desktop:
72   case Type_Dock:
73   case Type_Splash:
74     // none of these windows are manipulated by the window manager
75     _decorations = 0;
76     _functions = 0;
77     break;
78   }
79   
80   getMwmHints(); // this fucks (in good ways) with the decors and functions
81   getState();
82   getShaped();
83
84   updateProtocols();
85   updateNormalHints();
86   updateWMHints();
87   // XXX: updateTransientFor();
88   updateTitle();
89   updateIconTitle();
90   updateClass();
91
92 /*
93 #ifdef DEBUG
94   printf("Mapped window: 0x%lx\n"
95          "  title:         \t%s\t  icon title:    \t%s\n"
96          "  app name:      \t%s\t\t  class:         \t%s\n"
97          "  position:      \t%d, %d\t\t  size:          \t%d, %d\n"
98          "  desktop:       \t%lu\t\t  group:         \t0x%lx\n"
99          "  type:          \t%d\t\t  min size       \t%d, %d\n"
100          "  base size      \t%d, %d\t\t  max size       \t%d, %d\n"
101          "  size incr      \t%d, %d\t\t  gravity        \t%d\n"
102          "  wm state       \t%ld\t\t  can be focused:\t%s\n"
103          "  notify focus:  \t%s\t\t  urgent:        \t%s\n"
104          "  shaped:        \t%s\t\t  modal:         \t%s\n"
105          "  shaded:        \t%s\t\t  iconic:        \t%s\n"
106          "  vert maximized:\t%s\t\t  horz maximized:\t%s\n"
107          "  fullscreen:    \t%s\t\t  floating:      \t%s\n"
108          "  requested pos: \t%s\n",
109          _window,
110          _title.c_str(),
111          _icon_title.c_str(),
112          _app_name.c_str(),
113          _app_class.c_str(),
114          _area.x(), _area.y(),
115          _area.width(), _area.height(),
116          _desktop,
117          _group,
118          _type,
119          _min_x, _min_y,
120          _base_x, _base_y,
121          _max_x, _max_y,
122          _inc_x, _inc_y,
123          _gravity,
124          _wmstate,
125          _can_focus ? "yes" : "no",
126          _focus_notify ? "yes" : "no",
127          _urgent ? "yes" : "no",
128          _shaped ? "yes" : "no",
129          _modal ? "yes" : "no",
130          _shaded ? "yes" : "no",
131          _iconic ? "yes" : "no",
132          _max_vert ? "yes" : "no",
133          _max_horz ? "yes" : "no",
134          _fullscreen ? "yes" : "no",
135          _floating ? "yes" : "no",
136          _positioned ? "yes" : "no");
137 #endif
138 */
139 }
140
141
142 OBClient::~OBClient()
143 {
144   const otk::OBProperty *property = Openbox::instance->property();
145
146   // these values should not be persisted across a window unmapping/mapping
147   property->erase(_window, otk::OBProperty::net_wm_desktop);
148   property->erase(_window, otk::OBProperty::net_wm_state);
149 }
150
151
152 void OBClient::getDesktop()
153 {
154   const otk::OBProperty *property = Openbox::instance->property();
155
156   // defaults to the current desktop
157   _desktop = 0; // XXX: change this to the current desktop!
158
159   property->get(_window, otk::OBProperty::net_wm_desktop,
160                 otk::OBProperty::Atom_Cardinal,
161                 &_desktop);
162 }
163
164
165 void OBClient::getType()
166 {
167   const otk::OBProperty *property = Openbox::instance->property();
168
169   _type = (WindowType) -1;
170   
171   unsigned long *val;
172   unsigned long num = (unsigned) -1;
173   if (property->get(_window, otk::OBProperty::net_wm_window_type,
174                     otk::OBProperty::Atom_Atom,
175                     &num, &val)) {
176     // use the first value that we know about in the array
177     for (unsigned long i = 0; i < num; ++i) {
178       if (val[i] ==
179           property->atom(otk::OBProperty::net_wm_window_type_desktop))
180         _type = Type_Desktop;
181       else if (val[i] ==
182                property->atom(otk::OBProperty::net_wm_window_type_dock))
183         _type = Type_Dock;
184       else if (val[i] ==
185                property->atom(otk::OBProperty::net_wm_window_type_toolbar))
186         _type = Type_Toolbar;
187       else if (val[i] ==
188                property->atom(otk::OBProperty::net_wm_window_type_menu))
189         _type = Type_Menu;
190       else if (val[i] ==
191                property->atom(otk::OBProperty::net_wm_window_type_utility))
192         _type = Type_Utility;
193       else if (val[i] ==
194                property->atom(otk::OBProperty::net_wm_window_type_splash))
195         _type = Type_Splash;
196       else if (val[i] ==
197                property->atom(otk::OBProperty::net_wm_window_type_dialog))
198         _type = Type_Dialog;
199       else if (val[i] ==
200                property->atom(otk::OBProperty::net_wm_window_type_normal))
201         _type = Type_Normal;
202 //      else if (val[i] ==
203 //               property->atom(otk::OBProperty::kde_net_wm_window_type_override))
204 //        mwm_decorations = 0; // prevent this window from getting any decor
205       // XXX: make this work again
206     }
207     delete val;
208   }
209     
210   if (_type == (WindowType) -1) {
211     /*
212      * the window type hint was not set, which means we either classify ourself
213      * as a normal window or a dialog, depending on if we are a transient.
214      */
215     // XXX: make this code work!
216     //if (isTransient())
217     //  _type = Type_Dialog;
218     //else
219       _type = Type_Normal;
220   }
221 }
222
223
224 void OBClient::getMwmHints()
225 {
226   const otk::OBProperty *property = Openbox::instance->property();
227
228   unsigned long num;
229   MwmHints *hints;
230
231   num = MwmHints::elements;
232   if (!property->get(_window, otk::OBProperty::motif_wm_hints,
233                      otk::OBProperty::motif_wm_hints, &num,
234                      (unsigned long **)&hints))
235     return;
236   
237   if (num < MwmHints::elements) {
238     delete [] hints;
239     return;
240   }
241
242   // retrieved the hints
243   // Mwm Hints are applied subtractively to what has already been chosen for
244   // decor and functionality
245
246   if (hints->flags & MwmFlag_Decorations) {
247     if (! (hints->decorations & MwmDecor_All)) {
248       if (! (hints->decorations & MwmDecor_Border))
249         _decorations &= ~Decor_Border;
250       if (! (hints->decorations & MwmDecor_Handle))
251         _decorations &= ~Decor_Handle;
252       if (! (hints->decorations & MwmDecor_Title))
253         _decorations &= ~Decor_Titlebar;
254       if (! (hints->decorations & MwmDecor_Iconify))
255         _decorations &= ~Decor_Iconify;
256       if (! (hints->decorations & MwmDecor_Maximize))
257         _decorations &= ~Decor_Maximize;
258     }
259   }
260
261   if (hints->flags & MwmFlag_Functions) {
262     if (! (hints->functions & MwmFunc_All)) {
263       if (! (hints->functions & MwmFunc_Resize))
264         _functions &= ~Func_Resize;
265       if (! (hints->functions & MwmFunc_Move))
266         _functions &= ~Func_Move;
267       if (! (hints->functions & MwmFunc_Iconify))
268         _functions &= ~Func_Iconify;
269       if (! (hints->functions & MwmFunc_Maximize))
270         _functions &= ~Func_Maximize;
271       //if (! (hints->functions & MwmFunc_Close))
272       //  _functions &= ~Func_Close;
273     }
274   }
275   delete [] hints;
276 }
277
278
279 void OBClient::getArea()
280 {
281   XWindowAttributes wattrib;
282   Status ret;
283   
284   ret = XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib);
285   assert(ret != BadWindow);
286
287   _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
288   _border_width = wattrib.border_width;
289 }
290
291
292 void OBClient::getState()
293 {
294   const otk::OBProperty *property = Openbox::instance->property();
295
296   _modal = _shaded = _max_horz = _max_vert = _fullscreen = _floating = false;
297   
298   unsigned long *state;
299   unsigned long num = (unsigned) -1;
300   
301   if (property->get(_window, otk::OBProperty::net_wm_state,
302                     otk::OBProperty::Atom_Atom, &num, &state)) {
303     for (unsigned long i = 0; i < num; ++i) {
304       if (state[i] == property->atom(otk::OBProperty::net_wm_state_modal))
305         _modal = true;
306       else if (state[i] ==
307                property->atom(otk::OBProperty::net_wm_state_shaded))
308         _shaded = true;
309       else if (state[i] ==
310                property->atom(otk::OBProperty::net_wm_state_fullscreen))
311         _fullscreen = true;
312       else if (state[i] ==
313                property->atom(otk::OBProperty::net_wm_state_maximized_vert))
314         _max_vert = true;
315       else if (state[i] ==
316                property->atom(otk::OBProperty::net_wm_state_maximized_horz))
317         _max_horz = true;
318     }
319
320     delete [] state;
321   }
322 }
323
324
325 void OBClient::getShaped()
326 {
327   _shaped = false;
328 #ifdef   SHAPE
329   if (otk::OBDisplay::shape()) {
330     int foo;
331     unsigned int ufoo;
332     int s;
333
334     XShapeSelectInput(otk::OBDisplay::display, _window, ShapeNotifyMask);
335
336     XShapeQueryExtents(otk::OBDisplay::display, _window, &s, &foo,
337                        &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
338     _shaped = (s != 0);
339   }
340 #endif // SHAPE
341 }
342
343
344 void OBClient::updateProtocols()
345 {
346   const otk::OBProperty *property = Openbox::instance->property();
347
348   Atom *proto;
349   int num_return = 0;
350
351   _focus_notify = false;
352   _decorations &= ~Decor_Close;
353   _functions &= ~Func_Close;
354
355   if (XGetWMProtocols(otk::OBDisplay::display, _window, &proto, &num_return)) {
356     for (int i = 0; i < num_return; ++i) {
357       if (proto[i] == property->atom(otk::OBProperty::wm_delete_window)) {
358         _decorations |= Decor_Close;
359         _functions |= Func_Close;
360         // XXX: update the decor?
361       } else if (proto[i] == property->atom(otk::OBProperty::wm_take_focus))
362         // if this protocol is requested, then the window will be notified
363         // by the window manager whenever it receives focus
364         _focus_notify = true;
365     }
366     XFree(proto);
367   }
368 }
369
370
371 void OBClient::updateNormalHints()
372 {
373   XSizeHints size;
374   long ret;
375
376   // defaults
377   _gravity = NorthWestGravity;
378   _size_inc.setPoint(1, 1);
379   _base_size.setPoint(0, 0);
380   _min_size.setPoint(0, 0);
381   _max_size.setPoint(INT_MAX, INT_MAX);
382
383   // XXX: might want to cancel any interactive resizing of the window at this
384   // point..
385
386   // get the hints from the window
387   if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
388     _positioned = (size.flags & (PPosition|USPosition));
389
390     if (size.flags & PWinGravity)
391       _gravity = size.win_gravity;
392     
393     if (size.flags & PMinSize)
394       _min_size.setPoint(size.min_width, size.min_height);
395     
396     if (size.flags & PMaxSize)
397       _max_size.setPoint(size.max_width, size.max_height);
398     
399     if (size.flags & PBaseSize)
400       _base_size.setPoint(size.base_width, size.base_height);
401     
402     if (size.flags & PResizeInc)
403       _size_inc.setPoint(size.width_inc, size.height_inc);
404   }
405 }
406
407
408 void OBClient::updateWMHints()
409 {
410   XWMHints *hints;
411
412   // assume a window takes input if it doesnt specify
413   _can_focus = true;
414   _urgent = false;
415   
416   if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
417     if (hints->flags & InputHint)
418       _can_focus = hints->input;
419
420     if (hints->flags & XUrgencyHint)
421       _urgent = true;
422
423     if (hints->flags & WindowGroupHint) {
424       if (hints->window_group != _group) {
425         // XXX: remove from the old group if there was one
426         _group = hints->window_group;
427         // XXX: do stuff with the group
428       }
429     } else // no group!
430       _group = None;
431
432     XFree(hints);
433   }
434 }
435
436
437 void OBClient::updateTitle()
438 {
439   const otk::OBProperty *property = Openbox::instance->property();
440
441   _title = "";
442   
443   // try netwm
444   if (! property->get(_window, otk::OBProperty::net_wm_name,
445                       otk::OBProperty::utf8, &_title)) {
446     // try old x stuff
447     property->get(_window, otk::OBProperty::wm_name,
448                   otk::OBProperty::ascii, &_title);
449   }
450
451   if (_title.empty())
452     _title = _("Unnamed Window");
453 }
454
455
456 void OBClient::updateIconTitle()
457 {
458   const otk::OBProperty *property = Openbox::instance->property();
459
460   _icon_title = "";
461   
462   // try netwm
463   if (! property->get(_window, otk::OBProperty::net_wm_icon_name,
464                       otk::OBProperty::utf8, &_icon_title)) {
465     // try old x stuff
466     property->get(_window, otk::OBProperty::wm_icon_name,
467                   otk::OBProperty::ascii, &_icon_title);
468   }
469
470   if (_title.empty())
471     _icon_title = _("Unnamed Window");
472 }
473
474
475 void OBClient::updateClass()
476 {
477   const otk::OBProperty *property = Openbox::instance->property();
478
479   // set the defaults
480   _app_name = _app_class = "";
481
482   otk::OBProperty::StringVect v;
483   unsigned long num = 2;
484
485   if (! property->get(_window, otk::OBProperty::wm_class,
486                       otk::OBProperty::ascii, &num, &v))
487     return;
488
489   if (num > 0) _app_name = v[0];
490   if (num > 1) _app_class = v[1];
491 }
492
493
494 void OBClient::propertyHandler(const XPropertyEvent &e)
495 {
496   otk::OtkEventHandler::propertyHandler(e);
497   
498   const otk::OBProperty *property = Openbox::instance->property();
499
500   // compress changes to a single property into a single change
501   XEvent ce;
502   while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
503     // XXX: it would be nice to compress ALL changes to a property, not just
504     //      changes in a row without other props between.
505     if (ce.xproperty.atom != e.atom) {
506       XPutBackEvent(otk::OBDisplay::display, &ce);
507       break;
508     }
509   }
510
511   if (e.atom == XA_WM_NORMAL_HINTS)
512     updateNormalHints();
513   else if (e.atom == XA_WM_HINTS)
514     updateWMHints();
515   else if (e.atom == property->atom(otk::OBProperty::net_wm_name) ||
516            e.atom == property->atom(otk::OBProperty::wm_name))
517     updateTitle();
518   else if (e.atom == property->atom(otk::OBProperty::net_wm_icon_name) ||
519            e.atom == property->atom(otk::OBProperty::wm_icon_name))
520     updateIconTitle();
521   else if (e.atom == property->atom(otk::OBProperty::wm_class))
522     updateClass();
523   else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
524     updateProtocols();
525   // XXX: transient for hint
526   // XXX: strut hint
527 }
528
529
530 void OBClient::setWMState(long state)
531 {
532   if (state == _wmstate) return; // no change
533   
534   switch (state) {
535   case IconicState:
536     // XXX: cause it to iconify
537     break;
538   case NormalState:
539     // XXX: cause it to uniconify
540     break;
541   }
542   _wmstate = state;
543 }
544
545
546 void OBClient::setDesktop(long target)
547 {
548   assert(target >= 0);
549   //assert(target == 0xffffffff || target < MAX);
550   
551   // XXX: move the window to the new desktop
552   _desktop = target;
553 }
554
555
556 void OBClient::setState(StateAction action, long data1, long data2)
557 {
558   const otk::OBProperty *property = Openbox::instance->property();
559
560   if (!(action == State_Add || action == State_Remove ||
561         action == State_Toggle))
562     return; // an invalid action was passed to the client message, ignore it
563
564   for (int i = 0; i < 2; ++i) {
565     Atom state = i == 0 ? data1 : data2;
566     
567     if (! state) continue;
568
569     // if toggling, then pick whether we're adding or removing
570     if (action == State_Toggle) {
571       if (state == property->atom(otk::OBProperty::net_wm_state_modal))
572         action = _modal ? State_Remove : State_Add;
573       else if (state ==
574                property->atom(otk::OBProperty::net_wm_state_maximized_vert))
575         action = _max_vert ? State_Remove : State_Add;
576       else if (state ==
577                property->atom(otk::OBProperty::net_wm_state_maximized_horz))
578         action = _max_horz ? State_Remove : State_Add;
579       else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
580         action = _shaded ? State_Remove : State_Add;
581       else if (state ==
582                property->atom(otk::OBProperty::net_wm_state_fullscreen))
583         action = _fullscreen ? State_Remove : State_Add;
584       else if (state == property->atom(otk::OBProperty::net_wm_state_floating))
585         action = _floating ? State_Remove : State_Add;
586     }
587     
588     if (action == State_Add) {
589       if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
590         if (_modal) continue;
591         _modal = true;
592         // XXX: give it focus if another window has focus that shouldnt now
593       } else if (state ==
594                  property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
595         if (_max_vert) continue;
596         _max_vert = true;
597         // XXX: resize the window etc
598       } else if (state ==
599                  property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
600         if (_max_horz) continue;
601         _max_horz = true;
602         // XXX: resize the window etc
603       } else if (state ==
604                  property->atom(otk::OBProperty::net_wm_state_shaded)) {
605         if (_shaded) continue;
606         _shaded = true;
607         // XXX: hide the client window
608       } else if (state ==
609                  property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
610         if (_fullscreen) continue;
611         _fullscreen = true;
612         // XXX: raise the window n shit
613       } else if (state ==
614                  property->atom(otk::OBProperty::net_wm_state_floating)) {
615         if (_floating) continue;
616         _floating = true;
617         // XXX: raise the window n shit
618       }
619
620     } else { // action == State_Remove
621       if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
622         if (!_modal) continue;
623         _modal = false;
624       } else if (state ==
625                  property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
626         if (!_max_vert) continue;
627         _max_vert = false;
628         // XXX: resize the window etc
629       } else if (state ==
630                  property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
631         if (!_max_horz) continue;
632         _max_horz = false;
633         // XXX: resize the window etc
634       } else if (state ==
635                  property->atom(otk::OBProperty::net_wm_state_shaded)) {
636         if (!_shaded) continue;
637         _shaded = false;
638         // XXX: show the client window
639       } else if (state ==
640                  property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
641         if (!_fullscreen) continue;
642         _fullscreen = false;
643         // XXX: lower the window to its proper layer
644       } else if (state ==
645                  property->atom(otk::OBProperty::net_wm_state_floating)) {
646         if (!_floating) continue;
647         _floating = false;
648         // XXX: lower the window to its proper layer
649       }
650     }
651   }
652 }
653
654
655 void OBClient::clientMessageHandler(const XClientMessageEvent &e)
656 {
657   otk::OtkEventHandler::clientMessageHandler(e);
658   
659   if (e.format != 32) return;
660
661   const otk::OBProperty *property = Openbox::instance->property();
662   
663   if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) {
664     // compress changes into a single change
665     bool compress = false;
666     XEvent ce;
667     while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
668       // XXX: it would be nice to compress ALL messages of a type, not just
669       //      messages in a row without other message types between.
670       if (ce.xclient.message_type != e.message_type) {
671         XPutBackEvent(otk::OBDisplay::display, &ce);
672         break;
673       }
674       compress = true;
675     }
676     if (compress)
677       setWMState(ce.xclient.data.l[0]); // use the found event
678     else
679       setWMState(e.data.l[0]); // use the original event
680   } else if (e.message_type ==
681              property->atom(otk::OBProperty::net_wm_desktop)) {
682     // compress changes into a single change 
683     bool compress = false;
684     XEvent ce;
685     while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
686       // XXX: it would be nice to compress ALL messages of a type, not just
687       //      messages in a row without other message types between.
688       if (ce.xclient.message_type != e.message_type) {
689         XPutBackEvent(otk::OBDisplay::display, &ce);
690         break;
691       }
692       compress = true;
693     }
694     if (compress)
695       setDesktop(e.data.l[0]); // use the found event
696     else
697       setDesktop(e.data.l[0]); // use the original event
698   }
699   else if (e.message_type == property->atom(otk::OBProperty::net_wm_state))
700     // can't compress these
701     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
702 }
703
704
705 #if defined(SHAPE) || defined(DOXYGEN_IGNORE)
706 void OBClient::shapeHandler(const XShapeEvent &e)
707 {
708   otk::OtkEventHandler::shapeHandler(e);
709   
710   _shaped = e.shaped;
711 }
712 #endif
713
714
715 void OBClient::resize(Corner anchor, int w, int h)
716 {
717   w -= _base_size.x(); 
718   h -= _base_size.y();
719
720   // is the window resizable? if it is not, then don't check its sizes, the
721   // client can do what it wants and the user can't change it anyhow
722   if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
723     // smaller than min size or bigger than max size?
724     if (w < _min_size.x()) w = _min_size.x();
725     else if (w > _max_size.x()) w = _max_size.x();
726     if (h < _min_size.y()) h = _min_size.y();
727     else if (h > _max_size.y()) h = _max_size.y();
728   }
729
730   // keep to the increments
731   w /= _size_inc.x();
732   h /= _size_inc.y();
733
734   // store the logical size
735   _logical_size.setPoint(w, h);
736
737   w *= _size_inc.x();
738   h *= _size_inc.y();
739
740   w += _base_size.x();
741   h += _base_size.y();
742   
743   switch (anchor) {
744   case TopLeft:
745     break;
746   case TopRight:
747     _area.setX(_area.x() - _area.width() - w);
748     break;
749   case BottomLeft:
750     _area.setY(_area.y() - _area.height() - h);
751     break;
752   case BottomRight:
753     _area.setX(_area.x() - _area.width() - w);
754     _area.setY(_area.y() - _area.height() - h);
755     break;
756   }
757
758   _area.setSize(w, h);
759
760   // resize the frame to match
761   frame->adjust();
762 }
763
764
765 void OBClient::move(int x, int y)
766 {
767   _area.setPos(x, y);
768   // move the frame to be in the requested position
769   frame->applyGravity();
770 }
771
772
773 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
774 {
775   // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
776
777   if (e.value_mask & CWBorderWidth)
778     _border_width = e.border_width;
779
780     // resize, then move, as specified in the EWMH section 7.7
781   if (e.value_mask & (CWWidth | CWHeight)) {
782     int w = (e.value_mask & CWWidth) ? e.width : _area.width();
783     int h = (e.value_mask & CWHeight) ? e.height : _area.height();
784
785     Corner corner;
786     switch (_gravity) {
787     case NorthEastGravity:
788     case EastGravity:
789       corner = TopRight;
790       break;
791     case SouthWestGravity:
792     case SouthGravity:
793       corner = BottomLeft;
794       break;
795     case SouthEastGravity:
796       corner = BottomRight;
797       break;
798     default:     // NorthWest, Static, etc
799       corner = TopLeft;
800     }
801
802     resize(corner, w, h);
803   }
804
805   if (e.value_mask & (CWX | CWY)) {
806     int x = (e.value_mask & CWX) ? e.x : _area.x();
807     int y = (e.value_mask & CWY) ? e.y : _area.y();
808     move(x, y);
809   }
810
811   if (e.value_mask & CWStackMode) {
812     switch (e.detail) {
813     case Below:
814     case BottomIf:
815       // XXX: lower the window
816       break;
817
818     case Above:
819     case TopIf:
820     default:
821       // XXX: raise the window
822       break;
823     }
824   }
825 }
826
827
828 }