1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
10 #include "otk/display.hh"
11 #include "otk/property.hh"
15 #include <X11/Xutil.h>
16 #include <X11/Xatom.h>
19 #define _(str) gettext(str)
28 Client::Client(int screen
, Window window
)
29 : otk::EventHandler(),
30 frame(0), _screen(screen
), _window(window
)
37 // update EVERYTHING the first time!!
40 _wmstate
= NormalState
;
43 _layer
= Layer_Normal
;
46 _disabled_decorations
= 0;
53 getState(); // do this before updateTransientFor! (for _modal)
58 getType(); // this can change the mwmhints for special cases
62 getGravity(); // get the attribute gravity
63 updateNormalHints(); // this may override the attribute gravity
65 // got the type, the mwmhints, the protocols, and the normal hints (min/max
66 // sizes), so we're ready to set up
67 // the decorations/functions
68 setupDecorAndFunctions();
70 // also get the initial_state and set _iconic if we aren't "starting"
71 // when we're "starting" that means we should use whatever state was already
72 // on the window over the initial map state, because it was already mapped
73 updateWMHints(openbox
->state() != Openbox::State_Starting
);
79 // this makes sure that these windows appear on all desktops
80 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
81 _desktop
= 0xffffffff;
83 // set the desktop hint, to make sure that it always exists, and to reflect
84 // any changes we've made here
85 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
86 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
94 // clean up childrens' references
95 while (!_transients
.empty()) {
96 _transients
.front()->_transient_for
= 0;
97 _transients
.pop_front();
100 // clean up parents reference to this
102 _transient_for
->_transients
.remove(this); // remove from old parent
104 if (openbox
->state() != Openbox::State_Exiting
) {
105 // these values should not be persisted across a window unmapping/mapping
106 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
107 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
109 // if we're left in an iconic state, the client wont be mapped. this is
110 // bad, since we will no longer be managing the window on restart
112 XMapWindow(**otk::display
, _window
);
117 bool Client::validate() const
119 XSync(**otk::display
, false); // get all events on the server
122 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
123 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
124 XPutBackEvent(**otk::display
, &e
);
132 void Client::getGravity()
134 XWindowAttributes wattrib
;
137 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
138 assert(ret
!= BadWindow
);
139 _gravity
= wattrib
.win_gravity
;
143 void Client::getDesktop()
145 // defaults to the current desktop
146 _desktop
= openbox
->screen(_screen
)->desktop();
148 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
149 otk::Property::atoms
.cardinal
,
150 (long unsigned*)&_desktop
)) {
152 // printf("Window requested desktop: %ld\n", _desktop);
158 void Client::getType()
160 _type
= (WindowType
) -1;
163 unsigned long num
= (unsigned) -1;
164 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
165 otk::Property::atoms
.atom
, &num
, &val
)) {
166 // use the first value that we know about in the array
167 for (unsigned long i
= 0; i
< num
; ++i
) {
168 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
169 _type
= Type_Desktop
;
170 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
172 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
173 _type
= Type_Toolbar
;
174 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
176 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
177 _type
= Type_Utility
;
178 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
180 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
182 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
184 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
185 // prevent this window from getting any decor or functionality
186 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
187 _mwmhints
.decorations
= 0;
188 _mwmhints
.functions
= 0;
190 if (_type
!= (WindowType
) -1)
191 break; // grab the first known type
196 if (_type
== (WindowType
) -1) {
198 * the window type hint was not set, which means we either classify ourself
199 * as a normal window or a dialog, depending on if we are a transient.
209 void Client::setupDecorAndFunctions()
211 // start with everything (cept fullscreen)
212 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
213 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
214 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
216 if (_delete_window
) {
217 _decorations
|= Decor_Close
;
218 _functions
|= Func_Close
;
221 if (!(_min_size
.width() < _max_size
.width() ||
222 _min_size
.height() < _max_size
.height())) {
223 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
224 _functions
&= ~(Func_Resize
| Func_Maximize
);
229 // normal windows retain all of the possible decorations and
230 // functionality, and are the only windows that you can fullscreen
231 _functions
|= Func_Fullscreen
;
235 // dialogs cannot be maximized
236 _decorations
&= ~Decor_Maximize
;
237 _functions
&= ~Func_Maximize
;
243 // these windows get less functionality
244 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
245 _functions
&= ~(Func_Iconify
| Func_Resize
);
251 // none of these windows are manipulated by the window manager
257 // Mwm Hints are applied subtractively to what has already been chosen for
258 // decor and functionality
259 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
260 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
261 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
262 _decorations
&= ~Decor_Border
;
263 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
264 _decorations
&= ~Decor_Handle
;
265 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
266 _decorations
&= ~Decor_Titlebar
;
267 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
268 _decorations
&= ~Decor_Iconify
;
269 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
270 _decorations
&= ~Decor_Maximize
;
274 if (_mwmhints
.flags
& MwmFlag_Functions
) {
275 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
276 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
277 _functions
&= ~Func_Resize
;
278 if (! (_mwmhints
.functions
& MwmFunc_Move
))
279 _functions
&= ~Func_Move
;
280 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
281 _functions
&= ~Func_Iconify
;
282 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
283 _functions
&= ~Func_Maximize
;
284 // dont let mwm hints kill the close button
285 //if (! (_mwmhints.functions & MwmFunc_Close))
286 // _functions &= ~Func_Close;
290 // can't maximize without moving/resizing
291 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
292 _functions
&= ~Func_Maximize
;
294 // finally, user specified disabled decorations are applied to subtract
296 if (_disabled_decorations
& Decor_Titlebar
)
297 _decorations
&= ~Decor_Titlebar
;
298 if (_disabled_decorations
& Decor_Handle
)
299 _decorations
&= ~Decor_Handle
;
300 if (_disabled_decorations
& Decor_Border
)
301 _decorations
&= ~Decor_Border
;
302 if (_disabled_decorations
& Decor_Iconify
)
303 _decorations
&= ~Decor_Iconify
;
304 if (_disabled_decorations
& Decor_Maximize
)
305 _decorations
&= ~Decor_Maximize
;
306 if (_disabled_decorations
& Decor_AllDesktops
)
307 _decorations
&= ~Decor_AllDesktops
;
308 if (_disabled_decorations
& Decor_Close
)
309 _decorations
&= ~Decor_Close
;
311 // if we don't have a titlebar, then we cannot shade!
312 if (!(_decorations
& Decor_Titlebar
))
313 _functions
&= ~Func_Shade
;
315 changeAllowedActions();
318 frame
->adjustSize(); // change the decors on the frame
319 frame
->adjustPosition(); // with more/less decorations, we may need to be
321 remaximize(); // with new decor, the window's maximized size may change
326 void Client::getMwmHints()
328 unsigned long num
= MwmHints::elements
;
329 unsigned long *hints
;
331 _mwmhints
.flags
= 0; // default to none
333 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
334 otk::Property::atoms
.motif_wm_hints
, &num
,
335 (unsigned long **)&hints
))
338 if (num
>= MwmHints::elements
) {
339 // retrieved the hints
340 _mwmhints
.flags
= hints
[0];
341 _mwmhints
.functions
= hints
[1];
342 _mwmhints
.decorations
= hints
[2];
349 void Client::getArea()
351 XWindowAttributes wattrib
;
354 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
355 assert(ret
!= BadWindow
);
357 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
358 _border_width
= wattrib
.border_width
;
362 void Client::getState()
364 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
365 _iconic
= _skip_taskbar
= _skip_pager
= false;
367 unsigned long *state
;
368 unsigned long num
= (unsigned) -1;
370 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
371 otk::Property::atoms
.atom
, &num
, &state
)) {
372 for (unsigned long i
= 0; i
< num
; ++i
) {
373 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
375 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
377 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
379 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
380 _skip_taskbar
= true;
381 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
383 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
385 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
391 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
400 void Client::getShaped()
404 if (otk::display
->shape()) {
409 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
411 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
412 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
419 void Client::calcLayer() {
422 if (_iconic
) l
= Layer_Icon
;
423 else if (_fullscreen
) l
= Layer_Fullscreen
;
424 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
425 else if (_type
== Type_Dock
) {
426 if (!_below
) l
= Layer_Top
;
427 else l
= Layer_Normal
;
429 else if (_above
) l
= Layer_Above
;
430 else if (_below
) l
= Layer_Below
;
431 else l
= Layer_Normal
;
437 if we don't have a frame, then we aren't mapped yet (and this would
440 openbox
->screen(_screen
)->raiseWindow(this);
446 void Client::updateProtocols()
451 _focus_notify
= false;
452 _delete_window
= false;
454 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
455 for (int i
= 0; i
< num_return
; ++i
) {
456 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
457 // this means we can request the window to close
458 _delete_window
= true;
459 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
460 // if this protocol is requested, then the window will be notified
461 // by the window manager whenever it receives focus
462 _focus_notify
= true;
469 void Client::updateNormalHints()
473 int oldgravity
= _gravity
;
478 _size_inc
= otk::Size(1, 1);
479 _base_size
= otk::Size(0, 0);
480 _min_size
= otk::Size(0, 0);
481 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
483 // get the hints from the window
484 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
485 _positioned
= (size
.flags
& (PPosition
|USPosition
));
487 if (size
.flags
& PWinGravity
) {
488 _gravity
= size
.win_gravity
;
490 // if the client has a frame, i.e. has already been mapped and is
491 // changing its gravity
492 if (frame
&& _gravity
!= oldgravity
) {
493 // move our idea of the client's position based on its new gravity
494 int x
= frame
->area().x(), y
= frame
->area().y();
495 frame
->frameGravity(x
, y
);
496 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
500 if (size
.flags
& PAspect
) {
501 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
502 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
505 if (size
.flags
& PMinSize
)
506 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
508 if (size
.flags
& PMaxSize
)
509 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
511 if (size
.flags
& PBaseSize
)
512 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
514 if (size
.flags
& PResizeInc
)
515 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
520 void Client::updateWMHints(bool initstate
)
524 // assume a window takes input if it doesnt specify
528 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
529 if (hints
->flags
& InputHint
)
530 _can_focus
= hints
->input
;
532 // only do this when initstate is true!
533 if (initstate
&& (hints
->flags
& StateHint
))
534 _iconic
= hints
->initial_state
== IconicState
;
536 if (hints
->flags
& XUrgencyHint
)
539 if (hints
->flags
& WindowGroupHint
) {
540 if (hints
->window_group
!= _group
) {
541 // XXX: remove from the old group if there was one
542 _group
= hints
->window_group
;
543 // XXX: do stuff with the group
554 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
555 (long)_window
, _urgent
? "ON" : "OFF");
557 // fire the urgent callback if we're mapped, otherwise, wait until after
565 void Client::updateTitle()
570 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
571 otk::Property::utf8
, &_title
)) {
573 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
574 otk::Property::ascii
, &_title
);
578 _title
= _("Unnamed Window");
581 frame
->adjustTitle();
585 void Client::updateIconTitle()
590 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
591 otk::Property::utf8
, &_icon_title
)) {
593 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
594 otk::Property::ascii
, &_icon_title
);
598 _icon_title
= _("Unnamed Window");
602 void Client::updateClass()
605 _app_name
= _app_class
= _role
= "";
607 otk::Property::StringVect v
;
608 unsigned long num
= 2;
610 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
611 otk::Property::ascii
, &num
, &v
)) {
612 if (num
> 0) _app_name
= v
[0].c_str();
613 if (num
> 1) _app_class
= v
[1].c_str();
618 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
619 otk::Property::ascii
, &num
, &v
)) {
620 if (num
> 0) _role
= v
[0].c_str();
625 void Client::updateStrut()
627 unsigned long num
= 4;
629 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
630 otk::Property::atoms
.cardinal
, &num
, &data
))
634 _strut
.left
= data
[0];
635 _strut
.right
= data
[1];
636 _strut
.top
= data
[2];
637 _strut
.bottom
= data
[3];
639 // updating here is pointless while we're being mapped cuz we're not in
640 // the screen's client list yet
642 openbox
->screen(_screen
)->updateStrut();
649 void Client::updateTransientFor()
654 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
655 t
!= _window
) { // cant be transient to itself!
656 c
= openbox
->findClient(t
);
657 assert(c
!= this); // if this happens then we need to check for it
659 if (!c
/*XXX: && _group*/) {
660 // not transient to a client, see if it is transient for a group
661 if (//t == _group->leader() ||
663 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
664 // window is a transient for its group!
665 // XXX: for now this is treated as non-transient.
666 // this needs to be fixed!
671 // if anything has changed...
672 if (c
!= _transient_for
) {
678 _transient_for
->_transients
.remove(this); // remove from old parent
681 _transient_for
->_transients
.push_back(this); // add to new parent
689 void Client::propertyHandler(const XPropertyEvent
&e
)
691 otk::EventHandler::propertyHandler(e
);
693 // validate cuz we query stuff off the client here
694 if (!validate()) return;
696 // compress changes to a single property into a single change
698 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
699 // XXX: it would be nice to compress ALL changes to a property, not just
700 // changes in a row without other props between.
701 if (ce
.xproperty
.atom
!= e
.atom
) {
702 XPutBackEvent(**otk::display
, &ce
);
707 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
709 setupDecorAndFunctions(); // normal hints can make a window non-resizable
710 } else if (e
.atom
== XA_WM_HINTS
)
712 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
713 updateTransientFor();
715 calcLayer(); // type may have changed, so update the layer
716 setupDecorAndFunctions();
718 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
719 e
.atom
== otk::Property::atoms
.wm_name
)
721 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
722 e
.atom
== otk::Property::atoms
.wm_icon_name
)
724 else if (e
.atom
== otk::Property::atoms
.wm_class
)
726 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
728 setupDecorAndFunctions();
730 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
735 void Client::setWMState(long state
)
737 if (state
== _wmstate
) return; // no change
741 setDesktop(ICONIC_DESKTOP
);
744 setDesktop(openbox
->screen(_screen
)->desktop());
750 void Client::setDesktop(long target
)
752 if (target
== _desktop
) return;
754 printf("Setting desktop %ld\n", target
);
756 if (!(target
>= 0 || target
== (signed)0xffffffff ||
757 target
== ICONIC_DESKTOP
))
762 // set the desktop hint, but not if we're iconifying
763 if (_desktop
!= ICONIC_DESKTOP
)
764 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
765 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
767 // 'move' the window to the new desktop
768 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
769 _desktop
== (signed)0xffffffff)
774 // Handle Iconic state. Iconic state is maintained by the client being a
775 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
776 // uniconifying happen.
777 bool i
= _desktop
== ICONIC_DESKTOP
;
778 if (i
!= _iconic
) { // has the state changed?
781 _wmstate
= IconicState
;
783 // we unmap the client itself so that we can get MapRequest events, and
784 // because the ICCCM tells us to!
785 XUnmapWindow(**otk::display
, _window
);
787 _wmstate
= NormalState
;
788 XMapWindow(**otk::display
, _window
);
793 frame
->adjustState();
797 Client
*Client::findModalChild(Client
*skip
) const
801 // find a modal child recursively and try focus it
802 List::const_iterator it
, end
= _transients
.end();
803 for (it
= _transients
.begin(); it
!= end
; ++it
)
804 if ((*it
)->_modal
&& *it
!= skip
)
805 return *it
; // got one
806 // none of our direct children are modal, let them try check
807 for (it
= _transients
.begin(); it
!= end
; ++it
)
808 if ((ret
= (*it
)->findModalChild()))
809 return ret
; // got one
814 void Client::setModal(bool modal
)
816 if (modal
== _modal
) return;
820 while (c
->_transient_for
) {
821 c
= c
->_transient_for
;
822 if (c
== this) break; // circular?
823 if (c
->_modal_child
) break; // already has a modal child
824 c
->_modal_child
= this;
827 // try find a replacement modal dialog
828 Client
*replacement
= 0;
831 while (c
->_transient_for
) // go up the tree
832 c
= c
->_transient_for
;
833 replacement
= c
->findModalChild(this); // find a modal child, skipping this
834 assert(replacement
!= this);
837 while (c
->_transient_for
) {
838 c
= c
->_transient_for
;
839 if (c
== this) break; // circular?
840 if (c
->_modal_child
!= this) break; // has a different modal child
841 if (c
== replacement
) break; // found the replacement itself
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
;
1017 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
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());
1096 openbox
->screen(_screen
)->raiseWindow(this);
1097 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1099 setDesktop(openbox
->screen(_screen
)->desktop());
1100 if (e
.data
.l
[0] && _shaded
)
1104 openbox
->screen(_screen
)->raiseWindow(this);
1110 void Client::shapeHandler(const XShapeEvent
&e
)
1112 otk::EventHandler::shapeHandler(e
);
1114 if (e
.kind
== ShapeBounding
) {
1116 frame
->adjustShape();
1122 void Client::resize(Corner anchor
, int w
, int h
)
1124 if (!(_functions
& Func_Resize
)) return;
1125 internal_resize(anchor
, w
, h
);
1129 void Client::internal_resize(Corner anchor
, int w
, int h
,
1130 bool user
, int x
, int y
)
1132 w
-= _base_size
.width();
1133 h
-= _base_size
.height();
1136 // for interactive resizing. have to move half an increment in each
1138 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1139 int mh
= h
% _size_inc
.height();
1140 int aw
= _size_inc
.width() / 2; // amount to add
1141 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
);
1718 frame
->adjustFocus();
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
);
1733 frame
->adjustFocus();
1735 if (openbox
->focusedClient() == this)
1736 openbox
->setFocusedClient(0);
1740 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1743 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1746 otk::EventHandler::configureRequestHandler(ec
);
1749 XConfigureRequestEvent e
= ec
;
1751 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1753 // XXX if this causes bad things.. we can compress config req's with the
1755 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1756 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1757 e
.x
= ev
.xconfigurerequest
.x
;
1758 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1759 e
.y
= ev
.xconfigurerequest
.y
;
1760 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1761 e
.width
= ev
.xconfigurerequest
.width
;
1762 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1763 e
.height
= ev
.xconfigurerequest
.height
;
1764 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1765 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1766 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1767 e
.detail
= ev
.xconfigurerequest
.detail
;
1770 // if we are iconic (or shaded (fvwm does this)) ignore the event
1771 if (_iconic
|| _shaded
) return;
1773 if (e
.value_mask
& CWBorderWidth
)
1774 _border_width
= e
.border_width
;
1776 // resize, then move, as specified in the EWMH section 7.7
1777 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1778 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1779 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1783 case NorthEastGravity
:
1787 case SouthWestGravity
:
1789 corner
= BottomLeft
;
1791 case SouthEastGravity
:
1792 corner
= BottomRight
;
1794 default: // NorthWest, Static, etc
1798 // if moving AND resizing ...
1799 if (e
.value_mask
& (CWX
| CWY
)) {
1800 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1801 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1802 internal_resize(corner
, w
, h
, false, x
, y
);
1803 } else // if JUST resizing...
1804 internal_resize(corner
, w
, h
, false);
1805 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1806 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1807 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1808 internal_move(x
, y
);
1811 if (e
.value_mask
& CWStackMode
) {
1815 openbox
->screen(_screen
)->lowerWindow(this);
1821 openbox
->screen(_screen
)->raiseWindow(this);
1828 void Client::unmapHandler(const XUnmapEvent
&e
)
1830 if (ignore_unmaps
) {
1832 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1839 printf("UnmapNotify for 0x%lx\n", e
.window
);
1842 otk::EventHandler::unmapHandler(e
);
1844 // this deletes us etc
1845 openbox
->screen(_screen
)->unmanageWindow(this);
1849 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1852 printf("DestroyNotify for 0x%lx\n", e
.window
);
1855 otk::EventHandler::destroyHandler(e
);
1857 // this deletes us etc
1858 openbox
->screen(_screen
)->unmanageWindow(this);
1862 void Client::reparentHandler(const XReparentEvent
&e
)
1864 // this is when the client is first taken captive in the frame
1865 if (e
.parent
== frame
->plate()) return;
1868 printf("ReparentNotify for 0x%lx\n", e
.window
);
1871 otk::EventHandler::reparentHandler(e
);
1874 This event is quite rare and is usually handled in unmapHandler.
1875 However, if the window is unmapped when the reparent event occurs,
1876 the window manager never sees it because an unmap event is not sent
1877 to an already unmapped window.
1880 // we don't want the reparent event, put it back on the stack for the X
1881 // server to deal with after we unmanage the window
1884 XPutBackEvent(**otk::display
, &ev
);
1886 // this deletes us etc
1887 openbox
->screen(_screen
)->unmanageWindow(this);
1890 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1893 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1896 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1898 // move to the current desktop (uniconify)
1899 setDesktop(openbox
->screen(_screen
)->desktop());
1900 // XXX: should we focus/raise the window? (basically a net_wm_active_window)