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!!
43 _wmstate
= NormalState
;
46 _layer
= Layer_Normal
;
49 _disabled_decorations
= 0;
56 getState(); // do this before updateTransientFor! (for _modal)
61 getType(); // this can change the mwmhints for special cases
65 getGravity(); // get the attribute gravity
66 updateNormalHints(); // this may override the attribute gravity
68 // got the type, the mwmhints, the protocols, and the normal hints (min/max
69 // sizes), so we're ready to set up
70 // the decorations/functions
71 setupDecorAndFunctions();
73 // also get the initial_state and set _iconic if we aren't "starting"
74 // when we're "starting" that means we should use whatever state was already
75 // on the window over the initial map state, because it was already mapped
76 updateWMHints(openbox
->state() != Openbox::State_Starting
);
82 // this makes sure that these windows appear on all desktops
83 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
84 _desktop
= 0xffffffff;
86 // set the desktop hint, to make sure that it always exists, and to reflect
87 // any changes we've made here
88 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
89 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
97 // clean up childrens' references
98 while (!_transients
.empty()) {
99 _transients
.front()->_transient_for
= 0;
100 _transients
.pop_front();
103 // clean up parents reference to this
105 _transient_for
->_transients
.remove(this); // remove from old parent
107 if (openbox
->state() != Openbox::State_Exiting
) {
108 // these values should not be persisted across a window unmapping/mapping
109 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
110 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
112 // if we're left in an iconic state, the client wont be mapped. this is
113 // bad, since we will no longer be managing the window on restart
115 XMapWindow(**otk::display
, _window
);
120 bool Client::validate() const
122 XSync(**otk::display
, false); // get all events on the server
125 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
126 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
127 XPutBackEvent(**otk::display
, &e
);
135 void Client::getGravity()
137 XWindowAttributes wattrib
;
140 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
141 assert(ret
!= BadWindow
);
142 _gravity
= wattrib
.win_gravity
;
146 void Client::getDesktop()
148 // defaults to the current desktop
149 _desktop
= openbox
->screen(_screen
)->desktop();
151 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
152 otk::Property::atoms
.cardinal
,
153 (long unsigned*)&_desktop
)) {
155 // printf("Window requested desktop: %ld\n", _desktop);
161 void Client::getType()
163 _type
= (WindowType
) -1;
166 unsigned long num
= (unsigned) -1;
167 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
168 otk::Property::atoms
.atom
, &num
, &val
)) {
169 // use the first value that we know about in the array
170 for (unsigned long i
= 0; i
< num
; ++i
) {
171 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
172 _type
= Type_Desktop
;
173 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
175 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
176 _type
= Type_Toolbar
;
177 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
179 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
180 _type
= Type_Utility
;
181 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
183 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
185 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
187 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
188 // prevent this window from getting any decor or functionality
189 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
190 _mwmhints
.decorations
= 0;
191 _mwmhints
.functions
= 0;
193 if (_type
!= (WindowType
) -1)
194 break; // grab the first known type
199 if (_type
== (WindowType
) -1) {
201 * the window type hint was not set, which means we either classify ourself
202 * as a normal window or a dialog, depending on if we are a transient.
212 void Client::setupDecorAndFunctions()
214 // start with everything (cept fullscreen)
215 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
216 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
217 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
219 if (_delete_window
) {
220 _decorations
|= Decor_Close
;
221 _functions
|= Func_Close
;
224 if (!(_min_size
.x() < _max_size
.x() || _min_size
.y() < _max_size
.y())) {
225 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
226 _functions
&= ~(Func_Resize
| Func_Maximize
);
231 // normal windows retain all of the possible decorations and
232 // functionality, and are the only windows that you can fullscreen
233 _functions
|= Func_Fullscreen
;
237 // dialogs cannot be maximized
238 _decorations
&= ~Decor_Maximize
;
239 _functions
&= ~Func_Maximize
;
245 // these windows get less functionality
246 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
247 _functions
&= ~(Func_Iconify
| Func_Resize
);
253 // none of these windows are manipulated by the window manager
259 // Mwm Hints are applied subtractively to what has already been chosen for
260 // decor and functionality
261 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
262 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
263 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
264 _decorations
&= ~Decor_Border
;
265 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
266 _decorations
&= ~Decor_Handle
;
267 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
268 _decorations
&= ~Decor_Titlebar
;
269 // if we don't have a titlebar, then we cannot shade!
270 _functions
&= ~Func_Shade
;
272 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
273 _decorations
&= ~Decor_Iconify
;
274 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
275 _decorations
&= ~Decor_Maximize
;
279 if (_mwmhints
.flags
& MwmFlag_Functions
) {
280 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
281 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
282 _functions
&= ~Func_Resize
;
283 if (! (_mwmhints
.functions
& MwmFunc_Move
))
284 _functions
&= ~Func_Move
;
285 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
286 _functions
&= ~Func_Iconify
;
287 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
288 _functions
&= ~Func_Maximize
;
289 // dont let mwm hints kill the close button
290 //if (! (_mwmhints.functions & MwmFunc_Close))
291 // _functions &= ~Func_Close;
295 // can't maximize without moving/resizing
296 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
297 _functions
&= ~Func_Maximize
;
299 // finally, user specified disabled decorations are applied to subtract
301 if (_disabled_decorations
& Decor_Titlebar
)
302 _decorations
&= ~Decor_Titlebar
;
303 if (_disabled_decorations
& Decor_Handle
)
304 _decorations
&= ~Decor_Handle
;
305 if (_disabled_decorations
& Decor_Border
)
306 _decorations
&= ~Decor_Border
;
307 if (_disabled_decorations
& Decor_Iconify
)
308 _decorations
&= ~Decor_Iconify
;
309 if (_disabled_decorations
& Decor_Maximize
)
310 _decorations
&= ~Decor_Maximize
;
311 if (_disabled_decorations
& Decor_AllDesktops
)
312 _decorations
&= ~Decor_AllDesktops
;
313 if (_disabled_decorations
& Decor_Close
)
314 _decorations
&= ~Decor_Close
;
316 // You can't shade without a titlebar
317 if (!(_decorations
& Decor_Titlebar
))
318 _functions
&= ~Func_Shade
;
320 changeAllowedActions();
323 frame
->adjustSize(); // change the decors on the frame
324 frame
->adjustPosition(); // with more/less decorations, we may need to be
330 void Client::getMwmHints()
332 unsigned long num
= MwmHints::elements
;
333 unsigned long *hints
;
335 _mwmhints
.flags
= 0; // default to none
337 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
338 otk::Property::atoms
.motif_wm_hints
, &num
,
339 (unsigned long **)&hints
))
342 if (num
>= MwmHints::elements
) {
343 // retrieved the hints
344 _mwmhints
.flags
= hints
[0];
345 _mwmhints
.functions
= hints
[1];
346 _mwmhints
.decorations
= hints
[2];
353 void Client::getArea()
355 XWindowAttributes wattrib
;
358 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
359 assert(ret
!= BadWindow
);
361 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
362 _border_width
= wattrib
.border_width
;
366 void Client::getState()
368 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
369 _iconic
= _skip_taskbar
= _skip_pager
= false;
371 unsigned long *state
;
372 unsigned long num
= (unsigned) -1;
374 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
375 otk::Property::atoms
.atom
, &num
, &state
)) {
376 for (unsigned long i
= 0; i
< num
; ++i
) {
377 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
379 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
381 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
383 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
384 _skip_taskbar
= true;
385 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
391 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
393 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
395 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
404 void Client::getShaped()
408 if (otk::display
->shape()) {
413 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
415 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
416 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
423 void Client::calcLayer() {
426 if (_iconic
) l
= Layer_Icon
;
427 else if (_fullscreen
) l
= Layer_Fullscreen
;
428 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
429 else if (_type
== Type_Dock
) {
430 if (!_below
) l
= Layer_Top
;
431 else l
= Layer_Normal
;
433 else if (_above
) l
= Layer_Above
;
434 else if (_below
) l
= Layer_Below
;
435 else l
= Layer_Normal
;
441 if we don't have a frame, then we aren't mapped yet (and this would
444 openbox
->screen(_screen
)->raiseWindow(this);
450 void Client::updateProtocols()
455 _focus_notify
= false;
456 _delete_window
= false;
458 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
459 for (int i
= 0; i
< num_return
; ++i
) {
460 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
461 // this means we can request the window to close
462 _delete_window
= true;
463 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
464 // if this protocol is requested, then the window will be notified
465 // by the window manager whenever it receives focus
466 _focus_notify
= true;
473 void Client::updateNormalHints()
477 int oldgravity
= _gravity
;
482 _size_inc
.setPoint(1, 1);
483 _base_size
.setPoint(0, 0);
484 _min_size
.setPoint(0, 0);
485 _max_size
.setPoint(INT_MAX
, INT_MAX
);
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
498 int x
= frame
->rect().x(), y
= frame
->rect().y();
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
) {
682 _transient_for
->_transients
.remove(this); // remove from old parent
685 _transient_for
->_transients
.push_back(this); // add to new parent
693 void Client::propertyHandler(const XPropertyEvent
&e
)
695 otk::EventHandler::propertyHandler(e
);
697 // validate cuz we query stuff off the client here
698 if (!validate()) return;
700 // compress changes to a single property into a single change
702 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
703 // XXX: it would be nice to compress ALL changes to a property, not just
704 // changes in a row without other props between.
705 if (ce
.xproperty
.atom
!= e
.atom
) {
706 XPutBackEvent(**otk::display
, &ce
);
711 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
713 setupDecorAndFunctions(); // normal hints can make a window non-resizable
714 } else if (e
.atom
== XA_WM_HINTS
)
716 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
717 updateTransientFor();
719 calcLayer(); // type may have changed, so update the layer
720 setupDecorAndFunctions();
722 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
723 e
.atom
== otk::Property::atoms
.wm_name
)
725 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
726 e
.atom
== otk::Property::atoms
.wm_icon_name
)
728 else if (e
.atom
== otk::Property::atoms
.wm_class
)
730 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
732 setupDecorAndFunctions();
734 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
739 void Client::setWMState(long state
)
741 if (state
== _wmstate
) return; // no change
745 setDesktop(ICONIC_DESKTOP
);
748 setDesktop(openbox
->screen(_screen
)->desktop());
754 void Client::setDesktop(long target
)
756 if (target
== _desktop
) return;
758 printf("Setting desktop %ld\n", target
);
760 if (!(target
>= 0 || target
== (signed)0xffffffff ||
761 target
== ICONIC_DESKTOP
))
766 // set the desktop hint, but not if we're iconifying
767 if (_desktop
!= ICONIC_DESKTOP
)
768 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
769 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
771 // 'move' the window to the new desktop
772 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
773 _desktop
== (signed)0xffffffff)
778 // Handle Iconic state. Iconic state is maintained by the client being a
779 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
780 // uniconifying happen.
781 bool i
= _desktop
== ICONIC_DESKTOP
;
782 if (i
!= _iconic
) { // has the state changed?
785 _wmstate
= IconicState
;
787 // we unmap the client itself so that we can get MapRequest events, and
788 // because the ICCCM tells us to!
789 XUnmapWindow(**otk::display
, _window
);
791 _wmstate
= NormalState
;
792 XMapWindow(**otk::display
, _window
);
797 frame
->adjustState();
801 Client
*Client::findModalChild(Client
*skip
) const
805 // find a modal child recursively and try focus it
806 List::const_iterator it
, end
= _transients
.end();
807 for (it
= _transients
.begin(); it
!= end
; ++it
)
808 if ((*it
)->_modal
&& *it
!= skip
)
809 return *it
; // got one
810 // none of our direct children are modal, let them try check
811 for (it
= _transients
.begin(); it
!= end
; ++it
)
812 if ((ret
= (*it
)->findModalChild()))
813 return ret
; // got one
818 void Client::setModal(bool modal
)
820 if (modal
== _modal
) return;
824 while (c
->_transient_for
) {
825 c
= c
->_transient_for
;
826 if (c
->_modal_child
) break; // already has a modal child
827 c
->_modal_child
= this;
830 // try find a replacement modal dialog
831 Client
*replacement
= 0;
834 while (c
->_transient_for
) // go up the tree
835 c
= c
->_transient_for
;
836 replacement
= c
->findModalChild(this); // find a modal child, skipping this
839 while (c
->_transient_for
) {
840 c
= c
->_transient_for
;
841 if (c
->_modal_child
!= this) break; // has a different modal child
842 c
->_modal_child
= replacement
;
849 void Client::setState(StateAction action
, long data1
, long data2
)
851 bool shadestate
= _shaded
;
852 bool fsstate
= _fullscreen
;
853 bool maxh
= _max_horz
;
854 bool maxv
= _max_vert
;
857 if (!(action
== State_Add
|| action
== State_Remove
||
858 action
== State_Toggle
))
859 return; // an invalid action was passed to the client message, ignore it
861 for (int i
= 0; i
< 2; ++i
) {
862 Atom state
= i
== 0 ? data1
: data2
;
864 if (! state
) continue;
866 // if toggling, then pick whether we're adding or removing
867 if (action
== State_Toggle
) {
868 if (state
== otk::Property::atoms
.net_wm_state_modal
)
869 action
= _modal
? State_Remove
: State_Add
;
870 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
871 action
= _max_vert
? State_Remove
: State_Add
;
872 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
873 action
= _max_horz
? State_Remove
: State_Add
;
874 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
875 action
= _shaded
? State_Remove
: State_Add
;
876 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
877 action
= _skip_taskbar
? State_Remove
: State_Add
;
878 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
879 action
= _skip_pager
? State_Remove
: State_Add
;
880 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
881 action
= _fullscreen
? State_Remove
: State_Add
;
882 else if (state
== otk::Property::atoms
.net_wm_state_above
)
883 action
= _above
? State_Remove
: State_Add
;
884 else if (state
== otk::Property::atoms
.net_wm_state_below
)
885 action
= _below
? State_Remove
: State_Add
;
888 if (action
== State_Add
) {
889 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
890 if (_modal
) continue;
892 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
894 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
895 if (_max_horz
) continue;
897 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
899 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
900 _skip_taskbar
= true;
901 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
903 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
905 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
906 if (_above
) continue;
908 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
909 if (_below
) continue;
913 } else { // action == State_Remove
914 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
915 if (!_modal
) continue;
917 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
919 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
921 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
923 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
924 _skip_taskbar
= false;
925 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
927 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
929 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
930 if (!_above
) continue;
932 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
933 if (!_below
) continue;
938 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
939 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
940 if (maxh
== maxv
) { // both going the same way
941 maximize(maxh
, 0, true);
943 maximize(maxh
, 1, true);
944 maximize(maxv
, 2, true);
946 } else { // toggling one
947 if (maxh
!= _max_horz
)
948 maximize(maxh
, 1, true);
950 maximize(maxv
, 2, true);
955 // change fullscreen state before shading, as it will affect if the window
957 if (fsstate
!= _fullscreen
)
958 fullscreen(fsstate
, true);
959 if (shadestate
!= _shaded
)
962 changeState(); // change the hint to relect these changes
966 void Client::toggleClientBorder(bool addborder
)
968 // adjust our idea of where the client is, based on its border. When the
969 // border is removed, the client should now be considered to be in a
970 // different position.
971 // when re-adding the border to the client, the same operation needs to be
973 int oldx
= _area
.x(), oldy
= _area
.y();
974 int x
= oldx
, y
= oldy
;
977 case NorthWestGravity
:
979 case SouthWestGravity
:
981 case NorthEastGravity
:
983 case SouthEastGravity
:
984 if (addborder
) x
-= _border_width
* 2;
985 else x
+= _border_width
* 2;
992 if (addborder
) x
-= _border_width
;
993 else x
+= _border_width
;
998 case NorthWestGravity
:
1000 case NorthEastGravity
:
1002 case SouthWestGravity
:
1004 case SouthEastGravity
:
1005 if (addborder
) y
-= _border_width
* 2;
1006 else y
+= _border_width
* 2;
1013 if (addborder
) y
-= _border_width
;
1014 else y
+= _border_width
;
1020 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1022 // move the client so it is back it the right spot _with_ its border!
1023 if (x
!= oldx
|| y
!= oldy
)
1024 XMoveWindow(**otk::display
, _window
, x
, y
);
1026 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1030 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1032 otk::EventHandler::clientMessageHandler(e
);
1034 // validate cuz we query stuff off the client here
1035 if (!validate()) return;
1037 if (e
.format
!= 32) return;
1039 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1040 // compress changes into a single change
1041 bool compress
= false;
1043 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1044 // XXX: it would be nice to compress ALL messages of a type, not just
1045 // messages in a row without other message types between.
1046 if (ce
.xclient
.message_type
!= e
.message_type
) {
1047 XPutBackEvent(**otk::display
, &ce
);
1053 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1055 setWMState(e
.data
.l
[0]); // use the original event
1056 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1057 // compress changes into a single change
1058 bool compress
= false;
1060 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1061 // XXX: it would be nice to compress ALL messages of a type, not just
1062 // messages in a row without other message types between.
1063 if (ce
.xclient
.message_type
!= e
.message_type
) {
1064 XPutBackEvent(**otk::display
, &ce
);
1070 setDesktop(e
.data
.l
[0]); // use the found event
1072 setDesktop(e
.data
.l
[0]); // use the original event
1073 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1074 // can't compress these
1076 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1077 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1078 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1079 e
.data
.l
[1], e
.data
.l
[2], _window
);
1081 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1082 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1084 printf("net_close_window for 0x%lx\n", _window
);
1087 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1089 printf("net_active_window for 0x%lx\n", _window
);
1092 setDesktop(openbox
->screen(_screen
)->desktop());
1097 openbox
->screen(_screen
)->raiseWindow(this);
1103 void Client::shapeHandler(const XShapeEvent
&e
)
1105 otk::EventHandler::shapeHandler(e
);
1107 if (e
.kind
== ShapeBounding
) {
1109 frame
->adjustShape();
1115 void Client::resize(Corner anchor
, int w
, int h
)
1117 if (!(_functions
& Func_Resize
)) return;
1118 internal_resize(anchor
, w
, h
);
1122 void Client::internal_resize(Corner anchor
, int w
, int h
, bool user
,
1125 w
-= _base_size
.x();
1126 h
-= _base_size
.y();
1129 // for interactive resizing. have to move half an increment in each
1131 int mw
= w
% _size_inc
.x(); // how far we are towards the next size inc
1132 int mh
= h
% _size_inc
.y();
1133 int aw
= _size_inc
.x() / 2; // amount to add
1134 int ah
= _size_inc
.y() / 2;
1135 // don't let us move into a new size increment
1136 if (mw
+ aw
>= _size_inc
.x()) aw
= _size_inc
.x() - mw
- 1;
1137 if (mh
+ ah
>= _size_inc
.y()) ah
= _size_inc
.y() - mh
- 1;
1141 // if this is a user-requested resize, then check against min/max sizes
1142 // and aspect ratios
1144 // smaller than min size or bigger than max size?
1145 if (w
< _min_size
.x()) w
= _min_size
.x();
1146 else if (w
> _max_size
.x()) w
= _max_size
.x();
1147 if (h
< _min_size
.y()) h
= _min_size
.y();
1148 else if (h
> _max_size
.y()) h
= _max_size
.y();
1150 // adjust the height ot match the width for the aspect ratios
1152 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1154 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1157 // keep to the increments
1161 // you cannot resize to nothing
1165 // store the logical size
1166 _logical_size
.setPoint(w
, h
);
1171 w
+= _base_size
.x();
1172 h
+= _base_size
.y();
1174 if (x
== INT_MIN
|| y
== INT_MIN
) {
1181 x
-= w
- _area
.width();
1184 y
-= h
- _area
.height();
1187 x
-= w
- _area
.width();
1188 y
-= h
- _area
.height();
1193 _area
.setSize(w
, h
);
1195 XResizeWindow(**otk::display
, _window
, w
, h
);
1197 // resize the frame to match the request
1198 frame
->adjustSize();
1199 internal_move(x
, y
);
1203 void Client::move(int x
, int y
)
1205 if (!(_functions
& Func_Move
)) return;
1206 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1208 internal_move(x
, y
);
1212 void Client::internal_move(int x
, int y
)
1216 // move the frame to be in the requested position
1217 if (frame
) { // this can be called while mapping, before frame exists
1218 frame
->adjustPosition();
1220 // send synthetic configure notify (we don't need to if we aren't mapped
1223 event
.type
= ConfigureNotify
;
1224 event
.xconfigure
.display
= **otk::display
;
1225 event
.xconfigure
.event
= _window
;
1226 event
.xconfigure
.window
= _window
;
1228 // root window coords with border in mind
1229 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1230 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1232 event
.xconfigure
.width
= _area
.width();
1233 event
.xconfigure
.height
= _area
.height();
1234 event
.xconfigure
.border_width
= _border_width
;
1235 event
.xconfigure
.above
= frame
->plate();
1236 event
.xconfigure
.override_redirect
= False
;
1237 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1238 StructureNotifyMask
, &event
);
1240 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1241 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1242 event
.xconfigure
.height
, event
.xconfigure
.window
);
1248 void Client::close()
1252 if (!(_functions
& Func_Close
)) return;
1254 // XXX: itd be cool to do timeouts and shit here for killing the client's
1256 // like... if the window is around after 5 seconds, then the close button
1257 // turns a nice red, and if this function is called again, the client is
1258 // explicitly killed.
1260 ce
.xclient
.type
= ClientMessage
;
1261 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1262 ce
.xclient
.display
= **otk::display
;
1263 ce
.xclient
.window
= _window
;
1264 ce
.xclient
.format
= 32;
1265 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1266 ce
.xclient
.data
.l
[1] = CurrentTime
;
1267 ce
.xclient
.data
.l
[2] = 0l;
1268 ce
.xclient
.data
.l
[3] = 0l;
1269 ce
.xclient
.data
.l
[4] = 0l;
1270 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1274 void Client::changeState()
1276 unsigned long state
[2];
1277 state
[0] = _wmstate
;
1279 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1280 otk::Property::atoms
.wm_state
, state
, 2);
1285 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1287 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1289 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1291 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1293 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1295 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1297 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1299 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1301 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1303 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1304 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1305 otk::Property::atoms
.atom
, netstate
, num
);
1310 frame
->adjustState();
1314 void Client::changeAllowedActions(void)
1319 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1321 if (_functions
& Func_Shade
)
1322 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1323 if (_functions
& Func_Close
)
1324 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1325 if (_functions
& Func_Move
)
1326 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1327 if (_functions
& Func_Iconify
)
1328 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1329 if (_functions
& Func_Resize
)
1330 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1331 if (_functions
& Func_Fullscreen
)
1332 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1333 if (_functions
& Func_Maximize
) {
1334 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1335 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1338 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1339 otk::Property::atoms
.atom
, actions
, num
);
1343 void Client::remaximize()
1346 if (_max_horz
&& _max_vert
)
1353 return; // not maximized
1354 _max_horz
= _max_vert
= false;
1355 maximize(true, dir
, false);
1359 void Client::applyStartupState()
1361 // these are in a carefully crafted order..
1370 setDesktop(ICONIC_DESKTOP
);
1373 _fullscreen
= false;
1374 fullscreen(true, false);
1383 if (_max_vert
&& _max_horz
) {
1384 _max_vert
= _max_horz
= false;
1385 maximize(true, 0, false);
1386 } else if (_max_vert
) {
1388 maximize(true, 2, false);
1389 } else if (_max_horz
) {
1391 maximize(true, 1, false);
1394 if (_skip_taskbar
); // nothing to do for this
1395 if (_skip_pager
); // nothing to do for this
1396 if (_modal
); // nothing to do for this
1397 if (_above
); // nothing to do for this
1398 if (_below
); // nothing to do for this
1402 void Client::fireUrgent()
1404 // call the python UrgentWindow callbacks
1405 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1406 openbox
->bindings()->fireEvent(&data
);
1410 void Client::shade(bool shade
)
1412 if (!(_functions
& Func_Shade
) || // can't
1413 _shaded
== shade
) return; // already done
1415 // when we're iconic, don't change the wmstate
1417 _wmstate
= shade
? IconicState
: NormalState
;
1420 frame
->adjustSize();
1424 void Client::maximize(bool max
, int dir
, bool savearea
)
1426 assert(dir
== 0 || dir
== 1 || dir
== 2);
1427 if (!(_functions
& Func_Maximize
)) return; // can't
1429 // check if already done
1431 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1432 if (dir
== 1 && _max_horz
) return;
1433 if (dir
== 2 && _max_vert
) return;
1435 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1436 if (dir
== 1 && !_max_horz
) return;
1437 if (dir
== 2 && !_max_vert
) return;
1440 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1441 int x
= frame
->rect().x(), y
= frame
->rect().y(),
1442 w
= _area
.width(), h
= _area
.height();
1448 unsigned long n
= 4;
1455 // get the property off the window and use it for the dimentions we are
1457 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1458 otk::Property::atoms
.cardinal
, &n
,
1459 (long unsigned**) &readdim
)) {
1462 dimensions
[0] = readdim
[0];
1463 dimensions
[2] = readdim
[2];
1466 dimensions
[1] = readdim
[1];
1467 dimensions
[3] = readdim
[3];
1473 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1474 otk::Property::atoms
.cardinal
,
1475 (long unsigned*)dimensions
, 4);
1477 if (dir
== 0 || dir
== 1) { // horz
1481 if (dir
== 0 || dir
== 2) { // vert
1483 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1487 long unsigned n
= 4;
1489 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1490 otk::Property::atoms
.cardinal
, &n
,
1491 (long unsigned**) &dimensions
)) {
1493 if (dir
== 0 || dir
== 1) { // horz
1494 x
= (signed int)dimensions
[0];
1495 w
= (signed int)dimensions
[2];
1497 if (dir
== 0 || dir
== 2) { // vert
1498 y
= (signed int)dimensions
[1];
1499 h
= (signed int)dimensions
[3];
1504 // pick some fallbacks...
1505 if (dir
== 0 || dir
== 1) { // horz
1506 x
= a
.x() + a
.width() / 4;
1509 if (dir
== 0 || dir
== 2) { // vert
1510 y
= a
.y() + a
.height() / 4;
1516 if (dir
== 0 || dir
== 1) // horz
1518 if (dir
== 0 || dir
== 2) // vert
1521 if (!_max_horz
&& !_max_vert
)
1522 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1524 changeState(); // change the state hints on the client
1526 frame
->frameGravity(x
, y
); // figure out where the client should be going
1527 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1531 void Client::fullscreen(bool fs
, bool savearea
)
1533 static FunctionFlags saved_func
;
1534 static DecorationFlags saved_decor
;
1536 if (!(_functions
& Func_Fullscreen
) || // can't
1537 _fullscreen
== fs
) return; // already done
1540 changeState(); // change the state hints on the client
1542 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1545 // save the functions and remove them
1546 saved_func
= _functions
;
1547 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1548 // save the decorations and remove them
1549 saved_decor
= _decorations
;
1553 dimensions
[0] = _area
.x();
1554 dimensions
[1] = _area
.y();
1555 dimensions
[2] = _area
.width();
1556 dimensions
[3] = _area
.height();
1557 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1558 otk::Property::atoms
.cardinal
,
1559 (long unsigned*)dimensions
, 4);
1561 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1567 _functions
= saved_func
;
1568 _decorations
= saved_decor
;
1571 long unsigned n
= 4;
1573 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1574 otk::Property::atoms
.cardinal
, &n
,
1575 (long unsigned**) &dimensions
)) {
1584 // pick some fallbacks...
1585 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1586 x
= a
.x() + a
.width() / 4;
1587 y
= a
.y() + a
.height() / 4;
1593 changeAllowedActions(); // based on the new _functions
1595 // when fullscreening, don't obey things like increments, fill the screen
1596 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1598 // raise (back) into our stacking layer
1599 openbox
->screen(_screen
)->raiseWindow(this);
1601 // try focus us when we go into fullscreen mode
1606 void Client::disableDecorations(DecorationFlags flags
)
1608 _disabled_decorations
= flags
;
1609 setupDecorAndFunctions();
1613 void Client::installColormap(bool install
) const
1615 XWindowAttributes wa
;
1616 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1617 printf("%snstalling Window Colormap 0x%lx!\n", install
? "I" : "Uni", _window
);
1619 XInstallColormap(**otk::display
, wa
.colormap
);
1621 XUninstallColormap(**otk::display
, wa
.colormap
);
1626 bool Client::focus()
1628 // if we have a modal child, then focus it, not us
1630 return _modal_child
->focus();
1632 // won't try focus if the client doesn't want it, or if the window isn't
1633 // visible on the screen
1634 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1636 if (_focused
) return true;
1638 // do a check to see if the window has already been unmapped or destroyed
1639 // do this intelligently while watching out for unmaps we've generated
1640 // (ignore_unmaps > 0)
1642 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1643 XPutBackEvent(**otk::display
, &ev
);
1646 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1647 if (ignore_unmaps
) {
1648 unmapHandler(ev
.xunmap
);
1650 XPutBackEvent(**otk::display
, &ev
);
1656 XSetInputFocus(**otk::display
, _window
,
1657 RevertToNone
, CurrentTime
);
1659 if (_focus_notify
) {
1661 ce
.xclient
.type
= ClientMessage
;
1662 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1663 ce
.xclient
.display
= **otk::display
;
1664 ce
.xclient
.window
= _window
;
1665 ce
.xclient
.format
= 32;
1666 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1667 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1668 ce
.xclient
.data
.l
[2] = 0l;
1669 ce
.xclient
.data
.l
[3] = 0l;
1670 ce
.xclient
.data
.l
[4] = 0l;
1671 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1674 XSync(**otk::display
, False
);
1679 void Client::unfocus() const
1681 if (!_focused
) return;
1683 assert(openbox
->focusedClient() == this);
1684 openbox
->setFocusedClient(0);
1688 void Client::focusHandler(const XFocusChangeEvent
&e
)
1691 // printf("FocusIn for 0x%lx\n", e.window);
1694 otk::EventHandler::focusHandler(e
);
1699 openbox
->setFocusedClient(this);
1703 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1706 // printf("FocusOut for 0x%lx\n", e.window);
1709 otk::EventHandler::unfocusHandler(e
);
1714 if (openbox
->focusedClient() == this)
1715 openbox
->setFocusedClient(0);
1719 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1722 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1725 otk::EventHandler::configureRequestHandler(e
);
1727 // if we are iconic (or shaded (fvwm does this)) ignore the event
1728 if (_iconic
|| _shaded
) return;
1730 if (e
.value_mask
& CWBorderWidth
)
1731 _border_width
= e
.border_width
;
1733 // resize, then move, as specified in the EWMH section 7.7
1734 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1735 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1736 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1740 case NorthEastGravity
:
1744 case SouthWestGravity
:
1746 corner
= BottomLeft
;
1748 case SouthEastGravity
:
1749 corner
= BottomRight
;
1751 default: // NorthWest, Static, etc
1755 // if moving AND resizing ...
1756 if (e
.value_mask
& (CWX
| CWY
)) {
1757 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1758 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1759 internal_resize(corner
, w
, h
, false, x
, y
);
1760 } else // if JUST resizing...
1761 internal_resize(corner
, w
, h
, false);
1762 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1763 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1764 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1765 internal_move(x
, y
);
1768 if (e
.value_mask
& CWStackMode
) {
1772 openbox
->screen(_screen
)->lowerWindow(this);
1778 openbox
->screen(_screen
)->raiseWindow(this);
1785 void Client::unmapHandler(const XUnmapEvent
&e
)
1787 if (ignore_unmaps
) {
1789 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1796 printf("UnmapNotify for 0x%lx\n", e
.window
);
1799 otk::EventHandler::unmapHandler(e
);
1801 // this deletes us etc
1802 openbox
->screen(_screen
)->unmanageWindow(this);
1806 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1809 printf("DestroyNotify for 0x%lx\n", e
.window
);
1812 otk::EventHandler::destroyHandler(e
);
1814 // this deletes us etc
1815 openbox
->screen(_screen
)->unmanageWindow(this);
1819 void Client::reparentHandler(const XReparentEvent
&e
)
1821 // this is when the client is first taken captive in the frame
1822 if (e
.parent
== frame
->plate()) return;
1825 printf("ReparentNotify for 0x%lx\n", e
.window
);
1828 otk::EventHandler::reparentHandler(e
);
1831 This event is quite rare and is usually handled in unmapHandler.
1832 However, if the window is unmapped when the reparent event occurs,
1833 the window manager never sees it because an unmap event is not sent
1834 to an already unmapped window.
1837 // we don't want the reparent event, put it back on the stack for the X
1838 // server to deal with after we unmanage the window
1841 XPutBackEvent(**otk::display
, &ev
);
1843 // this deletes us etc
1844 openbox
->screen(_screen
)->unmanageWindow(this);
1847 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1850 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1853 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1855 // move to the current desktop (uniconify)
1856 setDesktop(openbox
->screen(_screen
)->desktop());
1857 // XXX: should we focus/raise the window? (basically a net_wm_active_window)