1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
11 #include "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
23 #define _(str) gettext(str)
30 Client::Client(int screen
, Window window
)
31 : otk::EventHandler(),
32 WidgetBase(WidgetBase::Type_Client
),
33 frame(0), _screen(screen
), _window(window
)
40 // update EVERYTHING the first time!!
42 // we default to NormalState, visible
43 _wmstate
= NormalState
;
46 // not a transient by default of course
48 // pick a layer to start from
49 _layer
= Layer_Normal
;
50 // default to not urgent
52 // not positioned unless specified
54 // nothing is disabled unless specified
55 _disabled_decorations
= 0;
69 getGravity(); // get the attribute gravity
70 updateNormalHints(); // this may override the attribute gravity
72 // got the type, the mwmhints, the protocols, and the normal hints (min/max
73 // sizes), so we're ready to set up
74 // the decorations/functions
75 setupDecorAndFunctions();
77 // also get the initial_state and set _iconic if we aren't "starting"
78 // when we're "starting" that means we should use whatever state was already
79 // on the window over the initial map state, because it was already mapped
80 updateWMHints(openbox
->state() != Openbox::State_Starting
);
86 // this makes sure that these windows appear on all desktops
87 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
88 _desktop
= 0xffffffff;
90 // set the desktop hint, to make sure that it always exists, and to reflect
91 // any changes we've made here
92 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
93 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
101 // clean up childrens' references
102 while (!_transients
.empty()) {
103 _transients
.front()->_transient_for
= 0;
104 _transients
.pop_front();
107 // clean up parents reference to this
109 _transient_for
->_transients
.remove(this); // remove from old parent
111 if (openbox
->state() != Openbox::State_Exiting
) {
112 // these values should not be persisted across a window unmapping/mapping
113 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
114 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
116 // if we're left in an iconic state, the client wont be mapped. this is
117 // bad, since we will no longer be managing the window on restart
119 XMapWindow(**otk::display
, _window
);
124 bool Client::validate() const
126 XSync(**otk::display
, false); // get all events on the server
129 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
130 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
131 XPutBackEvent(**otk::display
, &e
);
139 void Client::getGravity()
141 XWindowAttributes wattrib
;
144 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
145 assert(ret
!= BadWindow
);
146 _gravity
= wattrib
.win_gravity
;
150 void Client::getDesktop()
152 // defaults to the current desktop
153 _desktop
= openbox
->screen(_screen
)->desktop();
155 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
156 otk::Property::atoms
.cardinal
,
157 (long unsigned*)&_desktop
)) {
159 // printf("Window requested desktop: %ld\n", _desktop);
165 void Client::getType()
167 _type
= (WindowType
) -1;
170 unsigned long num
= (unsigned) -1;
171 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
172 otk::Property::atoms
.atom
, &num
, &val
)) {
173 // use the first value that we know about in the array
174 for (unsigned long i
= 0; i
< num
; ++i
) {
175 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
176 _type
= Type_Desktop
;
177 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
179 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
180 _type
= Type_Toolbar
;
181 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
183 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
184 _type
= Type_Utility
;
185 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
187 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
189 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
191 // XXX: make this work again
192 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
193 // mwm_decorations = 0; // prevent this window from getting any decor
194 if (_type
!= (WindowType
) -1)
195 break; // grab the first known type
200 if (_type
== (WindowType
) -1) {
202 * the window type hint was not set, which means we either classify ourself
203 * as a normal window or a dialog, depending on if we are a transient.
213 void Client::setupDecorAndFunctions()
215 // start with everything (cept fullscreen)
216 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
217 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
218 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
220 if (_delete_window
) {
221 _decorations
|= Decor_Close
;
222 _functions
|= Func_Close
;
225 if (!(_min_size
.x() < _max_size
.x() || _min_size
.y() < _max_size
.y())) {
226 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
227 _functions
&= ~(Func_Resize
| Func_Maximize
);
232 // normal windows retain all of the possible decorations and
233 // functionality, and are the only windows that you can fullscreen
234 _functions
|= Func_Fullscreen
;
238 // dialogs cannot be maximized
239 _decorations
&= ~Decor_Maximize
;
240 _functions
&= ~Func_Maximize
;
246 // these windows get less functionality
247 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
248 _functions
&= ~(Func_Iconify
| Func_Resize
);
254 // none of these windows are manipulated by the window manager
260 // Mwm Hints are applied subtractively to what has already been chosen for
261 // decor and functionality
262 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
263 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
264 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
265 _decorations
&= ~Decor_Border
;
266 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
267 _decorations
&= ~Decor_Handle
;
268 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
269 _decorations
&= ~Decor_Titlebar
;
270 // if we don't have a titlebar, then we cannot shade!
271 _functions
&= ~Func_Shade
;
273 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
274 _decorations
&= ~Decor_Iconify
;
275 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
276 _decorations
&= ~Decor_Maximize
;
280 if (_mwmhints
.flags
& MwmFlag_Functions
) {
281 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
282 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
283 _functions
&= ~Func_Resize
;
284 if (! (_mwmhints
.functions
& MwmFunc_Move
))
285 _functions
&= ~Func_Move
;
286 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
287 _functions
&= ~Func_Iconify
;
288 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
289 _functions
&= ~Func_Maximize
;
290 // dont let mwm hints kill the close button
291 //if (! (_mwmhints.functions & MwmFunc_Close))
292 // _functions &= ~Func_Close;
296 // finally, user specified disabled decorations are applied to subtract
298 if (_disabled_decorations
& Decor_Titlebar
)
299 _decorations
&= ~Decor_Titlebar
;
300 if (_disabled_decorations
& Decor_Handle
)
301 _decorations
&= ~Decor_Handle
;
302 if (_disabled_decorations
& Decor_Border
)
303 _decorations
&= ~Decor_Border
;
304 if (_disabled_decorations
& Decor_Iconify
)
305 _decorations
&= ~Decor_Iconify
;
306 if (_disabled_decorations
& Decor_Maximize
)
307 _decorations
&= ~Decor_Maximize
;
308 if (_disabled_decorations
& Decor_AllDesktops
)
309 _decorations
&= ~Decor_AllDesktops
;
310 if (_disabled_decorations
& Decor_Close
)
311 _decorations
&= ~Decor_Close
;
313 // You can't shade without a titlebar
314 if (!(_decorations
& Decor_Titlebar
))
315 _functions
&= ~Func_Shade
;
317 changeAllowedActions();
320 frame
->adjustSize(); // change the decors on the frame
321 frame
->adjustPosition(); // with more/less decorations, we may need to be
327 void Client::getMwmHints()
329 unsigned long num
= MwmHints::elements
;
330 unsigned long *hints
;
332 _mwmhints
.flags
= 0; // default to none
334 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
335 otk::Property::atoms
.motif_wm_hints
, &num
,
336 (unsigned long **)&hints
))
339 if (num
>= MwmHints::elements
) {
340 // retrieved the hints
341 _mwmhints
.flags
= hints
[0];
342 _mwmhints
.functions
= hints
[1];
343 _mwmhints
.decorations
= hints
[2];
350 void Client::getArea()
352 XWindowAttributes wattrib
;
355 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
356 assert(ret
!= BadWindow
);
358 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
359 _border_width
= wattrib
.border_width
;
363 void Client::getState()
365 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
366 _iconic
= _skip_taskbar
= _skip_pager
= false;
368 unsigned long *state
;
369 unsigned long num
= (unsigned) -1;
371 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
372 otk::Property::atoms
.atom
, &num
, &state
)) {
373 for (unsigned long i
= 0; i
< num
; ++i
) {
374 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
376 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
378 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
380 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
381 _skip_taskbar
= true;
382 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
384 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
386 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
388 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
390 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
392 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
401 void Client::getShaped()
405 if (otk::display
->shape()) {
410 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
412 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
413 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
420 void Client::calcLayer() {
423 if (_iconic
) l
= Layer_Icon
;
424 else if (_fullscreen
) l
= Layer_Fullscreen
;
425 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
426 else if (_type
== Type_Dock
) {
427 if (!_below
) l
= Layer_Top
;
428 else l
= Layer_Normal
;
430 else if (_above
) l
= Layer_Above
;
431 else if (_below
) l
= Layer_Below
;
432 else l
= Layer_Normal
;
438 if we don't have a frame, then we aren't mapped yet (and this would
441 openbox
->screen(_screen
)->raiseWindow(this);
447 void Client::updateProtocols()
452 _focus_notify
= false;
453 _delete_window
= false;
455 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
456 for (int i
= 0; i
< num_return
; ++i
) {
457 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
458 // this means we can request the window to close
459 _delete_window
= true;
460 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
461 // if this protocol is requested, then the window will be notified
462 // by the window manager whenever it receives focus
463 _focus_notify
= true;
470 void Client::updateNormalHints()
474 int oldgravity
= _gravity
;
479 _size_inc
.setPoint(1, 1);
480 _base_size
.setPoint(0, 0);
481 _min_size
.setPoint(0, 0);
482 _max_size
.setPoint(INT_MAX
, INT_MAX
);
484 // XXX: might want to cancel any interactive resizing of the window at this
487 // get the hints from the window
488 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
489 _positioned
= (size
.flags
& (PPosition
|USPosition
));
491 if (size
.flags
& PWinGravity
) {
492 _gravity
= size
.win_gravity
;
494 // if the client has a frame, i.e. has already been mapped and is
495 // changing its gravity
496 if (frame
&& _gravity
!= oldgravity
) {
497 // move our idea of the client's position based on its new gravity
499 frame
->frameGravity(x
, y
);
504 if (size
.flags
& PAspect
) {
505 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
506 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
509 if (size
.flags
& PMinSize
)
510 _min_size
.setPoint(size
.min_width
, size
.min_height
);
512 if (size
.flags
& PMaxSize
)
513 _max_size
.setPoint(size
.max_width
, size
.max_height
);
515 if (size
.flags
& PBaseSize
)
516 _base_size
.setPoint(size
.base_width
, size
.base_height
);
518 if (size
.flags
& PResizeInc
)
519 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
524 void Client::updateWMHints(bool initstate
)
528 // assume a window takes input if it doesnt specify
532 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
533 if (hints
->flags
& InputHint
)
534 _can_focus
= hints
->input
;
536 // only do this when initstate is true!
537 if (initstate
&& (hints
->flags
& StateHint
))
538 _iconic
= hints
->initial_state
== IconicState
;
540 if (hints
->flags
& XUrgencyHint
)
543 if (hints
->flags
& WindowGroupHint
) {
544 if (hints
->window_group
!= _group
) {
545 // XXX: remove from the old group if there was one
546 _group
= hints
->window_group
;
547 // XXX: do stuff with the group
558 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
559 (long)_window
, _urgent
? "ON" : "OFF");
561 // fire the urgent callback if we're mapped, otherwise, wait until after
569 void Client::updateTitle()
574 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
575 otk::Property::utf8
, &_title
)) {
577 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
578 otk::Property::ascii
, &_title
);
582 _title
= _("Unnamed Window");
585 frame
->setTitle(_title
);
589 void Client::updateIconTitle()
594 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
595 otk::Property::utf8
, &_icon_title
)) {
597 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
598 otk::Property::ascii
, &_icon_title
);
602 _icon_title
= _("Unnamed Window");
606 void Client::updateClass()
609 _app_name
= _app_class
= _role
= "";
611 otk::Property::StringVect v
;
612 unsigned long num
= 2;
614 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
615 otk::Property::ascii
, &num
, &v
)) {
616 if (num
> 0) _app_name
= v
[0].c_str();
617 if (num
> 1) _app_class
= v
[1].c_str();
622 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
623 otk::Property::ascii
, &num
, &v
)) {
624 if (num
> 0) _role
= v
[0].c_str();
629 void Client::updateStrut()
631 unsigned long num
= 4;
633 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
634 otk::Property::atoms
.cardinal
, &num
, &data
))
638 _strut
.left
= data
[0];
639 _strut
.right
= data
[1];
640 _strut
.top
= data
[2];
641 _strut
.bottom
= data
[3];
643 // updating here is pointless while we're being mapped cuz we're not in
644 // the screen's client list yet
646 openbox
->screen(_screen
)->updateStrut();
653 void Client::updateTransientFor()
658 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
659 t
!= _window
) { // cant be transient to itself!
660 c
= openbox
->findClient(t
);
661 assert(c
!= this); // if this happens then we need to check for it
663 if (!c
/*XXX: && _group*/) {
664 // not transient to a client, see if it is transient for a group
665 if (//t == _group->leader() ||
667 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
668 // window is a transient for its group!
669 // XXX: for now this is treated as non-transient.
670 // this needs to be fixed!
675 // if anything has changed...
676 if (c
!= _transient_for
) {
678 _transient_for
->_transients
.remove(this); // remove from old parent
681 _transient_for
->_transients
.push_back(this); // add to new parent
683 // XXX: change decor status?
688 void Client::propertyHandler(const XPropertyEvent
&e
)
690 otk::EventHandler::propertyHandler(e
);
692 // validate cuz we query stuff off the client here
693 if (!validate()) return;
695 // compress changes to a single property into a single change
697 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
698 // XXX: it would be nice to compress ALL changes to a property, not just
699 // changes in a row without other props between.
700 if (ce
.xproperty
.atom
!= e
.atom
) {
701 XPutBackEvent(**otk::display
, &ce
);
706 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
708 setupDecorAndFunctions(); // normal hints can make a window non-resizable
709 } else if (e
.atom
== XA_WM_HINTS
)
711 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
712 updateTransientFor();
714 calcLayer(); // type may have changed, so update the layer
715 setupDecorAndFunctions();
717 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
718 e
.atom
== otk::Property::atoms
.wm_name
)
720 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
721 e
.atom
== otk::Property::atoms
.wm_icon_name
)
723 else if (e
.atom
== otk::Property::atoms
.wm_class
)
725 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
727 setupDecorAndFunctions();
729 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
734 void Client::setWMState(long state
)
736 if (state
== _wmstate
) return; // no change
740 setDesktop(ICONIC_DESKTOP
);
743 setDesktop(openbox
->screen(_screen
)->desktop());
749 void Client::setDesktop(long target
)
751 if (target
== _desktop
) return;
753 printf("Setting desktop %ld\n", target
);
755 if (!(target
>= 0 || target
== (signed)0xffffffff ||
756 target
== ICONIC_DESKTOP
))
761 // set the desktop hint, but not if we're iconifying
762 if (_desktop
!= ICONIC_DESKTOP
)
763 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
764 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
766 // 'move' the window to the new desktop
767 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
768 _desktop
== (signed)0xffffffff)
773 // Handle Iconic state. Iconic state is maintained by the client being a
774 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
775 // uniconifying happen.
776 bool i
= _desktop
== ICONIC_DESKTOP
;
777 if (i
!= _iconic
) { // has the state changed?
780 _wmstate
= IconicState
;
782 // we unmap the client itself so that we can get MapRequest events, and
783 // because the ICCCM tells us to!
784 XUnmapWindow(**otk::display
, _window
);
786 _wmstate
= NormalState
;
787 XMapWindow(**otk::display
, _window
);
792 frame
->adjustState();
796 void Client::setState(StateAction action
, long data1
, long data2
)
798 bool shadestate
= _shaded
;
799 bool fsstate
= _fullscreen
;
800 bool maxh
= _max_horz
;
801 bool maxv
= _max_vert
;
803 if (!(action
== State_Add
|| action
== State_Remove
||
804 action
== State_Toggle
))
805 return; // an invalid action was passed to the client message, ignore it
807 for (int i
= 0; i
< 2; ++i
) {
808 Atom state
= i
== 0 ? data1
: data2
;
810 if (! state
) continue;
812 // if toggling, then pick whether we're adding or removing
813 if (action
== State_Toggle
) {
814 if (state
== otk::Property::atoms
.net_wm_state_modal
)
815 action
= _modal
? State_Remove
: State_Add
;
816 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
817 action
= _max_vert
? State_Remove
: State_Add
;
818 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
819 action
= _max_horz
? State_Remove
: State_Add
;
820 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
821 action
= _shaded
? State_Remove
: State_Add
;
822 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
823 action
= _skip_taskbar
? State_Remove
: State_Add
;
824 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
825 action
= _skip_pager
? State_Remove
: State_Add
;
826 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
827 action
= _fullscreen
? State_Remove
: State_Add
;
828 else if (state
== otk::Property::atoms
.net_wm_state_above
)
829 action
= _above
? State_Remove
: State_Add
;
830 else if (state
== otk::Property::atoms
.net_wm_state_below
)
831 action
= _below
? State_Remove
: State_Add
;
834 if (action
== State_Add
) {
835 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
836 if (_modal
) continue;
838 // XXX: give it focus if another window has focus that shouldnt now
839 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
841 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
842 if (_max_horz
) continue;
844 // XXX: resize the window etc
845 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
847 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
848 _skip_taskbar
= true;
849 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
851 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
853 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
854 if (_above
) continue;
856 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
857 if (_below
) continue;
861 } else { // action == State_Remove
862 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
863 if (!_modal
) continue;
865 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
867 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
869 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
871 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
872 _skip_taskbar
= false;
873 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
875 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
877 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
878 if (!_above
) continue;
880 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
881 if (!_below
) continue;
886 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
887 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
888 if (maxh
== maxv
) { // both going the same way
889 maximize(maxh
, 0, true);
891 maximize(maxh
, 1, true);
892 maximize(maxv
, 2, true);
894 } else { // toggling one
895 if (maxh
!= _max_horz
)
896 maximize(maxh
, 1, true);
898 maximize(maxv
, 2, true);
901 // change fullscreen state before shading, as it will affect if the window
903 if (fsstate
!= _fullscreen
)
904 fullscreen(fsstate
, true);
905 if (shadestate
!= _shaded
)
908 changeState(); // change the hint to relect these changes
912 void Client::toggleClientBorder(bool addborder
)
914 // adjust our idea of where the client is, based on its border. When the
915 // border is removed, the client should now be considered to be in a
916 // different position.
917 // when re-adding the border to the client, the same operation needs to be
919 int oldx
= _area
.x(), oldy
= _area
.y();
920 int x
= oldx
, y
= oldy
;
923 case NorthWestGravity
:
925 case SouthWestGravity
:
927 case NorthEastGravity
:
929 case SouthEastGravity
:
930 if (addborder
) x
-= _border_width
* 2;
931 else x
+= _border_width
* 2;
938 if (addborder
) x
-= _border_width
;
939 else x
+= _border_width
;
944 case NorthWestGravity
:
946 case NorthEastGravity
:
948 case SouthWestGravity
:
950 case SouthEastGravity
:
951 if (addborder
) y
-= _border_width
* 2;
952 else y
+= _border_width
* 2;
959 if (addborder
) y
-= _border_width
;
960 else y
+= _border_width
;
966 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
968 // move the client so it is back it the right spot _with_ its border!
969 if (x
!= oldx
|| y
!= oldy
)
970 XMoveWindow(**otk::display
, _window
, x
, y
);
972 XSetWindowBorderWidth(**otk::display
, _window
, 0);
976 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
978 otk::EventHandler::clientMessageHandler(e
);
980 // validate cuz we query stuff off the client here
981 if (!validate()) return;
983 if (e
.format
!= 32) return;
985 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
986 // compress changes into a single change
987 bool compress
= false;
989 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
990 // XXX: it would be nice to compress ALL messages of a type, not just
991 // messages in a row without other message types between.
992 if (ce
.xclient
.message_type
!= e
.message_type
) {
993 XPutBackEvent(**otk::display
, &ce
);
999 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1001 setWMState(e
.data
.l
[0]); // use the original event
1002 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1003 // compress changes into a single change
1004 bool compress
= false;
1006 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1007 // XXX: it would be nice to compress ALL messages of a type, not just
1008 // messages in a row without other message types between.
1009 if (ce
.xclient
.message_type
!= e
.message_type
) {
1010 XPutBackEvent(**otk::display
, &ce
);
1016 setDesktop(e
.data
.l
[0]); // use the found event
1018 setDesktop(e
.data
.l
[0]); // use the original event
1019 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1020 // can't compress these
1022 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1023 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1024 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1025 e
.data
.l
[1], e
.data
.l
[2], _window
);
1027 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1028 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1030 printf("net_close_window for 0x%lx\n", _window
);
1033 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1035 printf("net_active_window for 0x%lx\n", _window
);
1038 setDesktop(openbox
->screen(_screen
)->desktop());
1043 openbox
->screen(_screen
)->raiseWindow(this);
1049 void Client::shapeHandler(const XShapeEvent
&e
)
1051 otk::EventHandler::shapeHandler(e
);
1053 if (e
.kind
== ShapeBounding
) {
1055 frame
->adjustShape();
1061 void Client::resize(Corner anchor
, int w
, int h
)
1063 if (!(_functions
& Func_Resize
)) return;
1064 internal_resize(anchor
, w
, h
);
1068 void Client::internal_resize(Corner anchor
, int w
, int h
, bool user
,
1071 w
-= _base_size
.x();
1072 h
-= _base_size
.y();
1075 // for interactive resizing. have to move half an increment in each
1077 int mw
= w
% _size_inc
.x(); // how far we are towards the next size inc
1078 int mh
= h
% _size_inc
.y();
1079 int aw
= _size_inc
.x() / 2; // amount to add
1080 int ah
= _size_inc
.y() / 2;
1081 // don't let us move into a new size increment
1082 if (mw
+ aw
>= _size_inc
.x()) aw
= _size_inc
.x() - mw
- 1;
1083 if (mh
+ ah
>= _size_inc
.y()) ah
= _size_inc
.y() - mh
- 1;
1087 // if this is a user-requested resize, then check against min/max sizes
1088 // and aspect ratios
1090 // smaller than min size or bigger than max size?
1091 if (w
< _min_size
.x()) w
= _min_size
.x();
1092 else if (w
> _max_size
.x()) w
= _max_size
.x();
1093 if (h
< _min_size
.y()) h
= _min_size
.y();
1094 else if (h
> _max_size
.y()) h
= _max_size
.y();
1096 // adjust the height ot match the width for the aspect ratios
1098 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1100 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1103 // keep to the increments
1107 // you cannot resize to nothing
1111 // store the logical size
1112 _logical_size
.setPoint(w
, h
);
1117 w
+= _base_size
.x();
1118 h
+= _base_size
.y();
1120 if (x
== INT_MIN
|| y
== INT_MIN
) {
1127 x
-= w
- _area
.width();
1130 y
-= h
- _area
.height();
1133 x
-= w
- _area
.width();
1134 y
-= h
- _area
.height();
1139 _area
.setSize(w
, h
);
1141 XResizeWindow(**otk::display
, _window
, w
, h
);
1143 // resize the frame to match the request
1144 frame
->adjustSize();
1145 internal_move(x
, y
);
1149 void Client::move(int x
, int y
)
1151 if (!(_functions
& Func_Move
)) return;
1152 internal_move(x
, y
);
1156 void Client::internal_move(int x
, int y
)
1160 // move the frame to be in the requested position
1161 if (frame
) { // this can be called while mapping, before frame exists
1162 frame
->adjustPosition();
1164 // send synthetic configure notify (we don't need to if we aren't mapped
1167 event
.type
= ConfigureNotify
;
1168 event
.xconfigure
.display
= **otk::display
;
1169 event
.xconfigure
.event
= _window
;
1170 event
.xconfigure
.window
= _window
;
1172 // root window coords with border in mind
1173 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1174 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1176 event
.xconfigure
.width
= _area
.width();
1177 event
.xconfigure
.height
= _area
.height();
1178 event
.xconfigure
.border_width
= _border_width
;
1179 event
.xconfigure
.above
= frame
->plate();
1180 event
.xconfigure
.override_redirect
= False
;
1181 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1182 StructureNotifyMask
, &event
);
1184 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1185 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1186 event
.xconfigure
.height
, event
.xconfigure
.window
);
1192 void Client::close()
1196 if (!(_functions
& Func_Close
)) return;
1198 // XXX: itd be cool to do timeouts and shit here for killing the client's
1200 // like... if the window is around after 5 seconds, then the close button
1201 // turns a nice red, and if this function is called again, the client is
1202 // explicitly killed.
1204 ce
.xclient
.type
= ClientMessage
;
1205 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1206 ce
.xclient
.display
= **otk::display
;
1207 ce
.xclient
.window
= _window
;
1208 ce
.xclient
.format
= 32;
1209 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1210 ce
.xclient
.data
.l
[1] = CurrentTime
;
1211 ce
.xclient
.data
.l
[2] = 0l;
1212 ce
.xclient
.data
.l
[3] = 0l;
1213 ce
.xclient
.data
.l
[4] = 0l;
1214 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1218 void Client::changeState()
1220 unsigned long state
[2];
1221 state
[0] = _wmstate
;
1223 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1224 otk::Property::atoms
.wm_state
, state
, 2);
1229 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1231 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1233 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1235 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1237 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1239 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1241 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1243 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1245 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1247 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1248 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1249 otk::Property::atoms
.atom
, netstate
, num
);
1254 frame
->adjustState();
1258 void Client::changeAllowedActions(void)
1263 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1265 if (_functions
& Func_Shade
)
1266 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1267 if (_functions
& Func_Close
)
1268 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1269 if (_functions
& Func_Move
)
1270 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1271 if (_functions
& Func_Iconify
)
1272 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1273 if (_functions
& Func_Resize
)
1274 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1275 if (_functions
& Func_Fullscreen
)
1276 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1277 if (_functions
& Func_Maximize
) {
1278 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1279 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1282 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1283 otk::Property::atoms
.atom
, actions
, num
);
1287 void Client::applyStartupState()
1289 // these are in a carefully crafted order..
1293 setDesktop(ICONIC_DESKTOP
);
1296 _fullscreen
= false;
1297 fullscreen(true, false);
1306 if (_max_vert
&& _max_horz
) {
1307 _max_vert
= _max_horz
= false;
1308 maximize(true, 0, false);
1309 } else if (_max_vert
) {
1311 maximize(true, 2, false);
1312 } else if (_max_horz
) {
1314 maximize(true, 1, false);
1317 if (_skip_taskbar
); // nothing to do for this
1318 if (_skip_pager
); // nothing to do for this
1319 if (_modal
); // nothing to do for this
1320 if (_above
); // nothing to do for this
1321 if (_below
); // nothing to do for this
1325 void Client::fireUrgent()
1327 // call the python UrgentWindow callbacks
1328 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1329 openbox
->bindings()->fireEvent(&data
);
1333 void Client::shade(bool shade
)
1335 if (!(_functions
& Func_Shade
) || // can't
1336 _shaded
== shade
) return; // already done
1338 // when we're iconic, don't change the wmstate
1340 _wmstate
= shade
? IconicState
: NormalState
;
1343 frame
->adjustSize();
1347 void Client::maximize(bool max
, int dir
, bool savearea
)
1349 assert(dir
== 0 || dir
== 1 || dir
== 2);
1350 if (!(_functions
& Func_Maximize
)) return; // can't
1352 // check if already done
1354 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1355 if (dir
== 1 && _max_horz
) return;
1356 if (dir
== 2 && _max_vert
) return;
1358 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1359 if (dir
== 1 && !_max_horz
) return;
1360 if (dir
== 2 && !_max_vert
) return;
1365 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1366 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1369 // when maximizing, put the client where we want, NOT the frame!
1370 _gravity
= StaticGravity
;
1371 // adjust our idea of position based on StaticGravity, so we stay put
1373 frame
->frameGravity(x
, y
);
1378 unsigned long n
= 4;
1385 // get the property off the window and use it for the dimentions we are
1387 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1388 otk::Property::atoms
.cardinal
, &n
,
1389 (long unsigned**) &readdim
)) {
1392 dimensions
[0] = readdim
[0];
1393 dimensions
[2] = readdim
[2];
1396 dimensions
[1] = readdim
[1];
1397 dimensions
[3] = readdim
[3];
1403 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1404 otk::Property::atoms
.cardinal
,
1405 (long unsigned*)dimensions
, 4);
1407 if (dir
== 0 || dir
== 1) { // horz
1411 if (dir
== 0 || dir
== 2) { // vert
1412 y
= a
.y() + frame
->size().top
;
1413 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1417 long unsigned n
= 4;
1419 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1420 otk::Property::atoms
.cardinal
, &n
,
1421 (long unsigned**) &dimensions
)) {
1423 if (dir
== 0 || dir
== 1) { // horz
1424 x
= (signed int)dimensions
[0];
1425 w
= (signed int)dimensions
[2];
1427 if (dir
== 0 || dir
== 2) { // vert
1428 y
= (signed int)dimensions
[1];
1429 h
= (signed int)dimensions
[3];
1434 // pick some fallbacks...
1435 if (dir
== 0 || dir
== 1) { // horz
1436 x
= a
.x() + a
.width() / 4;
1439 if (dir
== 0 || dir
== 2) { // vert
1440 y
= a
.y() + a
.height() / 4;
1444 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1447 if (dir
== 0 || dir
== 1) // horz
1449 if (dir
== 0 || dir
== 2) // vert
1451 changeState(); // change the state hints on the client
1453 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1456 // because of my little gravity trick in here, we have to set the position
1457 // of the client to what it really is
1459 frame
->frameGravity(x
, y
);
1465 void Client::fullscreen(bool fs
, bool savearea
)
1467 static FunctionFlags saved_func
;
1468 static DecorationFlags saved_decor
;
1470 if (!(_functions
& Func_Fullscreen
) || // can't
1471 _fullscreen
== fs
) return; // already done
1474 changeState(); // change the state hints on the client
1476 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1479 // save the functions and remove them
1480 saved_func
= _functions
;
1481 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1482 // save the decorations and remove them
1483 saved_decor
= _decorations
;
1487 dimensions
[0] = _area
.x();
1488 dimensions
[1] = _area
.y();
1489 dimensions
[2] = _area
.width();
1490 dimensions
[3] = _area
.height();
1491 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1492 otk::Property::atoms
.cardinal
,
1493 (long unsigned*)dimensions
, 4);
1495 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1501 _functions
= saved_func
;
1502 _decorations
= saved_decor
;
1505 long unsigned n
= 4;
1507 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1508 otk::Property::atoms
.cardinal
, &n
,
1509 (long unsigned**) &dimensions
)) {
1518 // pick some fallbacks...
1519 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1520 x
= a
.x() + a
.width() / 4;
1521 y
= a
.y() + a
.height() / 4;
1527 changeAllowedActions(); // based on the new _functions
1529 // when fullscreening, don't obey things like increments, fill the screen
1530 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1532 // raise (back) into our stacking layer
1533 openbox
->screen(_screen
)->raiseWindow(this);
1535 // try focus us when we go into fullscreen mode
1540 void Client::disableDecorations(DecorationFlags flags
)
1542 _disabled_decorations
= flags
;
1543 setupDecorAndFunctions();
1547 bool Client::focus()
1549 // won't try focus if the client doesn't want it, or if the window isn't
1550 // visible on the screen
1551 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1553 if (_focused
) return true;
1555 // do a check to see if the window has already been unmapped or destroyed
1556 // do this intelligently while watching out for unmaps we've generated
1557 // (ignore_unmaps > 0)
1559 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1560 XPutBackEvent(**otk::display
, &ev
);
1563 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1564 if (ignore_unmaps
) {
1565 unmapHandler(ev
.xunmap
);
1567 XPutBackEvent(**otk::display
, &ev
);
1573 XSetInputFocus(**otk::display
, _window
,
1574 RevertToNone
, CurrentTime
);
1576 if (_focus_notify
) {
1578 ce
.xclient
.type
= ClientMessage
;
1579 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1580 ce
.xclient
.display
= **otk::display
;
1581 ce
.xclient
.window
= _window
;
1582 ce
.xclient
.format
= 32;
1583 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1584 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1585 ce
.xclient
.data
.l
[2] = 0l;
1586 ce
.xclient
.data
.l
[3] = 0l;
1587 ce
.xclient
.data
.l
[4] = 0l;
1588 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1591 XSync(**otk::display
, False
);
1596 void Client::unfocus() const
1598 if (!_focused
) return;
1600 assert(openbox
->focusedClient() == this);
1601 openbox
->setFocusedClient(0);
1605 void Client::focusHandler(const XFocusChangeEvent
&e
)
1608 // printf("FocusIn for 0x%lx\n", e.window);
1611 otk::EventHandler::focusHandler(e
);
1616 openbox
->setFocusedClient(this);
1620 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1623 // printf("FocusOut for 0x%lx\n", e.window);
1626 otk::EventHandler::unfocusHandler(e
);
1631 if (openbox
->focusedClient() == this)
1632 openbox
->setFocusedClient(0);
1636 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1639 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1642 otk::EventHandler::configureRequestHandler(e
);
1644 // if we are iconic (or shaded (fvwm does this)) ignore the event
1645 if (_iconic
|| _shaded
) return;
1647 if (e
.value_mask
& CWBorderWidth
)
1648 _border_width
= e
.border_width
;
1650 // resize, then move, as specified in the EWMH section 7.7
1651 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1652 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1653 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1657 case NorthEastGravity
:
1661 case SouthWestGravity
:
1663 corner
= BottomLeft
;
1665 case SouthEastGravity
:
1666 corner
= BottomRight
;
1668 default: // NorthWest, Static, etc
1672 // if moving AND resizing ...
1673 if (e
.value_mask
& (CWX
| CWY
)) {
1674 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1675 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1676 internal_resize(corner
, w
, h
, false, x
, y
);
1677 } else // if JUST resizing...
1678 internal_resize(corner
, w
, h
, false);
1679 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1680 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1681 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1682 internal_move(x
, y
);
1685 if (e
.value_mask
& CWStackMode
) {
1689 openbox
->screen(_screen
)->lowerWindow(this);
1695 openbox
->screen(_screen
)->raiseWindow(this);
1702 void Client::unmapHandler(const XUnmapEvent
&e
)
1704 if (ignore_unmaps
) {
1706 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1713 printf("UnmapNotify for 0x%lx\n", e
.window
);
1716 otk::EventHandler::unmapHandler(e
);
1718 // this deletes us etc
1719 openbox
->screen(_screen
)->unmanageWindow(this);
1723 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1726 printf("DestroyNotify for 0x%lx\n", e
.window
);
1729 otk::EventHandler::destroyHandler(e
);
1731 // this deletes us etc
1732 openbox
->screen(_screen
)->unmanageWindow(this);
1736 void Client::reparentHandler(const XReparentEvent
&e
)
1738 // this is when the client is first taken captive in the frame
1739 if (e
.parent
== frame
->plate()) return;
1742 printf("ReparentNotify for 0x%lx\n", e
.window
);
1745 otk::EventHandler::reparentHandler(e
);
1748 This event is quite rare and is usually handled in unmapHandler.
1749 However, if the window is unmapped when the reparent event occurs,
1750 the window manager never sees it because an unmap event is not sent
1751 to an already unmapped window.
1754 // we don't want the reparent event, put it back on the stack for the X
1755 // server to deal with after we unmanage the window
1758 XPutBackEvent(**otk::display
, &ev
);
1760 // this deletes us etc
1761 openbox
->screen(_screen
)->unmanageWindow(this);
1764 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1767 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1770 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1772 // move to the current desktop (uniconify)
1773 setDesktop(openbox
->screen(_screen
)->desktop());
1774 // XXX: should we focus/raise the window? (basically a net_wm_active_window)