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)
22 #include <cstring> // for memcpy
29 Client::Client(int screen
, Window window
)
30 : otk::EventHandler(),
31 frame(0), _screen(screen
), _window(window
)
38 // update EVERYTHING the first time!!
41 _wmstate
= NormalState
;
44 _layer
= Layer_Normal
;
47 _disabled_decorations
= 0;
54 getState(); // do this before updateTransientFor! (for _modal)
59 getType(); // this can change the mwmhints for special cases
63 getGravity(); // get the attribute gravity
64 updateNormalHints(); // this may override the attribute gravity
66 // got the type, the mwmhints, the protocols, and the normal hints (min/max
67 // sizes), so we're ready to set up
68 // the decorations/functions
69 setupDecorAndFunctions();
71 // also get the initial_state and set _iconic if we aren't "starting"
72 // when we're "starting" that means we should use whatever state was already
73 // on the window over the initial map state, because it was already mapped
74 updateWMHints(openbox
->state() != Openbox::State_Starting
);
81 // this makes sure that these windows appear on all desktops
82 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
83 _desktop
= 0xffffffff;
85 // set the desktop hint, to make sure that it always exists, and to reflect
86 // any changes we've made here
87 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
88 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
95 assert(_nicons
> 0); // there should always be a default..
96 for (int j
= 0; j
< _nicons
; ++j
)
97 delete [] _icons
[j
].data
;
100 // clean up childrens' references
101 while (!_transients
.empty()) {
102 _transients
.front()->_transient_for
= 0;
103 _transients
.pop_front();
106 // clean up parents reference to this
108 _transient_for
->_transients
.remove(this); // remove from old parent
110 if (openbox
->state() != Openbox::State_Exiting
) {
111 // these values should not be persisted across a window unmapping/mapping
112 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
113 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
115 // if we're left in an iconic state, the client wont be mapped. this is
116 // bad, since we will no longer be managing the window on restart
118 XMapWindow(**otk::display
, _window
);
123 bool Client::validate() const
125 XSync(**otk::display
, false); // get all events on the server
128 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
129 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
130 XPutBackEvent(**otk::display
, &e
);
138 void Client::getGravity()
140 XWindowAttributes wattrib
;
143 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
144 assert(ret
!= BadWindow
);
145 _gravity
= wattrib
.win_gravity
;
149 void Client::getDesktop()
151 // defaults to the current desktop
152 _desktop
= openbox
->screen(_screen
)->desktop();
154 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
155 otk::Property::atoms
.cardinal
,
156 (long unsigned*)&_desktop
)) {
158 // printf("Window requested desktop: %ld\n", _desktop);
164 void Client::getType()
166 _type
= (WindowType
) -1;
169 unsigned long num
= (unsigned) -1;
170 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
171 otk::Property::atoms
.atom
, &num
, &val
)) {
172 // use the first value that we know about in the array
173 for (unsigned long i
= 0; i
< num
; ++i
) {
174 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
175 _type
= Type_Desktop
;
176 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
178 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
179 _type
= Type_Toolbar
;
180 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
182 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
183 _type
= Type_Utility
;
184 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
186 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
188 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
190 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
191 // prevent this window from getting any decor or functionality
192 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
193 _mwmhints
.decorations
= 0;
194 _mwmhints
.functions
= 0;
196 if (_type
!= (WindowType
) -1)
197 break; // grab the first known type
202 if (_type
== (WindowType
) -1) {
204 * the window type hint was not set, which means we either classify ourself
205 * as a normal window or a dialog, depending on if we are a transient.
215 void Client::setupDecorAndFunctions()
217 // start with everything (cept fullscreen)
218 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
| Decor_Icon
|
219 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
220 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
222 if (_delete_window
) {
223 _decorations
|= Decor_Close
;
224 _functions
|= Func_Close
;
227 if (!(_min_size
.width() < _max_size
.width() ||
228 _min_size
.height() < _max_size
.height())) {
229 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
230 _functions
&= ~(Func_Resize
| Func_Maximize
);
235 // normal windows retain all of the possible decorations and
236 // functionality, and are the only windows that you can fullscreen
237 _functions
|= Func_Fullscreen
;
241 // dialogs cannot be maximized
242 _decorations
&= ~Decor_Maximize
;
243 _functions
&= ~Func_Maximize
;
249 // these windows get less functionality
250 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
251 _functions
&= ~(Func_Iconify
| Func_Resize
);
257 // none of these windows are manipulated by the window manager
263 // Mwm Hints are applied subtractively to what has already been chosen for
264 // decor and functionality
265 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
266 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
267 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
268 _decorations
&= ~Decor_Border
;
269 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
270 _decorations
&= ~Decor_Handle
;
271 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
272 _decorations
&= ~Decor_Titlebar
;
273 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
274 _decorations
&= ~Decor_Iconify
;
275 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
276 _decorations
&= ~Decor_Maximize
;
280 if (_mwmhints
.flags
& MwmFlag_Functions
) {
281 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
282 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
283 _functions
&= ~Func_Resize
;
284 if (! (_mwmhints
.functions
& MwmFunc_Move
))
285 _functions
&= ~Func_Move
;
286 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
287 _functions
&= ~Func_Iconify
;
288 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
289 _functions
&= ~Func_Maximize
;
290 // dont let mwm hints kill the close button
291 //if (! (_mwmhints.functions & MwmFunc_Close))
292 // _functions &= ~Func_Close;
296 // can't maximize without moving/resizing
297 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
298 _functions
&= ~Func_Maximize
;
300 // finally, user specified disabled decorations are applied to subtract
302 if (_disabled_decorations
& Decor_Titlebar
)
303 _decorations
&= ~Decor_Titlebar
;
304 if (_disabled_decorations
& Decor_Handle
)
305 _decorations
&= ~Decor_Handle
;
306 if (_disabled_decorations
& Decor_Border
)
307 _decorations
&= ~Decor_Border
;
308 if (_disabled_decorations
& Decor_Iconify
)
309 _decorations
&= ~Decor_Iconify
;
310 if (_disabled_decorations
& Decor_Maximize
)
311 _decorations
&= ~Decor_Maximize
;
312 if (_disabled_decorations
& Decor_AllDesktops
)
313 _decorations
&= ~Decor_AllDesktops
;
314 if (_disabled_decorations
& Decor_Close
)
315 _decorations
&= ~Decor_Close
;
317 // if we don't have a titlebar, then we cannot shade!
318 if (!(_decorations
& Decor_Titlebar
))
319 _functions
&= ~Func_Shade
;
321 changeAllowedActions();
324 frame
->adjustSize(); // change the decors on the frame
325 frame
->adjustPosition(); // with more/less decorations, we may need to be
327 remaximize(); // with new decor, the window's maximized size may change
332 void Client::getMwmHints()
334 unsigned long num
= MwmHints::elements
;
335 unsigned long *hints
;
337 _mwmhints
.flags
= 0; // default to none
339 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
340 otk::Property::atoms
.motif_wm_hints
, &num
,
341 (unsigned long **)&hints
))
344 if (num
>= MwmHints::elements
) {
345 // retrieved the hints
346 _mwmhints
.flags
= hints
[0];
347 _mwmhints
.functions
= hints
[1];
348 _mwmhints
.decorations
= hints
[2];
355 void Client::getArea()
357 XWindowAttributes wattrib
;
360 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
361 assert(ret
!= BadWindow
);
363 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
364 _border_width
= wattrib
.border_width
;
368 void Client::getState()
370 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
371 _iconic
= _skip_taskbar
= _skip_pager
= false;
373 unsigned long *state
;
374 unsigned long num
= (unsigned) -1;
376 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
377 otk::Property::atoms
.atom
, &num
, &state
)) {
378 for (unsigned long i
= 0; i
< num
; ++i
) {
379 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
381 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
383 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
385 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
386 _skip_taskbar
= true;
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
391 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
393 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
395 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
397 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
405 void Client::getShaped()
409 if (otk::display
->shape()) {
414 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
416 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
417 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
423 Client
*Client::searchFocusTree(Client
*node
, Client
*skip
)
425 List::const_iterator it
, end
= node
->_transients
.end();
428 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
429 if (*it
== skip
) continue; // circular?
430 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
431 if ((*it
)->_focused
) return *it
; // got one
436 void Client::calcLayer() {
440 // are we fullscreen, or do we have a fullscreen transient parent?
443 if (c
->_fullscreen
) {
447 c
= c
->_transient_for
;
450 // is one of our transients focused?
451 c
= searchFocusTree(this, this);
455 if (_iconic
) l
= Layer_Icon
;
456 else if (fs
) l
= Layer_Fullscreen
;
457 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
458 else if (_type
== Type_Dock
) {
459 if (!_below
) l
= Layer_Top
;
460 else l
= Layer_Normal
;
462 else if (_above
) l
= Layer_Above
;
463 else if (_below
) l
= Layer_Below
;
464 else l
= Layer_Normal
;
470 if we don't have a frame, then we aren't mapped yet (and this would
473 openbox
->screen(_screen
)->raiseWindow(this);
478 void Client::updateProtocols()
483 _focus_notify
= false;
484 _delete_window
= false;
486 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
487 for (int i
= 0; i
< num_return
; ++i
) {
488 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
489 // this means we can request the window to close
490 _delete_window
= true;
491 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
492 // if this protocol is requested, then the window will be notified
493 // by the window manager whenever it receives focus
494 _focus_notify
= true;
500 void Client::updateNormalHints()
504 int oldgravity
= _gravity
;
509 _size_inc
= otk::Size(1, 1);
510 _base_size
= otk::Size(0, 0);
511 _min_size
= otk::Size(0, 0);
512 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
514 // get the hints from the window
515 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
516 _positioned
= (size
.flags
& (PPosition
|USPosition
));
518 if (size
.flags
& PWinGravity
) {
519 _gravity
= size
.win_gravity
;
521 // if the client has a frame, i.e. has already been mapped and is
522 // changing its gravity
523 if (frame
&& _gravity
!= oldgravity
) {
524 // move our idea of the client's position based on its new gravity
525 int x
= frame
->area().x(), y
= frame
->area().y();
526 frame
->frameGravity(x
, y
);
527 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
531 if (size
.flags
& PAspect
) {
532 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
533 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
536 if (size
.flags
& PMinSize
)
537 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
539 if (size
.flags
& PMaxSize
)
540 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
542 if (size
.flags
& PBaseSize
)
543 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
545 if (size
.flags
& PResizeInc
)
546 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
550 void Client::updateWMHints(bool initstate
)
554 // assume a window takes input if it doesnt specify
558 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
559 if (hints
->flags
& InputHint
)
560 _can_focus
= hints
->input
;
562 // only do this when initstate is true!
563 if (initstate
&& (hints
->flags
& StateHint
))
564 _iconic
= hints
->initial_state
== IconicState
;
566 if (hints
->flags
& XUrgencyHint
)
569 if (hints
->flags
& WindowGroupHint
) {
570 if (hints
->window_group
!= _group
) {
571 // XXX: remove from the old group if there was one
572 _group
= hints
->window_group
;
573 // XXX: do stuff with the group
584 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
585 (long)_window
, _urgent
? "ON" : "OFF");
587 // fire the urgent callback if we're mapped, otherwise, wait until after
594 void Client::updateTitle()
599 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
600 otk::Property::utf8
, &_title
)) {
602 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
603 otk::Property::ascii
, &_title
);
607 _title
= _("Unnamed Window");
610 frame
->adjustTitle();
613 void Client::updateIconTitle()
618 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
619 otk::Property::utf8
, &_icon_title
)) {
621 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
622 otk::Property::ascii
, &_icon_title
);
626 _icon_title
= _("Unnamed Window");
629 void Client::updateClass()
632 _app_name
= _app_class
= _role
= "";
634 otk::Property::StringVect v
;
635 unsigned long num
= 2;
637 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
638 otk::Property::ascii
, &num
, &v
)) {
639 if (num
> 0) _app_name
= v
[0].c_str();
640 if (num
> 1) _app_class
= v
[1].c_str();
645 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
646 otk::Property::ascii
, &num
, &v
)) {
647 if (num
> 0) _role
= v
[0].c_str();
651 void Client::updateStrut()
653 unsigned long num
= 4;
655 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
656 otk::Property::atoms
.cardinal
, &num
, &data
))
660 _strut
.left
= data
[0];
661 _strut
.right
= data
[1];
662 _strut
.top
= data
[2];
663 _strut
.bottom
= data
[3];
665 // updating here is pointless while we're being mapped cuz we're not in
666 // the screen's client list yet
668 openbox
->screen(_screen
)->updateStruts();
674 void Client::updateTransientFor()
679 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
680 t
!= _window
) { // cant be transient to itself!
681 c
= openbox
->findClient(t
);
682 assert(c
!= this); // if this happens then we need to check for it
684 if (!c
/*XXX: && _group*/) {
685 // not transient to a client, see if it is transient for a group
686 if (//t == _group->leader() ||
688 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
689 // window is a transient for its group!
690 // XXX: for now this is treated as non-transient.
691 // this needs to be fixed!
696 // if anything has changed...
697 if (c
!= _transient_for
) {
699 _transient_for
->_transients
.remove(this); // remove from old parent
702 _transient_for
->_transients
.push_back(this); // add to new parent
706 void Client::updateIcons()
708 unsigned long num
= (unsigned) -1;
710 unsigned long w
, h
, i
= 0;
712 for (int j
= 0; j
< _nicons
; ++j
)
713 delete [] _icons
[j
].data
;
718 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon
,
719 otk::Property::atoms
.cardinal
, &num
, &data
)) {
720 // figure out how man valid icons are in here
721 while (num
- i
> 2) {
729 _icons
= new Icon
[_nicons
];
733 for (int j
= 0; j
< _nicons
; ++j
) {
734 w
= _icons
[j
].w
= data
[i
++];
735 h
= _icons
[j
].h
= data
[i
++];
736 _icons
[j
].data
= new unsigned long[w
* h
];
737 ::memcpy(_icons
[j
].data
, &data
[i
], w
* h
* sizeof(unsigned long));
741 printf("i: %lu\n", i
);
742 printf("bleffffffff\n");
748 // set the default icon(s) XXX load these from the py
750 _icons
= new Icon
[1];
756 assert(_nicons
> 0); // there should always be a default..
758 if (frame
) frame
->adjustIcon();
761 void Client::propertyHandler(const XPropertyEvent
&e
)
763 otk::EventHandler::propertyHandler(e
);
765 // validate cuz we query stuff off the client here
766 if (!validate()) return;
768 // compress changes to a single property into a single change
770 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
771 // XXX: it would be nice to compress ALL changes to a property, not just
772 // changes in a row without other props between.
773 if (ce
.xproperty
.atom
!= e
.atom
) {
774 XPutBackEvent(**otk::display
, &ce
);
779 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
781 setupDecorAndFunctions(); // normal hints can make a window non-resizable
782 } else if (e
.atom
== XA_WM_HINTS
)
784 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
785 updateTransientFor();
787 calcLayer(); // type may have changed, so update the layer
788 setupDecorAndFunctions();
790 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
791 e
.atom
== otk::Property::atoms
.wm_name
)
793 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
794 e
.atom
== otk::Property::atoms
.wm_icon_name
)
796 else if (e
.atom
== otk::Property::atoms
.wm_class
)
798 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
800 setupDecorAndFunctions();
802 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
804 else if (e
.atom
== otk::Property::atoms
.net_wm_icon
)
808 void Client::setWMState(long state
)
810 if (state
== _wmstate
) return; // no change
822 void Client::setDesktop(unsigned int target
)
824 if (target
== _desktop
) return;
826 printf("Setting desktop %u\n", target
);
828 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
829 target
== 0xffffffff))
833 // set the desktop hint
834 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
835 otk::Property::atoms
.cardinal
, _desktop
);
836 frame
->adjustState(); // the frame can display the current desktop state
837 // 'move' the window to the new desktop
839 openbox
->screen(_screen
)->updateStruts();
842 void Client::showhide()
845 Screen
*s
= openbox
->screen(_screen
);
847 if (_iconic
) show
= false;
848 else if (!(_desktop
== s
->desktop() ||
849 _desktop
== 0xffffffff)) show
= false;
850 else if (normal() && s
->showingDesktop()) show
= false;
853 if (show
) frame
->show();
857 void Client::setState(StateAction action
, long data1
, long data2
)
859 bool shadestate
= _shaded
;
860 bool fsstate
= _fullscreen
;
861 bool maxh
= _max_horz
;
862 bool maxv
= _max_vert
;
864 if (!(action
== State_Add
|| action
== State_Remove
||
865 action
== State_Toggle
))
866 return; // an invalid action was passed to the client message, ignore it
868 for (int i
= 0; i
< 2; ++i
) {
869 Atom state
= i
== 0 ? data1
: data2
;
871 if (! state
) continue;
873 // if toggling, then pick whether we're adding or removing
874 if (action
== State_Toggle
) {
875 if (state
== otk::Property::atoms
.net_wm_state_modal
)
876 action
= _modal
? State_Remove
: State_Add
;
877 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
878 action
= _max_vert
? State_Remove
: State_Add
;
879 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
880 action
= _max_horz
? State_Remove
: State_Add
;
881 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
882 action
= _shaded
? State_Remove
: State_Add
;
883 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
884 action
= _skip_taskbar
? State_Remove
: State_Add
;
885 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
886 action
= _skip_pager
? State_Remove
: State_Add
;
887 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
888 action
= _fullscreen
? State_Remove
: State_Add
;
889 else if (state
== otk::Property::atoms
.net_wm_state_above
)
890 action
= _above
? State_Remove
: State_Add
;
891 else if (state
== otk::Property::atoms
.net_wm_state_below
)
892 action
= _below
? State_Remove
: State_Add
;
895 if (action
== State_Add
) {
896 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
897 if (_modal
) continue;
899 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
901 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
902 if (_max_horz
) continue;
904 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
906 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
907 _skip_taskbar
= true;
908 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
910 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
912 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
913 if (_above
) continue;
915 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
916 if (_below
) continue;
920 } else { // action == State_Remove
921 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
922 if (!_modal
) continue;
924 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
926 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
928 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
930 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
931 _skip_taskbar
= false;
932 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
934 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
936 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
937 if (!_above
) continue;
939 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
940 if (!_below
) continue;
945 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
946 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
947 if (maxh
== maxv
) { // both going the same way
948 maximize(maxh
, 0, true);
950 maximize(maxh
, 1, true);
951 maximize(maxv
, 2, true);
953 } else { // toggling one
954 if (maxh
!= _max_horz
)
955 maximize(maxh
, 1, true);
957 maximize(maxv
, 2, true);
960 // change fullscreen state before shading, as it will affect if the window
962 if (fsstate
!= _fullscreen
)
963 fullscreen(fsstate
, true);
964 if (shadestate
!= _shaded
)
967 changeState(); // change the hint to relect these changes
970 void Client::toggleClientBorder(bool addborder
)
972 // adjust our idea of where the client is, based on its border. When the
973 // border is removed, the client should now be considered to be in a
974 // different position.
975 // when re-adding the border to the client, the same operation needs to be
977 int oldx
= _area
.x(), oldy
= _area
.y();
978 int x
= oldx
, y
= oldy
;
981 case NorthWestGravity
:
983 case SouthWestGravity
:
985 case NorthEastGravity
:
987 case SouthEastGravity
:
988 if (addborder
) x
-= _border_width
* 2;
989 else x
+= _border_width
* 2;
996 if (addborder
) x
-= _border_width
;
997 else x
+= _border_width
;
1002 case NorthWestGravity
:
1004 case NorthEastGravity
:
1006 case SouthWestGravity
:
1008 case SouthEastGravity
:
1009 if (addborder
) y
-= _border_width
* 2;
1010 else y
+= _border_width
* 2;
1017 if (addborder
) y
-= _border_width
;
1018 else y
+= _border_width
;
1021 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1024 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1026 // move the client so it is back it the right spot _with_ its border!
1027 if (x
!= oldx
|| y
!= oldy
)
1028 XMoveWindow(**otk::display
, _window
, x
, y
);
1030 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1033 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1035 otk::EventHandler::clientMessageHandler(e
);
1037 // validate cuz we query stuff off the client here
1038 if (!validate()) return;
1040 if (e
.format
!= 32) return;
1042 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1043 // compress changes into a single change
1044 bool compress
= false;
1046 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1047 // XXX: it would be nice to compress ALL messages of a type, not just
1048 // messages in a row without other message types between.
1049 if (ce
.xclient
.message_type
!= e
.message_type
) {
1050 XPutBackEvent(**otk::display
, &ce
);
1056 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1058 setWMState(e
.data
.l
[0]); // use the original event
1059 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1060 // compress changes into a single change
1061 bool compress
= false;
1063 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1064 // XXX: it would be nice to compress ALL messages of a type, not just
1065 // messages in a row without other message types between.
1066 if (ce
.xclient
.message_type
!= e
.message_type
) {
1067 XPutBackEvent(**otk::display
, &ce
);
1073 setDesktop(e
.data
.l
[0]); // use the found event
1075 setDesktop(e
.data
.l
[0]); // use the original event
1076 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1077 // can't compress these
1079 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1080 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1081 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1082 e
.data
.l
[1], e
.data
.l
[2], _window
);
1084 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1085 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1087 printf("net_close_window for 0x%lx\n", _window
);
1090 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1092 printf("net_active_window for 0x%lx\n", _window
);
1094 if (openbox
->screen(_screen
)->showingDesktop())
1095 openbox
->screen(_screen
)->showDesktop(false);
1098 else if (!frame
->visible()) // if its not visible for other reasons, then
1099 return; // don't mess with it
1103 openbox
->screen(_screen
)->raiseWindow(this);
1104 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1105 if (openbox
->screen(_screen
)->showingDesktop())
1106 openbox
->screen(_screen
)->showDesktop(false);
1109 else if (!frame
->visible()) // if its not visible for other reasons, then
1110 return; // don't mess with it
1111 if (e
.data
.l
[0] && _shaded
)
1115 openbox
->screen(_screen
)->raiseWindow(this);
1120 void Client::shapeHandler(const XShapeEvent
&e
)
1122 otk::EventHandler::shapeHandler(e
);
1124 if (e
.kind
== ShapeBounding
) {
1126 frame
->adjustShape();
1131 void Client::resize(Corner anchor
, int w
, int h
)
1133 if (!(_functions
& Func_Resize
)) return;
1134 internal_resize(anchor
, w
, h
);
1137 void Client::internal_resize(Corner anchor
, int w
, int h
,
1138 bool user
, int x
, int y
)
1140 w
-= _base_size
.width();
1141 h
-= _base_size
.height();
1144 // for interactive resizing. have to move half an increment in each
1146 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1147 int mh
= h
% _size_inc
.height();
1148 int aw
= _size_inc
.width() / 2; // amount to add
1149 int ah
= _size_inc
.height() / 2;
1150 // don't let us move into a new size increment
1151 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1152 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1156 // if this is a user-requested resize, then check against min/max sizes
1157 // and aspect ratios
1159 // smaller than min size or bigger than max size?
1160 if (w
> _max_size
.width()) w
= _max_size
.width();
1161 if (w
< _min_size
.width()) w
= _min_size
.width();
1162 if (h
> _max_size
.height()) h
= _max_size
.height();
1163 if (h
< _min_size
.height()) h
= _min_size
.height();
1165 // adjust the height ot match the width for the aspect ratios
1167 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1169 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1172 // keep to the increments
1173 w
/= _size_inc
.width();
1174 h
/= _size_inc
.height();
1176 // you cannot resize to nothing
1180 // store the logical size
1181 _logical_size
= otk::Size(w
, h
);
1183 w
*= _size_inc
.width();
1184 h
*= _size_inc
.height();
1186 w
+= _base_size
.width();
1187 h
+= _base_size
.height();
1189 if (x
== INT_MIN
|| y
== INT_MIN
) {
1196 x
-= w
- _area
.width();
1199 y
-= h
- _area
.height();
1202 x
-= w
- _area
.width();
1203 y
-= h
- _area
.height();
1208 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1210 XResizeWindow(**otk::display
, _window
, w
, h
);
1212 // resize the frame to match the request
1213 frame
->adjustSize();
1214 internal_move(x
, y
);
1217 const Icon
*Client::icon(const otk::Size
&s
) const
1219 unsigned long req
= s
.width() * s
.height();
1220 // si is the smallest image >= req
1221 // li is the largest image < req
1222 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1224 assert(_nicons
> 0); // there should always be a default..
1225 for (int i
= 0; i
< _nicons
; ++i
) {
1226 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1227 if (size
< smallest
&& size
>= req
) {
1231 if (size
> largest
&& size
<= req
) {
1236 if (smallest
== 0xffffffff) // didnt find one bigger than us...
1241 void Client::move(int x
, int y
)
1243 if (!(_functions
& Func_Move
)) return;
1244 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1246 internal_move(x
, y
);
1249 void Client::internal_move(int x
, int y
)
1251 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1253 // move the frame to be in the requested position
1254 if (frame
) { // this can be called while mapping, before frame exists
1255 frame
->adjustPosition();
1257 // send synthetic configure notify (we don't need to if we aren't mapped
1260 event
.type
= ConfigureNotify
;
1261 event
.xconfigure
.display
= **otk::display
;
1262 event
.xconfigure
.event
= _window
;
1263 event
.xconfigure
.window
= _window
;
1265 // root window coords with border in mind
1266 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1267 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1269 event
.xconfigure
.width
= _area
.width();
1270 event
.xconfigure
.height
= _area
.height();
1271 event
.xconfigure
.border_width
= _border_width
;
1272 event
.xconfigure
.above
= frame
->plate();
1273 event
.xconfigure
.override_redirect
= False
;
1274 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1275 StructureNotifyMask
, &event
);
1277 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1278 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1279 event
.xconfigure
.height
, event
.xconfigure
.window
);
1284 void Client::close()
1288 if (!(_functions
& Func_Close
)) return;
1290 // XXX: itd be cool to do timeouts and shit here for killing the client's
1292 // like... if the window is around after 5 seconds, then the close button
1293 // turns a nice red, and if this function is called again, the client is
1294 // explicitly killed.
1296 ce
.xclient
.type
= ClientMessage
;
1297 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1298 ce
.xclient
.display
= **otk::display
;
1299 ce
.xclient
.window
= _window
;
1300 ce
.xclient
.format
= 32;
1301 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1302 ce
.xclient
.data
.l
[1] = CurrentTime
;
1303 ce
.xclient
.data
.l
[2] = 0l;
1304 ce
.xclient
.data
.l
[3] = 0l;
1305 ce
.xclient
.data
.l
[4] = 0l;
1306 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1309 void Client::changeState()
1311 unsigned long state
[2];
1312 state
[0] = _wmstate
;
1314 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1315 otk::Property::atoms
.wm_state
, state
, 2);
1320 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1322 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1324 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1326 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1328 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1330 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1332 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1334 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1336 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1338 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1339 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1340 otk::Property::atoms
.atom
, netstate
, num
);
1345 frame
->adjustState();
1348 void Client::changeAllowedActions(void)
1353 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1355 if (_functions
& Func_Shade
)
1356 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1357 if (_functions
& Func_Close
)
1358 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1359 if (_functions
& Func_Move
)
1360 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1361 if (_functions
& Func_Iconify
)
1362 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1363 if (_functions
& Func_Resize
)
1364 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1365 if (_functions
& Func_Fullscreen
)
1366 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1367 if (_functions
& Func_Maximize
) {
1368 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1369 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1372 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1373 otk::Property::atoms
.atom
, actions
, num
);
1375 // make sure the window isn't breaking any rules now
1377 if (!(_functions
& Func_Shade
) && _shaded
)
1378 if (frame
) shade(false);
1379 else _shaded
= false;
1380 if (!(_functions
& Func_Iconify
) && _iconic
)
1381 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1382 else _iconic
= false;
1383 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1384 if (frame
) fullscreen(false);
1385 else _fullscreen
= false;
1386 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1387 if (frame
) maximize(false, 0);
1388 else _max_vert
= _max_horz
= false;
1391 void Client::remaximize()
1394 if (_max_horz
&& _max_vert
)
1401 return; // not maximized
1402 _max_horz
= _max_vert
= false;
1403 maximize(true, dir
, false);
1406 void Client::applyStartupState()
1408 // these are in a carefully crafted order..
1415 _fullscreen
= false;
1416 fullscreen(true, false);
1425 if (_max_vert
&& _max_horz
) {
1426 _max_vert
= _max_horz
= false;
1427 maximize(true, 0, false);
1428 } else if (_max_vert
) {
1430 maximize(true, 2, false);
1431 } else if (_max_horz
) {
1433 maximize(true, 1, false);
1436 if (_skip_taskbar
); // nothing to do for this
1437 if (_skip_pager
); // nothing to do for this
1438 if (_modal
); // nothing to do for this
1439 if (_above
); // nothing to do for this
1440 if (_below
); // nothing to do for this
1443 void Client::fireUrgent()
1445 // call the python UrgentWindow callbacks
1446 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1447 openbox
->bindings()->fireEvent(&data
);
1450 void Client::shade(bool shade
)
1452 if (!(_functions
& Func_Shade
) || // can't
1453 _shaded
== shade
) return; // already done
1455 // when we're iconic, don't change the wmstate
1457 _wmstate
= shade
? IconicState
: NormalState
;
1460 frame
->adjustSize();
1463 void Client::maximize(bool max
, int dir
, bool savearea
)
1465 assert(dir
== 0 || dir
== 1 || dir
== 2);
1466 if (!(_functions
& Func_Maximize
)) return; // can't
1468 // check if already done
1470 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1471 if (dir
== 1 && _max_horz
) return;
1472 if (dir
== 2 && _max_vert
) return;
1474 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1475 if (dir
== 1 && !_max_horz
) return;
1476 if (dir
== 2 && !_max_vert
) return;
1479 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1480 int x
= frame
->area().x(), y
= frame
->area().y(),
1481 w
= _area
.width(), h
= _area
.height();
1487 unsigned long n
= 4;
1494 // get the property off the window and use it for the dimentions we are
1496 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1497 otk::Property::atoms
.cardinal
, &n
,
1498 (long unsigned**) &readdim
)) {
1501 dimensions
[0] = readdim
[0];
1502 dimensions
[2] = readdim
[2];
1505 dimensions
[1] = readdim
[1];
1506 dimensions
[3] = readdim
[3];
1512 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1513 otk::Property::atoms
.cardinal
,
1514 (long unsigned*)dimensions
, 4);
1516 if (dir
== 0 || dir
== 1) { // horz
1520 if (dir
== 0 || dir
== 2) { // vert
1522 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1526 long unsigned n
= 4;
1528 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1529 otk::Property::atoms
.cardinal
, &n
,
1530 (long unsigned**) &dimensions
)) {
1532 if (dir
== 0 || dir
== 1) { // horz
1533 x
= (signed int)dimensions
[0];
1534 w
= (signed int)dimensions
[2];
1536 if (dir
== 0 || dir
== 2) { // vert
1537 y
= (signed int)dimensions
[1];
1538 h
= (signed int)dimensions
[3];
1543 // pick some fallbacks...
1544 if (dir
== 0 || dir
== 1) { // horz
1545 x
= a
.x() + a
.width() / 4;
1548 if (dir
== 0 || dir
== 2) { // vert
1549 y
= a
.y() + a
.height() / 4;
1555 if (dir
== 0 || dir
== 1) // horz
1557 if (dir
== 0 || dir
== 2) // vert
1560 if (!_max_horz
&& !_max_vert
)
1561 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1563 changeState(); // change the state hints on the client
1565 frame
->frameGravity(x
, y
); // figure out where the client should be going
1566 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1569 void Client::fullscreen(bool fs
, bool savearea
)
1571 static FunctionFlags saved_func
;
1572 static DecorationFlags saved_decor
;
1574 if (!(_functions
& Func_Fullscreen
) || // can't
1575 _fullscreen
== fs
) return; // already done
1578 changeState(); // change the state hints on the client
1580 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1583 // save the functions and remove them
1584 saved_func
= _functions
;
1585 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1586 // save the decorations and remove them
1587 saved_decor
= _decorations
;
1591 dimensions
[0] = _area
.x();
1592 dimensions
[1] = _area
.y();
1593 dimensions
[2] = _area
.width();
1594 dimensions
[3] = _area
.height();
1595 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1596 otk::Property::atoms
.cardinal
,
1597 (long unsigned*)dimensions
, 4);
1599 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1602 w
= info
->size().width();
1603 h
= info
->size().height();
1605 _functions
= saved_func
;
1606 _decorations
= saved_decor
;
1609 long unsigned n
= 4;
1611 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1612 otk::Property::atoms
.cardinal
, &n
,
1613 (long unsigned**) &dimensions
)) {
1622 // pick some fallbacks...
1623 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1624 x
= a
.x() + a
.width() / 4;
1625 y
= a
.y() + a
.height() / 4;
1631 changeAllowedActions(); // based on the new _functions
1633 // when fullscreening, don't obey things like increments, fill the screen
1634 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1636 // raise (back) into our stacking layer
1637 openbox
->screen(_screen
)->raiseWindow(this);
1639 // try focus us when we go into fullscreen mode
1643 void Client::iconify(bool iconic
, bool curdesk
)
1645 if (_iconic
== iconic
) return; // nothing to do
1648 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1654 _wmstate
= IconicState
;
1656 // we unmap the client itself so that we can get MapRequest events, and
1657 // because the ICCCM tells us to!
1658 XUnmapWindow(**otk::display
, _window
);
1661 setDesktop(openbox
->screen(_screen
)->desktop());
1662 _wmstate
= NormalState
;
1663 XMapWindow(**otk::display
, _window
);
1667 openbox
->screen(_screen
)->updateStruts();
1670 void Client::disableDecorations(DecorationFlags flags
)
1672 _disabled_decorations
= flags
;
1673 setupDecorAndFunctions();
1676 void Client::installColormap(bool install
) const
1678 XWindowAttributes wa
;
1679 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1681 XInstallColormap(**otk::display
, wa
.colormap
);
1683 XUninstallColormap(**otk::display
, wa
.colormap
);
1687 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1689 List::const_iterator it
, end
= node
->_transients
.end();
1692 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1693 if (*it
== skip
) continue; // circular?
1694 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1695 if ((*it
)->_modal
) return *it
; // got one
1700 Client
*Client::findModalChild()
1702 return searchModalTree(this, this);
1706 bool Client::focus()
1708 // if we have a modal child, then focus it, not us
1709 Client
*c
= findModalChild();
1710 if (c
) return c
->focus();
1712 // won't try focus if the client doesn't want it, or if the window isn't
1713 // visible on the screen
1714 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1716 if (_focused
) return true;
1718 // do a check to see if the window has already been unmapped or destroyed
1719 // do this intelligently while watching out for unmaps we've generated
1720 // (ignore_unmaps > 0)
1722 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1723 XPutBackEvent(**otk::display
, &ev
);
1726 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1727 if (ignore_unmaps
) {
1728 unmapHandler(ev
.xunmap
);
1730 XPutBackEvent(**otk::display
, &ev
);
1736 XSetInputFocus(**otk::display
, _window
,
1737 RevertToNone
, CurrentTime
);
1739 if (_focus_notify
) {
1741 ce
.xclient
.type
= ClientMessage
;
1742 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1743 ce
.xclient
.display
= **otk::display
;
1744 ce
.xclient
.window
= _window
;
1745 ce
.xclient
.format
= 32;
1746 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1747 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1748 ce
.xclient
.data
.l
[2] = 0l;
1749 ce
.xclient
.data
.l
[3] = 0l;
1750 ce
.xclient
.data
.l
[4] = 0l;
1751 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1754 XSync(**otk::display
, False
);
1759 void Client::unfocus() const
1761 if (!_focused
) return;
1763 assert(openbox
->focusedClient() == this);
1764 openbox
->setFocusedClient(0);
1768 void Client::focusHandler(const XFocusChangeEvent
&e
)
1771 // printf("FocusIn for 0x%lx\n", e.window);
1774 otk::EventHandler::focusHandler(e
);
1777 frame
->adjustFocus();
1779 calcLayer(); // focus state can affect the stacking layer
1781 openbox
->setFocusedClient(this);
1785 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1788 // printf("FocusOut for 0x%lx\n", e.window);
1791 otk::EventHandler::unfocusHandler(e
);
1794 frame
->adjustFocus();
1796 calcLayer(); // focus state can affect the stacking layer
1798 if (openbox
->focusedClient() == this)
1799 openbox
->setFocusedClient(0);
1803 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1806 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1809 otk::EventHandler::configureRequestHandler(ec
);
1812 XConfigureRequestEvent e
= ec
;
1814 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1816 // XXX if this causes bad things.. we can compress config req's with the
1818 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1819 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1820 e
.x
= ev
.xconfigurerequest
.x
;
1821 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1822 e
.y
= ev
.xconfigurerequest
.y
;
1823 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1824 e
.width
= ev
.xconfigurerequest
.width
;
1825 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1826 e
.height
= ev
.xconfigurerequest
.height
;
1827 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1828 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1829 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1830 e
.detail
= ev
.xconfigurerequest
.detail
;
1833 // if we are iconic (or shaded (fvwm does this)) ignore the event
1834 if (_iconic
|| _shaded
) return;
1836 if (e
.value_mask
& CWBorderWidth
)
1837 _border_width
= e
.border_width
;
1839 // resize, then move, as specified in the EWMH section 7.7
1840 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1841 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1842 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1846 case NorthEastGravity
:
1850 case SouthWestGravity
:
1852 corner
= BottomLeft
;
1854 case SouthEastGravity
:
1855 corner
= BottomRight
;
1857 default: // NorthWest, Static, etc
1861 // if moving AND resizing ...
1862 if (e
.value_mask
& (CWX
| CWY
)) {
1863 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1864 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1865 internal_resize(corner
, w
, h
, false, x
, y
);
1866 } else // if JUST resizing...
1867 internal_resize(corner
, w
, h
, false);
1868 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1869 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1870 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1871 internal_move(x
, y
);
1874 if (e
.value_mask
& CWStackMode
) {
1878 openbox
->screen(_screen
)->lowerWindow(this);
1884 openbox
->screen(_screen
)->raiseWindow(this);
1891 void Client::unmapHandler(const XUnmapEvent
&e
)
1893 if (ignore_unmaps
) {
1895 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1902 printf("UnmapNotify for 0x%lx\n", e
.window
);
1905 otk::EventHandler::unmapHandler(e
);
1907 // this deletes us etc
1908 openbox
->screen(_screen
)->unmanageWindow(this);
1912 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1915 printf("DestroyNotify for 0x%lx\n", e
.window
);
1918 otk::EventHandler::destroyHandler(e
);
1920 // this deletes us etc
1921 openbox
->screen(_screen
)->unmanageWindow(this);
1925 void Client::reparentHandler(const XReparentEvent
&e
)
1927 // this is when the client is first taken captive in the frame
1928 if (e
.parent
== frame
->plate()) return;
1931 printf("ReparentNotify for 0x%lx\n", e
.window
);
1934 otk::EventHandler::reparentHandler(e
);
1937 This event is quite rare and is usually handled in unmapHandler.
1938 However, if the window is unmapped when the reparent event occurs,
1939 the window manager never sees it because an unmap event is not sent
1940 to an already unmapped window.
1943 // we don't want the reparent event, put it back on the stack for the X
1944 // server to deal with after we unmanage the window
1947 XPutBackEvent(**otk::display
, &ev
);
1949 // this deletes us etc
1950 openbox
->screen(_screen
)->unmanageWindow(this);
1953 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1956 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1959 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1961 // move to the current desktop (uniconify)
1963 // XXX: should we focus/raise the window? (basically a net_wm_active_window)