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;
62 getType(); // this can change the mwmhints for special cases
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 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
192 // prevent this window from getting any decor
193 _mwmhints
.flags
&= MwmFlag_Decorations
;
194 _mwmhints
.decorations
= 0;
196 if (_type
!= (WindowType
) -1)
197 break; // grab the first known type
202 if (_type
== (WindowType
) -1) {
204 * the window type hint was not set, which means we either classify ourself
205 * as a normal window or a dialog, depending on if we are a transient.
215 void Client::setupDecorAndFunctions()
217 // start with everything (cept fullscreen)
218 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
219 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
220 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
222 if (_delete_window
) {
223 _decorations
|= Decor_Close
;
224 _functions
|= Func_Close
;
227 if (!(_min_size
.x() < _max_size
.x() || _min_size
.y() < _max_size
.y())) {
228 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
229 _functions
&= ~(Func_Resize
| Func_Maximize
);
234 // normal windows retain all of the possible decorations and
235 // functionality, and are the only windows that you can fullscreen
236 _functions
|= Func_Fullscreen
;
240 // dialogs cannot be maximized
241 _decorations
&= ~Decor_Maximize
;
242 _functions
&= ~Func_Maximize
;
248 // these windows get less functionality
249 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
250 _functions
&= ~(Func_Iconify
| Func_Resize
);
256 // none of these windows are manipulated by the window manager
262 // Mwm Hints are applied subtractively to what has already been chosen for
263 // decor and functionality
264 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
265 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
266 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
267 _decorations
&= ~Decor_Border
;
268 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
269 _decorations
&= ~Decor_Handle
;
270 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
271 _decorations
&= ~Decor_Titlebar
;
272 // if we don't have a titlebar, then we cannot shade!
273 _functions
&= ~Func_Shade
;
275 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
276 _decorations
&= ~Decor_Iconify
;
277 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
278 _decorations
&= ~Decor_Maximize
;
282 if (_mwmhints
.flags
& MwmFlag_Functions
) {
283 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
284 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
285 _functions
&= ~Func_Resize
;
286 if (! (_mwmhints
.functions
& MwmFunc_Move
))
287 _functions
&= ~Func_Move
;
288 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
289 _functions
&= ~Func_Iconify
;
290 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
291 _functions
&= ~Func_Maximize
;
292 // dont let mwm hints kill the close button
293 //if (! (_mwmhints.functions & MwmFunc_Close))
294 // _functions &= ~Func_Close;
298 // finally, user specified disabled decorations are applied to subtract
300 if (_disabled_decorations
& Decor_Titlebar
)
301 _decorations
&= ~Decor_Titlebar
;
302 if (_disabled_decorations
& Decor_Handle
)
303 _decorations
&= ~Decor_Handle
;
304 if (_disabled_decorations
& Decor_Border
)
305 _decorations
&= ~Decor_Border
;
306 if (_disabled_decorations
& Decor_Iconify
)
307 _decorations
&= ~Decor_Iconify
;
308 if (_disabled_decorations
& Decor_Maximize
)
309 _decorations
&= ~Decor_Maximize
;
310 if (_disabled_decorations
& Decor_AllDesktops
)
311 _decorations
&= ~Decor_AllDesktops
;
312 if (_disabled_decorations
& Decor_Close
)
313 _decorations
&= ~Decor_Close
;
315 // You can't shade without a titlebar
316 if (!(_decorations
& Decor_Titlebar
))
317 _functions
&= ~Func_Shade
;
319 changeAllowedActions();
322 frame
->adjustSize(); // change the decors on the frame
323 frame
->adjustPosition(); // with more/less decorations, we may need to be
329 void Client::getMwmHints()
331 unsigned long num
= MwmHints::elements
;
332 unsigned long *hints
;
334 _mwmhints
.flags
= 0; // default to none
336 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
337 otk::Property::atoms
.motif_wm_hints
, &num
,
338 (unsigned long **)&hints
))
341 if (num
>= MwmHints::elements
) {
342 // retrieved the hints
343 _mwmhints
.flags
= hints
[0];
344 _mwmhints
.functions
= hints
[1];
345 _mwmhints
.decorations
= hints
[2];
352 void Client::getArea()
354 XWindowAttributes wattrib
;
357 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
358 assert(ret
!= BadWindow
);
360 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
361 _border_width
= wattrib
.border_width
;
365 void Client::getState()
367 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
368 _iconic
= _skip_taskbar
= _skip_pager
= false;
370 unsigned long *state
;
371 unsigned long num
= (unsigned) -1;
373 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
374 otk::Property::atoms
.atom
, &num
, &state
)) {
375 for (unsigned long i
= 0; i
< num
; ++i
) {
376 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
378 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
380 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
382 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
383 _skip_taskbar
= true;
384 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
386 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
388 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
390 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
392 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
394 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
403 void Client::getShaped()
407 if (otk::display
->shape()) {
412 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
414 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
415 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
422 void Client::calcLayer() {
425 if (_iconic
) l
= Layer_Icon
;
426 else if (_fullscreen
) l
= Layer_Fullscreen
;
427 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
428 else if (_type
== Type_Dock
) {
429 if (!_below
) l
= Layer_Top
;
430 else l
= Layer_Normal
;
432 else if (_above
) l
= Layer_Above
;
433 else if (_below
) l
= Layer_Below
;
434 else l
= Layer_Normal
;
440 if we don't have a frame, then we aren't mapped yet (and this would
443 openbox
->screen(_screen
)->raiseWindow(this);
449 void Client::updateProtocols()
454 _focus_notify
= false;
455 _delete_window
= false;
457 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
458 for (int i
= 0; i
< num_return
; ++i
) {
459 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
460 // this means we can request the window to close
461 _delete_window
= true;
462 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
463 // if this protocol is requested, then the window will be notified
464 // by the window manager whenever it receives focus
465 _focus_notify
= true;
472 void Client::updateNormalHints()
476 int oldgravity
= _gravity
;
481 _size_inc
.setPoint(1, 1);
482 _base_size
.setPoint(0, 0);
483 _min_size
.setPoint(0, 0);
484 _max_size
.setPoint(INT_MAX
, INT_MAX
);
486 // XXX: might want to cancel any interactive resizing of the window at this
489 // get the hints from the window
490 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
491 _positioned
= (size
.flags
& (PPosition
|USPosition
));
493 if (size
.flags
& PWinGravity
) {
494 _gravity
= size
.win_gravity
;
496 // if the client has a frame, i.e. has already been mapped and is
497 // changing its gravity
498 if (frame
&& _gravity
!= oldgravity
) {
499 // move our idea of the client's position based on its new gravity
500 int x
= frame
->rect().x(), y
= frame
->rect().y();
501 frame
->frameGravity(x
, y
);
506 if (size
.flags
& PAspect
) {
507 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
508 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
511 if (size
.flags
& PMinSize
)
512 _min_size
.setPoint(size
.min_width
, size
.min_height
);
514 if (size
.flags
& PMaxSize
)
515 _max_size
.setPoint(size
.max_width
, size
.max_height
);
517 if (size
.flags
& PBaseSize
)
518 _base_size
.setPoint(size
.base_width
, size
.base_height
);
520 if (size
.flags
& PResizeInc
)
521 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
526 void Client::updateWMHints(bool initstate
)
530 // assume a window takes input if it doesnt specify
534 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
535 if (hints
->flags
& InputHint
)
536 _can_focus
= hints
->input
;
538 // only do this when initstate is true!
539 if (initstate
&& (hints
->flags
& StateHint
))
540 _iconic
= hints
->initial_state
== IconicState
;
542 if (hints
->flags
& XUrgencyHint
)
545 if (hints
->flags
& WindowGroupHint
) {
546 if (hints
->window_group
!= _group
) {
547 // XXX: remove from the old group if there was one
548 _group
= hints
->window_group
;
549 // XXX: do stuff with the group
560 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
561 (long)_window
, _urgent
? "ON" : "OFF");
563 // fire the urgent callback if we're mapped, otherwise, wait until after
571 void Client::updateTitle()
576 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
577 otk::Property::utf8
, &_title
)) {
579 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
580 otk::Property::ascii
, &_title
);
584 _title
= _("Unnamed Window");
587 frame
->setTitle(_title
);
591 void Client::updateIconTitle()
596 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
597 otk::Property::utf8
, &_icon_title
)) {
599 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
600 otk::Property::ascii
, &_icon_title
);
604 _icon_title
= _("Unnamed Window");
608 void Client::updateClass()
611 _app_name
= _app_class
= _role
= "";
613 otk::Property::StringVect v
;
614 unsigned long num
= 2;
616 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
617 otk::Property::ascii
, &num
, &v
)) {
618 if (num
> 0) _app_name
= v
[0].c_str();
619 if (num
> 1) _app_class
= v
[1].c_str();
624 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
625 otk::Property::ascii
, &num
, &v
)) {
626 if (num
> 0) _role
= v
[0].c_str();
631 void Client::updateStrut()
633 unsigned long num
= 4;
635 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
636 otk::Property::atoms
.cardinal
, &num
, &data
))
640 _strut
.left
= data
[0];
641 _strut
.right
= data
[1];
642 _strut
.top
= data
[2];
643 _strut
.bottom
= data
[3];
645 // updating here is pointless while we're being mapped cuz we're not in
646 // the screen's client list yet
648 openbox
->screen(_screen
)->updateStrut();
655 void Client::updateTransientFor()
660 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
661 t
!= _window
) { // cant be transient to itself!
662 c
= openbox
->findClient(t
);
663 assert(c
!= this); // if this happens then we need to check for it
665 if (!c
/*XXX: && _group*/) {
666 // not transient to a client, see if it is transient for a group
667 if (//t == _group->leader() ||
669 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
670 // window is a transient for its group!
671 // XXX: for now this is treated as non-transient.
672 // this needs to be fixed!
677 // if anything has changed...
678 if (c
!= _transient_for
) {
680 _transient_for
->_transients
.remove(this); // remove from old parent
683 _transient_for
->_transients
.push_back(this); // add to new parent
685 // XXX: change decor status?
690 void Client::propertyHandler(const XPropertyEvent
&e
)
692 otk::EventHandler::propertyHandler(e
);
694 // validate cuz we query stuff off the client here
695 if (!validate()) return;
697 // compress changes to a single property into a single change
699 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
700 // XXX: it would be nice to compress ALL changes to a property, not just
701 // changes in a row without other props between.
702 if (ce
.xproperty
.atom
!= e
.atom
) {
703 XPutBackEvent(**otk::display
, &ce
);
708 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
710 setupDecorAndFunctions(); // normal hints can make a window non-resizable
711 } else if (e
.atom
== XA_WM_HINTS
)
713 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
714 updateTransientFor();
716 calcLayer(); // type may have changed, so update the layer
717 setupDecorAndFunctions();
719 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
720 e
.atom
== otk::Property::atoms
.wm_name
)
722 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
723 e
.atom
== otk::Property::atoms
.wm_icon_name
)
725 else if (e
.atom
== otk::Property::atoms
.wm_class
)
727 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
729 setupDecorAndFunctions();
731 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
736 void Client::setWMState(long state
)
738 if (state
== _wmstate
) return; // no change
742 setDesktop(ICONIC_DESKTOP
);
745 setDesktop(openbox
->screen(_screen
)->desktop());
751 void Client::setDesktop(long target
)
753 if (target
== _desktop
) return;
755 printf("Setting desktop %ld\n", target
);
757 if (!(target
>= 0 || target
== (signed)0xffffffff ||
758 target
== ICONIC_DESKTOP
))
763 // set the desktop hint, but not if we're iconifying
764 if (_desktop
!= ICONIC_DESKTOP
)
765 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
766 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
768 // 'move' the window to the new desktop
769 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
770 _desktop
== (signed)0xffffffff)
775 // Handle Iconic state. Iconic state is maintained by the client being a
776 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
777 // uniconifying happen.
778 bool i
= _desktop
== ICONIC_DESKTOP
;
779 if (i
!= _iconic
) { // has the state changed?
782 _wmstate
= IconicState
;
784 // we unmap the client itself so that we can get MapRequest events, and
785 // because the ICCCM tells us to!
786 XUnmapWindow(**otk::display
, _window
);
788 _wmstate
= NormalState
;
789 XMapWindow(**otk::display
, _window
);
794 frame
->adjustState();
798 void Client::setState(StateAction action
, long data1
, long data2
)
800 bool shadestate
= _shaded
;
801 bool fsstate
= _fullscreen
;
802 bool maxh
= _max_horz
;
803 bool maxv
= _max_vert
;
805 if (!(action
== State_Add
|| action
== State_Remove
||
806 action
== State_Toggle
))
807 return; // an invalid action was passed to the client message, ignore it
809 for (int i
= 0; i
< 2; ++i
) {
810 Atom state
= i
== 0 ? data1
: data2
;
812 if (! state
) continue;
814 // if toggling, then pick whether we're adding or removing
815 if (action
== State_Toggle
) {
816 if (state
== otk::Property::atoms
.net_wm_state_modal
)
817 action
= _modal
? State_Remove
: State_Add
;
818 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
819 action
= _max_vert
? State_Remove
: State_Add
;
820 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
821 action
= _max_horz
? State_Remove
: State_Add
;
822 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
823 action
= _shaded
? State_Remove
: State_Add
;
824 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
825 action
= _skip_taskbar
? State_Remove
: State_Add
;
826 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
827 action
= _skip_pager
? State_Remove
: State_Add
;
828 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
829 action
= _fullscreen
? State_Remove
: State_Add
;
830 else if (state
== otk::Property::atoms
.net_wm_state_above
)
831 action
= _above
? State_Remove
: State_Add
;
832 else if (state
== otk::Property::atoms
.net_wm_state_below
)
833 action
= _below
? State_Remove
: State_Add
;
836 if (action
== State_Add
) {
837 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
838 if (_modal
) continue;
840 // XXX: give it focus if another window has focus that shouldnt now
841 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
843 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
844 if (_max_horz
) continue;
846 // XXX: resize the window etc
847 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
849 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
850 _skip_taskbar
= true;
851 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
853 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
855 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
856 if (_above
) continue;
858 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
859 if (_below
) continue;
863 } else { // action == State_Remove
864 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
865 if (!_modal
) continue;
867 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
869 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
871 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
873 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
874 _skip_taskbar
= false;
875 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
877 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
879 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
880 if (!_above
) continue;
882 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
883 if (!_below
) continue;
888 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
889 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
890 if (maxh
== maxv
) { // both going the same way
891 maximize(maxh
, 0, true);
893 maximize(maxh
, 1, true);
894 maximize(maxv
, 2, true);
896 } else { // toggling one
897 if (maxh
!= _max_horz
)
898 maximize(maxh
, 1, true);
900 maximize(maxv
, 2, true);
903 // change fullscreen state before shading, as it will affect if the window
905 if (fsstate
!= _fullscreen
)
906 fullscreen(fsstate
, true);
907 if (shadestate
!= _shaded
)
910 changeState(); // change the hint to relect these changes
914 void Client::toggleClientBorder(bool addborder
)
916 // adjust our idea of where the client is, based on its border. When the
917 // border is removed, the client should now be considered to be in a
918 // different position.
919 // when re-adding the border to the client, the same operation needs to be
921 int oldx
= _area
.x(), oldy
= _area
.y();
922 int x
= oldx
, y
= oldy
;
925 case NorthWestGravity
:
927 case SouthWestGravity
:
929 case NorthEastGravity
:
931 case SouthEastGravity
:
932 if (addborder
) x
-= _border_width
* 2;
933 else x
+= _border_width
* 2;
940 if (addborder
) x
-= _border_width
;
941 else x
+= _border_width
;
946 case NorthWestGravity
:
948 case NorthEastGravity
:
950 case SouthWestGravity
:
952 case SouthEastGravity
:
953 if (addborder
) y
-= _border_width
* 2;
954 else y
+= _border_width
* 2;
961 if (addborder
) y
-= _border_width
;
962 else y
+= _border_width
;
968 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
970 // move the client so it is back it the right spot _with_ its border!
971 if (x
!= oldx
|| y
!= oldy
)
972 XMoveWindow(**otk::display
, _window
, x
, y
);
974 XSetWindowBorderWidth(**otk::display
, _window
, 0);
978 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
980 otk::EventHandler::clientMessageHandler(e
);
982 // validate cuz we query stuff off the client here
983 if (!validate()) return;
985 if (e
.format
!= 32) return;
987 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
988 // compress changes into a single change
989 bool compress
= false;
991 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
992 // XXX: it would be nice to compress ALL messages of a type, not just
993 // messages in a row without other message types between.
994 if (ce
.xclient
.message_type
!= e
.message_type
) {
995 XPutBackEvent(**otk::display
, &ce
);
1001 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1003 setWMState(e
.data
.l
[0]); // use the original event
1004 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1005 // compress changes into a single change
1006 bool compress
= false;
1008 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1009 // XXX: it would be nice to compress ALL messages of a type, not just
1010 // messages in a row without other message types between.
1011 if (ce
.xclient
.message_type
!= e
.message_type
) {
1012 XPutBackEvent(**otk::display
, &ce
);
1018 setDesktop(e
.data
.l
[0]); // use the found event
1020 setDesktop(e
.data
.l
[0]); // use the original event
1021 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1022 // can't compress these
1024 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1025 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1026 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1027 e
.data
.l
[1], e
.data
.l
[2], _window
);
1029 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1030 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1032 printf("net_close_window for 0x%lx\n", _window
);
1035 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1037 printf("net_active_window for 0x%lx\n", _window
);
1040 setDesktop(openbox
->screen(_screen
)->desktop());
1045 openbox
->screen(_screen
)->raiseWindow(this);
1051 void Client::shapeHandler(const XShapeEvent
&e
)
1053 otk::EventHandler::shapeHandler(e
);
1055 if (e
.kind
== ShapeBounding
) {
1057 frame
->adjustShape();
1063 void Client::resize(Corner anchor
, int w
, int h
)
1065 if (!(_functions
& Func_Resize
)) return;
1066 internal_resize(anchor
, w
, h
);
1070 void Client::internal_resize(Corner anchor
, int w
, int h
, bool user
,
1073 w
-= _base_size
.x();
1074 h
-= _base_size
.y();
1077 // for interactive resizing. have to move half an increment in each
1079 int mw
= w
% _size_inc
.x(); // how far we are towards the next size inc
1080 int mh
= h
% _size_inc
.y();
1081 int aw
= _size_inc
.x() / 2; // amount to add
1082 int ah
= _size_inc
.y() / 2;
1083 // don't let us move into a new size increment
1084 if (mw
+ aw
>= _size_inc
.x()) aw
= _size_inc
.x() - mw
- 1;
1085 if (mh
+ ah
>= _size_inc
.y()) ah
= _size_inc
.y() - mh
- 1;
1089 // if this is a user-requested resize, then check against min/max sizes
1090 // and aspect ratios
1092 // smaller than min size or bigger than max size?
1093 if (w
< _min_size
.x()) w
= _min_size
.x();
1094 else if (w
> _max_size
.x()) w
= _max_size
.x();
1095 if (h
< _min_size
.y()) h
= _min_size
.y();
1096 else if (h
> _max_size
.y()) h
= _max_size
.y();
1098 // adjust the height ot match the width for the aspect ratios
1100 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1102 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1105 // keep to the increments
1109 // you cannot resize to nothing
1113 // store the logical size
1114 _logical_size
.setPoint(w
, h
);
1119 w
+= _base_size
.x();
1120 h
+= _base_size
.y();
1122 if (x
== INT_MIN
|| y
== INT_MIN
) {
1129 x
-= w
- _area
.width();
1132 y
-= h
- _area
.height();
1135 x
-= w
- _area
.width();
1136 y
-= h
- _area
.height();
1141 _area
.setSize(w
, h
);
1143 XResizeWindow(**otk::display
, _window
, w
, h
);
1145 // resize the frame to match the request
1146 frame
->adjustSize();
1147 internal_move(x
, y
);
1151 void Client::move(int x
, int y
)
1153 if (!(_functions
& Func_Move
)) return;
1154 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1156 internal_move(x
, y
);
1160 void Client::internal_move(int x
, int y
)
1164 // move the frame to be in the requested position
1165 if (frame
) { // this can be called while mapping, before frame exists
1166 frame
->adjustPosition();
1168 // send synthetic configure notify (we don't need to if we aren't mapped
1171 event
.type
= ConfigureNotify
;
1172 event
.xconfigure
.display
= **otk::display
;
1173 event
.xconfigure
.event
= _window
;
1174 event
.xconfigure
.window
= _window
;
1176 // root window coords with border in mind
1177 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1178 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1180 event
.xconfigure
.width
= _area
.width();
1181 event
.xconfigure
.height
= _area
.height();
1182 event
.xconfigure
.border_width
= _border_width
;
1183 event
.xconfigure
.above
= frame
->plate();
1184 event
.xconfigure
.override_redirect
= False
;
1185 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1186 StructureNotifyMask
, &event
);
1188 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1189 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1190 event
.xconfigure
.height
, event
.xconfigure
.window
);
1196 void Client::close()
1200 if (!(_functions
& Func_Close
)) return;
1202 // XXX: itd be cool to do timeouts and shit here for killing the client's
1204 // like... if the window is around after 5 seconds, then the close button
1205 // turns a nice red, and if this function is called again, the client is
1206 // explicitly killed.
1208 ce
.xclient
.type
= ClientMessage
;
1209 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1210 ce
.xclient
.display
= **otk::display
;
1211 ce
.xclient
.window
= _window
;
1212 ce
.xclient
.format
= 32;
1213 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1214 ce
.xclient
.data
.l
[1] = CurrentTime
;
1215 ce
.xclient
.data
.l
[2] = 0l;
1216 ce
.xclient
.data
.l
[3] = 0l;
1217 ce
.xclient
.data
.l
[4] = 0l;
1218 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1222 void Client::changeState()
1224 unsigned long state
[2];
1225 state
[0] = _wmstate
;
1227 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1228 otk::Property::atoms
.wm_state
, state
, 2);
1233 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1235 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1237 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1239 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1241 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1243 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1245 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1247 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1249 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1251 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1252 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1253 otk::Property::atoms
.atom
, netstate
, num
);
1258 frame
->adjustState();
1262 void Client::changeAllowedActions(void)
1267 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1269 if (_functions
& Func_Shade
)
1270 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1271 if (_functions
& Func_Close
)
1272 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1273 if (_functions
& Func_Move
)
1274 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1275 if (_functions
& Func_Iconify
)
1276 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1277 if (_functions
& Func_Resize
)
1278 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1279 if (_functions
& Func_Fullscreen
)
1280 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1281 if (_functions
& Func_Maximize
) {
1282 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1283 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1286 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1287 otk::Property::atoms
.atom
, actions
, num
);
1291 void Client::remaximize()
1294 if (_max_horz
&& _max_vert
)
1301 return; // not maximized
1302 _max_horz
= _max_vert
= false;
1303 maximize(true, dir
, false);
1307 void Client::applyStartupState()
1309 // these are in a carefully crafted order..
1313 setDesktop(ICONIC_DESKTOP
);
1316 _fullscreen
= false;
1317 fullscreen(true, false);
1326 if (_max_vert
&& _max_horz
) {
1327 _max_vert
= _max_horz
= false;
1328 maximize(true, 0, false);
1329 } else if (_max_vert
) {
1331 maximize(true, 2, false);
1332 } else if (_max_horz
) {
1334 maximize(true, 1, false);
1337 if (_skip_taskbar
); // nothing to do for this
1338 if (_skip_pager
); // nothing to do for this
1339 if (_modal
); // nothing to do for this
1340 if (_above
); // nothing to do for this
1341 if (_below
); // nothing to do for this
1345 void Client::fireUrgent()
1347 // call the python UrgentWindow callbacks
1348 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1349 openbox
->bindings()->fireEvent(&data
);
1353 void Client::shade(bool shade
)
1355 if (!(_functions
& Func_Shade
) || // can't
1356 _shaded
== shade
) return; // already done
1358 // when we're iconic, don't change the wmstate
1360 _wmstate
= shade
? IconicState
: NormalState
;
1363 frame
->adjustSize();
1367 void Client::maximize(bool max
, int dir
, bool savearea
)
1369 assert(dir
== 0 || dir
== 1 || dir
== 2);
1370 if (!(_functions
& Func_Maximize
)) return; // can't
1372 // check if already done
1374 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1375 if (dir
== 1 && _max_horz
) return;
1376 if (dir
== 2 && _max_vert
) return;
1378 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1379 if (dir
== 1 && !_max_horz
) return;
1380 if (dir
== 2 && !_max_vert
) return;
1383 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1384 int x
= frame
->rect().x(), y
= frame
->rect().y(),
1385 w
= _area
.width(), h
= _area
.height();
1391 unsigned long n
= 4;
1398 // get the property off the window and use it for the dimentions we are
1400 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1401 otk::Property::atoms
.cardinal
, &n
,
1402 (long unsigned**) &readdim
)) {
1405 dimensions
[0] = readdim
[0];
1406 dimensions
[2] = readdim
[2];
1409 dimensions
[1] = readdim
[1];
1410 dimensions
[3] = readdim
[3];
1416 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1417 otk::Property::atoms
.cardinal
,
1418 (long unsigned*)dimensions
, 4);
1420 if (dir
== 0 || dir
== 1) { // horz
1424 if (dir
== 0 || dir
== 2) { // vert
1426 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1430 long unsigned n
= 4;
1432 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1433 otk::Property::atoms
.cardinal
, &n
,
1434 (long unsigned**) &dimensions
)) {
1436 if (dir
== 0 || dir
== 1) { // horz
1437 x
= (signed int)dimensions
[0];
1438 w
= (signed int)dimensions
[2];
1440 if (dir
== 0 || dir
== 2) { // vert
1441 y
= (signed int)dimensions
[1];
1442 h
= (signed int)dimensions
[3];
1447 // pick some fallbacks...
1448 if (dir
== 0 || dir
== 1) { // horz
1449 x
= a
.x() + a
.width() / 4;
1452 if (dir
== 0 || dir
== 2) { // vert
1453 y
= a
.y() + a
.height() / 4;
1459 if (dir
== 0 || dir
== 1) // horz
1461 if (dir
== 0 || dir
== 2) // vert
1464 if (!_max_horz
&& !_max_vert
)
1465 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1467 changeState(); // change the state hints on the client
1469 frame
->frameGravity(x
, y
); // figure out where the client should be going
1470 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1474 void Client::fullscreen(bool fs
, bool savearea
)
1476 static FunctionFlags saved_func
;
1477 static DecorationFlags saved_decor
;
1479 if (!(_functions
& Func_Fullscreen
) || // can't
1480 _fullscreen
== fs
) return; // already done
1483 changeState(); // change the state hints on the client
1485 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1488 // save the functions and remove them
1489 saved_func
= _functions
;
1490 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1491 // save the decorations and remove them
1492 saved_decor
= _decorations
;
1496 dimensions
[0] = _area
.x();
1497 dimensions
[1] = _area
.y();
1498 dimensions
[2] = _area
.width();
1499 dimensions
[3] = _area
.height();
1500 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1501 otk::Property::atoms
.cardinal
,
1502 (long unsigned*)dimensions
, 4);
1504 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1510 _functions
= saved_func
;
1511 _decorations
= saved_decor
;
1514 long unsigned n
= 4;
1516 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1517 otk::Property::atoms
.cardinal
, &n
,
1518 (long unsigned**) &dimensions
)) {
1527 // pick some fallbacks...
1528 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1529 x
= a
.x() + a
.width() / 4;
1530 y
= a
.y() + a
.height() / 4;
1536 changeAllowedActions(); // based on the new _functions
1538 // when fullscreening, don't obey things like increments, fill the screen
1539 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1541 // raise (back) into our stacking layer
1542 openbox
->screen(_screen
)->raiseWindow(this);
1544 // try focus us when we go into fullscreen mode
1549 void Client::disableDecorations(DecorationFlags flags
)
1551 _disabled_decorations
= flags
;
1552 setupDecorAndFunctions();
1556 bool Client::focus()
1558 // won't try focus if the client doesn't want it, or if the window isn't
1559 // visible on the screen
1560 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1562 if (_focused
) return true;
1564 // do a check to see if the window has already been unmapped or destroyed
1565 // do this intelligently while watching out for unmaps we've generated
1566 // (ignore_unmaps > 0)
1568 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1569 XPutBackEvent(**otk::display
, &ev
);
1572 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1573 if (ignore_unmaps
) {
1574 unmapHandler(ev
.xunmap
);
1576 XPutBackEvent(**otk::display
, &ev
);
1582 XSetInputFocus(**otk::display
, _window
,
1583 RevertToNone
, CurrentTime
);
1585 if (_focus_notify
) {
1587 ce
.xclient
.type
= ClientMessage
;
1588 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1589 ce
.xclient
.display
= **otk::display
;
1590 ce
.xclient
.window
= _window
;
1591 ce
.xclient
.format
= 32;
1592 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1593 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1594 ce
.xclient
.data
.l
[2] = 0l;
1595 ce
.xclient
.data
.l
[3] = 0l;
1596 ce
.xclient
.data
.l
[4] = 0l;
1597 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1600 XSync(**otk::display
, False
);
1605 void Client::unfocus() const
1607 if (!_focused
) return;
1609 assert(openbox
->focusedClient() == this);
1610 openbox
->setFocusedClient(0);
1614 void Client::focusHandler(const XFocusChangeEvent
&e
)
1617 // printf("FocusIn for 0x%lx\n", e.window);
1620 otk::EventHandler::focusHandler(e
);
1625 openbox
->setFocusedClient(this);
1629 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1632 // printf("FocusOut for 0x%lx\n", e.window);
1635 otk::EventHandler::unfocusHandler(e
);
1640 if (openbox
->focusedClient() == this)
1641 openbox
->setFocusedClient(0);
1645 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1648 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1651 otk::EventHandler::configureRequestHandler(e
);
1653 // if we are iconic (or shaded (fvwm does this)) ignore the event
1654 if (_iconic
|| _shaded
) return;
1656 if (e
.value_mask
& CWBorderWidth
)
1657 _border_width
= e
.border_width
;
1659 // resize, then move, as specified in the EWMH section 7.7
1660 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1661 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1662 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1666 case NorthEastGravity
:
1670 case SouthWestGravity
:
1672 corner
= BottomLeft
;
1674 case SouthEastGravity
:
1675 corner
= BottomRight
;
1677 default: // NorthWest, Static, etc
1681 // if moving AND resizing ...
1682 if (e
.value_mask
& (CWX
| CWY
)) {
1683 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1684 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1685 internal_resize(corner
, w
, h
, false, x
, y
);
1686 } else // if JUST resizing...
1687 internal_resize(corner
, w
, h
, false);
1688 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1689 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1690 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1691 internal_move(x
, y
);
1694 if (e
.value_mask
& CWStackMode
) {
1698 openbox
->screen(_screen
)->lowerWindow(this);
1704 openbox
->screen(_screen
)->raiseWindow(this);
1711 void Client::unmapHandler(const XUnmapEvent
&e
)
1713 if (ignore_unmaps
) {
1715 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1722 printf("UnmapNotify for 0x%lx\n", e
.window
);
1725 otk::EventHandler::unmapHandler(e
);
1727 // this deletes us etc
1728 openbox
->screen(_screen
)->unmanageWindow(this);
1732 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1735 printf("DestroyNotify for 0x%lx\n", e
.window
);
1738 otk::EventHandler::destroyHandler(e
);
1740 // this deletes us etc
1741 openbox
->screen(_screen
)->unmanageWindow(this);
1745 void Client::reparentHandler(const XReparentEvent
&e
)
1747 // this is when the client is first taken captive in the frame
1748 if (e
.parent
== frame
->plate()) return;
1751 printf("ReparentNotify for 0x%lx\n", e
.window
);
1754 otk::EventHandler::reparentHandler(e
);
1757 This event is quite rare and is usually handled in unmapHandler.
1758 However, if the window is unmapped when the reparent event occurs,
1759 the window manager never sees it because an unmap event is not sent
1760 to an already unmapped window.
1763 // we don't want the reparent event, put it back on the stack for the X
1764 // server to deal with after we unmanage the window
1767 XPutBackEvent(**otk::display
, &ev
);
1769 // this deletes us etc
1770 openbox
->screen(_screen
)->unmanageWindow(this);
1773 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1776 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1779 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1781 // move to the current desktop (uniconify)
1782 setDesktop(openbox
->screen(_screen
)->desktop());
1783 // XXX: should we focus/raise the window? (basically a net_wm_active_window)