]> icculus.org git repositories - mikachu/openbox.git/blob - src/client.cc
add printf for when a window requests its desktop
[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 "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
14
15 extern "C" {
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
19
20 #include <assert.h>
21
22 #include "gettext.h"
23 #define _(str) gettext(str)
24 }
25
26 namespace ob {
27
28 Client::Client(int screen, Window window)
29   : otk::EventHandler(),
30     WidgetBase(WidgetBase::Type_Client),
31     frame(0), _screen(screen), _window(window)
32 {
33   assert(screen >= 0);
34   assert(window);
35
36   ignore_unmaps = 0;
37   
38   // update EVERYTHING the first time!!
39
40   // we default to NormalState, visible
41   _wmstate = NormalState;
42   // start unfocused
43   _focused = false;
44   // not a transient by default of course
45   _transient_for = 0;
46   // pick a layer to start from
47   _layer = Layer_Normal;
48   // default to not urgent
49   _urgent = false;
50   
51   getArea();
52   getDesktop();
53
54   updateTransientFor();
55   getType();
56   getMwmHints();
57
58   getState();
59   getShaped();
60
61   updateProtocols();
62
63   // got the type, the mwmhints, and the protocols, so we're ready to set up
64   // the decorations/functions
65   setupDecorAndFunctions();
66   
67   getGravity();        // get the attribute gravity
68   updateNormalHints(); // this may override the attribute gravity
69   // also get the initial_state and set _iconic if we aren't "starting"
70   // when we're "starting" that means we should use whatever state was already
71   // on the window over the initial map state, because it was already mapped
72   updateWMHints(openbox->state() != Openbox::State_Starting);
73   updateTitle();
74   updateIconTitle();
75   updateClass();
76   updateStrut();
77
78   // this makes sure that these windows appear on all desktops
79   if (_type == Type_Dock || _type == Type_Desktop)
80     _desktop = 0xffffffff;
81   
82   // set the desktop hint, to make sure that it always exists, and to reflect
83   // any changes we've made here
84   otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
85                      otk::Property::atoms.cardinal, (unsigned)_desktop);
86   
87   changeState();
88 }
89
90
91 Client::~Client()
92 {
93   // clean up childrens' references
94   while (!_transients.empty()) {
95     _transients.front()->_transient_for = 0;
96     _transients.pop_front();
97   }
98   
99   // clean up parents reference to this
100   if (_transient_for)
101     _transient_for->_transients.remove(this); // remove from old parent
102   
103   if (openbox->state() != Openbox::State_Exiting) {
104     // these values should not be persisted across a window unmapping/mapping
105     otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
106     otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
107   } else {
108     // if we're left in an iconic state, the client wont be mapped. this is
109     // bad, since we will no longer be managing the window on restart
110     if (_iconic)
111       XMapWindow(**otk::display, _window);
112   }
113 }
114
115
116 void Client::getGravity()
117 {
118   XWindowAttributes wattrib;
119   Status ret;
120
121   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
122   assert(ret != BadWindow);
123   _gravity = wattrib.win_gravity;
124 }
125
126
127 void Client::getDesktop()
128 {
129   // defaults to the current desktop
130   _desktop = openbox->screen(_screen)->desktop();
131
132   if (otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
133                          otk::Property::atoms.cardinal,
134                          (long unsigned*)&_desktop)) {
135 #ifdef DEBUG
136     printf("DEBUG: Window requested desktop: %d\n", _desktop);
137 #endif
138   }
139 }
140
141
142 void Client::getType()
143 {
144   _type = (WindowType) -1;
145   
146   unsigned long *val;
147   unsigned long num = (unsigned) -1;
148   if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
149                          otk::Property::atoms.atom, &num, &val)) {
150     // use the first value that we know about in the array
151     for (unsigned long i = 0; i < num; ++i) {
152       if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
153         _type = Type_Desktop;
154       else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
155         _type = Type_Dock;
156       else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
157         _type = Type_Toolbar;
158       else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
159         _type = Type_Menu;
160       else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
161         _type = Type_Utility;
162       else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
163         _type = Type_Splash;
164       else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
165         _type = Type_Dialog;
166       else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
167         _type = Type_Normal;
168 //    XXX: make this work again
169 //    else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
170 //      mwm_decorations = 0; // prevent this window from getting any decor
171       if (_type != (WindowType) -1)
172         break; // grab the first known type
173     }
174     delete val;
175   }
176     
177   if (_type == (WindowType) -1) {
178     /*
179      * the window type hint was not set, which means we either classify ourself
180      * as a normal window or a dialog, depending on if we are a transient.
181      */
182     if (_transient_for)
183       _type = Type_Dialog;
184     else
185       _type = Type_Normal;
186   }
187 }
188
189
190 void Client::setupDecorAndFunctions()
191 {
192   // start with everything (cept fullscreen)
193   _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
194     Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
195   _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
196     Func_Shade;
197   if (_delete_window) {
198     _decorations |= Decor_Close;
199     _functions |= Func_Close;
200   }
201   
202   switch (_type) {
203   case Type_Normal:
204     // normal windows retain all of the possible decorations and
205     // functionality, and are the only windows that you can fullscreen
206     _functions |= Func_Fullscreen;
207
208   case Type_Dialog:
209     // dialogs cannot be maximized
210     _decorations &= ~Decor_Maximize;
211     _functions &= ~Func_Maximize;
212     break;
213
214   case Type_Menu:
215   case Type_Toolbar:
216   case Type_Utility:
217     // these windows get less functionality
218     _decorations &= ~(Decor_Iconify | Decor_Handle);
219     _functions &= ~(Func_Iconify | Func_Resize);
220     break;
221
222   case Type_Desktop:
223   case Type_Dock:
224   case Type_Splash:
225     // none of these windows are manipulated by the window manager
226     _decorations = 0;
227     _functions = 0;
228     break;
229   }
230
231   // Mwm Hints are applied subtractively to what has already been chosen for
232   // decor and functionality
233   if (_mwmhints.flags & MwmFlag_Decorations) {
234     if (! (_mwmhints.decorations & MwmDecor_All)) {
235       if (! (_mwmhints.decorations & MwmDecor_Border))
236         _decorations &= ~Decor_Border;
237       if (! (_mwmhints.decorations & MwmDecor_Handle))
238         _decorations &= ~Decor_Handle;
239       if (! (_mwmhints.decorations & MwmDecor_Title)) {
240         _decorations &= ~Decor_Titlebar;
241         // if we don't have a titlebar, then we cannot shade!
242         _functions &= ~Func_Shade;
243       }
244       if (! (_mwmhints.decorations & MwmDecor_Iconify))
245         _decorations &= ~Decor_Iconify;
246       if (! (_mwmhints.decorations & MwmDecor_Maximize))
247         _decorations &= ~Decor_Maximize;
248     }
249   }
250
251   if (_mwmhints.flags & MwmFlag_Functions) {
252     if (! (_mwmhints.functions & MwmFunc_All)) {
253       if (! (_mwmhints.functions & MwmFunc_Resize))
254         _functions &= ~Func_Resize;
255       if (! (_mwmhints.functions & MwmFunc_Move))
256         _functions &= ~Func_Move;
257       if (! (_mwmhints.functions & MwmFunc_Iconify))
258         _functions &= ~Func_Iconify;
259       if (! (_mwmhints.functions & MwmFunc_Maximize))
260         _functions &= ~Func_Maximize;
261       // dont let mwm hints kill the close button
262       //if (! (_mwmhints.functions & MwmFunc_Close))
263       //  _functions &= ~Func_Close;
264     }
265   }
266
267   changeAllowedActions();
268 }
269
270
271 void Client::getMwmHints()
272 {
273   unsigned long num = MwmHints::elements;
274   unsigned long *hints;
275
276   _mwmhints.flags = 0; // default to none
277   
278   if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
279                           otk::Property::atoms.motif_wm_hints, &num,
280                           (unsigned long **)&hints))
281     return;
282   
283   if (num >= MwmHints::elements) {
284     // retrieved the hints
285     _mwmhints.flags = hints[0];
286     _mwmhints.functions = hints[1];
287     _mwmhints.decorations = hints[2];
288   }
289
290   delete [] hints;
291 }
292
293
294 void Client::getArea()
295 {
296   XWindowAttributes wattrib;
297   Status ret;
298   
299   ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
300   assert(ret != BadWindow);
301
302   _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
303   _border_width = wattrib.border_width;
304 }
305
306
307 void Client::getState()
308 {
309   _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
310     _iconic = _skip_taskbar = _skip_pager = false;
311   
312   unsigned long *state;
313   unsigned long num = (unsigned) -1;
314   
315   if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
316                          otk::Property::atoms.atom, &num, &state)) {
317     for (unsigned long i = 0; i < num; ++i) {
318       if (state[i] == otk::Property::atoms.net_wm_state_modal)
319         _modal = true;
320       else if (state[i] == otk::Property::atoms.net_wm_state_shaded)
321         _shaded = true;
322       else if (state[i] == otk::Property::atoms.net_wm_state_hidden)
323         _iconic = true;
324       else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
325         _skip_taskbar = true;
326       else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
327         _skip_pager = true;
328       else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
329         _fullscreen = true;
330       else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
331         _max_vert = true;
332       else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
333         _max_horz = true;
334       else if (state[i] == otk::Property::atoms.net_wm_state_above)
335         _above = true;
336       else if (state[i] == otk::Property::atoms.net_wm_state_below)
337         _below = true;
338     }
339
340     delete [] state;
341   }
342 }
343
344
345 void Client::getShaped()
346 {
347   _shaped = false;
348 #ifdef   SHAPE
349   if (otk::display->shape()) {
350     int foo;
351     unsigned int ufoo;
352     int s;
353
354     XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
355
356     XShapeQueryExtents(**otk::display, _window, &s, &foo,
357                        &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
358     _shaped = (s != 0);
359   }
360 #endif // SHAPE
361 }
362
363
364 void Client::calcLayer() {
365   StackLayer l;
366
367   if (_iconic) l = Layer_Icon;
368   else if (_fullscreen) l = Layer_Fullscreen;
369   else if (_type == Type_Desktop) l = Layer_Desktop;
370   else if (_type == Type_Dock) {
371     if (!_below) l = Layer_Top;
372     else l = Layer_Normal;
373   }
374   else if (_above) l = Layer_Above;
375   else if (_below) l = Layer_Below;
376   else l = Layer_Normal;
377
378   if (l != _layer) {
379     _layer = l;
380     if (frame) {
381       /*
382         if we don't have a frame, then we aren't mapped yet (and this would
383         SIGSEGV :)
384       */
385       openbox->screen(_screen)->raiseWindow(this);
386     }
387   }
388 }
389
390
391 void Client::updateProtocols()
392 {
393   Atom *proto;
394   int num_return = 0;
395
396   _focus_notify = false;
397   _delete_window = false;
398
399   if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
400     for (int i = 0; i < num_return; ++i) {
401       if (proto[i] == otk::Property::atoms.wm_delete_window) {
402         // this means we can request the window to close
403         _delete_window = true;
404       } else if (proto[i] == otk::Property::atoms.wm_take_focus)
405         // if this protocol is requested, then the window will be notified
406         // by the window manager whenever it receives focus
407         _focus_notify = true;
408     }
409     XFree(proto);
410   }
411 }
412
413
414 void Client::updateNormalHints()
415 {
416   XSizeHints size;
417   long ret;
418   int oldgravity = _gravity;
419
420   // defaults
421   _size_inc.setPoint(1, 1);
422   _base_size.setPoint(0, 0);
423   _min_size.setPoint(0, 0);
424   _max_size.setPoint(INT_MAX, INT_MAX);
425
426   // XXX: might want to cancel any interactive resizing of the window at this
427   // point..
428
429   // get the hints from the window
430   if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
431     _positioned = (size.flags & (PPosition|USPosition));
432
433     if (size.flags & PWinGravity) {
434       _gravity = size.win_gravity;
435       
436       // if the client has a frame, i.e. has already been mapped and is
437       // changing its gravity
438       if (frame && _gravity != oldgravity) {
439         // move our idea of the client's position based on its new gravity
440         int x, y;
441         frame->frameGravity(x, y);
442         _area.setPos(x, y);
443       }
444     }
445
446     if (size.flags & PMinSize)
447       _min_size.setPoint(size.min_width, size.min_height);
448     
449     if (size.flags & PMaxSize)
450       _max_size.setPoint(size.max_width, size.max_height);
451     
452     if (size.flags & PBaseSize)
453       _base_size.setPoint(size.base_width, size.base_height);
454     
455     if (size.flags & PResizeInc)
456       _size_inc.setPoint(size.width_inc, size.height_inc);
457   }
458 }
459
460
461 void Client::updateWMHints(bool initstate)
462 {
463   XWMHints *hints;
464
465   // assume a window takes input if it doesnt specify
466   _can_focus = true;
467   bool ur = false;
468   
469   if ((hints = XGetWMHints(**otk::display, _window)) != NULL) {
470     if (hints->flags & InputHint)
471       _can_focus = hints->input;
472
473     // only do this when initstate is true!
474     if (initstate && (hints->flags & StateHint))
475       _iconic = hints->initial_state == IconicState;
476
477     if (hints->flags & XUrgencyHint)
478       ur = true;
479
480     if (hints->flags & WindowGroupHint) {
481       if (hints->window_group != _group) {
482         // XXX: remove from the old group if there was one
483         _group = hints->window_group;
484         // XXX: do stuff with the group
485       }
486     } else // no group!
487       _group = None;
488
489     XFree(hints);
490   }
491
492   if (ur != _urgent) {
493     _urgent = ur;
494     // fire the urgent callback if we're mapped, otherwise, wait until after
495     // we're mapped
496     if (_urgent && frame)
497       fireUrgent();
498   }
499 }
500
501
502 void Client::updateTitle()
503 {
504   _title = "";
505   
506   // try netwm
507   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_name,
508                           otk::Property::utf8, &_title)) {
509     // try old x stuff
510     otk::Property::get(_window, otk::Property::atoms.wm_name,
511                        otk::Property::ascii, &_title);
512   }
513
514   if (_title.empty())
515     _title = _("Unnamed Window");
516
517   if (frame)
518     frame->setTitle(_title);
519 }
520
521
522 void Client::updateIconTitle()
523 {
524   _icon_title = "";
525   
526   // try netwm
527   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon_name,
528                           otk::Property::utf8, &_icon_title)) {
529     // try old x stuff
530     otk::Property::get(_window, otk::Property::atoms.wm_icon_name,
531                        otk::Property::ascii, &_icon_title);
532   }
533
534   if (_title.empty())
535     _icon_title = _("Unnamed Window");
536 }
537
538
539 void Client::updateClass()
540 {
541   // set the defaults
542   _app_name = _app_class = _role = "";
543
544   otk::Property::StringVect v;
545   unsigned long num = 2;
546
547   if (otk::Property::get(_window, otk::Property::atoms.wm_class,
548                          otk::Property::ascii, &num, &v)) {
549     if (num > 0) _app_name = v[0].c_str();
550     if (num > 1) _app_class = v[1].c_str();
551   }
552
553   v.clear();
554   num = 1;
555   if (otk::Property::get(_window, otk::Property::atoms.wm_window_role,
556                          otk::Property::ascii, &num, &v)) {
557     if (num > 0) _role = v[0].c_str();
558   }
559 }
560
561
562 void Client::updateStrut()
563 {
564   unsigned long num = 4;
565   unsigned long *data;
566   if (!otk::Property::get(_window, otk::Property::atoms.net_wm_strut,
567                           otk::Property::atoms.cardinal, &num, &data))
568     return;
569
570   if (num == 4) {
571     _strut.left = data[0];
572     _strut.right = data[1];
573     _strut.top = data[2];
574     _strut.bottom = data[3];
575     
576     openbox->screen(_screen)->updateStrut();
577   }
578
579   delete [] data;
580 }
581
582
583 void Client::updateTransientFor()
584 {
585   Window t = 0;
586   Client *c = 0;
587
588   if (XGetTransientForHint(**otk::display, _window, &t) &&
589       t != _window) { // cant be transient to itself!
590     c = openbox->findClient(t);
591     assert(c != this); // if this happens then we need to check for it
592
593     if (!c /*XXX: && _group*/) {
594       // not transient to a client, see if it is transient for a group
595       if (//t == _group->leader() ||
596         t == None ||
597         t == otk::display->screenInfo(_screen)->rootWindow()) {
598         // window is a transient for its group!
599         // XXX: for now this is treated as non-transient.
600         //      this needs to be fixed!
601       }
602     }
603   }
604
605   // if anything has changed...
606   if (c != _transient_for) {
607     if (_transient_for)
608       _transient_for->_transients.remove(this); // remove from old parent
609     _transient_for = c;
610     if (_transient_for)
611       _transient_for->_transients.push_back(this); // add to new parent
612
613     // XXX: change decor status?
614   }
615 }
616
617
618 void Client::propertyHandler(const XPropertyEvent &e)
619 {
620   otk::EventHandler::propertyHandler(e);
621   
622   // compress changes to a single property into a single change
623   XEvent ce;
624   while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
625     // XXX: it would be nice to compress ALL changes to a property, not just
626     //      changes in a row without other props between.
627     if (ce.xproperty.atom != e.atom) {
628       XPutBackEvent(**otk::display, &ce);
629       break;
630     }
631   }
632
633   if (e.atom == XA_WM_NORMAL_HINTS)
634     updateNormalHints();
635   else if (e.atom == XA_WM_HINTS)
636     updateWMHints();
637   else if (e.atom == XA_WM_TRANSIENT_FOR) {
638     updateTransientFor();
639     getType();
640     calcLayer(); // type may have changed, so update the layer
641     setupDecorAndFunctions();
642     frame->adjustSize(); // this updates the frame for any new decor settings
643   }
644   else if (e.atom == otk::Property::atoms.net_wm_name ||
645            e.atom == otk::Property::atoms.wm_name)
646     updateTitle();
647   else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
648            e.atom == otk::Property::atoms.wm_icon_name)
649     updateIconTitle();
650   else if (e.atom == otk::Property::atoms.wm_class)
651     updateClass();
652   else if (e.atom == otk::Property::atoms.wm_protocols) {
653     updateProtocols();
654     setupDecorAndFunctions();
655     frame->adjustSize(); // update the decorations
656   }
657   else if (e.atom == otk::Property::atoms.net_wm_strut)
658     updateStrut();
659 }
660
661
662 void Client::setWMState(long state)
663 {
664   if (state == _wmstate) return; // no change
665   
666   switch (state) {
667   case IconicState:
668     setDesktop(ICONIC_DESKTOP);
669     break;
670   case NormalState:
671     setDesktop(openbox->screen(_screen)->desktop());
672     break;
673   }
674 }
675
676
677 void Client::setDesktop(long target)
678 {
679   if (target == _desktop) return;
680   
681   printf("Setting desktop %ld\n", target);
682
683   if (!(target >= 0 || target == (signed)0xffffffff ||
684         target == ICONIC_DESKTOP))
685     return;
686   
687   _desktop = target;
688
689   // set the desktop hint, but not if we're iconifying
690   if (_desktop != ICONIC_DESKTOP)
691     otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
692                        otk::Property::atoms.cardinal, (unsigned)_desktop);
693   
694   // 'move' the window to the new desktop
695   if (_desktop == openbox->screen(_screen)->desktop() ||
696       _desktop == (signed)0xffffffff)
697     frame->show();
698   else
699     frame->hide();
700
701   // Handle Iconic state. Iconic state is maintained by the client being a
702   // member of the ICONIC_DESKTOP, so this is where we make iconifying and
703   // uniconifying happen.
704   bool i = _desktop == ICONIC_DESKTOP;
705   if (i != _iconic) { // has the state changed?
706     _iconic = i;
707     if (_iconic) {
708       _wmstate = IconicState;
709       ignore_unmaps++;
710       // we unmap the client itself so that we can get MapRequest events, and
711       // because the ICCCM tells us to!
712       XUnmapWindow(**otk::display, _window);
713     } else {
714       _wmstate = NormalState;
715       XMapWindow(**otk::display, _window);
716     }
717     changeState();
718   }
719   
720   frame->adjustState();
721 }
722
723
724 void Client::setState(StateAction action, long data1, long data2)
725 {
726   bool shadestate = _shaded;
727   bool fsstate = _fullscreen;
728
729   if (!(action == State_Add || action == State_Remove ||
730         action == State_Toggle))
731     return; // an invalid action was passed to the client message, ignore it
732
733   for (int i = 0; i < 2; ++i) {
734     Atom state = i == 0 ? data1 : data2;
735     
736     if (! state) continue;
737
738     // if toggling, then pick whether we're adding or removing
739     if (action == State_Toggle) {
740       if (state == otk::Property::atoms.net_wm_state_modal)
741         action = _modal ? State_Remove : State_Add;
742       else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
743         action = _max_vert ? State_Remove : State_Add;
744       else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
745         action = _max_horz ? State_Remove : State_Add;
746       else if (state == otk::Property::atoms.net_wm_state_shaded)
747         action = _shaded ? State_Remove : State_Add;
748       else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
749         action = _skip_taskbar ? State_Remove : State_Add;
750       else if (state == otk::Property::atoms.net_wm_state_skip_pager)
751         action = _skip_pager ? State_Remove : State_Add;
752       else if (state == otk::Property::atoms.net_wm_state_fullscreen)
753         action = _fullscreen ? State_Remove : State_Add;
754       else if (state == otk::Property::atoms.net_wm_state_above)
755         action = _above ? State_Remove : State_Add;
756       else if (state == otk::Property::atoms.net_wm_state_below)
757         action = _below ? State_Remove : State_Add;
758     }
759     
760     if (action == State_Add) {
761       if (state == otk::Property::atoms.net_wm_state_modal) {
762         if (_modal) continue;
763         _modal = true;
764         // XXX: give it focus if another window has focus that shouldnt now
765       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
766         if (_max_vert) continue;
767         _max_vert = true;
768         // XXX: resize the window etc
769       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
770         if (_max_horz) continue;
771         _max_horz = true;
772         // XXX: resize the window etc
773       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
774         shadestate = true;
775       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
776         _skip_taskbar = true;
777       } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
778         _skip_pager = true;
779       } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
780         fsstate = true;
781       } else if (state == otk::Property::atoms.net_wm_state_above) {
782         if (_above) continue;
783         _above = true;
784       } else if (state == otk::Property::atoms.net_wm_state_below) {
785         if (_below) continue;
786         _below = true;
787       }
788
789     } else { // action == State_Remove
790       if (state == otk::Property::atoms.net_wm_state_modal) {
791         if (!_modal) continue;
792         _modal = false;
793       } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
794         if (!_max_vert) continue;
795         _max_vert = false;
796         // XXX: resize the window etc
797       } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
798         if (!_max_horz) continue;
799         _max_horz = false;
800         // XXX: resize the window etc
801       } else if (state == otk::Property::atoms.net_wm_state_shaded) {
802         shadestate = false;
803       } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
804         _skip_taskbar = false;
805       } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
806         _skip_pager = false;
807       } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
808         fsstate = false;
809       } else if (state == otk::Property::atoms.net_wm_state_above) {
810         if (!_above) continue;
811         _above = false;
812       } else if (state == otk::Property::atoms.net_wm_state_below) {
813         if (!_below) continue;
814         _below = false;
815       }
816     }
817   }
818   // change fullscreen state before shading, as it will affect if the window
819   // can shade or not
820   if (fsstate != _fullscreen)
821     fullscreen(fsstate);
822   if (shadestate != _shaded)
823     shade(shadestate);
824   calcLayer();
825 }
826
827
828 void Client::toggleClientBorder(bool addborder)
829 {
830   // adjust our idea of where the client is, based on its border. When the
831   // border is removed, the client should now be considered to be in a
832   // different position.
833   // when re-adding the border to the client, the same operation needs to be
834   // reversed.
835   int x = _area.x(), y = _area.y();
836   switch(_gravity) {
837   default:
838   case NorthWestGravity:
839   case WestGravity:
840   case SouthWestGravity:
841     break;
842   case NorthEastGravity:
843   case EastGravity:
844   case SouthEastGravity:
845     if (addborder) x -= _border_width * 2;
846     else           x += _border_width * 2;
847     break;
848   case NorthGravity:
849   case SouthGravity:
850   case CenterGravity:
851   case ForgetGravity:
852   case StaticGravity:
853     if (addborder) x -= _border_width;
854     else           x += _border_width;
855     break;
856   }
857   switch(_gravity) {
858   default:
859   case NorthWestGravity:
860   case NorthGravity:
861   case NorthEastGravity:
862     break;
863   case SouthWestGravity:
864   case SouthGravity:
865   case SouthEastGravity:
866     if (addborder) y -= _border_width * 2;
867     else           y += _border_width * 2;
868     break;
869   case WestGravity:
870   case EastGravity:
871   case CenterGravity:
872   case ForgetGravity:
873   case StaticGravity:
874     if (addborder) y -= _border_width;
875     else           y += _border_width;
876     break;
877   }
878   _area.setPos(x, y);
879
880   if (addborder) {
881     XSetWindowBorderWidth(**otk::display, _window, _border_width);
882
883     // move the client so it is back it the right spot _with_ its border!
884     XMoveWindow(**otk::display, _window, x, y);
885   } else
886     XSetWindowBorderWidth(**otk::display, _window, 0);
887 }
888
889
890 void Client::clientMessageHandler(const XClientMessageEvent &e)
891 {
892   otk::EventHandler::clientMessageHandler(e);
893   
894   if (e.format != 32) return;
895
896   if (e.message_type == otk::Property::atoms.wm_change_state) {
897     // compress changes into a single change
898     bool compress = false;
899     XEvent ce;
900     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
901       // XXX: it would be nice to compress ALL messages of a type, not just
902       //      messages in a row without other message types between.
903       if (ce.xclient.message_type != e.message_type) {
904         XPutBackEvent(**otk::display, &ce);
905         break;
906       }
907       compress = true;
908     }
909     if (compress)
910       setWMState(ce.xclient.data.l[0]); // use the found event
911     else
912       setWMState(e.data.l[0]); // use the original event
913   } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
914     // compress changes into a single change 
915     bool compress = false;
916     XEvent ce;
917     while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
918       // XXX: it would be nice to compress ALL messages of a type, not just
919       //      messages in a row without other message types between.
920       if (ce.xclient.message_type != e.message_type) {
921         XPutBackEvent(**otk::display, &ce);
922         break;
923       }
924       compress = true;
925     }
926     if (compress)
927       setDesktop(e.data.l[0]); // use the found event
928     else
929       setDesktop(e.data.l[0]); // use the original event
930   } else if (e.message_type == otk::Property::atoms.net_wm_state) {
931     // can't compress these
932 #ifdef DEBUG
933     printf("net_wm_state %s %ld %ld for 0x%lx\n",
934            (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
935             e.data.l[0] == 2 ? "Toggle" : "INVALID"),
936            e.data.l[1], e.data.l[2], _window);
937 #endif
938     setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
939   } else if (e.message_type == otk::Property::atoms.net_close_window) {
940 #ifdef DEBUG
941     printf("net_close_window for 0x%lx\n", _window);
942 #endif
943     close();
944   } else if (e.message_type == otk::Property::atoms.net_active_window) {
945 #ifdef DEBUG
946     printf("net_active_window for 0x%lx\n", _window);
947 #endif
948     if (_iconic)
949       setDesktop(openbox->screen(_screen)->desktop());
950     if (_shaded)
951       shade(false);
952     // XXX: deiconify
953     focus();
954     openbox->screen(_screen)->raiseWindow(this);
955   }
956 }
957
958
959 #if defined(SHAPE)
960 void Client::shapeHandler(const XShapeEvent &e)
961 {
962   otk::EventHandler::shapeHandler(e);
963
964   if (e.kind == ShapeBounding) {
965     _shaped = e.shaped;
966     frame->adjustShape();
967   }
968 }
969 #endif
970
971
972 void Client::resize(Corner anchor, int w, int h)
973 {
974   if (!(_functions & Func_Resize)) return;
975   internal_resize(anchor, w, h);
976 }
977
978
979 void Client::internal_resize(Corner anchor, int w, int h, int x, int y)
980 {
981   w -= _base_size.x(); 
982   h -= _base_size.y();
983
984   // for interactive resizing. have to move half an increment in each
985   // direction.
986   w += _size_inc.x() / 2;
987   h += _size_inc.y() / 2;
988
989   // is the window resizable? if it is not, then don't check its sizes, the
990   // client can do what it wants and the user can't change it anyhow
991   if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
992     // smaller than min size or bigger than max size?
993     if (w < _min_size.x()) w = _min_size.x();
994     else if (w > _max_size.x()) w = _max_size.x();
995     if (h < _min_size.y()) h = _min_size.y();
996     else if (h > _max_size.y()) h = _max_size.y();
997   }
998
999   // keep to the increments
1000   w /= _size_inc.x();
1001   h /= _size_inc.y();
1002
1003   // you cannot resize to nothing
1004   if (w < 1) w = 1;
1005   if (h < 1) h = 1;
1006   
1007   // store the logical size
1008   _logical_size.setPoint(w, h);
1009
1010   w *= _size_inc.x();
1011   h *= _size_inc.y();
1012
1013   w += _base_size.x();
1014   h += _base_size.y();
1015
1016   if (x == INT_MIN || y == INT_MIN) {
1017     x = _area.x();
1018     y = _area.y();
1019     switch (anchor) {
1020     case TopLeft:
1021       break;
1022     case TopRight:
1023       x -= w - _area.width();
1024       break;
1025     case BottomLeft:
1026       y -= h - _area.height();
1027       break;
1028     case BottomRight:
1029       x -= w - _area.width();
1030       y -= h - _area.height();
1031       break;
1032     }
1033   }
1034
1035   _area.setSize(w, h);
1036
1037   XResizeWindow(**otk::display, _window, w, h);
1038
1039   // resize the frame to match the request
1040   frame->adjustSize();
1041   internal_move(x, y);
1042 }
1043
1044
1045 void Client::move(int x, int y)
1046 {
1047   if (!(_functions & Func_Move)) return;
1048   internal_move(x, y);
1049 }
1050
1051
1052 void Client::internal_move(int x, int y)
1053 {
1054   _area.setPos(x, y);
1055
1056   // move the frame to be in the requested position
1057   if (frame) { // this can be called while mapping, before frame exists
1058     frame->adjustPosition();
1059
1060     // send synthetic configure notify (we don't need to if we aren't mapped
1061     // yet)
1062     XEvent event;
1063     event.type = ConfigureNotify;
1064     event.xconfigure.display = **otk::display;
1065     event.xconfigure.event = _window;
1066     event.xconfigure.window = _window;
1067     event.xconfigure.x = x;
1068     event.xconfigure.y = y;
1069     event.xconfigure.width = _area.width();
1070     event.xconfigure.height = _area.height();
1071     event.xconfigure.border_width = _border_width;
1072     event.xconfigure.above = frame->window();
1073     event.xconfigure.override_redirect = False;
1074     XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1075                StructureNotifyMask, &event);
1076   }
1077 }
1078
1079
1080 void Client::close()
1081 {
1082   XEvent ce;
1083
1084   if (!(_functions & Func_Close)) return;
1085
1086   // XXX: itd be cool to do timeouts and shit here for killing the client's
1087   //      process off
1088   // like... if the window is around after 5 seconds, then the close button
1089   // turns a nice red, and if this function is called again, the client is
1090   // explicitly killed.
1091
1092   ce.xclient.type = ClientMessage;
1093   ce.xclient.message_type =  otk::Property::atoms.wm_protocols;
1094   ce.xclient.display = **otk::display;
1095   ce.xclient.window = _window;
1096   ce.xclient.format = 32;
1097   ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1098   ce.xclient.data.l[1] = CurrentTime;
1099   ce.xclient.data.l[2] = 0l;
1100   ce.xclient.data.l[3] = 0l;
1101   ce.xclient.data.l[4] = 0l;
1102   XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1103 }
1104
1105
1106 void Client::changeState()
1107 {
1108   unsigned long state[2];
1109   state[0] = _wmstate;
1110   state[1] = None;
1111   otk::Property::set(_window, otk::Property::atoms.wm_state,
1112                      otk::Property::atoms.wm_state, state, 2);
1113   
1114   Atom netstate[10];
1115   int num = 0;
1116   if (_modal)
1117     netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1118   if (_shaded)
1119     netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1120   if (_iconic)
1121     netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1122   if (_skip_taskbar)
1123     netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1124   if (_skip_pager)
1125     netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1126   if (_fullscreen)
1127     netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1128   if (_max_vert)
1129     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1130   if (_max_horz)
1131     netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1132   if (_above)
1133     netstate[num++] = otk::Property::atoms.net_wm_state_above;
1134   if (_below)
1135     netstate[num++] = otk::Property::atoms.net_wm_state_below;
1136   otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1137                      otk::Property::atoms.atom, netstate, num);
1138
1139   calcLayer();
1140
1141   if (frame)
1142     frame->adjustState();
1143 }
1144
1145
1146 void Client::changeAllowedActions(void)
1147 {
1148   Atom actions[9];
1149   int num = 0;
1150
1151   actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1152
1153   if (_functions & Func_Shade)
1154     actions[num++] = otk::Property::atoms.net_wm_action_shade;
1155   if (_functions & Func_Close)
1156     actions[num++] = otk::Property::atoms.net_wm_action_close;
1157   if (_functions & Func_Move)
1158     actions[num++] = otk::Property::atoms.net_wm_action_move;
1159   if (_functions & Func_Iconify)
1160     actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1161   if (_functions & Func_Resize)
1162     actions[num++] = otk::Property::atoms.net_wm_action_resize;
1163   if (_functions & Func_Fullscreen)
1164     actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1165   if (_functions & Func_Maximize) {
1166     actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1167     actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1168   }
1169
1170   otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1171                      otk::Property::atoms.atom, actions, num);
1172 }
1173
1174
1175 void Client::applyStartupState()
1176 {
1177   // these are in a carefully crafted order..
1178
1179   if (_iconic) {
1180     printf("MAP ICONIC\n");
1181     _iconic = false;
1182     setDesktop(ICONIC_DESKTOP);
1183   }
1184   if (_fullscreen) {
1185     _fullscreen = false;
1186     fullscreen(true);
1187   }
1188   if (_shaded) {
1189     _shaded = false;
1190     shade(true);
1191   }
1192   if (_urgent)
1193     fireUrgent();
1194   
1195   if (_max_vert); // XXX: incomplete
1196   if (_max_horz); // XXX: incomplete
1197
1198   if (_skip_taskbar); // nothing to do for this
1199   if (_skip_pager);   // nothing to do for this
1200   if (_modal);        // nothing to do for this
1201   if (_above);        // nothing to do for this
1202   if (_below);        // nothing to do for this
1203 }
1204
1205
1206 void Client::fireUrgent()
1207 {
1208   // call the python UrgentNotify callbacks
1209   EventData data(_screen, this, EventUrgentNotify, 0);
1210   openbox->bindings()->fireEvent(&data);
1211 }
1212
1213
1214 void Client::shade(bool shade)
1215 {
1216   if (!(_functions & Func_Shade) || // can't
1217       _shaded == shade) return;     // already done
1218
1219   // when we're iconic, don't change the wmstate
1220   if (!_iconic)
1221     _wmstate = shade ? IconicState : NormalState;
1222   _shaded = shade;
1223   changeState();
1224   frame->adjustSize();
1225 }
1226
1227
1228 void Client::fullscreen(bool fs)
1229 {
1230   static FunctionFlags saved_func;
1231   static DecorationFlags saved_decor;
1232   static otk::Rect saved_area;
1233   static otk::Point saved_logical_size;
1234
1235   if (!(_functions & Func_Fullscreen) || // can't
1236       _fullscreen == fs) return;         // already done
1237
1238   _fullscreen = fs;
1239   changeState(); // change the state hints on the client
1240
1241   if (fs) {
1242     // save the functions and remove them
1243     saved_func = _functions;
1244     _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1245     // save the decorations and remove them
1246     saved_decor = _decorations;
1247     _decorations = 0;
1248     // save the area and adjust it (we don't call internal resize here for
1249     // constraints on the size, etc, we just make it fullscreen).
1250     saved_area = _area;
1251     const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1252     _area.setRect(0, 0, info->width(), info->height());
1253     saved_logical_size = _logical_size;
1254     _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
1255                            (info->height() - _base_size.y()) / _size_inc.y());
1256   } else {
1257     _functions = saved_func;
1258     _decorations = saved_decor;
1259     _area = saved_area;
1260     _logical_size = saved_logical_size;
1261   }
1262   
1263   changeAllowedActions();  // based on the new _functions
1264   
1265   frame->adjustSize();     // drop/replace the decor's and resize
1266   frame->adjustPosition(); // get (back) in position!
1267
1268   // raise (back) into our stacking layer
1269   openbox->screen(_screen)->raiseWindow(this);
1270
1271   // try focus us when we go into fullscreen mode
1272   if (fs) focus();
1273 }
1274
1275
1276 bool Client::focus()
1277 {
1278   // won't try focus if the client doesn't want it, or if the window isn't
1279   // visible on the screen
1280   if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1281
1282   if (_focused) return true;
1283
1284   // do a check to see if the window has already been unmapped or destroyed
1285   // do this intelligently while watching out for unmaps we've generated
1286   // (ignore_unmaps > 0)
1287   XEvent ev;
1288   if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1289     XPutBackEvent(**otk::display, &ev);
1290     return false;
1291   }
1292   while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1293     if (ignore_unmaps) {
1294       unmapHandler(ev.xunmap);
1295     } else {
1296       XPutBackEvent(**otk::display, &ev);
1297       return false;
1298     }
1299   }
1300
1301   if (_can_focus)
1302     XSetInputFocus(**otk::display, _window,
1303                    RevertToNone, CurrentTime);
1304
1305   if (_focus_notify) {
1306     XEvent ce;
1307     ce.xclient.type = ClientMessage;
1308     ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1309     ce.xclient.display = **otk::display;
1310     ce.xclient.window = _window;
1311     ce.xclient.format = 32;
1312     ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1313     ce.xclient.data.l[1] = openbox->lastTime();
1314     ce.xclient.data.l[2] = 0l;
1315     ce.xclient.data.l[3] = 0l;
1316     ce.xclient.data.l[4] = 0l;
1317     XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1318   }
1319
1320   return true;
1321 }
1322
1323
1324 void Client::unfocus() const
1325 {
1326   if (!_focused) return;
1327
1328   assert(openbox->focusedClient() == this);
1329   openbox->setFocusedClient(0);
1330 }
1331
1332
1333 void Client::focusHandler(const XFocusChangeEvent &e)
1334 {
1335 #ifdef    DEBUG
1336 //  printf("FocusIn for 0x%lx\n", e.window);
1337 #endif // DEBUG
1338   
1339   otk::EventHandler::focusHandler(e);
1340
1341   frame->focus();
1342   _focused = true;
1343
1344   openbox->setFocusedClient(this);
1345 }
1346
1347
1348 void Client::unfocusHandler(const XFocusChangeEvent &e)
1349 {
1350 #ifdef    DEBUG
1351 //  printf("FocusOut for 0x%lx\n", e.window);
1352 #endif // DEBUG
1353   
1354   otk::EventHandler::unfocusHandler(e);
1355
1356   frame->unfocus();
1357   _focused = false;
1358
1359   if (openbox->focusedClient() == this)
1360     openbox->setFocusedClient(0);
1361 }
1362
1363
1364 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1365 {
1366 #ifdef    DEBUG
1367   printf("ConfigureRequest for 0x%lx\n", e.window);
1368 #endif // DEBUG
1369   
1370   otk::EventHandler::configureRequestHandler(e);
1371
1372   // if we are iconic (or shaded (fvwm does this)) ignore the event
1373   if (_iconic || _shaded) return;
1374
1375   if (e.value_mask & CWBorderWidth)
1376     _border_width = e.border_width;
1377
1378   // resize, then move, as specified in the EWMH section 7.7
1379   if (e.value_mask & (CWWidth | CWHeight)) {
1380     int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1381     int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1382
1383     Corner corner;
1384     switch (_gravity) {
1385     case NorthEastGravity:
1386     case EastGravity:
1387       corner = TopRight;
1388       break;
1389     case SouthWestGravity:
1390     case SouthGravity:
1391       corner = BottomLeft;
1392       break;
1393     case SouthEastGravity:
1394       corner = BottomRight;
1395       break;
1396     default:     // NorthWest, Static, etc
1397       corner = TopLeft;
1398     }
1399
1400     // if moving AND resizing ...
1401     if (e.value_mask & (CWX | CWY)) {
1402       int x = (e.value_mask & CWX) ? e.x : _area.x();
1403       int y = (e.value_mask & CWY) ? e.y : _area.y();
1404       internal_resize(corner, w, h, x, y);
1405     } else // if JUST resizing...
1406       internal_resize(corner, w, h);
1407   } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1408     int x = (e.value_mask & CWX) ? e.x : _area.x();
1409     int y = (e.value_mask & CWY) ? e.y : _area.y();
1410     internal_move(x, y);
1411   }
1412
1413   if (e.value_mask & CWStackMode) {
1414     switch (e.detail) {
1415     case Below:
1416     case BottomIf:
1417       openbox->screen(_screen)->lowerWindow(this);
1418       break;
1419
1420     case Above:
1421     case TopIf:
1422     default:
1423       openbox->screen(_screen)->raiseWindow(this);
1424       break;
1425     }
1426   }
1427 }
1428
1429
1430 void Client::unmapHandler(const XUnmapEvent &e)
1431 {
1432   if (ignore_unmaps) {
1433 #ifdef    DEBUG
1434     printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1435 #endif // DEBUG
1436     ignore_unmaps--;
1437     return;
1438   }
1439   
1440 #ifdef    DEBUG
1441   printf("UnmapNotify for 0x%lx\n", e.window);
1442 #endif // DEBUG
1443
1444   otk::EventHandler::unmapHandler(e);
1445
1446   // this deletes us etc
1447   openbox->screen(_screen)->unmanageWindow(this);
1448 }
1449
1450
1451 void Client::destroyHandler(const XDestroyWindowEvent &e)
1452 {
1453 #ifdef    DEBUG
1454   printf("DestroyNotify for 0x%lx\n", e.window);
1455 #endif // DEBUG
1456
1457   otk::EventHandler::destroyHandler(e);
1458
1459   // this deletes us etc
1460   openbox->screen(_screen)->unmanageWindow(this);
1461 }
1462
1463
1464 void Client::reparentHandler(const XReparentEvent &e)
1465 {
1466   // this is when the client is first taken captive in the frame
1467   if (e.parent == frame->plate()) return;
1468
1469 #ifdef    DEBUG
1470   printf("ReparentNotify for 0x%lx\n", e.window);
1471 #endif // DEBUG
1472
1473   otk::EventHandler::reparentHandler(e);
1474
1475   /*
1476     This event is quite rare and is usually handled in unmapHandler.
1477     However, if the window is unmapped when the reparent event occurs,
1478     the window manager never sees it because an unmap event is not sent
1479     to an already unmapped window.
1480   */
1481
1482   // we don't want the reparent event, put it back on the stack for the X
1483   // server to deal with after we unmanage the window
1484   XEvent ev;
1485   ev.xreparent = e;
1486   XPutBackEvent(**otk::display, &ev);
1487   
1488   // this deletes us etc
1489   openbox->screen(_screen)->unmanageWindow(this);
1490 }
1491
1492 void Client::mapRequestHandler(const XMapRequestEvent &e)
1493 {
1494 #ifdef    DEBUG
1495   printf("MapRequest for already managed 0x%lx\n", e.window);
1496 #endif // DEBUG
1497
1498   assert(_iconic); // we shouldn't be able to get this unless we're iconic
1499
1500   // move to the current desktop (uniconify)
1501   setDesktop(openbox->screen(_screen)->desktop());
1502   // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1503 }
1504
1505 }