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
;
449 if (!fs
&& _fullscreen
) {
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));
746 // set the default icon(s) XXX load these from the py
748 _icons
= new Icon
[1];
754 assert(_nicons
> 0); // there should always be a default..
756 if (frame
) frame
->adjustIcon();
759 void Client::propertyHandler(const XPropertyEvent
&e
)
761 otk::EventHandler::propertyHandler(e
);
763 // validate cuz we query stuff off the client here
764 if (!validate()) return;
766 // compress changes to a single property into a single change
768 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
769 // XXX: it would be nice to compress ALL changes to a property, not just
770 // changes in a row without other props between.
771 if (ce
.xproperty
.atom
!= e
.atom
) {
772 XPutBackEvent(**otk::display
, &ce
);
777 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
779 setupDecorAndFunctions(); // normal hints can make a window non-resizable
780 } else if (e
.atom
== XA_WM_HINTS
)
782 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
783 updateTransientFor();
785 calcLayer(); // type may have changed, so update the layer
786 setupDecorAndFunctions();
788 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
789 e
.atom
== otk::Property::atoms
.wm_name
)
791 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
792 e
.atom
== otk::Property::atoms
.wm_icon_name
)
794 else if (e
.atom
== otk::Property::atoms
.wm_class
)
796 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
798 setupDecorAndFunctions();
800 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
802 else if (e
.atom
== otk::Property::atoms
.net_wm_icon
)
806 void Client::setWMState(long state
)
808 if (state
== _wmstate
) return; // no change
820 void Client::setDesktop(unsigned int target
)
822 if (target
== _desktop
) return;
824 printf("Setting desktop %u\n", target
);
826 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
827 target
== 0xffffffff))
831 // set the desktop hint
832 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
833 otk::Property::atoms
.cardinal
, _desktop
);
834 frame
->adjustState(); // the frame can display the current desktop state
835 // 'move' the window to the new desktop
837 openbox
->screen(_screen
)->updateStruts();
840 void Client::showhide()
843 Screen
*s
= openbox
->screen(_screen
);
845 if (_iconic
) show
= false;
846 else if (!(_desktop
== s
->desktop() ||
847 _desktop
== 0xffffffff)) show
= false;
848 else if (normal() && s
->showingDesktop()) show
= false;
851 if (show
) frame
->show();
855 void Client::setState(StateAction action
, long data1
, long data2
)
857 bool shadestate
= _shaded
;
858 bool fsstate
= _fullscreen
;
859 bool maxh
= _max_horz
;
860 bool maxv
= _max_vert
;
862 if (!(action
== State_Add
|| action
== State_Remove
||
863 action
== State_Toggle
))
864 return; // an invalid action was passed to the client message, ignore it
866 for (int i
= 0; i
< 2; ++i
) {
867 Atom state
= i
== 0 ? data1
: data2
;
869 if (! state
) continue;
871 // if toggling, then pick whether we're adding or removing
872 if (action
== State_Toggle
) {
873 if (state
== otk::Property::atoms
.net_wm_state_modal
)
874 action
= _modal
? State_Remove
: State_Add
;
875 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
876 action
= _max_vert
? State_Remove
: State_Add
;
877 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
878 action
= _max_horz
? State_Remove
: State_Add
;
879 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
880 action
= _shaded
? State_Remove
: State_Add
;
881 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
882 action
= _skip_taskbar
? State_Remove
: State_Add
;
883 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
884 action
= _skip_pager
? State_Remove
: State_Add
;
885 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
886 action
= _fullscreen
? State_Remove
: State_Add
;
887 else if (state
== otk::Property::atoms
.net_wm_state_above
)
888 action
= _above
? State_Remove
: State_Add
;
889 else if (state
== otk::Property::atoms
.net_wm_state_below
)
890 action
= _below
? State_Remove
: State_Add
;
893 if (action
== State_Add
) {
894 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
895 if (_modal
) continue;
897 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
899 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
900 if (_max_horz
) continue;
902 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
904 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
905 _skip_taskbar
= true;
906 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
908 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
910 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
911 if (_above
) continue;
913 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
914 if (_below
) continue;
918 } else { // action == State_Remove
919 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
920 if (!_modal
) continue;
922 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
924 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
926 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
928 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
929 _skip_taskbar
= false;
930 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
932 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
934 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
935 if (!_above
) continue;
937 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
938 if (!_below
) continue;
943 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
944 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
945 if (maxh
== maxv
) { // both going the same way
946 maximize(maxh
, 0, true);
948 maximize(maxh
, 1, true);
949 maximize(maxv
, 2, true);
951 } else { // toggling one
952 if (maxh
!= _max_horz
)
953 maximize(maxh
, 1, true);
955 maximize(maxv
, 2, true);
958 // change fullscreen state before shading, as it will affect if the window
960 if (fsstate
!= _fullscreen
)
961 fullscreen(fsstate
, true);
962 if (shadestate
!= _shaded
)
965 changeState(); // change the hint to relect these changes
968 void Client::toggleClientBorder(bool addborder
)
970 // adjust our idea of where the client is, based on its border. When the
971 // border is removed, the client should now be considered to be in a
972 // different position.
973 // when re-adding the border to the client, the same operation needs to be
975 int oldx
= _area
.x(), oldy
= _area
.y();
976 int x
= oldx
, y
= oldy
;
979 case NorthWestGravity
:
981 case SouthWestGravity
:
983 case NorthEastGravity
:
985 case SouthEastGravity
:
986 if (addborder
) x
-= _border_width
* 2;
987 else x
+= _border_width
* 2;
994 if (addborder
) x
-= _border_width
;
995 else x
+= _border_width
;
1000 case NorthWestGravity
:
1002 case NorthEastGravity
:
1004 case SouthWestGravity
:
1006 case SouthEastGravity
:
1007 if (addborder
) y
-= _border_width
* 2;
1008 else y
+= _border_width
* 2;
1015 if (addborder
) y
-= _border_width
;
1016 else y
+= _border_width
;
1019 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1022 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1024 // move the client so it is back it the right spot _with_ its border!
1025 if (x
!= oldx
|| y
!= oldy
)
1026 XMoveWindow(**otk::display
, _window
, x
, y
);
1028 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1031 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1033 otk::EventHandler::clientMessageHandler(e
);
1035 // validate cuz we query stuff off the client here
1036 if (!validate()) return;
1038 if (e
.format
!= 32) return;
1040 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1041 // compress changes into a single change
1042 bool compress
= false;
1044 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1045 // XXX: it would be nice to compress ALL messages of a type, not just
1046 // messages in a row without other message types between.
1047 if (ce
.xclient
.message_type
!= e
.message_type
) {
1048 XPutBackEvent(**otk::display
, &ce
);
1054 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1056 setWMState(e
.data
.l
[0]); // use the original event
1057 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1058 // compress changes into a single change
1059 bool compress
= false;
1061 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1062 // XXX: it would be nice to compress ALL messages of a type, not just
1063 // messages in a row without other message types between.
1064 if (ce
.xclient
.message_type
!= e
.message_type
) {
1065 XPutBackEvent(**otk::display
, &ce
);
1071 setDesktop(e
.data
.l
[0]); // use the found event
1073 setDesktop(e
.data
.l
[0]); // use the original event
1074 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1075 // can't compress these
1077 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1078 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1079 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1080 e
.data
.l
[1], e
.data
.l
[2], _window
);
1082 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1083 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1085 printf("net_close_window for 0x%lx\n", _window
);
1088 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1090 printf("net_active_window for 0x%lx\n", _window
);
1092 if (openbox
->screen(_screen
)->showingDesktop())
1093 openbox
->screen(_screen
)->showDesktop(false);
1096 else if (!frame
->visible()) // if its not visible for other reasons, then
1097 return; // don't mess with it
1101 openbox
->screen(_screen
)->raiseWindow(this);
1102 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1103 if (openbox
->screen(_screen
)->showingDesktop())
1104 openbox
->screen(_screen
)->showDesktop(false);
1107 else if (!frame
->visible()) // if its not visible for other reasons, then
1108 return; // don't mess with it
1109 if (e
.data
.l
[0] && _shaded
)
1113 openbox
->screen(_screen
)->raiseWindow(this);
1118 void Client::shapeHandler(const XShapeEvent
&e
)
1120 otk::EventHandler::shapeHandler(e
);
1122 if (e
.kind
== ShapeBounding
) {
1124 frame
->adjustShape();
1129 void Client::resize(Corner anchor
, int w
, int h
)
1131 if (!(_functions
& Func_Resize
)) return;
1132 internal_resize(anchor
, w
, h
);
1135 void Client::internal_resize(Corner anchor
, int w
, int h
,
1136 bool user
, int x
, int y
)
1138 w
-= _base_size
.width();
1139 h
-= _base_size
.height();
1142 // for interactive resizing. have to move half an increment in each
1144 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1145 int mh
= h
% _size_inc
.height();
1146 int aw
= _size_inc
.width() / 2; // amount to add
1147 int ah
= _size_inc
.height() / 2;
1148 // don't let us move into a new size increment
1149 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1150 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1154 // if this is a user-requested resize, then check against min/max sizes
1155 // and aspect ratios
1157 // smaller than min size or bigger than max size?
1158 if (w
> _max_size
.width()) w
= _max_size
.width();
1159 if (w
< _min_size
.width()) w
= _min_size
.width();
1160 if (h
> _max_size
.height()) h
= _max_size
.height();
1161 if (h
< _min_size
.height()) h
= _min_size
.height();
1163 // adjust the height ot match the width for the aspect ratios
1165 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1167 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1170 // keep to the increments
1171 w
/= _size_inc
.width();
1172 h
/= _size_inc
.height();
1174 // you cannot resize to nothing
1178 // store the logical size
1179 _logical_size
= otk::Size(w
, h
);
1181 w
*= _size_inc
.width();
1182 h
*= _size_inc
.height();
1184 w
+= _base_size
.width();
1185 h
+= _base_size
.height();
1187 if (x
== INT_MIN
|| y
== INT_MIN
) {
1194 x
-= w
- _area
.width();
1197 y
-= h
- _area
.height();
1200 x
-= w
- _area
.width();
1201 y
-= h
- _area
.height();
1206 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1208 XResizeWindow(**otk::display
, _window
, w
, h
);
1210 // resize the frame to match the request
1211 frame
->adjustSize();
1212 internal_move(x
, y
);
1215 const Icon
*Client::icon(const otk::Size
&s
) const
1217 unsigned long req
= s
.width() * s
.height();
1218 // si is the smallest image >= req
1219 // li is the largest image < req
1220 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1222 assert(_nicons
> 0); // there should always be a default..
1223 for (int i
= 0; i
< _nicons
; ++i
) {
1224 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1225 if (size
< smallest
&& size
>= req
) {
1229 if (size
> largest
&& size
<= req
) {
1234 if (smallest
== 0xffffffff) // didnt find one bigger than us...
1239 void Client::move(int x
, int y
)
1241 if (!(_functions
& Func_Move
)) return;
1242 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1244 internal_move(x
, y
);
1247 void Client::internal_move(int x
, int y
)
1249 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1251 // move the frame to be in the requested position
1252 if (frame
) { // this can be called while mapping, before frame exists
1253 frame
->adjustPosition();
1255 // send synthetic configure notify (we don't need to if we aren't mapped
1258 event
.type
= ConfigureNotify
;
1259 event
.xconfigure
.display
= **otk::display
;
1260 event
.xconfigure
.event
= _window
;
1261 event
.xconfigure
.window
= _window
;
1263 // root window coords with border in mind
1264 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1265 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1267 event
.xconfigure
.width
= _area
.width();
1268 event
.xconfigure
.height
= _area
.height();
1269 event
.xconfigure
.border_width
= _border_width
;
1270 event
.xconfigure
.above
= frame
->plate();
1271 event
.xconfigure
.override_redirect
= False
;
1272 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1273 StructureNotifyMask
, &event
);
1275 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1276 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1277 event
.xconfigure
.height
, event
.xconfigure
.window
);
1282 void Client::close()
1286 if (!(_functions
& Func_Close
)) return;
1288 // XXX: itd be cool to do timeouts and shit here for killing the client's
1290 // like... if the window is around after 5 seconds, then the close button
1291 // turns a nice red, and if this function is called again, the client is
1292 // explicitly killed.
1294 ce
.xclient
.type
= ClientMessage
;
1295 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1296 ce
.xclient
.display
= **otk::display
;
1297 ce
.xclient
.window
= _window
;
1298 ce
.xclient
.format
= 32;
1299 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1300 ce
.xclient
.data
.l
[1] = CurrentTime
;
1301 ce
.xclient
.data
.l
[2] = 0l;
1302 ce
.xclient
.data
.l
[3] = 0l;
1303 ce
.xclient
.data
.l
[4] = 0l;
1304 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1307 void Client::changeState()
1309 unsigned long state
[2];
1310 state
[0] = _wmstate
;
1312 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1313 otk::Property::atoms
.wm_state
, state
, 2);
1318 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1320 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1322 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1324 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1326 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1328 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1330 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1332 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1334 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1336 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1337 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1338 otk::Property::atoms
.atom
, netstate
, num
);
1343 frame
->adjustState();
1346 void Client::changeAllowedActions(void)
1351 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1353 if (_functions
& Func_Shade
)
1354 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1355 if (_functions
& Func_Close
)
1356 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1357 if (_functions
& Func_Move
)
1358 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1359 if (_functions
& Func_Iconify
)
1360 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1361 if (_functions
& Func_Resize
)
1362 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1363 if (_functions
& Func_Fullscreen
)
1364 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1365 if (_functions
& Func_Maximize
) {
1366 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1367 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1370 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1371 otk::Property::atoms
.atom
, actions
, num
);
1373 // make sure the window isn't breaking any rules now
1375 if (!(_functions
& Func_Shade
) && _shaded
)
1376 if (frame
) shade(false);
1377 else _shaded
= false;
1378 if (!(_functions
& Func_Iconify
) && _iconic
)
1379 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1380 else _iconic
= false;
1381 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1382 if (frame
) fullscreen(false);
1383 else _fullscreen
= false;
1384 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1385 if (frame
) maximize(false, 0);
1386 else _max_vert
= _max_horz
= false;
1389 void Client::remaximize()
1392 if (_max_horz
&& _max_vert
)
1399 return; // not maximized
1400 _max_horz
= _max_vert
= false;
1401 maximize(true, dir
, false);
1404 void Client::applyStartupState()
1406 // these are in a carefully crafted order..
1413 _fullscreen
= false;
1414 fullscreen(true, false);
1423 if (_max_vert
&& _max_horz
) {
1424 _max_vert
= _max_horz
= false;
1425 maximize(true, 0, false);
1426 } else if (_max_vert
) {
1428 maximize(true, 2, false);
1429 } else if (_max_horz
) {
1431 maximize(true, 1, false);
1434 if (_skip_taskbar
); // nothing to do for this
1435 if (_skip_pager
); // nothing to do for this
1436 if (_modal
); // nothing to do for this
1437 if (_above
); // nothing to do for this
1438 if (_below
); // nothing to do for this
1441 void Client::fireUrgent()
1443 // call the python UrgentWindow callbacks
1444 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1445 openbox
->bindings()->fireEvent(&data
);
1448 void Client::shade(bool shade
)
1450 if (!(_functions
& Func_Shade
) || // can't
1451 _shaded
== shade
) return; // already done
1453 // when we're iconic, don't change the wmstate
1455 _wmstate
= shade
? IconicState
: NormalState
;
1458 frame
->adjustSize();
1461 void Client::maximize(bool max
, int dir
, bool savearea
)
1463 assert(dir
== 0 || dir
== 1 || dir
== 2);
1464 if (!(_functions
& Func_Maximize
)) return; // can't
1466 // check if already done
1468 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1469 if (dir
== 1 && _max_horz
) return;
1470 if (dir
== 2 && _max_vert
) return;
1472 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1473 if (dir
== 1 && !_max_horz
) return;
1474 if (dir
== 2 && !_max_vert
) return;
1477 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1478 int x
= frame
->area().x(), y
= frame
->area().y(),
1479 w
= _area
.width(), h
= _area
.height();
1485 unsigned long n
= 4;
1492 // get the property off the window and use it for the dimentions we are
1494 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1495 otk::Property::atoms
.cardinal
, &n
,
1496 (long unsigned**) &readdim
)) {
1499 dimensions
[0] = readdim
[0];
1500 dimensions
[2] = readdim
[2];
1503 dimensions
[1] = readdim
[1];
1504 dimensions
[3] = readdim
[3];
1510 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1511 otk::Property::atoms
.cardinal
,
1512 (long unsigned*)dimensions
, 4);
1514 if (dir
== 0 || dir
== 1) { // horz
1518 if (dir
== 0 || dir
== 2) { // vert
1520 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1524 long unsigned n
= 4;
1526 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1527 otk::Property::atoms
.cardinal
, &n
,
1528 (long unsigned**) &dimensions
)) {
1530 if (dir
== 0 || dir
== 1) { // horz
1531 x
= (signed int)dimensions
[0];
1532 w
= (signed int)dimensions
[2];
1534 if (dir
== 0 || dir
== 2) { // vert
1535 y
= (signed int)dimensions
[1];
1536 h
= (signed int)dimensions
[3];
1541 // pick some fallbacks...
1542 if (dir
== 0 || dir
== 1) { // horz
1543 x
= a
.x() + a
.width() / 4;
1546 if (dir
== 0 || dir
== 2) { // vert
1547 y
= a
.y() + a
.height() / 4;
1553 if (dir
== 0 || dir
== 1) // horz
1555 if (dir
== 0 || dir
== 2) // vert
1558 if (!_max_horz
&& !_max_vert
)
1559 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1561 changeState(); // change the state hints on the client
1563 frame
->frameGravity(x
, y
); // figure out where the client should be going
1564 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1567 void Client::fullscreen(bool fs
, bool savearea
)
1569 static FunctionFlags saved_func
;
1570 static DecorationFlags saved_decor
;
1572 if (!(_functions
& Func_Fullscreen
) || // can't
1573 _fullscreen
== fs
) return; // already done
1576 changeState(); // change the state hints on the client
1578 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1581 // save the functions and remove them
1582 saved_func
= _functions
;
1583 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1584 // save the decorations and remove them
1585 saved_decor
= _decorations
;
1589 dimensions
[0] = _area
.x();
1590 dimensions
[1] = _area
.y();
1591 dimensions
[2] = _area
.width();
1592 dimensions
[3] = _area
.height();
1593 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1594 otk::Property::atoms
.cardinal
,
1595 (long unsigned*)dimensions
, 4);
1597 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1600 w
= info
->size().width();
1601 h
= info
->size().height();
1603 _functions
= saved_func
;
1604 _decorations
= saved_decor
;
1607 long unsigned n
= 4;
1609 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1610 otk::Property::atoms
.cardinal
, &n
,
1611 (long unsigned**) &dimensions
)) {
1620 // pick some fallbacks...
1621 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1622 x
= a
.x() + a
.width() / 4;
1623 y
= a
.y() + a
.height() / 4;
1629 changeAllowedActions(); // based on the new _functions
1631 // when fullscreening, don't obey things like increments, fill the screen
1632 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1634 // raise (back) into our stacking layer
1635 openbox
->screen(_screen
)->raiseWindow(this);
1637 // try focus us when we go into fullscreen mode
1641 void Client::iconify(bool iconic
, bool curdesk
)
1643 if (_iconic
== iconic
) return; // nothing to do
1646 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1652 _wmstate
= IconicState
;
1654 // we unmap the client itself so that we can get MapRequest events, and
1655 // because the ICCCM tells us to!
1656 XUnmapWindow(**otk::display
, _window
);
1659 setDesktop(openbox
->screen(_screen
)->desktop());
1660 _wmstate
= NormalState
;
1661 XMapWindow(**otk::display
, _window
);
1665 openbox
->screen(_screen
)->updateStruts();
1668 void Client::disableDecorations(DecorationFlags flags
)
1670 _disabled_decorations
= flags
;
1671 setupDecorAndFunctions();
1674 void Client::installColormap(bool install
) const
1676 XWindowAttributes wa
;
1677 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1679 XInstallColormap(**otk::display
, wa
.colormap
);
1681 XUninstallColormap(**otk::display
, wa
.colormap
);
1685 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1687 List::const_iterator it
, end
= node
->_transients
.end();
1690 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1691 if (*it
== skip
) continue; // circular?
1692 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1693 if ((*it
)->_modal
) return *it
; // got one
1698 Client
*Client::findModalChild()
1700 return searchModalTree(this, this);
1704 bool Client::focus()
1706 // if we have a modal child, then focus it, not us
1707 Client
*c
= findModalChild();
1708 if (c
) return c
->focus();
1710 // won't try focus if the client doesn't want it, or if the window isn't
1711 // visible on the screen
1712 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1714 if (_focused
) return true;
1716 // do a check to see if the window has already been unmapped or destroyed
1717 // do this intelligently while watching out for unmaps we've generated
1718 // (ignore_unmaps > 0)
1720 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1721 XPutBackEvent(**otk::display
, &ev
);
1724 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1725 if (ignore_unmaps
) {
1726 unmapHandler(ev
.xunmap
);
1728 XPutBackEvent(**otk::display
, &ev
);
1734 XSetInputFocus(**otk::display
, _window
,
1735 RevertToNone
, CurrentTime
);
1737 if (_focus_notify
) {
1739 ce
.xclient
.type
= ClientMessage
;
1740 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1741 ce
.xclient
.display
= **otk::display
;
1742 ce
.xclient
.window
= _window
;
1743 ce
.xclient
.format
= 32;
1744 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1745 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1746 ce
.xclient
.data
.l
[2] = 0l;
1747 ce
.xclient
.data
.l
[3] = 0l;
1748 ce
.xclient
.data
.l
[4] = 0l;
1749 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1752 XSync(**otk::display
, False
);
1757 void Client::unfocus() const
1759 if (!_focused
) return;
1761 assert(openbox
->focusedClient() == this);
1762 openbox
->setFocusedClient(0);
1766 void Client::focusHandler(const XFocusChangeEvent
&e
)
1769 // printf("FocusIn for 0x%lx\n", e.window);
1772 otk::EventHandler::focusHandler(e
);
1775 frame
->adjustFocus();
1777 calcLayer(); // focus state can affect the stacking layer
1779 openbox
->setFocusedClient(this);
1783 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1786 // printf("FocusOut for 0x%lx\n", e.window);
1789 otk::EventHandler::unfocusHandler(e
);
1792 frame
->adjustFocus();
1794 calcLayer(); // focus state can affect the stacking layer
1796 if (openbox
->focusedClient() == this)
1797 openbox
->setFocusedClient(0);
1801 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1804 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1807 otk::EventHandler::configureRequestHandler(ec
);
1810 XConfigureRequestEvent e
= ec
;
1812 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1814 // XXX if this causes bad things.. we can compress config req's with the
1816 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1817 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1818 e
.x
= ev
.xconfigurerequest
.x
;
1819 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1820 e
.y
= ev
.xconfigurerequest
.y
;
1821 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1822 e
.width
= ev
.xconfigurerequest
.width
;
1823 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1824 e
.height
= ev
.xconfigurerequest
.height
;
1825 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1826 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1827 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1828 e
.detail
= ev
.xconfigurerequest
.detail
;
1831 // if we are iconic (or shaded (fvwm does this)) ignore the event
1832 if (_iconic
|| _shaded
) return;
1834 if (e
.value_mask
& CWBorderWidth
)
1835 _border_width
= e
.border_width
;
1837 // resize, then move, as specified in the EWMH section 7.7
1838 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1839 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1840 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1844 case NorthEastGravity
:
1848 case SouthWestGravity
:
1850 corner
= BottomLeft
;
1852 case SouthEastGravity
:
1853 corner
= BottomRight
;
1855 default: // NorthWest, Static, etc
1859 // if moving AND resizing ...
1860 if (e
.value_mask
& (CWX
| CWY
)) {
1861 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1862 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1863 internal_resize(corner
, w
, h
, false, x
, y
);
1864 } else // if JUST resizing...
1865 internal_resize(corner
, w
, h
, false);
1866 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1867 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1868 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1869 internal_move(x
, y
);
1872 if (e
.value_mask
& CWStackMode
) {
1876 openbox
->screen(_screen
)->lowerWindow(this);
1882 openbox
->screen(_screen
)->raiseWindow(this);
1889 void Client::unmapHandler(const XUnmapEvent
&e
)
1891 if (ignore_unmaps
) {
1893 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1900 printf("UnmapNotify for 0x%lx\n", e
.window
);
1903 otk::EventHandler::unmapHandler(e
);
1905 // this deletes us etc
1906 openbox
->screen(_screen
)->unmanageWindow(this);
1910 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1913 printf("DestroyNotify for 0x%lx\n", e
.window
);
1916 otk::EventHandler::destroyHandler(e
);
1918 // this deletes us etc
1919 openbox
->screen(_screen
)->unmanageWindow(this);
1923 void Client::reparentHandler(const XReparentEvent
&e
)
1925 // this is when the client is first taken captive in the frame
1926 if (e
.parent
== frame
->plate()) return;
1929 printf("ReparentNotify for 0x%lx\n", e
.window
);
1932 otk::EventHandler::reparentHandler(e
);
1935 This event is quite rare and is usually handled in unmapHandler.
1936 However, if the window is unmapped when the reparent event occurs,
1937 the window manager never sees it because an unmap event is not sent
1938 to an already unmapped window.
1941 // we don't want the reparent event, put it back on the stack for the X
1942 // server to deal with after we unmanage the window
1945 XPutBackEvent(**otk::display
, &ev
);
1947 // this deletes us etc
1948 openbox
->screen(_screen
)->unmanageWindow(this);
1951 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1954 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1957 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1959 // move to the current desktop (uniconify)
1961 // XXX: should we focus/raise the window? (basically a net_wm_active_window)