1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
11 #include "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
23 #define _(str) gettext(str)
30 Client::Client(int screen
, Window window
)
31 : otk::EventHandler(),
32 WidgetBase(WidgetBase::Type_Client
),
33 frame(0), _screen(screen
), _window(window
)
40 // update EVERYTHING the first time!!
43 _wmstate
= NormalState
;
46 _layer
= Layer_Normal
;
49 _disabled_decorations
= 0;
56 getState(); // do this before updateTransientFor! (for _modal)
61 getType(); // this can change the mwmhints for special cases
65 getGravity(); // get the attribute gravity
66 updateNormalHints(); // this may override the attribute gravity
68 // got the type, the mwmhints, the protocols, and the normal hints (min/max
69 // sizes), so we're ready to set up
70 // the decorations/functions
71 setupDecorAndFunctions();
73 // also get the initial_state and set _iconic if we aren't "starting"
74 // when we're "starting" that means we should use whatever state was already
75 // on the window over the initial map state, because it was already mapped
76 updateWMHints(openbox
->state() != Openbox::State_Starting
);
82 // this makes sure that these windows appear on all desktops
83 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
84 _desktop
= 0xffffffff;
86 // set the desktop hint, to make sure that it always exists, and to reflect
87 // any changes we've made here
88 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
89 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
97 // clean up childrens' references
98 while (!_transients
.empty()) {
99 _transients
.front()->_transient_for
= 0;
100 _transients
.pop_front();
103 // clean up parents reference to this
105 _transient_for
->_transients
.remove(this); // remove from old parent
107 if (openbox
->state() != Openbox::State_Exiting
) {
108 // these values should not be persisted across a window unmapping/mapping
109 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
110 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
112 // if we're left in an iconic state, the client wont be mapped. this is
113 // bad, since we will no longer be managing the window on restart
115 XMapWindow(**otk::display
, _window
);
120 bool Client::validate() const
122 XSync(**otk::display
, false); // get all events on the server
125 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
126 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
127 XPutBackEvent(**otk::display
, &e
);
135 void Client::getGravity()
137 XWindowAttributes wattrib
;
140 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
141 assert(ret
!= BadWindow
);
142 _gravity
= wattrib
.win_gravity
;
146 void Client::getDesktop()
148 // defaults to the current desktop
149 _desktop
= openbox
->screen(_screen
)->desktop();
151 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
152 otk::Property::atoms
.cardinal
,
153 (long unsigned*)&_desktop
)) {
155 // printf("Window requested desktop: %ld\n", _desktop);
161 void Client::getType()
163 _type
= (WindowType
) -1;
166 unsigned long num
= (unsigned) -1;
167 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
168 otk::Property::atoms
.atom
, &num
, &val
)) {
169 // use the first value that we know about in the array
170 for (unsigned long i
= 0; i
< num
; ++i
) {
171 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
172 _type
= Type_Desktop
;
173 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
175 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
176 _type
= Type_Toolbar
;
177 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
179 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
180 _type
= Type_Utility
;
181 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
183 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
185 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
187 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
188 // prevent this window from getting any decor or functionality
189 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
190 _mwmhints
.decorations
= 0;
191 _mwmhints
.functions
= 0;
193 if (_type
!= (WindowType
) -1)
194 break; // grab the first known type
199 if (_type
== (WindowType
) -1) {
201 * the window type hint was not set, which means we either classify ourself
202 * as a normal window or a dialog, depending on if we are a transient.
212 void Client::setupDecorAndFunctions()
214 // start with everything (cept fullscreen)
215 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
216 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
217 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
219 if (_delete_window
) {
220 _decorations
|= Decor_Close
;
221 _functions
|= Func_Close
;
224 if (!(_min_size
.x() < _max_size
.x() || _min_size
.y() < _max_size
.y())) {
225 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
226 _functions
&= ~(Func_Resize
| Func_Maximize
);
231 // normal windows retain all of the possible decorations and
232 // functionality, and are the only windows that you can fullscreen
233 _functions
|= Func_Fullscreen
;
237 // dialogs cannot be maximized
238 _decorations
&= ~Decor_Maximize
;
239 _functions
&= ~Func_Maximize
;
245 // these windows get less functionality
246 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
247 _functions
&= ~(Func_Iconify
| Func_Resize
);
253 // none of these windows are manipulated by the window manager
259 // Mwm Hints are applied subtractively to what has already been chosen for
260 // decor and functionality
261 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
262 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
263 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
264 _decorations
&= ~Decor_Border
;
265 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
266 _decorations
&= ~Decor_Handle
;
267 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
268 _decorations
&= ~Decor_Titlebar
;
269 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
270 _decorations
&= ~Decor_Iconify
;
271 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
272 _decorations
&= ~Decor_Maximize
;
276 if (_mwmhints
.flags
& MwmFlag_Functions
) {
277 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
278 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
279 _functions
&= ~Func_Resize
;
280 if (! (_mwmhints
.functions
& MwmFunc_Move
))
281 _functions
&= ~Func_Move
;
282 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
283 _functions
&= ~Func_Iconify
;
284 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
285 _functions
&= ~Func_Maximize
;
286 // dont let mwm hints kill the close button
287 //if (! (_mwmhints.functions & MwmFunc_Close))
288 // _functions &= ~Func_Close;
292 // can't maximize without moving/resizing
293 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
294 _functions
&= ~Func_Maximize
;
296 // finally, user specified disabled decorations are applied to subtract
298 if (_disabled_decorations
& Decor_Titlebar
)
299 _decorations
&= ~Decor_Titlebar
;
300 if (_disabled_decorations
& Decor_Handle
)
301 _decorations
&= ~Decor_Handle
;
302 if (_disabled_decorations
& Decor_Border
)
303 _decorations
&= ~Decor_Border
;
304 if (_disabled_decorations
& Decor_Iconify
)
305 _decorations
&= ~Decor_Iconify
;
306 if (_disabled_decorations
& Decor_Maximize
)
307 _decorations
&= ~Decor_Maximize
;
308 if (_disabled_decorations
& Decor_AllDesktops
)
309 _decorations
&= ~Decor_AllDesktops
;
310 if (_disabled_decorations
& Decor_Close
)
311 _decorations
&= ~Decor_Close
;
313 // if we don't have a titlebar, then we cannot shade!
314 if (!(_decorations
& Decor_Titlebar
))
315 _functions
&= ~Func_Shade
;
317 changeAllowedActions();
320 frame
->adjustSize(); // change the decors on the frame
321 frame
->adjustPosition(); // with more/less decorations, we may need to be
323 remaximize(); // with new decor, the window's maximized size may change
328 void Client::getMwmHints()
330 unsigned long num
= MwmHints::elements
;
331 unsigned long *hints
;
333 _mwmhints
.flags
= 0; // default to none
335 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
336 otk::Property::atoms
.motif_wm_hints
, &num
,
337 (unsigned long **)&hints
))
340 if (num
>= MwmHints::elements
) {
341 // retrieved the hints
342 _mwmhints
.flags
= hints
[0];
343 _mwmhints
.functions
= hints
[1];
344 _mwmhints
.decorations
= hints
[2];
351 void Client::getArea()
353 XWindowAttributes wattrib
;
356 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
357 assert(ret
!= BadWindow
);
359 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
360 _border_width
= wattrib
.border_width
;
364 void Client::getState()
366 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
367 _iconic
= _skip_taskbar
= _skip_pager
= false;
369 unsigned long *state
;
370 unsigned long num
= (unsigned) -1;
372 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
373 otk::Property::atoms
.atom
, &num
, &state
)) {
374 for (unsigned long i
= 0; i
< num
; ++i
) {
375 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
377 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
379 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
381 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
382 _skip_taskbar
= true;
383 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
385 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
391 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
393 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
402 void Client::getShaped()
406 if (otk::display
->shape()) {
411 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
413 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
414 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
421 void Client::calcLayer() {
424 if (_iconic
) l
= Layer_Icon
;
425 else if (_fullscreen
) l
= Layer_Fullscreen
;
426 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
427 else if (_type
== Type_Dock
) {
428 if (!_below
) l
= Layer_Top
;
429 else l
= Layer_Normal
;
431 else if (_above
) l
= Layer_Above
;
432 else if (_below
) l
= Layer_Below
;
433 else l
= Layer_Normal
;
439 if we don't have a frame, then we aren't mapped yet (and this would
442 openbox
->screen(_screen
)->raiseWindow(this);
448 void Client::updateProtocols()
453 _focus_notify
= false;
454 _delete_window
= false;
456 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
457 for (int i
= 0; i
< num_return
; ++i
) {
458 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
459 // this means we can request the window to close
460 _delete_window
= true;
461 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
462 // if this protocol is requested, then the window will be notified
463 // by the window manager whenever it receives focus
464 _focus_notify
= true;
471 void Client::updateNormalHints()
475 int oldgravity
= _gravity
;
480 _size_inc
.setPoint(1, 1);
481 _base_size
.setPoint(0, 0);
482 _min_size
.setPoint(0, 0);
483 _max_size
.setPoint(INT_MAX
, INT_MAX
);
485 // get the hints from the window
486 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
487 _positioned
= (size
.flags
& (PPosition
|USPosition
));
489 if (size
.flags
& PWinGravity
) {
490 _gravity
= size
.win_gravity
;
492 // if the client has a frame, i.e. has already been mapped and is
493 // changing its gravity
494 if (frame
&& _gravity
!= oldgravity
) {
495 // move our idea of the client's position based on its new gravity
496 int x
= frame
->rect().x(), y
= frame
->rect().y();
497 frame
->frameGravity(x
, y
);
502 if (size
.flags
& PAspect
) {
503 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
504 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
507 if (size
.flags
& PMinSize
)
508 _min_size
.setPoint(size
.min_width
, size
.min_height
);
510 if (size
.flags
& PMaxSize
)
511 _max_size
.setPoint(size
.max_width
, size
.max_height
);
513 if (size
.flags
& PBaseSize
)
514 _base_size
.setPoint(size
.base_width
, size
.base_height
);
516 if (size
.flags
& PResizeInc
)
517 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
522 void Client::updateWMHints(bool initstate
)
526 // assume a window takes input if it doesnt specify
530 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
531 if (hints
->flags
& InputHint
)
532 _can_focus
= hints
->input
;
534 // only do this when initstate is true!
535 if (initstate
&& (hints
->flags
& StateHint
))
536 _iconic
= hints
->initial_state
== IconicState
;
538 if (hints
->flags
& XUrgencyHint
)
541 if (hints
->flags
& WindowGroupHint
) {
542 if (hints
->window_group
!= _group
) {
543 // XXX: remove from the old group if there was one
544 _group
= hints
->window_group
;
545 // XXX: do stuff with the group
556 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
557 (long)_window
, _urgent
? "ON" : "OFF");
559 // fire the urgent callback if we're mapped, otherwise, wait until after
567 void Client::updateTitle()
572 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
573 otk::Property::utf8
, &_title
)) {
575 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
576 otk::Property::ascii
, &_title
);
580 _title
= _("Unnamed Window");
583 frame
->setTitle(_title
);
587 void Client::updateIconTitle()
592 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
593 otk::Property::utf8
, &_icon_title
)) {
595 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
596 otk::Property::ascii
, &_icon_title
);
600 _icon_title
= _("Unnamed Window");
604 void Client::updateClass()
607 _app_name
= _app_class
= _role
= "";
609 otk::Property::StringVect v
;
610 unsigned long num
= 2;
612 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
613 otk::Property::ascii
, &num
, &v
)) {
614 if (num
> 0) _app_name
= v
[0].c_str();
615 if (num
> 1) _app_class
= v
[1].c_str();
620 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
621 otk::Property::ascii
, &num
, &v
)) {
622 if (num
> 0) _role
= v
[0].c_str();
627 void Client::updateStrut()
629 unsigned long num
= 4;
631 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
632 otk::Property::atoms
.cardinal
, &num
, &data
))
636 _strut
.left
= data
[0];
637 _strut
.right
= data
[1];
638 _strut
.top
= data
[2];
639 _strut
.bottom
= data
[3];
641 // updating here is pointless while we're being mapped cuz we're not in
642 // the screen's client list yet
644 openbox
->screen(_screen
)->updateStrut();
651 void Client::updateTransientFor()
656 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
657 t
!= _window
) { // cant be transient to itself!
658 c
= openbox
->findClient(t
);
659 assert(c
!= this); // if this happens then we need to check for it
661 if (!c
/*XXX: && _group*/) {
662 // not transient to a client, see if it is transient for a group
663 if (//t == _group->leader() ||
665 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
666 // window is a transient for its group!
667 // XXX: for now this is treated as non-transient.
668 // this needs to be fixed!
673 // if anything has changed...
674 if (c
!= _transient_for
) {
680 _transient_for
->_transients
.remove(this); // remove from old parent
683 _transient_for
->_transients
.push_back(this); // add to new parent
691 void Client::propertyHandler(const XPropertyEvent
&e
)
693 otk::EventHandler::propertyHandler(e
);
695 // validate cuz we query stuff off the client here
696 if (!validate()) return;
698 // compress changes to a single property into a single change
700 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
701 // XXX: it would be nice to compress ALL changes to a property, not just
702 // changes in a row without other props between.
703 if (ce
.xproperty
.atom
!= e
.atom
) {
704 XPutBackEvent(**otk::display
, &ce
);
709 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
711 setupDecorAndFunctions(); // normal hints can make a window non-resizable
712 } else if (e
.atom
== XA_WM_HINTS
)
714 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
715 updateTransientFor();
717 calcLayer(); // type may have changed, so update the layer
718 setupDecorAndFunctions();
720 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
721 e
.atom
== otk::Property::atoms
.wm_name
)
723 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
724 e
.atom
== otk::Property::atoms
.wm_icon_name
)
726 else if (e
.atom
== otk::Property::atoms
.wm_class
)
728 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
730 setupDecorAndFunctions();
732 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
737 void Client::setWMState(long state
)
739 if (state
== _wmstate
) return; // no change
743 setDesktop(ICONIC_DESKTOP
);
746 setDesktop(openbox
->screen(_screen
)->desktop());
752 void Client::setDesktop(long target
)
754 if (target
== _desktop
) return;
756 printf("Setting desktop %ld\n", target
);
758 if (!(target
>= 0 || target
== (signed)0xffffffff ||
759 target
== ICONIC_DESKTOP
))
764 // set the desktop hint, but not if we're iconifying
765 if (_desktop
!= ICONIC_DESKTOP
)
766 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
767 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
769 // 'move' the window to the new desktop
770 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
771 _desktop
== (signed)0xffffffff)
776 // Handle Iconic state. Iconic state is maintained by the client being a
777 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
778 // uniconifying happen.
779 bool i
= _desktop
== ICONIC_DESKTOP
;
780 if (i
!= _iconic
) { // has the state changed?
783 _wmstate
= IconicState
;
785 // we unmap the client itself so that we can get MapRequest events, and
786 // because the ICCCM tells us to!
787 XUnmapWindow(**otk::display
, _window
);
789 _wmstate
= NormalState
;
790 XMapWindow(**otk::display
, _window
);
795 frame
->adjustState();
799 Client
*Client::findModalChild(Client
*skip
) const
803 // find a modal child recursively and try focus it
804 List::const_iterator it
, end
= _transients
.end();
805 for (it
= _transients
.begin(); it
!= end
; ++it
)
806 if ((*it
)->_modal
&& *it
!= skip
)
807 return *it
; // got one
808 // none of our direct children are modal, let them try check
809 for (it
= _transients
.begin(); it
!= end
; ++it
)
810 if ((ret
= (*it
)->findModalChild()))
811 return ret
; // got one
816 void Client::setModal(bool modal
)
818 if (modal
== _modal
) return;
822 while (c
->_transient_for
) {
823 c
= c
->_transient_for
;
824 if (c
->_modal_child
) break; // already has a modal child
825 c
->_modal_child
= this;
828 // try find a replacement modal dialog
829 Client
*replacement
= 0;
832 while (c
->_transient_for
) // go up the tree
833 c
= c
->_transient_for
;
834 replacement
= c
->findModalChild(this); // find a modal child, skipping this
837 while (c
->_transient_for
) {
838 c
= c
->_transient_for
;
839 if (c
->_modal_child
!= this) break; // has a different modal child
840 c
->_modal_child
= replacement
;
847 void Client::setState(StateAction action
, long data1
, long data2
)
849 bool shadestate
= _shaded
;
850 bool fsstate
= _fullscreen
;
851 bool maxh
= _max_horz
;
852 bool maxv
= _max_vert
;
855 if (!(action
== State_Add
|| action
== State_Remove
||
856 action
== State_Toggle
))
857 return; // an invalid action was passed to the client message, ignore it
859 for (int i
= 0; i
< 2; ++i
) {
860 Atom state
= i
== 0 ? data1
: data2
;
862 if (! state
) continue;
864 // if toggling, then pick whether we're adding or removing
865 if (action
== State_Toggle
) {
866 if (state
== otk::Property::atoms
.net_wm_state_modal
)
867 action
= _modal
? State_Remove
: State_Add
;
868 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
869 action
= _max_vert
? State_Remove
: State_Add
;
870 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
871 action
= _max_horz
? State_Remove
: State_Add
;
872 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
873 action
= _shaded
? State_Remove
: State_Add
;
874 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
875 action
= _skip_taskbar
? State_Remove
: State_Add
;
876 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
877 action
= _skip_pager
? State_Remove
: State_Add
;
878 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
879 action
= _fullscreen
? State_Remove
: State_Add
;
880 else if (state
== otk::Property::atoms
.net_wm_state_above
)
881 action
= _above
? State_Remove
: State_Add
;
882 else if (state
== otk::Property::atoms
.net_wm_state_below
)
883 action
= _below
? State_Remove
: State_Add
;
886 if (action
== State_Add
) {
887 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
888 if (_modal
) continue;
890 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
892 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
893 if (_max_horz
) continue;
895 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
897 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
898 _skip_taskbar
= true;
899 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
901 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
903 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
904 if (_above
) continue;
906 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
907 if (_below
) continue;
911 } else { // action == State_Remove
912 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
913 if (!_modal
) continue;
915 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
917 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
919 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
921 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
922 _skip_taskbar
= false;
923 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
925 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
927 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
928 if (!_above
) continue;
930 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
931 if (!_below
) continue;
936 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
937 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
938 if (maxh
== maxv
) { // both going the same way
939 maximize(maxh
, 0, true);
941 maximize(maxh
, 1, true);
942 maximize(maxv
, 2, true);
944 } else { // toggling one
945 if (maxh
!= _max_horz
)
946 maximize(maxh
, 1, true);
948 maximize(maxv
, 2, true);
953 // change fullscreen state before shading, as it will affect if the window
955 if (fsstate
!= _fullscreen
)
956 fullscreen(fsstate
, true);
957 if (shadestate
!= _shaded
)
960 changeState(); // change the hint to relect these changes
964 void Client::toggleClientBorder(bool addborder
)
966 // adjust our idea of where the client is, based on its border. When the
967 // border is removed, the client should now be considered to be in a
968 // different position.
969 // when re-adding the border to the client, the same operation needs to be
971 int oldx
= _area
.x(), oldy
= _area
.y();
972 int x
= oldx
, y
= oldy
;
975 case NorthWestGravity
:
977 case SouthWestGravity
:
979 case NorthEastGravity
:
981 case SouthEastGravity
:
982 if (addborder
) x
-= _border_width
* 2;
983 else x
+= _border_width
* 2;
990 if (addborder
) x
-= _border_width
;
991 else x
+= _border_width
;
996 case NorthWestGravity
:
998 case NorthEastGravity
:
1000 case SouthWestGravity
:
1002 case SouthEastGravity
:
1003 if (addborder
) y
-= _border_width
* 2;
1004 else y
+= _border_width
* 2;
1011 if (addborder
) y
-= _border_width
;
1012 else y
+= _border_width
;
1018 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1020 // move the client so it is back it the right spot _with_ its border!
1021 if (x
!= oldx
|| y
!= oldy
)
1022 XMoveWindow(**otk::display
, _window
, x
, y
);
1024 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1028 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1030 otk::EventHandler::clientMessageHandler(e
);
1032 // validate cuz we query stuff off the client here
1033 if (!validate()) return;
1035 if (e
.format
!= 32) return;
1037 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1038 // compress changes into a single change
1039 bool compress
= false;
1041 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1042 // XXX: it would be nice to compress ALL messages of a type, not just
1043 // messages in a row without other message types between.
1044 if (ce
.xclient
.message_type
!= e
.message_type
) {
1045 XPutBackEvent(**otk::display
, &ce
);
1051 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1053 setWMState(e
.data
.l
[0]); // use the original event
1054 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1055 // compress changes into a single change
1056 bool compress
= false;
1058 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1059 // XXX: it would be nice to compress ALL messages of a type, not just
1060 // messages in a row without other message types between.
1061 if (ce
.xclient
.message_type
!= e
.message_type
) {
1062 XPutBackEvent(**otk::display
, &ce
);
1068 setDesktop(e
.data
.l
[0]); // use the found event
1070 setDesktop(e
.data
.l
[0]); // use the original event
1071 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1072 // can't compress these
1074 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1075 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1076 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1077 e
.data
.l
[1], e
.data
.l
[2], _window
);
1079 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1080 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1082 printf("net_close_window for 0x%lx\n", _window
);
1085 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1087 printf("net_active_window for 0x%lx\n", _window
);
1090 setDesktop(openbox
->screen(_screen
)->desktop());
1094 openbox
->screen(_screen
)->raiseWindow(this);
1095 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1097 setDesktop(openbox
->screen(_screen
)->desktop());
1098 if (e
.data
.l
[0] && _shaded
)
1102 openbox
->screen(_screen
)->raiseWindow(this);
1108 void Client::shapeHandler(const XShapeEvent
&e
)
1110 otk::EventHandler::shapeHandler(e
);
1112 if (e
.kind
== ShapeBounding
) {
1114 frame
->adjustShape();
1120 void Client::resize(Corner anchor
, int w
, int h
)
1122 if (!(_functions
& Func_Resize
)) return;
1123 internal_resize(anchor
, w
, h
);
1127 void Client::internal_resize(Corner anchor
, int w
, int h
, bool user
,
1130 w
-= _base_size
.x();
1131 h
-= _base_size
.y();
1134 // for interactive resizing. have to move half an increment in each
1136 int mw
= w
% _size_inc
.x(); // how far we are towards the next size inc
1137 int mh
= h
% _size_inc
.y();
1138 int aw
= _size_inc
.x() / 2; // amount to add
1139 int ah
= _size_inc
.y() / 2;
1140 // don't let us move into a new size increment
1141 if (mw
+ aw
>= _size_inc
.x()) aw
= _size_inc
.x() - mw
- 1;
1142 if (mh
+ ah
>= _size_inc
.y()) ah
= _size_inc
.y() - mh
- 1;
1146 // if this is a user-requested resize, then check against min/max sizes
1147 // and aspect ratios
1149 // smaller than min size or bigger than max size?
1150 if (w
< _min_size
.x()) w
= _min_size
.x();
1151 else if (w
> _max_size
.x()) w
= _max_size
.x();
1152 if (h
< _min_size
.y()) h
= _min_size
.y();
1153 else if (h
> _max_size
.y()) h
= _max_size
.y();
1155 // adjust the height ot match the width for the aspect ratios
1157 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1159 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1162 // keep to the increments
1166 // you cannot resize to nothing
1170 // store the logical size
1171 _logical_size
.setPoint(w
, h
);
1176 w
+= _base_size
.x();
1177 h
+= _base_size
.y();
1179 if (x
== INT_MIN
|| y
== INT_MIN
) {
1186 x
-= w
- _area
.width();
1189 y
-= h
- _area
.height();
1192 x
-= w
- _area
.width();
1193 y
-= h
- _area
.height();
1198 _area
.setSize(w
, h
);
1200 XResizeWindow(**otk::display
, _window
, w
, h
);
1202 // resize the frame to match the request
1203 frame
->adjustSize();
1204 internal_move(x
, y
);
1208 void Client::move(int x
, int y
)
1210 if (!(_functions
& Func_Move
)) return;
1211 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1213 internal_move(x
, y
);
1217 void Client::internal_move(int x
, int y
)
1221 // move the frame to be in the requested position
1222 if (frame
) { // this can be called while mapping, before frame exists
1223 frame
->adjustPosition();
1225 // send synthetic configure notify (we don't need to if we aren't mapped
1228 event
.type
= ConfigureNotify
;
1229 event
.xconfigure
.display
= **otk::display
;
1230 event
.xconfigure
.event
= _window
;
1231 event
.xconfigure
.window
= _window
;
1233 // root window coords with border in mind
1234 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1235 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1237 event
.xconfigure
.width
= _area
.width();
1238 event
.xconfigure
.height
= _area
.height();
1239 event
.xconfigure
.border_width
= _border_width
;
1240 event
.xconfigure
.above
= frame
->plate();
1241 event
.xconfigure
.override_redirect
= False
;
1242 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1243 StructureNotifyMask
, &event
);
1245 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1246 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1247 event
.xconfigure
.height
, event
.xconfigure
.window
);
1253 void Client::close()
1257 if (!(_functions
& Func_Close
)) return;
1259 // XXX: itd be cool to do timeouts and shit here for killing the client's
1261 // like... if the window is around after 5 seconds, then the close button
1262 // turns a nice red, and if this function is called again, the client is
1263 // explicitly killed.
1265 ce
.xclient
.type
= ClientMessage
;
1266 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1267 ce
.xclient
.display
= **otk::display
;
1268 ce
.xclient
.window
= _window
;
1269 ce
.xclient
.format
= 32;
1270 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1271 ce
.xclient
.data
.l
[1] = CurrentTime
;
1272 ce
.xclient
.data
.l
[2] = 0l;
1273 ce
.xclient
.data
.l
[3] = 0l;
1274 ce
.xclient
.data
.l
[4] = 0l;
1275 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1279 void Client::changeState()
1281 unsigned long state
[2];
1282 state
[0] = _wmstate
;
1284 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1285 otk::Property::atoms
.wm_state
, state
, 2);
1290 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1292 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1294 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1296 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1298 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1300 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1302 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1304 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1306 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1308 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1309 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1310 otk::Property::atoms
.atom
, netstate
, num
);
1315 frame
->adjustState();
1319 void Client::changeAllowedActions(void)
1324 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1326 if (_functions
& Func_Shade
)
1327 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1328 if (_functions
& Func_Close
)
1329 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1330 if (_functions
& Func_Move
)
1331 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1332 if (_functions
& Func_Iconify
)
1333 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1334 if (_functions
& Func_Resize
)
1335 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1336 if (_functions
& Func_Fullscreen
)
1337 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1338 if (_functions
& Func_Maximize
) {
1339 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1340 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1343 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1344 otk::Property::atoms
.atom
, actions
, num
);
1346 // make sure the window isn't breaking any rules now
1348 if (!(_functions
& Func_Shade
) && _shaded
)
1349 if (frame
) shade(false);
1350 else _shaded
= false;
1351 if (!(_functions
& Func_Iconify
) && _iconic
)
1352 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1353 else _iconic
= false;
1354 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1355 if (frame
) fullscreen(false);
1356 else _fullscreen
= false;
1357 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1358 if (frame
) maximize(false, 0);
1359 else _max_vert
= _max_horz
= false;
1363 void Client::remaximize()
1366 if (_max_horz
&& _max_vert
)
1373 return; // not maximized
1374 _max_horz
= _max_vert
= false;
1375 maximize(true, dir
, false);
1379 void Client::applyStartupState()
1381 // these are in a carefully crafted order..
1390 setDesktop(ICONIC_DESKTOP
);
1393 _fullscreen
= false;
1394 fullscreen(true, false);
1403 if (_max_vert
&& _max_horz
) {
1404 _max_vert
= _max_horz
= false;
1405 maximize(true, 0, false);
1406 } else if (_max_vert
) {
1408 maximize(true, 2, false);
1409 } else if (_max_horz
) {
1411 maximize(true, 1, false);
1414 if (_skip_taskbar
); // nothing to do for this
1415 if (_skip_pager
); // nothing to do for this
1416 if (_modal
); // nothing to do for this
1417 if (_above
); // nothing to do for this
1418 if (_below
); // nothing to do for this
1422 void Client::fireUrgent()
1424 // call the python UrgentWindow callbacks
1425 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1426 openbox
->bindings()->fireEvent(&data
);
1430 void Client::shade(bool shade
)
1432 if (!(_functions
& Func_Shade
) || // can't
1433 _shaded
== shade
) return; // already done
1435 // when we're iconic, don't change the wmstate
1437 _wmstate
= shade
? IconicState
: NormalState
;
1440 frame
->adjustSize();
1444 void Client::maximize(bool max
, int dir
, bool savearea
)
1446 assert(dir
== 0 || dir
== 1 || dir
== 2);
1447 if (!(_functions
& Func_Maximize
)) return; // can't
1449 // check if already done
1451 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1452 if (dir
== 1 && _max_horz
) return;
1453 if (dir
== 2 && _max_vert
) return;
1455 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1456 if (dir
== 1 && !_max_horz
) return;
1457 if (dir
== 2 && !_max_vert
) return;
1460 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1461 int x
= frame
->rect().x(), y
= frame
->rect().y(),
1462 w
= _area
.width(), h
= _area
.height();
1468 unsigned long n
= 4;
1475 // get the property off the window and use it for the dimentions we are
1477 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1478 otk::Property::atoms
.cardinal
, &n
,
1479 (long unsigned**) &readdim
)) {
1482 dimensions
[0] = readdim
[0];
1483 dimensions
[2] = readdim
[2];
1486 dimensions
[1] = readdim
[1];
1487 dimensions
[3] = readdim
[3];
1493 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1494 otk::Property::atoms
.cardinal
,
1495 (long unsigned*)dimensions
, 4);
1497 if (dir
== 0 || dir
== 1) { // horz
1501 if (dir
== 0 || dir
== 2) { // vert
1503 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1507 long unsigned n
= 4;
1509 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1510 otk::Property::atoms
.cardinal
, &n
,
1511 (long unsigned**) &dimensions
)) {
1513 if (dir
== 0 || dir
== 1) { // horz
1514 x
= (signed int)dimensions
[0];
1515 w
= (signed int)dimensions
[2];
1517 if (dir
== 0 || dir
== 2) { // vert
1518 y
= (signed int)dimensions
[1];
1519 h
= (signed int)dimensions
[3];
1524 // pick some fallbacks...
1525 if (dir
== 0 || dir
== 1) { // horz
1526 x
= a
.x() + a
.width() / 4;
1529 if (dir
== 0 || dir
== 2) { // vert
1530 y
= a
.y() + a
.height() / 4;
1536 if (dir
== 0 || dir
== 1) // horz
1538 if (dir
== 0 || dir
== 2) // vert
1541 if (!_max_horz
&& !_max_vert
)
1542 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1544 changeState(); // change the state hints on the client
1546 frame
->frameGravity(x
, y
); // figure out where the client should be going
1547 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1551 void Client::fullscreen(bool fs
, bool savearea
)
1553 static FunctionFlags saved_func
;
1554 static DecorationFlags saved_decor
;
1556 if (!(_functions
& Func_Fullscreen
) || // can't
1557 _fullscreen
== fs
) return; // already done
1560 changeState(); // change the state hints on the client
1562 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1565 // save the functions and remove them
1566 saved_func
= _functions
;
1567 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1568 // save the decorations and remove them
1569 saved_decor
= _decorations
;
1573 dimensions
[0] = _area
.x();
1574 dimensions
[1] = _area
.y();
1575 dimensions
[2] = _area
.width();
1576 dimensions
[3] = _area
.height();
1577 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1578 otk::Property::atoms
.cardinal
,
1579 (long unsigned*)dimensions
, 4);
1581 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1587 _functions
= saved_func
;
1588 _decorations
= saved_decor
;
1591 long unsigned n
= 4;
1593 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1594 otk::Property::atoms
.cardinal
, &n
,
1595 (long unsigned**) &dimensions
)) {
1604 // pick some fallbacks...
1605 const otk::Rect
&a
= openbox
->screen(_screen
)->area();
1606 x
= a
.x() + a
.width() / 4;
1607 y
= a
.y() + a
.height() / 4;
1613 changeAllowedActions(); // based on the new _functions
1615 // when fullscreening, don't obey things like increments, fill the screen
1616 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1618 // raise (back) into our stacking layer
1619 openbox
->screen(_screen
)->raiseWindow(this);
1621 // try focus us when we go into fullscreen mode
1626 void Client::disableDecorations(DecorationFlags flags
)
1628 _disabled_decorations
= flags
;
1629 setupDecorAndFunctions();
1633 void Client::installColormap(bool install
) const
1635 XWindowAttributes wa
;
1636 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1638 XInstallColormap(**otk::display
, wa
.colormap
);
1640 XUninstallColormap(**otk::display
, wa
.colormap
);
1645 bool Client::focus()
1647 // if we have a modal child, then focus it, not us
1649 return _modal_child
->focus();
1651 // won't try focus if the client doesn't want it, or if the window isn't
1652 // visible on the screen
1653 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1655 if (_focused
) return true;
1657 // do a check to see if the window has already been unmapped or destroyed
1658 // do this intelligently while watching out for unmaps we've generated
1659 // (ignore_unmaps > 0)
1661 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1662 XPutBackEvent(**otk::display
, &ev
);
1665 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1666 if (ignore_unmaps
) {
1667 unmapHandler(ev
.xunmap
);
1669 XPutBackEvent(**otk::display
, &ev
);
1675 XSetInputFocus(**otk::display
, _window
,
1676 RevertToNone
, CurrentTime
);
1678 if (_focus_notify
) {
1680 ce
.xclient
.type
= ClientMessage
;
1681 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1682 ce
.xclient
.display
= **otk::display
;
1683 ce
.xclient
.window
= _window
;
1684 ce
.xclient
.format
= 32;
1685 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1686 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1687 ce
.xclient
.data
.l
[2] = 0l;
1688 ce
.xclient
.data
.l
[3] = 0l;
1689 ce
.xclient
.data
.l
[4] = 0l;
1690 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1693 XSync(**otk::display
, False
);
1698 void Client::unfocus() const
1700 if (!_focused
) return;
1702 assert(openbox
->focusedClient() == this);
1703 openbox
->setFocusedClient(0);
1707 void Client::focusHandler(const XFocusChangeEvent
&e
)
1710 // printf("FocusIn for 0x%lx\n", e.window);
1713 otk::EventHandler::focusHandler(e
);
1718 openbox
->setFocusedClient(this);
1722 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1725 // printf("FocusOut for 0x%lx\n", e.window);
1728 otk::EventHandler::unfocusHandler(e
);
1733 if (openbox
->focusedClient() == this)
1734 openbox
->setFocusedClient(0);
1738 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1741 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1744 otk::EventHandler::configureRequestHandler(e
);
1746 // if we are iconic (or shaded (fvwm does this)) ignore the event
1747 if (_iconic
|| _shaded
) return;
1749 if (e
.value_mask
& CWBorderWidth
)
1750 _border_width
= e
.border_width
;
1752 // resize, then move, as specified in the EWMH section 7.7
1753 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1754 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1755 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1759 case NorthEastGravity
:
1763 case SouthWestGravity
:
1765 corner
= BottomLeft
;
1767 case SouthEastGravity
:
1768 corner
= BottomRight
;
1770 default: // NorthWest, Static, etc
1774 // if moving AND resizing ...
1775 if (e
.value_mask
& (CWX
| CWY
)) {
1776 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1777 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1778 internal_resize(corner
, w
, h
, false, x
, y
);
1779 } else // if JUST resizing...
1780 internal_resize(corner
, w
, h
, false);
1781 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1782 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1783 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1784 internal_move(x
, y
);
1787 if (e
.value_mask
& CWStackMode
) {
1791 openbox
->screen(_screen
)->lowerWindow(this);
1797 openbox
->screen(_screen
)->raiseWindow(this);
1804 void Client::unmapHandler(const XUnmapEvent
&e
)
1806 if (ignore_unmaps
) {
1808 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1815 printf("UnmapNotify for 0x%lx\n", e
.window
);
1818 otk::EventHandler::unmapHandler(e
);
1820 // this deletes us etc
1821 openbox
->screen(_screen
)->unmanageWindow(this);
1825 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1828 printf("DestroyNotify for 0x%lx\n", e
.window
);
1831 otk::EventHandler::destroyHandler(e
);
1833 // this deletes us etc
1834 openbox
->screen(_screen
)->unmanageWindow(this);
1838 void Client::reparentHandler(const XReparentEvent
&e
)
1840 // this is when the client is first taken captive in the frame
1841 if (e
.parent
== frame
->plate()) return;
1844 printf("ReparentNotify for 0x%lx\n", e
.window
);
1847 otk::EventHandler::reparentHandler(e
);
1850 This event is quite rare and is usually handled in unmapHandler.
1851 However, if the window is unmapped when the reparent event occurs,
1852 the window manager never sees it because an unmap event is not sent
1853 to an already unmapped window.
1856 // we don't want the reparent event, put it back on the stack for the X
1857 // server to deal with after we unmanage the window
1860 XPutBackEvent(**otk::display
, &ev
);
1862 // this deletes us etc
1863 openbox
->screen(_screen
)->unmanageWindow(this);
1866 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1869 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1872 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1874 // move to the current desktop (uniconify)
1875 setDesktop(openbox
->screen(_screen
)->desktop());
1876 // XXX: should we focus/raise the window? (basically a net_wm_active_window)