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;
56 // no modal children until they set themselves
64 getType(); // this can change the mwmhints for special cases
71 getGravity(); // get the attribute gravity
72 updateNormalHints(); // this may override the attribute gravity
74 // got the type, the mwmhints, the protocols, and the normal hints (min/max
75 // sizes), so we're ready to set up
76 // the decorations/functions
77 setupDecorAndFunctions();
79 // also get the initial_state and set _iconic if we aren't "starting"
80 // when we're "starting" that means we should use whatever state was already
81 // on the window over the initial map state, because it was already mapped
82 updateWMHints(openbox
->state() != Openbox::State_Starting
);
88 // this makes sure that these windows appear on all desktops
89 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
90 _desktop
= 0xffffffff;
92 // set the desktop hint, to make sure that it always exists, and to reflect
93 // any changes we've made here
94 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
95 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
103 // clean up childrens' references
104 while (!_transients
.empty()) {
105 _transients
.front()->_transient_for
= 0;
106 _transients
.pop_front();
109 // clean up parents reference to this
113 _transient_for
->_transients
.remove(this); // remove from old parent
115 if (openbox
->state() != Openbox::State_Exiting
) {
116 // these values should not be persisted across a window unmapping/mapping
117 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
118 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
120 // if we're left in an iconic state, the client wont be mapped. this is
121 // bad, since we will no longer be managing the window on restart
123 XMapWindow(**otk::display
, _window
);
128 bool Client::validate() const
130 XSync(**otk::display
, false); // get all events on the server
133 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
134 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
135 XPutBackEvent(**otk::display
, &e
);
143 void Client::getGravity()
145 XWindowAttributes wattrib
;
148 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
149 assert(ret
!= BadWindow
);
150 _gravity
= wattrib
.win_gravity
;
154 void Client::getDesktop()
156 // defaults to the current desktop
157 _desktop
= openbox
->screen(_screen
)->desktop();
159 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
160 otk::Property::atoms
.cardinal
,
161 (long unsigned*)&_desktop
)) {
163 // printf("Window requested desktop: %ld\n", _desktop);
169 void Client::getType()
171 _type
= (WindowType
) -1;
174 unsigned long num
= (unsigned) -1;
175 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
176 otk::Property::atoms
.atom
, &num
, &val
)) {
177 // use the first value that we know about in the array
178 for (unsigned long i
= 0; i
< num
; ++i
) {
179 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
180 _type
= Type_Desktop
;
181 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
183 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
184 _type
= Type_Toolbar
;
185 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
187 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
188 _type
= Type_Utility
;
189 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
191 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
193 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
195 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
196 // prevent this window from getting any decor or functionality
197 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
198 _mwmhints
.decorations
= 0;
199 _mwmhints
.functions
= 0;
201 if (_type
!= (WindowType
) -1)
202 break; // grab the first known type
207 if (_type
== (WindowType
) -1) {
209 * the window type hint was not set, which means we either classify ourself
210 * as a normal window or a dialog, depending on if we are a transient.
220 void Client::setupDecorAndFunctions()
222 // start with everything (cept fullscreen)
223 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
224 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
225 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
227 if (_delete_window
) {
228 _decorations
|= Decor_Close
;
229 _functions
|= Func_Close
;
232 if (!(_min_size
.x() < _max_size
.x() || _min_size
.y() < _max_size
.y())) {
233 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
234 _functions
&= ~(Func_Resize
| Func_Maximize
);
239 // normal windows retain all of the possible decorations and
240 // functionality, and are the only windows that you can fullscreen
241 _functions
|= Func_Fullscreen
;
245 // dialogs cannot be maximized
246 _decorations
&= ~Decor_Maximize
;
247 _functions
&= ~Func_Maximize
;
253 // these windows get less functionality
254 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
255 _functions
&= ~(Func_Iconify
| Func_Resize
);
261 // none of these windows are manipulated by the window manager
267 // Mwm Hints are applied subtractively to what has already been chosen for
268 // decor and functionality
269 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
270 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
271 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
272 _decorations
&= ~Decor_Border
;
273 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
274 _decorations
&= ~Decor_Handle
;
275 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
276 _decorations
&= ~Decor_Titlebar
;
277 // if we don't have a titlebar, then we cannot shade!
278 _functions
&= ~Func_Shade
;
280 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
281 _decorations
&= ~Decor_Iconify
;
282 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
283 _decorations
&= ~Decor_Maximize
;
287 if (_mwmhints
.flags
& MwmFlag_Functions
) {
288 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
289 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
290 _functions
&= ~Func_Resize
;
291 if (! (_mwmhints
.functions
& MwmFunc_Move
))
292 _functions
&= ~Func_Move
;
293 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
294 _functions
&= ~Func_Iconify
;
295 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
296 _functions
&= ~Func_Maximize
;
297 // dont let mwm hints kill the close button
298 //if (! (_mwmhints.functions & MwmFunc_Close))
299 // _functions &= ~Func_Close;
303 // can't maximize without moving/resizing
304 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
305 _functions
&= ~Func_Maximize
;
307 // finally, user specified disabled decorations are applied to subtract
309 if (_disabled_decorations
& Decor_Titlebar
)
310 _decorations
&= ~Decor_Titlebar
;
311 if (_disabled_decorations
& Decor_Handle
)
312 _decorations
&= ~Decor_Handle
;
313 if (_disabled_decorations
& Decor_Border
)
314 _decorations
&= ~Decor_Border
;
315 if (_disabled_decorations
& Decor_Iconify
)
316 _decorations
&= ~Decor_Iconify
;
317 if (_disabled_decorations
& Decor_Maximize
)
318 _decorations
&= ~Decor_Maximize
;
319 if (_disabled_decorations
& Decor_AllDesktops
)
320 _decorations
&= ~Decor_AllDesktops
;
321 if (_disabled_decorations
& Decor_Close
)
322 _decorations
&= ~Decor_Close
;
324 // You can't shade without a titlebar
325 if (!(_decorations
& Decor_Titlebar
))
326 _functions
&= ~Func_Shade
;
328 changeAllowedActions();
331 frame
->adjustSize(); // change the decors on the frame
332 frame
->adjustPosition(); // with more/less decorations, we may need to be
338 void Client::getMwmHints()
340 unsigned long num
= MwmHints::elements
;
341 unsigned long *hints
;
343 _mwmhints
.flags
= 0; // default to none
345 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
346 otk::Property::atoms
.motif_wm_hints
, &num
,
347 (unsigned long **)&hints
))
350 if (num
>= MwmHints::elements
) {
351 // retrieved the hints
352 _mwmhints
.flags
= hints
[0];
353 _mwmhints
.functions
= hints
[1];
354 _mwmhints
.decorations
= hints
[2];
361 void Client::getArea()
363 XWindowAttributes wattrib
;
366 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
367 assert(ret
!= BadWindow
);
369 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
370 _border_width
= wattrib
.border_width
;
374 void Client::getState()
376 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
377 _iconic
= _skip_taskbar
= _skip_pager
= false;
379 unsigned long *state
;
380 unsigned long num
= (unsigned) -1;
382 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
383 otk::Property::atoms
.atom
, &num
, &state
)) {
384 for (unsigned long i
= 0; i
< num
; ++i
) {
385 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
391 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
392 _skip_taskbar
= true;
393 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
395 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
397 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
399 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
401 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
403 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
412 void Client::getShaped()
416 if (otk::display
->shape()) {
421 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
423 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
424 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
431 void Client::calcLayer() {
434 if (_iconic
) l
= Layer_Icon
;
435 else if (_fullscreen
) l
= Layer_Fullscreen
;
436 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
437 else if (_type
== Type_Dock
) {
438 if (!_below
) l
= Layer_Top
;
439 else l
= Layer_Normal
;
441 else if (_above
) l
= Layer_Above
;
442 else if (_below
) l
= Layer_Below
;
443 else l
= Layer_Normal
;
449 if we don't have a frame, then we aren't mapped yet (and this would
452 openbox
->screen(_screen
)->raiseWindow(this);
458 void Client::updateProtocols()
463 _focus_notify
= false;
464 _delete_window
= false;
466 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
467 for (int i
= 0; i
< num_return
; ++i
) {
468 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
469 // this means we can request the window to close
470 _delete_window
= true;
471 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
472 // if this protocol is requested, then the window will be notified
473 // by the window manager whenever it receives focus
474 _focus_notify
= true;
481 void Client::updateNormalHints()
485 int oldgravity
= _gravity
;
490 _size_inc
.setPoint(1, 1);
491 _base_size
.setPoint(0, 0);
492 _min_size
.setPoint(0, 0);
493 _max_size
.setPoint(INT_MAX
, INT_MAX
);
495 // get the hints from the window
496 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
497 _positioned
= (size
.flags
& (PPosition
|USPosition
));
499 if (size
.flags
& PWinGravity
) {
500 _gravity
= size
.win_gravity
;
502 // if the client has a frame, i.e. has already been mapped and is
503 // changing its gravity
504 if (frame
&& _gravity
!= oldgravity
) {
505 // move our idea of the client's position based on its new gravity
506 int x
= frame
->rect().x(), y
= frame
->rect().y();
507 frame
->frameGravity(x
, y
);
512 if (size
.flags
& PAspect
) {
513 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
514 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
517 if (size
.flags
& PMinSize
)
518 _min_size
.setPoint(size
.min_width
, size
.min_height
);
520 if (size
.flags
& PMaxSize
)
521 _max_size
.setPoint(size
.max_width
, size
.max_height
);
523 if (size
.flags
& PBaseSize
)
524 _base_size
.setPoint(size
.base_width
, size
.base_height
);
526 if (size
.flags
& PResizeInc
)
527 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
532 void Client::updateWMHints(bool initstate
)
536 // assume a window takes input if it doesnt specify
540 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
541 if (hints
->flags
& InputHint
)
542 _can_focus
= hints
->input
;
544 // only do this when initstate is true!
545 if (initstate
&& (hints
->flags
& StateHint
))
546 _iconic
= hints
->initial_state
== IconicState
;
548 if (hints
->flags
& XUrgencyHint
)
551 if (hints
->flags
& WindowGroupHint
) {
552 if (hints
->window_group
!= _group
) {
553 // XXX: remove from the old group if there was one
554 _group
= hints
->window_group
;
555 // XXX: do stuff with the group
566 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
567 (long)_window
, _urgent
? "ON" : "OFF");
569 // fire the urgent callback if we're mapped, otherwise, wait until after
577 void Client::updateTitle()
582 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
583 otk::Property::utf8
, &_title
)) {
585 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
586 otk::Property::ascii
, &_title
);
590 _title
= _("Unnamed Window");
593 frame
->setTitle(_title
);
597 void Client::updateIconTitle()
602 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
603 otk::Property::utf8
, &_icon_title
)) {
605 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
606 otk::Property::ascii
, &_icon_title
);
610 _icon_title
= _("Unnamed Window");
614 void Client::updateClass()
617 _app_name
= _app_class
= _role
= "";
619 otk::Property::StringVect v
;
620 unsigned long num
= 2;
622 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
623 otk::Property::ascii
, &num
, &v
)) {
624 if (num
> 0) _app_name
= v
[0].c_str();
625 if (num
> 1) _app_class
= v
[1].c_str();
630 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
631 otk::Property::ascii
, &num
, &v
)) {
632 if (num
> 0) _role
= v
[0].c_str();
637 void Client::updateStrut()
639 unsigned long num
= 4;
641 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
642 otk::Property::atoms
.cardinal
, &num
, &data
))
646 _strut
.left
= data
[0];
647 _strut
.right
= data
[1];
648 _strut
.top
= data
[2];
649 _strut
.bottom
= data
[3];
651 // updating here is pointless while we're being mapped cuz we're not in
652 // the screen's client list yet
654 openbox
->screen(_screen
)->updateStrut();
661 void Client::updateTransientFor()
666 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
667 t
!= _window
) { // cant be transient to itself!
668 c
= openbox
->findClient(t
);
669 assert(c
!= this); // if this happens then we need to check for it
671 if (!c
/*XXX: && _group*/) {
672 // not transient to a client, see if it is transient for a group
673 if (//t == _group->leader() ||
675 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
676 // window is a transient for its group!
677 // XXX: for now this is treated as non-transient.
678 // this needs to be fixed!
683 // if anything has changed...
684 if (c
!= _transient_for
) {
690 _transient_for
->_transients
.remove(this); // remove from old parent
693 _transient_for
->_transients
.push_back(this); // add to new parent
701 void Client::propertyHandler(const XPropertyEvent
&e
)
703 otk::EventHandler::propertyHandler(e
);
705 // validate cuz we query stuff off the client here
706 if (!validate()) return;
708 // compress changes to a single property into a single change
710 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
711 // XXX: it would be nice to compress ALL changes to a property, not just
712 // changes in a row without other props between.
713 if (ce
.xproperty
.atom
!= e
.atom
) {
714 XPutBackEvent(**otk::display
, &ce
);
719 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
721 setupDecorAndFunctions(); // normal hints can make a window non-resizable
722 } else if (e
.atom
== XA_WM_HINTS
)
724 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
725 updateTransientFor();
727 calcLayer(); // type may have changed, so update the layer
728 setupDecorAndFunctions();
730 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
731 e
.atom
== otk::Property::atoms
.wm_name
)
733 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
734 e
.atom
== otk::Property::atoms
.wm_icon_name
)
736 else if (e
.atom
== otk::Property::atoms
.wm_class
)
738 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
740 setupDecorAndFunctions();
742 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
747 void Client::setWMState(long state
)
749 if (state
== _wmstate
) return; // no change
753 setDesktop(ICONIC_DESKTOP
);
756 setDesktop(openbox
->screen(_screen
)->desktop());
762 void Client::setDesktop(long target
)
764 if (target
== _desktop
) return;
766 printf("Setting desktop %ld\n", target
);
768 if (!(target
>= 0 || target
== (signed)0xffffffff ||
769 target
== ICONIC_DESKTOP
))
774 // set the desktop hint, but not if we're iconifying
775 if (_desktop
!= ICONIC_DESKTOP
)
776 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
777 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
779 // 'move' the window to the new desktop
780 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
781 _desktop
== (signed)0xffffffff)
786 // Handle Iconic state. Iconic state is maintained by the client being a
787 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
788 // uniconifying happen.
789 bool i
= _desktop
== ICONIC_DESKTOP
;
790 if (i
!= _iconic
) { // has the state changed?
793 _wmstate
= IconicState
;
795 // we unmap the client itself so that we can get MapRequest events, and
796 // because the ICCCM tells us to!
797 XUnmapWindow(**otk::display
, _window
);
799 _wmstate
= NormalState
;
800 XMapWindow(**otk::display
, _window
);
805 frame
->adjustState();
809 Client
*Client::findModalChild(Client
*skip
) const
813 // find a modal child recursively and try focus it
814 List::const_iterator it
, end
= _transients
.end();
815 for (it
= _transients
.begin(); it
!= end
; ++it
)
816 if ((*it
)->_modal
&& *it
!= skip
)
817 return *it
; // got one
818 // none of our direct children are modal, let them try check
819 for (it
= _transients
.begin(); it
!= end
; ++it
)
820 if ((ret
= (*it
)->findModalChild()))
821 return ret
; // got one
826 void Client::setModal(bool modal
)
830 while (c
->_transient_for
) {
831 c
= c
->_transient_for
;
832 if (c
->_modal_child
) break; // already has a modal child
833 c
->_modal_child
= this;
836 // try find a replacement modal dialog
837 Client
*replacement
= 0;
840 while (c
->_transient_for
) // go up the tree
841 c
= c
->_transient_for
;
842 replacement
= c
->findModalChild(this); // find a modal child, skipping this
845 while (c
->_transient_for
) {
846 c
= c
->_transient_for
;
847 if (c
->_modal_child
!= this) break; // has a different modal child
848 c
->_modal_child
= replacement
;
855 void Client::setState(StateAction action
, long data1
, long data2
)
857 bool shadestate
= _shaded
;
858 bool fsstate
= _fullscreen
;
859 bool maxh
= _max_horz
;
860 bool maxv
= _max_vert
;
863 if (!(action
== State_Add
|| action
== State_Remove
||
864 action
== State_Toggle
))
865 return; // an invalid action was passed to the client message, ignore it
867 for (int i
= 0; i
< 2; ++i
) {
868 Atom state
= i
== 0 ? data1
: data2
;
870 if (! state
) continue;
872 // if toggling, then pick whether we're adding or removing
873 if (action
== State_Toggle
) {
874 if (state
== otk::Property::atoms
.net_wm_state_modal
)
875 action
= _modal
? State_Remove
: State_Add
;
876 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
877 action
= _max_vert
? State_Remove
: State_Add
;
878 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
879 action
= _max_horz
? State_Remove
: State_Add
;
880 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
881 action
= _shaded
? State_Remove
: State_Add
;
882 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
883 action
= _skip_taskbar
? State_Remove
: State_Add
;
884 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
885 action
= _skip_pager
? State_Remove
: State_Add
;
886 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
887 action
= _fullscreen
? State_Remove
: State_Add
;
888 else if (state
== otk::Property::atoms
.net_wm_state_above
)
889 action
= _above
? State_Remove
: State_Add
;
890 else if (state
== otk::Property::atoms
.net_wm_state_below
)
891 action
= _below
? State_Remove
: State_Add
;
894 if (action
== State_Add
) {
895 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
896 if (_modal
) continue;
898 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
900 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
901 if (_max_horz
) continue;
903 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
905 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
906 _skip_taskbar
= true;
907 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
909 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
911 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
912 if (_above
) continue;
914 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
915 if (_below
) continue;
919 } else { // action == State_Remove
920 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
921 if (!_modal
) continue;
923 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
925 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
927 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
929 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
930 _skip_taskbar
= false;
931 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
933 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
935 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
936 if (!_above
) continue;
938 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
939 if (!_below
) continue;
944 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
945 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
946 if (maxh
== maxv
) { // both going the same way
947 maximize(maxh
, 0, true);
949 maximize(maxh
, 1, true);
950 maximize(maxv
, 2, true);
952 } else { // toggling one
953 if (maxh
!= _max_horz
)
954 maximize(maxh
, 1, true);
956 maximize(maxv
, 2, true);
961 // change fullscreen state before shading, as it will affect if the window
963 if (fsstate
!= _fullscreen
)
964 fullscreen(fsstate
, true);
965 if (shadestate
!= _shaded
)
968 changeState(); // change the hint to relect these changes
972 void Client::toggleClientBorder(bool addborder
)
974 // adjust our idea of where the client is, based on its border. When the
975 // border is removed, the client should now be considered to be in a
976 // different position.
977 // when re-adding the border to the client, the same operation needs to be
979 int oldx
= _area
.x(), oldy
= _area
.y();
980 int x
= oldx
, y
= oldy
;
983 case NorthWestGravity
:
985 case SouthWestGravity
:
987 case NorthEastGravity
:
989 case SouthEastGravity
:
990 if (addborder
) x
-= _border_width
* 2;
991 else x
+= _border_width
* 2;
998 if (addborder
) x
-= _border_width
;
999 else x
+= _border_width
;
1004 case NorthWestGravity
:
1006 case NorthEastGravity
:
1008 case SouthWestGravity
:
1010 case SouthEastGravity
:
1011 if (addborder
) y
-= _border_width
* 2;
1012 else y
+= _border_width
* 2;
1019 if (addborder
) y
-= _border_width
;
1020 else y
+= _border_width
;
1026 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1028 // move the client so it is back it the right spot _with_ its border!
1029 if (x
!= oldx
|| y
!= oldy
)
1030 XMoveWindow(**otk::display
, _window
, x
, y
);
1032 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1036 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1038 otk::EventHandler::clientMessageHandler(e
);
1040 // validate cuz we query stuff off the client here
1041 if (!validate()) return;
1043 if (e
.format
!= 32) return;
1045 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1046 // compress changes into a single change
1047 bool compress
= false;
1049 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1050 // XXX: it would be nice to compress ALL messages of a type, not just
1051 // messages in a row without other message types between.
1052 if (ce
.xclient
.message_type
!= e
.message_type
) {
1053 XPutBackEvent(**otk::display
, &ce
);
1059 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1061 setWMState(e
.data
.l
[0]); // use the original event
1062 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1063 // compress changes into a single change
1064 bool compress
= false;
1066 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1067 // XXX: it would be nice to compress ALL messages of a type, not just
1068 // messages in a row without other message types between.
1069 if (ce
.xclient
.message_type
!= e
.message_type
) {
1070 XPutBackEvent(**otk::display
, &ce
);
1076 setDesktop(e
.data
.l
[0]); // use the found event
1078 setDesktop(e
.data
.l
[0]); // use the original event
1079 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1080 // can't compress these
1082 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1083 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1084 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1085 e
.data
.l
[1], e
.data
.l
[2], _window
);
1087 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1088 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1090 printf("net_close_window for 0x%lx\n", _window
);
1093 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1095 printf("net_active_window for 0x%lx\n", _window
);
1098 setDesktop(openbox
->screen(_screen
)->desktop());
1103 openbox
->screen(_screen
)->raiseWindow(this);
1109 void Client::shapeHandler(const XShapeEvent
&e
)
1111 otk::EventHandler::shapeHandler(e
);
1113 if (e
.kind
== ShapeBounding
) {
1115 frame
->adjustShape();
1121 void Client::resize(Corner anchor
, int w
, int h
)
1123 if (!(_functions
& Func_Resize
)) return;
1124 internal_resize(anchor
, w
, h
);
1128 void Client::internal_resize(Corner anchor
, int w
, int h
, bool user
,
1131 w
-= _base_size
.x();
1132 h
-= _base_size
.y();
1135 // for interactive resizing. have to move half an increment in each
1137 int mw
= w
% _size_inc
.x(); // how far we are towards the next size inc
1138 int mh
= h
% _size_inc
.y();
1139 int aw
= _size_inc
.x() / 2; // amount to add
1140 int ah
= _size_inc
.y() / 2;
1141 // don't let us move into a new size increment
1142 if (mw
+ aw
>= _size_inc
.x()) aw
= _size_inc
.x() - mw
- 1;
1143 if (mh
+ ah
>= _size_inc
.y()) ah
= _size_inc
.y() - mh
- 1;
1147 // if this is a user-requested resize, then check against min/max sizes
1148 // and aspect ratios
1150 // smaller than min size or bigger than max size?
1151 if (w
< _min_size
.x()) w
= _min_size
.x();
1152 else if (w
> _max_size
.x()) w
= _max_size
.x();
1153 if (h
< _min_size
.y()) h
= _min_size
.y();
1154 else if (h
> _max_size
.y()) h
= _max_size
.y();
1156 // adjust the height ot match the width for the aspect ratios
1158 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1160 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1163 // keep to the increments
1167 // you cannot resize to nothing
1171 // store the logical size
1172 _logical_size
.setPoint(w
, h
);
1177 w
+= _base_size
.x();
1178 h
+= _base_size
.y();
1180 if (x
== INT_MIN
|| y
== INT_MIN
) {
1187 x
-= w
- _area
.width();
1190 y
-= h
- _area
.height();
1193 x
-= w
- _area
.width();
1194 y
-= h
- _area
.height();
1199 _area
.setSize(w
, h
);
1201 XResizeWindow(**otk::display
, _window
, w
, h
);
1203 // resize the frame to match the request
1204 frame
->adjustSize();
1205 internal_move(x
, y
);
1209 void Client::move(int x
, int y
)
1211 if (!(_functions
& Func_Move
)) return;
1212 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1214 internal_move(x
, y
);
1218 void Client::internal_move(int x
, int y
)
1222 // move the frame to be in the requested position
1223 if (frame
) { // this can be called while mapping, before frame exists
1224 frame
->adjustPosition();
1226 // send synthetic configure notify (we don't need to if we aren't mapped
1229 event
.type
= ConfigureNotify
;
1230 event
.xconfigure
.display
= **otk::display
;
1231 event
.xconfigure
.event
= _window
;
1232 event
.xconfigure
.window
= _window
;
1234 // root window coords with border in mind
1235 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1236 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1238 event
.xconfigure
.width
= _area
.width();
1239 event
.xconfigure
.height
= _area
.height();
1240 event
.xconfigure
.border_width
= _border_width
;
1241 event
.xconfigure
.above
= frame
->plate();
1242 event
.xconfigure
.override_redirect
= False
;
1243 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1244 StructureNotifyMask
, &event
);
1246 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1247 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1248 event
.xconfigure
.height
, event
.xconfigure
.window
);
1254 void Client::close()
1258 if (!(_functions
& Func_Close
)) return;
1260 // XXX: itd be cool to do timeouts and shit here for killing the client's
1262 // like... if the window is around after 5 seconds, then the close button
1263 // turns a nice red, and if this function is called again, the client is
1264 // explicitly killed.
1266 ce
.xclient
.type
= ClientMessage
;
1267 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1268 ce
.xclient
.display
= **otk::display
;
1269 ce
.xclient
.window
= _window
;
1270 ce
.xclient
.format
= 32;
1271 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1272 ce
.xclient
.data
.l
[1] = CurrentTime
;
1273 ce
.xclient
.data
.l
[2] = 0l;
1274 ce
.xclient
.data
.l
[3] = 0l;
1275 ce
.xclient
.data
.l
[4] = 0l;
1276 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1280 void Client::changeState()
1282 unsigned long state
[2];
1283 state
[0] = _wmstate
;
1285 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1286 otk::Property::atoms
.wm_state
, state
, 2);
1291 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1293 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1295 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1297 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1299 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1301 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1303 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1305 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1307 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1309 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1310 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1311 otk::Property::atoms
.atom
, netstate
, num
);
1316 frame
->adjustState();
1320 void Client::changeAllowedActions(void)
1325 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1327 if (_functions
& Func_Shade
)
1328 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1329 if (_functions
& Func_Close
)
1330 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1331 if (_functions
& Func_Move
)
1332 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1333 if (_functions
& Func_Iconify
)
1334 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1335 if (_functions
& Func_Resize
)
1336 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1337 if (_functions
& Func_Fullscreen
)
1338 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1339 if (_functions
& Func_Maximize
) {
1340 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1341 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1344 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1345 otk::Property::atoms
.atom
, actions
, num
);
1349 void Client::remaximize()
1352 if (_max_horz
&& _max_vert
)
1359 return; // not maximized
1360 _max_horz
= _max_vert
= false;
1361 maximize(true, dir
, false);
1365 void Client::applyStartupState()
1367 // these are in a carefully crafted order..
1376 setDesktop(ICONIC_DESKTOP
);
1379 _fullscreen
= false;
1380 fullscreen(true, false);
1389 if (_max_vert
&& _max_horz
) {
1390 _max_vert
= _max_horz
= false;
1391 maximize(true, 0, false);
1392 } else if (_max_vert
) {
1394 maximize(true, 2, false);
1395 } else if (_max_horz
) {
1397 maximize(true, 1, false);
1400 if (_skip_taskbar
); // nothing to do for this
1401 if (_skip_pager
); // nothing to do for this
1402 if (_modal
); // nothing to do for this
1403 if (_above
); // nothing to do for this
1404 if (_below
); // nothing to do for this
1408 void Client::fireUrgent()
1410 // call the python UrgentWindow callbacks
1411 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1412 openbox
->bindings()->fireEvent(&data
);
1416 void Client::shade(bool shade
)
1418 if (!(_functions
& Func_Shade
) || // can't
1419 _shaded
== shade
) return; // already done
1421 // when we're iconic, don't change the wmstate
1423 _wmstate
= shade
? IconicState
: NormalState
;
1426 frame
->adjustSize();
1430 void Client::maximize(bool max
, int dir
, bool savearea
)
1432 assert(dir
== 0 || dir
== 1 || dir
== 2);
1433 if (!(_functions
& Func_Maximize
)) return; // can't
1435 // check if already done
1437 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1438 if (dir
== 1 && _max_horz
) return;
1439 if (dir
== 2 && _max_vert
) return;
1441 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1442 if (dir
== 1 && !_max_horz
) return;
1443 if (dir
== 2 && !_max_vert
) return;
1446 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1447 int x
= frame
->rect().x(), y
= frame
->rect().y(),
1448 w
= _area
.width(), h
= _area
.height();
1454 unsigned long n
= 4;
1461 // get the property off the window and use it for the dimentions we are
1463 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1464 otk::Property::atoms
.cardinal
, &n
,
1465 (long unsigned**) &readdim
)) {
1468 dimensions
[0] = readdim
[0];
1469 dimensions
[2] = readdim
[2];
1472 dimensions
[1] = readdim
[1];
1473 dimensions
[3] = readdim
[3];
1479 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1480 otk::Property::atoms
.cardinal
,
1481 (long unsigned*)dimensions
, 4);
1483 if (dir
== 0 || dir
== 1) { // horz
1487 if (dir
== 0 || dir
== 2) { // vert
1489 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1493 long unsigned n
= 4;
1495 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1496 otk::Property::atoms
.cardinal
, &n
,
1497 (long unsigned**) &dimensions
)) {
1499 if (dir
== 0 || dir
== 1) { // horz
1500 x
= (signed int)dimensions
[0];
1501 w
= (signed int)dimensions
[2];
1503 if (dir
== 0 || dir
== 2) { // vert
1504 y
= (signed int)dimensions
[1];
1505 h
= (signed int)dimensions
[3];
1510 // pick some fallbacks...
1511 if (dir
== 0 || dir
== 1) { // horz
1512 x
= a
.x() + a
.width() / 4;
1515 if (dir
== 0 || dir
== 2) { // vert
1516 y
= a
.y() + a
.height() / 4;
1522 if (dir
== 0 || dir
== 1) // horz
1524 if (dir
== 0 || dir
== 2) // vert
1527 if (!_max_horz
&& !_max_vert
)
1528 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1530 changeState(); // change the state hints on the client
1532 frame
->frameGravity(x
, y
); // figure out where the client should be going
1533 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1537 void Client::fullscreen(bool fs
, bool savearea
)
1539 static FunctionFlags saved_func
;
1540 static DecorationFlags saved_decor
;
1542 if (!(_functions
& Func_Fullscreen
) || // can't
1543 _fullscreen
== fs
) return; // already done
1546 changeState(); // change the state hints on the client
1548 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1551 // save the functions and remove them
1552 saved_func
= _functions
;
1553 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1554 // save the decorations and remove them
1555 saved_decor
= _decorations
;
1559 dimensions
[0] = _area
.x();
1560 dimensions
[1] = _area
.y();
1561 dimensions
[2] = _area
.width();
1562 dimensions
[3] = _area
.height();
1563 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1564 otk::Property::atoms
.cardinal
,
1565 (long unsigned*)dimensions
, 4);
1567 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1573 _functions
= saved_func
;
1574 _decorations
= saved_decor
;
1577 long unsigned n
= 4;
1579 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1580 otk::Property::atoms
.cardinal
, &n
,
1581 (long unsigned**) &dimensions
)) {
1590 // pick some fallbacks...
1591 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1592 x
= a
.x() + a
.width() / 4;
1593 y
= a
.y() + a
.height() / 4;
1599 changeAllowedActions(); // based on the new _functions
1601 // when fullscreening, don't obey things like increments, fill the screen
1602 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1604 // raise (back) into our stacking layer
1605 openbox
->screen(_screen
)->raiseWindow(this);
1607 // try focus us when we go into fullscreen mode
1612 void Client::disableDecorations(DecorationFlags flags
)
1614 _disabled_decorations
= flags
;
1615 setupDecorAndFunctions();
1619 bool Client::focus()
1621 // if we have a modal child, then focus it, not us
1623 return _modal_child
->focus();
1625 // won't try focus if the client doesn't want it, or if the window isn't
1626 // visible on the screen
1627 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1629 if (_focused
) return true;
1631 // do a check to see if the window has already been unmapped or destroyed
1632 // do this intelligently while watching out for unmaps we've generated
1633 // (ignore_unmaps > 0)
1635 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1636 XPutBackEvent(**otk::display
, &ev
);
1639 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1640 if (ignore_unmaps
) {
1641 unmapHandler(ev
.xunmap
);
1643 XPutBackEvent(**otk::display
, &ev
);
1649 XSetInputFocus(**otk::display
, _window
,
1650 RevertToNone
, CurrentTime
);
1652 if (_focus_notify
) {
1654 ce
.xclient
.type
= ClientMessage
;
1655 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1656 ce
.xclient
.display
= **otk::display
;
1657 ce
.xclient
.window
= _window
;
1658 ce
.xclient
.format
= 32;
1659 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1660 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1661 ce
.xclient
.data
.l
[2] = 0l;
1662 ce
.xclient
.data
.l
[3] = 0l;
1663 ce
.xclient
.data
.l
[4] = 0l;
1664 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1667 XSync(**otk::display
, False
);
1672 void Client::unfocus() const
1674 if (!_focused
) return;
1676 assert(openbox
->focusedClient() == this);
1677 openbox
->setFocusedClient(0);
1681 void Client::focusHandler(const XFocusChangeEvent
&e
)
1684 // printf("FocusIn for 0x%lx\n", e.window);
1687 otk::EventHandler::focusHandler(e
);
1692 openbox
->setFocusedClient(this);
1696 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1699 // printf("FocusOut for 0x%lx\n", e.window);
1702 otk::EventHandler::unfocusHandler(e
);
1707 if (openbox
->focusedClient() == this)
1708 openbox
->setFocusedClient(0);
1712 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1715 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1718 otk::EventHandler::configureRequestHandler(e
);
1720 // if we are iconic (or shaded (fvwm does this)) ignore the event
1721 if (_iconic
|| _shaded
) return;
1723 if (e
.value_mask
& CWBorderWidth
)
1724 _border_width
= e
.border_width
;
1726 // resize, then move, as specified in the EWMH section 7.7
1727 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1728 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1729 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1733 case NorthEastGravity
:
1737 case SouthWestGravity
:
1739 corner
= BottomLeft
;
1741 case SouthEastGravity
:
1742 corner
= BottomRight
;
1744 default: // NorthWest, Static, etc
1748 // if moving AND resizing ...
1749 if (e
.value_mask
& (CWX
| CWY
)) {
1750 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1751 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1752 internal_resize(corner
, w
, h
, false, x
, y
);
1753 } else // if JUST resizing...
1754 internal_resize(corner
, w
, h
, false);
1755 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1756 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1757 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1758 internal_move(x
, y
);
1761 if (e
.value_mask
& CWStackMode
) {
1765 openbox
->screen(_screen
)->lowerWindow(this);
1771 openbox
->screen(_screen
)->raiseWindow(this);
1778 void Client::unmapHandler(const XUnmapEvent
&e
)
1780 if (ignore_unmaps
) {
1782 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1789 printf("UnmapNotify for 0x%lx\n", e
.window
);
1792 otk::EventHandler::unmapHandler(e
);
1794 // this deletes us etc
1795 openbox
->screen(_screen
)->unmanageWindow(this);
1799 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1802 printf("DestroyNotify for 0x%lx\n", e
.window
);
1805 otk::EventHandler::destroyHandler(e
);
1807 // this deletes us etc
1808 openbox
->screen(_screen
)->unmanageWindow(this);
1812 void Client::reparentHandler(const XReparentEvent
&e
)
1814 // this is when the client is first taken captive in the frame
1815 if (e
.parent
== frame
->plate()) return;
1818 printf("ReparentNotify for 0x%lx\n", e
.window
);
1821 otk::EventHandler::reparentHandler(e
);
1824 This event is quite rare and is usually handled in unmapHandler.
1825 However, if the window is unmapped when the reparent event occurs,
1826 the window manager never sees it because an unmap event is not sent
1827 to an already unmapped window.
1830 // we don't want the reparent event, put it back on the stack for the X
1831 // server to deal with after we unmanage the window
1834 XPutBackEvent(**otk::display
, &ev
);
1836 // this deletes us etc
1837 openbox
->screen(_screen
)->unmanageWindow(this);
1840 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1843 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1846 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1848 // move to the current desktop (uniconify)
1849 setDesktop(openbox
->screen(_screen
)->desktop());
1850 // XXX: should we focus/raise the window? (basically a net_wm_active_window)