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>
21 #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
.width() < _max_size
.width() ||
225 _min_size
.height() < _max_size
.height())) {
226 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
227 _functions
&= ~(Func_Resize
| Func_Maximize
);
232 // normal windows retain all of the possible decorations and
233 // functionality, and are the only windows that you can fullscreen
234 _functions
|= Func_Fullscreen
;
238 // dialogs cannot be maximized
239 _decorations
&= ~Decor_Maximize
;
240 _functions
&= ~Func_Maximize
;
246 // these windows get less functionality
247 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
248 _functions
&= ~(Func_Iconify
| Func_Resize
);
254 // none of these windows are manipulated by the window manager
260 // Mwm Hints are applied subtractively to what has already been chosen for
261 // decor and functionality
262 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
263 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
264 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
265 _decorations
&= ~Decor_Border
;
266 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
267 _decorations
&= ~Decor_Handle
;
268 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
269 _decorations
&= ~Decor_Titlebar
;
270 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
271 _decorations
&= ~Decor_Iconify
;
272 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
273 _decorations
&= ~Decor_Maximize
;
277 if (_mwmhints
.flags
& MwmFlag_Functions
) {
278 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
279 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
280 _functions
&= ~Func_Resize
;
281 if (! (_mwmhints
.functions
& MwmFunc_Move
))
282 _functions
&= ~Func_Move
;
283 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
284 _functions
&= ~Func_Iconify
;
285 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
286 _functions
&= ~Func_Maximize
;
287 // dont let mwm hints kill the close button
288 //if (! (_mwmhints.functions & MwmFunc_Close))
289 // _functions &= ~Func_Close;
293 // can't maximize without moving/resizing
294 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
295 _functions
&= ~Func_Maximize
;
297 // finally, user specified disabled decorations are applied to subtract
299 if (_disabled_decorations
& Decor_Titlebar
)
300 _decorations
&= ~Decor_Titlebar
;
301 if (_disabled_decorations
& Decor_Handle
)
302 _decorations
&= ~Decor_Handle
;
303 if (_disabled_decorations
& Decor_Border
)
304 _decorations
&= ~Decor_Border
;
305 if (_disabled_decorations
& Decor_Iconify
)
306 _decorations
&= ~Decor_Iconify
;
307 if (_disabled_decorations
& Decor_Maximize
)
308 _decorations
&= ~Decor_Maximize
;
309 if (_disabled_decorations
& Decor_AllDesktops
)
310 _decorations
&= ~Decor_AllDesktops
;
311 if (_disabled_decorations
& Decor_Close
)
312 _decorations
&= ~Decor_Close
;
314 // if we don't have a titlebar, then we cannot shade!
315 if (!(_decorations
& Decor_Titlebar
))
316 _functions
&= ~Func_Shade
;
318 changeAllowedActions();
321 frame
->adjustSize(); // change the decors on the frame
322 frame
->adjustPosition(); // with more/less decorations, we may need to be
324 remaximize(); // with new decor, the window's maximized size may change
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
= otk::Rect(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
= otk::Size(1, 1);
482 _base_size
= otk::Size(0, 0);
483 _min_size
= otk::Size(0, 0);
484 _max_size
= otk::Size(UINT_MAX
, UINT_MAX
);
486 // get the hints from the window
487 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
488 _positioned
= (size
.flags
& (PPosition
|USPosition
));
490 if (size
.flags
& PWinGravity
) {
491 _gravity
= size
.win_gravity
;
493 // if the client has a frame, i.e. has already been mapped and is
494 // changing its gravity
495 if (frame
&& _gravity
!= oldgravity
) {
496 // move our idea of the client's position based on its new gravity
497 int x
= frame
->area().x(), y
= frame
->area().y();
498 frame
->frameGravity(x
, y
);
499 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
503 if (size
.flags
& PAspect
) {
504 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
505 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
508 if (size
.flags
& PMinSize
)
509 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
511 if (size
.flags
& PMaxSize
)
512 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
514 if (size
.flags
& PBaseSize
)
515 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
517 if (size
.flags
& PResizeInc
)
518 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
523 void Client::updateWMHints(bool initstate
)
527 // assume a window takes input if it doesnt specify
531 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
532 if (hints
->flags
& InputHint
)
533 _can_focus
= hints
->input
;
535 // only do this when initstate is true!
536 if (initstate
&& (hints
->flags
& StateHint
))
537 _iconic
= hints
->initial_state
== IconicState
;
539 if (hints
->flags
& XUrgencyHint
)
542 if (hints
->flags
& WindowGroupHint
) {
543 if (hints
->window_group
!= _group
) {
544 // XXX: remove from the old group if there was one
545 _group
= hints
->window_group
;
546 // XXX: do stuff with the group
557 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
558 (long)_window
, _urgent
? "ON" : "OFF");
560 // fire the urgent callback if we're mapped, otherwise, wait until after
568 void Client::updateTitle()
573 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
574 otk::Property::utf8
, &_title
)) {
576 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
577 otk::Property::ascii
, &_title
);
581 _title
= _("Unnamed Window");
584 frame
->setTitle(_title
);
588 void Client::updateIconTitle()
593 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
594 otk::Property::utf8
, &_icon_title
)) {
596 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
597 otk::Property::ascii
, &_icon_title
);
601 _icon_title
= _("Unnamed Window");
605 void Client::updateClass()
608 _app_name
= _app_class
= _role
= "";
610 otk::Property::StringVect v
;
611 unsigned long num
= 2;
613 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
614 otk::Property::ascii
, &num
, &v
)) {
615 if (num
> 0) _app_name
= v
[0].c_str();
616 if (num
> 1) _app_class
= v
[1].c_str();
621 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
622 otk::Property::ascii
, &num
, &v
)) {
623 if (num
> 0) _role
= v
[0].c_str();
628 void Client::updateStrut()
630 unsigned long num
= 4;
632 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
633 otk::Property::atoms
.cardinal
, &num
, &data
))
637 _strut
.left
= data
[0];
638 _strut
.right
= data
[1];
639 _strut
.top
= data
[2];
640 _strut
.bottom
= data
[3];
642 // updating here is pointless while we're being mapped cuz we're not in
643 // the screen's client list yet
645 openbox
->screen(_screen
)->updateStrut();
652 void Client::updateTransientFor()
657 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
658 t
!= _window
) { // cant be transient to itself!
659 c
= openbox
->findClient(t
);
660 assert(c
!= this); // if this happens then we need to check for it
662 if (!c
/*XXX: && _group*/) {
663 // not transient to a client, see if it is transient for a group
664 if (//t == _group->leader() ||
666 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
667 // window is a transient for its group!
668 // XXX: for now this is treated as non-transient.
669 // this needs to be fixed!
674 // if anything has changed...
675 if (c
!= _transient_for
) {
681 _transient_for
->_transients
.remove(this); // remove from old parent
684 _transient_for
->_transients
.push_back(this); // add to new parent
692 void Client::propertyHandler(const XPropertyEvent
&e
)
694 otk::EventHandler::propertyHandler(e
);
696 // validate cuz we query stuff off the client here
697 if (!validate()) return;
699 // compress changes to a single property into a single change
701 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
702 // XXX: it would be nice to compress ALL changes to a property, not just
703 // changes in a row without other props between.
704 if (ce
.xproperty
.atom
!= e
.atom
) {
705 XPutBackEvent(**otk::display
, &ce
);
710 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
712 setupDecorAndFunctions(); // normal hints can make a window non-resizable
713 } else if (e
.atom
== XA_WM_HINTS
)
715 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
716 updateTransientFor();
718 calcLayer(); // type may have changed, so update the layer
719 setupDecorAndFunctions();
721 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
722 e
.atom
== otk::Property::atoms
.wm_name
)
724 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
725 e
.atom
== otk::Property::atoms
.wm_icon_name
)
727 else if (e
.atom
== otk::Property::atoms
.wm_class
)
729 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
731 setupDecorAndFunctions();
733 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
738 void Client::setWMState(long state
)
740 if (state
== _wmstate
) return; // no change
744 setDesktop(ICONIC_DESKTOP
);
747 setDesktop(openbox
->screen(_screen
)->desktop());
753 void Client::setDesktop(long target
)
755 if (target
== _desktop
) return;
757 printf("Setting desktop %ld\n", target
);
759 if (!(target
>= 0 || target
== (signed)0xffffffff ||
760 target
== ICONIC_DESKTOP
))
765 // set the desktop hint, but not if we're iconifying
766 if (_desktop
!= ICONIC_DESKTOP
)
767 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
768 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
770 // 'move' the window to the new desktop
771 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
772 _desktop
== (signed)0xffffffff)
777 // Handle Iconic state. Iconic state is maintained by the client being a
778 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
779 // uniconifying happen.
780 bool i
= _desktop
== ICONIC_DESKTOP
;
781 if (i
!= _iconic
) { // has the state changed?
784 _wmstate
= IconicState
;
786 // we unmap the client itself so that we can get MapRequest events, and
787 // because the ICCCM tells us to!
788 XUnmapWindow(**otk::display
, _window
);
790 _wmstate
= NormalState
;
791 XMapWindow(**otk::display
, _window
);
796 frame
->adjustState();
800 Client
*Client::findModalChild(Client
*skip
) const
804 // find a modal child recursively and try focus it
805 List::const_iterator it
, end
= _transients
.end();
806 for (it
= _transients
.begin(); it
!= end
; ++it
)
807 if ((*it
)->_modal
&& *it
!= skip
)
808 return *it
; // got one
809 // none of our direct children are modal, let them try check
810 for (it
= _transients
.begin(); it
!= end
; ++it
)
811 if ((ret
= (*it
)->findModalChild()))
812 return ret
; // got one
817 void Client::setModal(bool modal
)
819 if (modal
== _modal
) return;
823 while (c
->_transient_for
) {
824 c
= c
->_transient_for
;
825 if (c
->_modal_child
) break; // already has a modal child
826 c
->_modal_child
= this;
829 // try find a replacement modal dialog
830 Client
*replacement
= 0;
833 while (c
->_transient_for
) // go up the tree
834 c
= c
->_transient_for
;
835 replacement
= c
->findModalChild(this); // find a modal child, skipping this
838 while (c
->_transient_for
) {
839 c
= c
->_transient_for
;
840 if (c
->_modal_child
!= this) break; // has a different modal child
841 c
->_modal_child
= replacement
;
848 void Client::setState(StateAction action
, long data1
, long data2
)
850 bool shadestate
= _shaded
;
851 bool fsstate
= _fullscreen
;
852 bool maxh
= _max_horz
;
853 bool maxv
= _max_vert
;
856 if (!(action
== State_Add
|| action
== State_Remove
||
857 action
== State_Toggle
))
858 return; // an invalid action was passed to the client message, ignore it
860 for (int i
= 0; i
< 2; ++i
) {
861 Atom state
= i
== 0 ? data1
: data2
;
863 if (! state
) continue;
865 // if toggling, then pick whether we're adding or removing
866 if (action
== State_Toggle
) {
867 if (state
== otk::Property::atoms
.net_wm_state_modal
)
868 action
= _modal
? State_Remove
: State_Add
;
869 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
870 action
= _max_vert
? State_Remove
: State_Add
;
871 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
872 action
= _max_horz
? State_Remove
: State_Add
;
873 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
874 action
= _shaded
? State_Remove
: State_Add
;
875 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
876 action
= _skip_taskbar
? State_Remove
: State_Add
;
877 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
878 action
= _skip_pager
? State_Remove
: State_Add
;
879 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
880 action
= _fullscreen
? State_Remove
: State_Add
;
881 else if (state
== otk::Property::atoms
.net_wm_state_above
)
882 action
= _above
? State_Remove
: State_Add
;
883 else if (state
== otk::Property::atoms
.net_wm_state_below
)
884 action
= _below
? State_Remove
: State_Add
;
887 if (action
== State_Add
) {
888 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
889 if (_modal
) continue;
891 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
893 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
894 if (_max_horz
) continue;
896 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
898 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
899 _skip_taskbar
= true;
900 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
902 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
904 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
905 if (_above
) continue;
907 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
908 if (_below
) continue;
912 } else { // action == State_Remove
913 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
914 if (!_modal
) continue;
916 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
918 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
920 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
922 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
923 _skip_taskbar
= false;
924 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
926 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
928 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
929 if (!_above
) continue;
931 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
932 if (!_below
) continue;
937 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
938 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
939 if (maxh
== maxv
) { // both going the same way
940 maximize(maxh
, 0, true);
942 maximize(maxh
, 1, true);
943 maximize(maxv
, 2, true);
945 } else { // toggling one
946 if (maxh
!= _max_horz
)
947 maximize(maxh
, 1, true);
949 maximize(maxv
, 2, true);
954 // change fullscreen state before shading, as it will affect if the window
956 if (fsstate
!= _fullscreen
)
957 fullscreen(fsstate
, true);
958 if (shadestate
!= _shaded
)
961 changeState(); // change the hint to relect these changes
965 void Client::toggleClientBorder(bool addborder
)
967 // adjust our idea of where the client is, based on its border. When the
968 // border is removed, the client should now be considered to be in a
969 // different position.
970 // when re-adding the border to the client, the same operation needs to be
972 int oldx
= _area
.x(), oldy
= _area
.y();
973 int x
= oldx
, y
= oldy
;
976 case NorthWestGravity
:
978 case SouthWestGravity
:
980 case NorthEastGravity
:
982 case SouthEastGravity
:
983 if (addborder
) x
-= _border_width
* 2;
984 else x
+= _border_width
* 2;
991 if (addborder
) x
-= _border_width
;
992 else x
+= _border_width
;
997 case NorthWestGravity
:
999 case NorthEastGravity
:
1001 case SouthWestGravity
:
1003 case SouthEastGravity
:
1004 if (addborder
) y
-= _border_width
* 2;
1005 else y
+= _border_width
* 2;
1012 if (addborder
) y
-= _border_width
;
1013 else y
+= _border_width
;
1016 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1019 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1021 // move the client so it is back it the right spot _with_ its border!
1022 if (x
!= oldx
|| y
!= oldy
)
1023 XMoveWindow(**otk::display
, _window
, x
, y
);
1025 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1029 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1031 otk::EventHandler::clientMessageHandler(e
);
1033 // validate cuz we query stuff off the client here
1034 if (!validate()) return;
1036 if (e
.format
!= 32) return;
1038 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1039 // compress changes into a single change
1040 bool compress
= false;
1042 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1043 // XXX: it would be nice to compress ALL messages of a type, not just
1044 // messages in a row without other message types between.
1045 if (ce
.xclient
.message_type
!= e
.message_type
) {
1046 XPutBackEvent(**otk::display
, &ce
);
1052 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1054 setWMState(e
.data
.l
[0]); // use the original event
1055 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1056 // compress changes into a single change
1057 bool compress
= false;
1059 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1060 // XXX: it would be nice to compress ALL messages of a type, not just
1061 // messages in a row without other message types between.
1062 if (ce
.xclient
.message_type
!= e
.message_type
) {
1063 XPutBackEvent(**otk::display
, &ce
);
1069 setDesktop(e
.data
.l
[0]); // use the found event
1071 setDesktop(e
.data
.l
[0]); // use the original event
1072 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1073 // can't compress these
1075 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1076 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1077 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1078 e
.data
.l
[1], e
.data
.l
[2], _window
);
1080 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1081 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1083 printf("net_close_window for 0x%lx\n", _window
);
1086 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1088 printf("net_active_window for 0x%lx\n", _window
);
1091 setDesktop(openbox
->screen(_screen
)->desktop());
1095 openbox
->screen(_screen
)->raiseWindow(this);
1096 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1098 setDesktop(openbox
->screen(_screen
)->desktop());
1099 if (e
.data
.l
[0] && _shaded
)
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
, unsigned int w
, unsigned int h
)
1123 if (!(_functions
& Func_Resize
)) return;
1124 internal_resize(anchor
, w
, h
);
1128 void Client::internal_resize(Corner anchor
, unsigned int w
, unsigned int h
,
1129 bool user
, int x
, int y
)
1131 w
-= _base_size
.width();
1132 h
-= _base_size
.height();
1135 // for interactive resizing. have to move half an increment in each
1137 unsigned int mw
= w
% _size_inc
.width(); // how far we are towards the next
1139 unsigned int mh
= h
% _size_inc
.height();
1140 unsigned int aw
= _size_inc
.width() / 2; // amount to add
1141 unsigned int ah
= _size_inc
.height() / 2;
1142 // don't let us move into a new size increment
1143 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1144 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1148 // if this is a user-requested resize, then check against min/max sizes
1149 // and aspect ratios
1151 // smaller than min size or bigger than max size?
1152 if (w
< _min_size
.width()) w
= _min_size
.width();
1153 else if (w
> _max_size
.width()) w
= _max_size
.width();
1154 if (h
< _min_size
.height()) h
= _min_size
.height();
1155 else if (h
> _max_size
.height()) h
= _max_size
.height();
1157 // adjust the height ot match the width for the aspect ratios
1159 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1161 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1164 // keep to the increments
1165 w
/= _size_inc
.width();
1166 h
/= _size_inc
.height();
1168 // you cannot resize to nothing
1172 // store the logical size
1173 _logical_size
= otk::Size(w
, h
);
1175 w
*= _size_inc
.width();
1176 h
*= _size_inc
.height();
1178 w
+= _base_size
.width();
1179 h
+= _base_size
.height();
1181 if (x
== INT_MIN
|| y
== INT_MIN
) {
1188 x
-= w
- _area
.width();
1191 y
-= h
- _area
.height();
1194 x
-= w
- _area
.width();
1195 y
-= h
- _area
.height();
1200 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1202 XResizeWindow(**otk::display
, _window
, w
, h
);
1204 // resize the frame to match the request
1205 frame
->adjustSize();
1206 internal_move(x
, y
);
1210 void Client::move(int x
, int y
)
1212 if (!(_functions
& Func_Move
)) return;
1213 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1215 internal_move(x
, y
);
1219 void Client::internal_move(int x
, int y
)
1221 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1223 // move the frame to be in the requested position
1224 if (frame
) { // this can be called while mapping, before frame exists
1225 frame
->adjustPosition();
1227 // send synthetic configure notify (we don't need to if we aren't mapped
1230 event
.type
= ConfigureNotify
;
1231 event
.xconfigure
.display
= **otk::display
;
1232 event
.xconfigure
.event
= _window
;
1233 event
.xconfigure
.window
= _window
;
1235 // root window coords with border in mind
1236 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1237 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1239 event
.xconfigure
.width
= _area
.width();
1240 event
.xconfigure
.height
= _area
.height();
1241 event
.xconfigure
.border_width
= _border_width
;
1242 event
.xconfigure
.above
= frame
->plate();
1243 event
.xconfigure
.override_redirect
= False
;
1244 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1245 StructureNotifyMask
, &event
);
1247 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1248 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1249 event
.xconfigure
.height
, event
.xconfigure
.window
);
1255 void Client::close()
1259 if (!(_functions
& Func_Close
)) return;
1261 // XXX: itd be cool to do timeouts and shit here for killing the client's
1263 // like... if the window is around after 5 seconds, then the close button
1264 // turns a nice red, and if this function is called again, the client is
1265 // explicitly killed.
1267 ce
.xclient
.type
= ClientMessage
;
1268 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1269 ce
.xclient
.display
= **otk::display
;
1270 ce
.xclient
.window
= _window
;
1271 ce
.xclient
.format
= 32;
1272 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1273 ce
.xclient
.data
.l
[1] = CurrentTime
;
1274 ce
.xclient
.data
.l
[2] = 0l;
1275 ce
.xclient
.data
.l
[3] = 0l;
1276 ce
.xclient
.data
.l
[4] = 0l;
1277 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1281 void Client::changeState()
1283 unsigned long state
[2];
1284 state
[0] = _wmstate
;
1286 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1287 otk::Property::atoms
.wm_state
, state
, 2);
1292 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1294 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1296 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1298 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1300 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1302 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1304 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1306 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1308 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1310 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1311 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1312 otk::Property::atoms
.atom
, netstate
, num
);
1317 frame
->adjustState();
1321 void Client::changeAllowedActions(void)
1326 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1328 if (_functions
& Func_Shade
)
1329 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1330 if (_functions
& Func_Close
)
1331 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1332 if (_functions
& Func_Move
)
1333 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1334 if (_functions
& Func_Iconify
)
1335 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1336 if (_functions
& Func_Resize
)
1337 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1338 if (_functions
& Func_Fullscreen
)
1339 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1340 if (_functions
& Func_Maximize
) {
1341 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1342 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1345 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1346 otk::Property::atoms
.atom
, actions
, num
);
1348 // make sure the window isn't breaking any rules now
1350 if (!(_functions
& Func_Shade
) && _shaded
)
1351 if (frame
) shade(false);
1352 else _shaded
= false;
1353 if (!(_functions
& Func_Iconify
) && _iconic
)
1354 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1355 else _iconic
= false;
1356 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1357 if (frame
) fullscreen(false);
1358 else _fullscreen
= false;
1359 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1360 if (frame
) maximize(false, 0);
1361 else _max_vert
= _max_horz
= false;
1365 void Client::remaximize()
1368 if (_max_horz
&& _max_vert
)
1375 return; // not maximized
1376 _max_horz
= _max_vert
= false;
1377 maximize(true, dir
, false);
1381 void Client::applyStartupState()
1383 // these are in a carefully crafted order..
1392 setDesktop(ICONIC_DESKTOP
);
1395 _fullscreen
= false;
1396 fullscreen(true, false);
1405 if (_max_vert
&& _max_horz
) {
1406 _max_vert
= _max_horz
= false;
1407 maximize(true, 0, false);
1408 } else if (_max_vert
) {
1410 maximize(true, 2, false);
1411 } else if (_max_horz
) {
1413 maximize(true, 1, false);
1416 if (_skip_taskbar
); // nothing to do for this
1417 if (_skip_pager
); // nothing to do for this
1418 if (_modal
); // nothing to do for this
1419 if (_above
); // nothing to do for this
1420 if (_below
); // nothing to do for this
1424 void Client::fireUrgent()
1426 // call the python UrgentWindow callbacks
1427 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1428 openbox
->bindings()->fireEvent(&data
);
1432 void Client::shade(bool shade
)
1434 if (!(_functions
& Func_Shade
) || // can't
1435 _shaded
== shade
) return; // already done
1437 // when we're iconic, don't change the wmstate
1439 _wmstate
= shade
? IconicState
: NormalState
;
1442 frame
->adjustSize();
1446 void Client::maximize(bool max
, int dir
, bool savearea
)
1448 assert(dir
== 0 || dir
== 1 || dir
== 2);
1449 if (!(_functions
& Func_Maximize
)) return; // can't
1451 // check if already done
1453 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1454 if (dir
== 1 && _max_horz
) return;
1455 if (dir
== 2 && _max_vert
) return;
1457 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1458 if (dir
== 1 && !_max_horz
) return;
1459 if (dir
== 2 && !_max_vert
) return;
1462 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1463 int x
= frame
->area().x(), y
= frame
->area().y(),
1464 w
= _area
.width(), h
= _area
.height();
1470 unsigned long n
= 4;
1477 // get the property off the window and use it for the dimentions we are
1479 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1480 otk::Property::atoms
.cardinal
, &n
,
1481 (long unsigned**) &readdim
)) {
1484 dimensions
[0] = readdim
[0];
1485 dimensions
[2] = readdim
[2];
1488 dimensions
[1] = readdim
[1];
1489 dimensions
[3] = readdim
[3];
1495 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1496 otk::Property::atoms
.cardinal
,
1497 (long unsigned*)dimensions
, 4);
1499 if (dir
== 0 || dir
== 1) { // horz
1503 if (dir
== 0 || dir
== 2) { // vert
1505 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1509 long unsigned n
= 4;
1511 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1512 otk::Property::atoms
.cardinal
, &n
,
1513 (long unsigned**) &dimensions
)) {
1515 if (dir
== 0 || dir
== 1) { // horz
1516 x
= (signed int)dimensions
[0];
1517 w
= (signed int)dimensions
[2];
1519 if (dir
== 0 || dir
== 2) { // vert
1520 y
= (signed int)dimensions
[1];
1521 h
= (signed int)dimensions
[3];
1526 // pick some fallbacks...
1527 if (dir
== 0 || dir
== 1) { // horz
1528 x
= a
.x() + a
.width() / 4;
1531 if (dir
== 0 || dir
== 2) { // vert
1532 y
= a
.y() + a
.height() / 4;
1538 if (dir
== 0 || dir
== 1) // horz
1540 if (dir
== 0 || dir
== 2) // vert
1543 if (!_max_horz
&& !_max_vert
)
1544 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1546 changeState(); // change the state hints on the client
1548 frame
->frameGravity(x
, y
); // figure out where the client should be going
1549 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1553 void Client::fullscreen(bool fs
, bool savearea
)
1555 static FunctionFlags saved_func
;
1556 static DecorationFlags saved_decor
;
1558 if (!(_functions
& Func_Fullscreen
) || // can't
1559 _fullscreen
== fs
) return; // already done
1562 changeState(); // change the state hints on the client
1564 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1567 // save the functions and remove them
1568 saved_func
= _functions
;
1569 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1570 // save the decorations and remove them
1571 saved_decor
= _decorations
;
1575 dimensions
[0] = _area
.x();
1576 dimensions
[1] = _area
.y();
1577 dimensions
[2] = _area
.width();
1578 dimensions
[3] = _area
.height();
1579 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1580 otk::Property::atoms
.cardinal
,
1581 (long unsigned*)dimensions
, 4);
1583 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1586 w
= info
->size().width();
1587 h
= info
->size().height();
1589 _functions
= saved_func
;
1590 _decorations
= saved_decor
;
1593 long unsigned n
= 4;
1595 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1596 otk::Property::atoms
.cardinal
, &n
,
1597 (long unsigned**) &dimensions
)) {
1606 // pick some fallbacks...
1607 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1608 x
= a
.x() + a
.width() / 4;
1609 y
= a
.y() + a
.height() / 4;
1615 changeAllowedActions(); // based on the new _functions
1617 // when fullscreening, don't obey things like increments, fill the screen
1618 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1620 // raise (back) into our stacking layer
1621 openbox
->screen(_screen
)->raiseWindow(this);
1623 // try focus us when we go into fullscreen mode
1628 void Client::disableDecorations(DecorationFlags flags
)
1630 _disabled_decorations
= flags
;
1631 setupDecorAndFunctions();
1635 void Client::installColormap(bool install
) const
1637 XWindowAttributes wa
;
1638 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1640 XInstallColormap(**otk::display
, wa
.colormap
);
1642 XUninstallColormap(**otk::display
, wa
.colormap
);
1647 bool Client::focus()
1649 // if we have a modal child, then focus it, not us
1651 return _modal_child
->focus();
1653 // won't try focus if the client doesn't want it, or if the window isn't
1654 // visible on the screen
1655 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1657 if (_focused
) return true;
1659 // do a check to see if the window has already been unmapped or destroyed
1660 // do this intelligently while watching out for unmaps we've generated
1661 // (ignore_unmaps > 0)
1663 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1664 XPutBackEvent(**otk::display
, &ev
);
1667 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1668 if (ignore_unmaps
) {
1669 unmapHandler(ev
.xunmap
);
1671 XPutBackEvent(**otk::display
, &ev
);
1677 XSetInputFocus(**otk::display
, _window
,
1678 RevertToNone
, CurrentTime
);
1680 if (_focus_notify
) {
1682 ce
.xclient
.type
= ClientMessage
;
1683 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1684 ce
.xclient
.display
= **otk::display
;
1685 ce
.xclient
.window
= _window
;
1686 ce
.xclient
.format
= 32;
1687 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1688 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1689 ce
.xclient
.data
.l
[2] = 0l;
1690 ce
.xclient
.data
.l
[3] = 0l;
1691 ce
.xclient
.data
.l
[4] = 0l;
1692 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1695 XSync(**otk::display
, False
);
1700 void Client::unfocus() const
1702 if (!_focused
) return;
1704 assert(openbox
->focusedClient() == this);
1705 openbox
->setFocusedClient(0);
1709 void Client::focusHandler(const XFocusChangeEvent
&e
)
1712 // printf("FocusIn for 0x%lx\n", e.window);
1715 otk::EventHandler::focusHandler(e
);
1720 openbox
->setFocusedClient(this);
1724 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1727 // printf("FocusOut for 0x%lx\n", e.window);
1730 otk::EventHandler::unfocusHandler(e
);
1735 if (openbox
->focusedClient() == this)
1736 openbox
->setFocusedClient(0);
1740 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1743 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1746 otk::EventHandler::configureRequestHandler(e
);
1750 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1752 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1753 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1754 e
.x
= ev
.xconfigurerequest
.x
;
1755 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1756 e
.y
= ev
.xconfigurerequest
.y
;
1757 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1758 e
.width
= ev
.xconfigurerequest
.width
;
1759 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1760 e
.height
= ev
.xconfigurerequest
.height
;
1761 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1762 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1763 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1764 e
.detail
= ev
.xconfigurerequest
.detail
;
1767 // if we are iconic (or shaded (fvwm does this)) ignore the event
1768 if (_iconic
|| _shaded
) return;
1770 if (e
.value_mask
& CWBorderWidth
)
1771 _border_width
= e
.border_width
;
1773 // resize, then move, as specified in the EWMH section 7.7
1774 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1775 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1776 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1780 case NorthEastGravity
:
1784 case SouthWestGravity
:
1786 corner
= BottomLeft
;
1788 case SouthEastGravity
:
1789 corner
= BottomRight
;
1791 default: // NorthWest, Static, etc
1795 // if moving AND resizing ...
1796 if (e
.value_mask
& (CWX
| CWY
)) {
1797 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1798 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1799 internal_resize(corner
, w
, h
, false, x
, y
);
1800 } else // if JUST resizing...
1801 internal_resize(corner
, w
, h
, false);
1802 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1803 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1804 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1805 internal_move(x
, y
);
1808 if (e
.value_mask
& CWStackMode
) {
1812 openbox
->screen(_screen
)->lowerWindow(this);
1818 openbox
->screen(_screen
)->raiseWindow(this);
1825 void Client::unmapHandler(const XUnmapEvent
&e
)
1827 if (ignore_unmaps
) {
1829 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1836 printf("UnmapNotify for 0x%lx\n", e
.window
);
1839 otk::EventHandler::unmapHandler(e
);
1841 // this deletes us etc
1842 openbox
->screen(_screen
)->unmanageWindow(this);
1846 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1849 printf("DestroyNotify for 0x%lx\n", e
.window
);
1852 otk::EventHandler::destroyHandler(e
);
1854 // this deletes us etc
1855 openbox
->screen(_screen
)->unmanageWindow(this);
1859 void Client::reparentHandler(const XReparentEvent
&e
)
1861 // this is when the client is first taken captive in the frame
1862 if (e
.parent
== frame
->plate()) return;
1865 printf("ReparentNotify for 0x%lx\n", e
.window
);
1868 otk::EventHandler::reparentHandler(e
);
1871 This event is quite rare and is usually handled in unmapHandler.
1872 However, if the window is unmapped when the reparent event occurs,
1873 the window manager never sees it because an unmap event is not sent
1874 to an already unmapped window.
1877 // we don't want the reparent event, put it back on the stack for the X
1878 // server to deal with after we unmanage the window
1881 XPutBackEvent(**otk::display
, &ev
);
1883 // this deletes us etc
1884 openbox
->screen(_screen
)->unmanageWindow(this);
1887 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1890 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1893 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1895 // move to the current desktop (uniconify)
1896 setDesktop(openbox
->screen(_screen
)->desktop());
1897 // XXX: should we focus/raise the window? (basically a net_wm_active_window)