1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
10 #include "otk/display.hh"
11 #include "otk/property.hh"
15 #include <X11/Xutil.h>
16 #include <X11/Xatom.h>
19 #define _(str) gettext(str)
28 Client::Client(int screen
, Window window
)
29 : otk::EventHandler(),
30 frame(0), _screen(screen
), _window(window
)
37 // update EVERYTHING the first time!!
40 _wmstate
= NormalState
;
43 _layer
= Layer_Normal
;
46 _disabled_decorations
= 0;
52 getState(); // do this before updateTransientFor! (for _modal)
57 getType(); // this can change the mwmhints for special cases
61 getGravity(); // get the attribute gravity
62 updateNormalHints(); // this may override the attribute gravity
64 // got the type, the mwmhints, the protocols, and the normal hints (min/max
65 // sizes), so we're ready to set up
66 // the decorations/functions
67 setupDecorAndFunctions();
69 // also get the initial_state and set _iconic if we aren't "starting"
70 // when we're "starting" that means we should use whatever state was already
71 // on the window over the initial map state, because it was already mapped
72 updateWMHints(openbox
->state() != Openbox::State_Starting
);
78 // this makes sure that these windows appear on all desktops
79 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
80 _desktop
= 0xffffffff;
82 // set the desktop hint, to make sure that it always exists, and to reflect
83 // any changes we've made here
84 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
85 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
93 // clean up childrens' references
94 while (!_transients
.empty()) {
95 _transients
.front()->_transient_for
= 0;
96 _transients
.pop_front();
99 // clean up parents reference to this
101 _transient_for
->_transients
.remove(this); // remove from old parent
103 if (openbox
->state() != Openbox::State_Exiting
) {
104 // these values should not be persisted across a window unmapping/mapping
105 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
106 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
108 // if we're left in an iconic state, the client wont be mapped. this is
109 // bad, since we will no longer be managing the window on restart
111 XMapWindow(**otk::display
, _window
);
116 bool Client::validate() const
118 XSync(**otk::display
, false); // get all events on the server
121 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
122 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
123 XPutBackEvent(**otk::display
, &e
);
131 void Client::getGravity()
133 XWindowAttributes wattrib
;
136 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
137 assert(ret
!= BadWindow
);
138 _gravity
= wattrib
.win_gravity
;
142 void Client::getDesktop()
144 // defaults to the current desktop
145 _desktop
= openbox
->screen(_screen
)->desktop();
147 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
148 otk::Property::atoms
.cardinal
,
149 (long unsigned*)&_desktop
)) {
151 // printf("Window requested desktop: %ld\n", _desktop);
157 void Client::getType()
159 _type
= (WindowType
) -1;
162 unsigned long num
= (unsigned) -1;
163 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
164 otk::Property::atoms
.atom
, &num
, &val
)) {
165 // use the first value that we know about in the array
166 for (unsigned long i
= 0; i
< num
; ++i
) {
167 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
168 _type
= Type_Desktop
;
169 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
171 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
172 _type
= Type_Toolbar
;
173 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
175 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
176 _type
= Type_Utility
;
177 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
179 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
181 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
183 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
184 // prevent this window from getting any decor or functionality
185 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
186 _mwmhints
.decorations
= 0;
187 _mwmhints
.functions
= 0;
189 if (_type
!= (WindowType
) -1)
190 break; // grab the first known type
195 if (_type
== (WindowType
) -1) {
197 * the window type hint was not set, which means we either classify ourself
198 * as a normal window or a dialog, depending on if we are a transient.
208 void Client::setupDecorAndFunctions()
210 // start with everything (cept fullscreen)
211 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
212 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
213 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
215 if (_delete_window
) {
216 _decorations
|= Decor_Close
;
217 _functions
|= Func_Close
;
220 if (!(_min_size
.width() < _max_size
.width() ||
221 _min_size
.height() < _max_size
.height())) {
222 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
223 _functions
&= ~(Func_Resize
| Func_Maximize
);
228 // normal windows retain all of the possible decorations and
229 // functionality, and are the only windows that you can fullscreen
230 _functions
|= Func_Fullscreen
;
234 // dialogs cannot be maximized
235 _decorations
&= ~Decor_Maximize
;
236 _functions
&= ~Func_Maximize
;
242 // these windows get less functionality
243 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
244 _functions
&= ~(Func_Iconify
| Func_Resize
);
250 // none of these windows are manipulated by the window manager
256 // Mwm Hints are applied subtractively to what has already been chosen for
257 // decor and functionality
258 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
259 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
260 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
261 _decorations
&= ~Decor_Border
;
262 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
263 _decorations
&= ~Decor_Handle
;
264 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
265 _decorations
&= ~Decor_Titlebar
;
266 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
267 _decorations
&= ~Decor_Iconify
;
268 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
269 _decorations
&= ~Decor_Maximize
;
273 if (_mwmhints
.flags
& MwmFlag_Functions
) {
274 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
275 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
276 _functions
&= ~Func_Resize
;
277 if (! (_mwmhints
.functions
& MwmFunc_Move
))
278 _functions
&= ~Func_Move
;
279 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
280 _functions
&= ~Func_Iconify
;
281 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
282 _functions
&= ~Func_Maximize
;
283 // dont let mwm hints kill the close button
284 //if (! (_mwmhints.functions & MwmFunc_Close))
285 // _functions &= ~Func_Close;
289 // can't maximize without moving/resizing
290 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
291 _functions
&= ~Func_Maximize
;
293 // finally, user specified disabled decorations are applied to subtract
295 if (_disabled_decorations
& Decor_Titlebar
)
296 _decorations
&= ~Decor_Titlebar
;
297 if (_disabled_decorations
& Decor_Handle
)
298 _decorations
&= ~Decor_Handle
;
299 if (_disabled_decorations
& Decor_Border
)
300 _decorations
&= ~Decor_Border
;
301 if (_disabled_decorations
& Decor_Iconify
)
302 _decorations
&= ~Decor_Iconify
;
303 if (_disabled_decorations
& Decor_Maximize
)
304 _decorations
&= ~Decor_Maximize
;
305 if (_disabled_decorations
& Decor_AllDesktops
)
306 _decorations
&= ~Decor_AllDesktops
;
307 if (_disabled_decorations
& Decor_Close
)
308 _decorations
&= ~Decor_Close
;
310 // if we don't have a titlebar, then we cannot shade!
311 if (!(_decorations
& Decor_Titlebar
))
312 _functions
&= ~Func_Shade
;
314 changeAllowedActions();
317 frame
->adjustSize(); // change the decors on the frame
318 frame
->adjustPosition(); // with more/less decorations, we may need to be
320 remaximize(); // with new decor, the window's maximized size may change
325 void Client::getMwmHints()
327 unsigned long num
= MwmHints::elements
;
328 unsigned long *hints
;
330 _mwmhints
.flags
= 0; // default to none
332 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
333 otk::Property::atoms
.motif_wm_hints
, &num
,
334 (unsigned long **)&hints
))
337 if (num
>= MwmHints::elements
) {
338 // retrieved the hints
339 _mwmhints
.flags
= hints
[0];
340 _mwmhints
.functions
= hints
[1];
341 _mwmhints
.decorations
= hints
[2];
348 void Client::getArea()
350 XWindowAttributes wattrib
;
353 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
354 assert(ret
!= BadWindow
);
356 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
357 _border_width
= wattrib
.border_width
;
361 void Client::getState()
363 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
364 _iconic
= _skip_taskbar
= _skip_pager
= false;
366 unsigned long *state
;
367 unsigned long num
= (unsigned) -1;
369 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
370 otk::Property::atoms
.atom
, &num
, &state
)) {
371 for (unsigned long i
= 0; i
< num
; ++i
) {
372 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
374 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
376 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
378 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
379 _skip_taskbar
= true;
380 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
382 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
384 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
386 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
388 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
390 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
399 void Client::getShaped()
403 if (otk::display
->shape()) {
408 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
410 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
411 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
418 void Client::calcLayer() {
421 if (_iconic
) l
= Layer_Icon
;
422 else if (_fullscreen
) l
= Layer_Fullscreen
;
423 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
424 else if (_type
== Type_Dock
) {
425 if (!_below
) l
= Layer_Top
;
426 else l
= Layer_Normal
;
428 else if (_above
) l
= Layer_Above
;
429 else if (_below
) l
= Layer_Below
;
430 else l
= Layer_Normal
;
436 if we don't have a frame, then we aren't mapped yet (and this would
439 openbox
->screen(_screen
)->raiseWindow(this);
445 void Client::updateProtocols()
450 _focus_notify
= false;
451 _delete_window
= false;
453 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
454 for (int i
= 0; i
< num_return
; ++i
) {
455 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
456 // this means we can request the window to close
457 _delete_window
= true;
458 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
459 // if this protocol is requested, then the window will be notified
460 // by the window manager whenever it receives focus
461 _focus_notify
= true;
468 void Client::updateNormalHints()
472 int oldgravity
= _gravity
;
477 _size_inc
= otk::Size(1, 1);
478 _base_size
= otk::Size(0, 0);
479 _min_size
= otk::Size(0, 0);
480 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
482 // get the hints from the window
483 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
484 _positioned
= (size
.flags
& (PPosition
|USPosition
));
486 if (size
.flags
& PWinGravity
) {
487 _gravity
= size
.win_gravity
;
489 // if the client has a frame, i.e. has already been mapped and is
490 // changing its gravity
491 if (frame
&& _gravity
!= oldgravity
) {
492 // move our idea of the client's position based on its new gravity
493 int x
= frame
->area().x(), y
= frame
->area().y();
494 frame
->frameGravity(x
, y
);
495 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
499 if (size
.flags
& PAspect
) {
500 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
501 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
504 if (size
.flags
& PMinSize
)
505 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
507 if (size
.flags
& PMaxSize
)
508 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
510 if (size
.flags
& PBaseSize
)
511 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
513 if (size
.flags
& PResizeInc
)
514 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
519 void Client::updateWMHints(bool initstate
)
523 // assume a window takes input if it doesnt specify
527 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
528 if (hints
->flags
& InputHint
)
529 _can_focus
= hints
->input
;
531 // only do this when initstate is true!
532 if (initstate
&& (hints
->flags
& StateHint
))
533 _iconic
= hints
->initial_state
== IconicState
;
535 if (hints
->flags
& XUrgencyHint
)
538 if (hints
->flags
& WindowGroupHint
) {
539 if (hints
->window_group
!= _group
) {
540 // XXX: remove from the old group if there was one
541 _group
= hints
->window_group
;
542 // XXX: do stuff with the group
553 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
554 (long)_window
, _urgent
? "ON" : "OFF");
556 // fire the urgent callback if we're mapped, otherwise, wait until after
564 void Client::updateTitle()
569 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
570 otk::Property::utf8
, &_title
)) {
572 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
573 otk::Property::ascii
, &_title
);
577 _title
= _("Unnamed Window");
580 frame
->adjustTitle();
584 void Client::updateIconTitle()
589 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
590 otk::Property::utf8
, &_icon_title
)) {
592 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
593 otk::Property::ascii
, &_icon_title
);
597 _icon_title
= _("Unnamed Window");
601 void Client::updateClass()
604 _app_name
= _app_class
= _role
= "";
606 otk::Property::StringVect v
;
607 unsigned long num
= 2;
609 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
610 otk::Property::ascii
, &num
, &v
)) {
611 if (num
> 0) _app_name
= v
[0].c_str();
612 if (num
> 1) _app_class
= v
[1].c_str();
617 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
618 otk::Property::ascii
, &num
, &v
)) {
619 if (num
> 0) _role
= v
[0].c_str();
624 void Client::updateStrut()
626 unsigned long num
= 4;
628 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
629 otk::Property::atoms
.cardinal
, &num
, &data
))
633 _strut
.left
= data
[0];
634 _strut
.right
= data
[1];
635 _strut
.top
= data
[2];
636 _strut
.bottom
= data
[3];
638 // updating here is pointless while we're being mapped cuz we're not in
639 // the screen's client list yet
641 openbox
->screen(_screen
)->updateStruts();
648 void Client::updateTransientFor()
653 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
654 t
!= _window
) { // cant be transient to itself!
655 c
= openbox
->findClient(t
);
656 assert(c
!= this); // if this happens then we need to check for it
658 if (!c
/*XXX: && _group*/) {
659 // not transient to a client, see if it is transient for a group
660 if (//t == _group->leader() ||
662 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
663 // window is a transient for its group!
664 // XXX: for now this is treated as non-transient.
665 // this needs to be fixed!
670 // if anything has changed...
671 if (c
!= _transient_for
) {
673 _transient_for
->_transients
.remove(this); // remove from old parent
676 _transient_for
->_transients
.push_back(this); // add to new parent
681 void Client::propertyHandler(const XPropertyEvent
&e
)
683 otk::EventHandler::propertyHandler(e
);
685 // validate cuz we query stuff off the client here
686 if (!validate()) return;
688 // compress changes to a single property into a single change
690 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
691 // XXX: it would be nice to compress ALL changes to a property, not just
692 // changes in a row without other props between.
693 if (ce
.xproperty
.atom
!= e
.atom
) {
694 XPutBackEvent(**otk::display
, &ce
);
699 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
701 setupDecorAndFunctions(); // normal hints can make a window non-resizable
702 } else if (e
.atom
== XA_WM_HINTS
)
704 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
705 updateTransientFor();
707 calcLayer(); // type may have changed, so update the layer
708 setupDecorAndFunctions();
710 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
711 e
.atom
== otk::Property::atoms
.wm_name
)
713 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
714 e
.atom
== otk::Property::atoms
.wm_icon_name
)
716 else if (e
.atom
== otk::Property::atoms
.wm_class
)
718 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
720 setupDecorAndFunctions();
722 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
727 void Client::setWMState(long state
)
729 if (state
== _wmstate
) return; // no change
742 void Client::setDesktop(unsigned int target
)
744 if (target
== _desktop
) return;
746 printf("Setting desktop %u\n", target
);
748 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
749 target
== 0xffffffff))
754 // set the desktop hint
755 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
756 otk::Property::atoms
.cardinal
, _desktop
);
758 // 'move' the window to the new desktop
761 openbox
->screen(_screen
)->updateStruts();
765 void Client::showhide()
768 Screen
*s
= openbox
->screen(_screen
);
770 if (_iconic
) show
= false;
771 else if (!(_desktop
== s
->desktop() ||
772 _desktop
== 0xffffffff)) show
= false;
773 else if (normal() && s
->showingDesktop()) show
= false;
776 if (show
) frame
->show();
781 void Client::setState(StateAction action
, long data1
, long data2
)
783 bool shadestate
= _shaded
;
784 bool fsstate
= _fullscreen
;
785 bool maxh
= _max_horz
;
786 bool maxv
= _max_vert
;
788 if (!(action
== State_Add
|| action
== State_Remove
||
789 action
== State_Toggle
))
790 return; // an invalid action was passed to the client message, ignore it
792 for (int i
= 0; i
< 2; ++i
) {
793 Atom state
= i
== 0 ? data1
: data2
;
795 if (! state
) continue;
797 // if toggling, then pick whether we're adding or removing
798 if (action
== State_Toggle
) {
799 if (state
== otk::Property::atoms
.net_wm_state_modal
)
800 action
= _modal
? State_Remove
: State_Add
;
801 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
802 action
= _max_vert
? State_Remove
: State_Add
;
803 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
804 action
= _max_horz
? State_Remove
: State_Add
;
805 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
806 action
= _shaded
? State_Remove
: State_Add
;
807 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
808 action
= _skip_taskbar
? State_Remove
: State_Add
;
809 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
810 action
= _skip_pager
? State_Remove
: State_Add
;
811 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
812 action
= _fullscreen
? State_Remove
: State_Add
;
813 else if (state
== otk::Property::atoms
.net_wm_state_above
)
814 action
= _above
? State_Remove
: State_Add
;
815 else if (state
== otk::Property::atoms
.net_wm_state_below
)
816 action
= _below
? State_Remove
: State_Add
;
819 if (action
== State_Add
) {
820 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
821 if (_modal
) continue;
823 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
825 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
826 if (_max_horz
) continue;
828 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
830 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
831 _skip_taskbar
= true;
832 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
834 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
836 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
837 if (_above
) continue;
839 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
840 if (_below
) continue;
844 } else { // action == State_Remove
845 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
846 if (!_modal
) continue;
848 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
850 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
852 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
854 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
855 _skip_taskbar
= false;
856 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
858 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
860 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
861 if (!_above
) continue;
863 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
864 if (!_below
) continue;
869 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
870 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
871 if (maxh
== maxv
) { // both going the same way
872 maximize(maxh
, 0, true);
874 maximize(maxh
, 1, true);
875 maximize(maxv
, 2, true);
877 } else { // toggling one
878 if (maxh
!= _max_horz
)
879 maximize(maxh
, 1, true);
881 maximize(maxv
, 2, true);
884 // change fullscreen state before shading, as it will affect if the window
886 if (fsstate
!= _fullscreen
)
887 fullscreen(fsstate
, true);
888 if (shadestate
!= _shaded
)
891 changeState(); // change the hint to relect these changes
895 void Client::toggleClientBorder(bool addborder
)
897 // adjust our idea of where the client is, based on its border. When the
898 // border is removed, the client should now be considered to be in a
899 // different position.
900 // when re-adding the border to the client, the same operation needs to be
902 int oldx
= _area
.x(), oldy
= _area
.y();
903 int x
= oldx
, y
= oldy
;
906 case NorthWestGravity
:
908 case SouthWestGravity
:
910 case NorthEastGravity
:
912 case SouthEastGravity
:
913 if (addborder
) x
-= _border_width
* 2;
914 else x
+= _border_width
* 2;
921 if (addborder
) x
-= _border_width
;
922 else x
+= _border_width
;
927 case NorthWestGravity
:
929 case NorthEastGravity
:
931 case SouthWestGravity
:
933 case SouthEastGravity
:
934 if (addborder
) y
-= _border_width
* 2;
935 else y
+= _border_width
* 2;
942 if (addborder
) y
-= _border_width
;
943 else y
+= _border_width
;
946 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
949 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
951 // move the client so it is back it the right spot _with_ its border!
952 if (x
!= oldx
|| y
!= oldy
)
953 XMoveWindow(**otk::display
, _window
, x
, y
);
955 XSetWindowBorderWidth(**otk::display
, _window
, 0);
959 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
961 otk::EventHandler::clientMessageHandler(e
);
963 // validate cuz we query stuff off the client here
964 if (!validate()) return;
966 if (e
.format
!= 32) return;
968 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
969 // compress changes into a single change
970 bool compress
= false;
972 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
973 // XXX: it would be nice to compress ALL messages of a type, not just
974 // messages in a row without other message types between.
975 if (ce
.xclient
.message_type
!= e
.message_type
) {
976 XPutBackEvent(**otk::display
, &ce
);
982 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
984 setWMState(e
.data
.l
[0]); // use the original event
985 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
986 // compress changes into a single change
987 bool compress
= false;
989 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
990 // XXX: it would be nice to compress ALL messages of a type, not just
991 // messages in a row without other message types between.
992 if (ce
.xclient
.message_type
!= e
.message_type
) {
993 XPutBackEvent(**otk::display
, &ce
);
999 setDesktop(e
.data
.l
[0]); // use the found event
1001 setDesktop(e
.data
.l
[0]); // use the original event
1002 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1003 // can't compress these
1005 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1006 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1007 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1008 e
.data
.l
[1], e
.data
.l
[2], _window
);
1010 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1011 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1013 printf("net_close_window for 0x%lx\n", _window
);
1016 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1018 printf("net_active_window for 0x%lx\n", _window
);
1020 if (openbox
->screen(_screen
)->showingDesktop())
1021 openbox
->screen(_screen
)->showDesktop(false);
1024 else if (!frame
->visible()) // if its not visible for other reasons, then
1025 return; // don't mess with it
1029 openbox
->screen(_screen
)->raiseWindow(this);
1030 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1031 if (openbox
->screen(_screen
)->showingDesktop())
1032 openbox
->screen(_screen
)->showDesktop(false);
1035 else if (!frame
->visible()) // if its not visible for other reasons, then
1036 return; // don't mess with it
1037 if (e
.data
.l
[0] && _shaded
)
1041 openbox
->screen(_screen
)->raiseWindow(this);
1047 void Client::shapeHandler(const XShapeEvent
&e
)
1049 otk::EventHandler::shapeHandler(e
);
1051 if (e
.kind
== ShapeBounding
) {
1053 frame
->adjustShape();
1059 void Client::resize(Corner anchor
, int w
, int h
)
1061 if (!(_functions
& Func_Resize
)) return;
1062 internal_resize(anchor
, w
, h
);
1066 void Client::internal_resize(Corner anchor
, int w
, int h
,
1067 bool user
, int x
, int y
)
1069 w
-= _base_size
.width();
1070 h
-= _base_size
.height();
1073 // for interactive resizing. have to move half an increment in each
1075 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1076 int mh
= h
% _size_inc
.height();
1077 int aw
= _size_inc
.width() / 2; // amount to add
1078 int ah
= _size_inc
.height() / 2;
1079 // don't let us move into a new size increment
1080 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1081 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1085 // if this is a user-requested resize, then check against min/max sizes
1086 // and aspect ratios
1088 // smaller than min size or bigger than max size?
1089 if (w
> _max_size
.width()) w
= _max_size
.width();
1090 if (w
< _min_size
.width()) w
= _min_size
.width();
1091 if (h
> _max_size
.height()) h
= _max_size
.height();
1092 if (h
< _min_size
.height()) h
= _min_size
.height();
1094 // adjust the height ot match the width for the aspect ratios
1096 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1098 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1101 // keep to the increments
1102 w
/= _size_inc
.width();
1103 h
/= _size_inc
.height();
1105 // you cannot resize to nothing
1109 // store the logical size
1110 _logical_size
= otk::Size(w
, h
);
1112 w
*= _size_inc
.width();
1113 h
*= _size_inc
.height();
1115 w
+= _base_size
.width();
1116 h
+= _base_size
.height();
1118 if (x
== INT_MIN
|| y
== INT_MIN
) {
1125 x
-= w
- _area
.width();
1128 y
-= h
- _area
.height();
1131 x
-= w
- _area
.width();
1132 y
-= h
- _area
.height();
1137 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1139 XResizeWindow(**otk::display
, _window
, w
, h
);
1141 // resize the frame to match the request
1142 frame
->adjustSize();
1143 internal_move(x
, y
);
1147 void Client::move(int x
, int y
)
1149 if (!(_functions
& Func_Move
)) return;
1150 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1152 internal_move(x
, y
);
1156 void Client::internal_move(int x
, int y
)
1158 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1160 // move the frame to be in the requested position
1161 if (frame
) { // this can be called while mapping, before frame exists
1162 frame
->adjustPosition();
1164 // send synthetic configure notify (we don't need to if we aren't mapped
1167 event
.type
= ConfigureNotify
;
1168 event
.xconfigure
.display
= **otk::display
;
1169 event
.xconfigure
.event
= _window
;
1170 event
.xconfigure
.window
= _window
;
1172 // root window coords with border in mind
1173 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1174 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1176 event
.xconfigure
.width
= _area
.width();
1177 event
.xconfigure
.height
= _area
.height();
1178 event
.xconfigure
.border_width
= _border_width
;
1179 event
.xconfigure
.above
= frame
->plate();
1180 event
.xconfigure
.override_redirect
= False
;
1181 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1182 StructureNotifyMask
, &event
);
1184 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1185 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1186 event
.xconfigure
.height
, event
.xconfigure
.window
);
1192 void Client::close()
1196 if (!(_functions
& Func_Close
)) return;
1198 // XXX: itd be cool to do timeouts and shit here for killing the client's
1200 // like... if the window is around after 5 seconds, then the close button
1201 // turns a nice red, and if this function is called again, the client is
1202 // explicitly killed.
1204 ce
.xclient
.type
= ClientMessage
;
1205 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1206 ce
.xclient
.display
= **otk::display
;
1207 ce
.xclient
.window
= _window
;
1208 ce
.xclient
.format
= 32;
1209 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1210 ce
.xclient
.data
.l
[1] = CurrentTime
;
1211 ce
.xclient
.data
.l
[2] = 0l;
1212 ce
.xclient
.data
.l
[3] = 0l;
1213 ce
.xclient
.data
.l
[4] = 0l;
1214 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1218 void Client::changeState()
1220 unsigned long state
[2];
1221 state
[0] = _wmstate
;
1223 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1224 otk::Property::atoms
.wm_state
, state
, 2);
1229 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1231 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1233 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1235 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1237 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1239 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1241 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1243 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1245 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1247 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1248 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1249 otk::Property::atoms
.atom
, netstate
, num
);
1254 frame
->adjustState();
1258 void Client::changeAllowedActions(void)
1263 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1265 if (_functions
& Func_Shade
)
1266 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1267 if (_functions
& Func_Close
)
1268 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1269 if (_functions
& Func_Move
)
1270 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1271 if (_functions
& Func_Iconify
)
1272 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1273 if (_functions
& Func_Resize
)
1274 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1275 if (_functions
& Func_Fullscreen
)
1276 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1277 if (_functions
& Func_Maximize
) {
1278 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1279 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1282 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1283 otk::Property::atoms
.atom
, actions
, num
);
1285 // make sure the window isn't breaking any rules now
1287 if (!(_functions
& Func_Shade
) && _shaded
)
1288 if (frame
) shade(false);
1289 else _shaded
= false;
1290 if (!(_functions
& Func_Iconify
) && _iconic
)
1291 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1292 else _iconic
= false;
1293 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1294 if (frame
) fullscreen(false);
1295 else _fullscreen
= false;
1296 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1297 if (frame
) maximize(false, 0);
1298 else _max_vert
= _max_horz
= false;
1302 void Client::remaximize()
1305 if (_max_horz
&& _max_vert
)
1312 return; // not maximized
1313 _max_horz
= _max_vert
= false;
1314 maximize(true, dir
, false);
1318 void Client::applyStartupState()
1320 // these are in a carefully crafted order..
1327 _fullscreen
= false;
1328 fullscreen(true, false);
1337 if (_max_vert
&& _max_horz
) {
1338 _max_vert
= _max_horz
= false;
1339 maximize(true, 0, false);
1340 } else if (_max_vert
) {
1342 maximize(true, 2, false);
1343 } else if (_max_horz
) {
1345 maximize(true, 1, false);
1348 if (_skip_taskbar
); // nothing to do for this
1349 if (_skip_pager
); // nothing to do for this
1350 if (_modal
); // nothing to do for this
1351 if (_above
); // nothing to do for this
1352 if (_below
); // nothing to do for this
1356 void Client::fireUrgent()
1358 // call the python UrgentWindow callbacks
1359 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1360 openbox
->bindings()->fireEvent(&data
);
1364 void Client::shade(bool shade
)
1366 if (!(_functions
& Func_Shade
) || // can't
1367 _shaded
== shade
) return; // already done
1369 // when we're iconic, don't change the wmstate
1371 _wmstate
= shade
? IconicState
: NormalState
;
1374 frame
->adjustSize();
1378 void Client::maximize(bool max
, int dir
, bool savearea
)
1380 assert(dir
== 0 || dir
== 1 || dir
== 2);
1381 if (!(_functions
& Func_Maximize
)) return; // can't
1383 // check if already done
1385 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1386 if (dir
== 1 && _max_horz
) return;
1387 if (dir
== 2 && _max_vert
) return;
1389 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1390 if (dir
== 1 && !_max_horz
) return;
1391 if (dir
== 2 && !_max_vert
) return;
1394 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1395 int x
= frame
->area().x(), y
= frame
->area().y(),
1396 w
= _area
.width(), h
= _area
.height();
1402 unsigned long n
= 4;
1409 // get the property off the window and use it for the dimentions we are
1411 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1412 otk::Property::atoms
.cardinal
, &n
,
1413 (long unsigned**) &readdim
)) {
1416 dimensions
[0] = readdim
[0];
1417 dimensions
[2] = readdim
[2];
1420 dimensions
[1] = readdim
[1];
1421 dimensions
[3] = readdim
[3];
1427 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1428 otk::Property::atoms
.cardinal
,
1429 (long unsigned*)dimensions
, 4);
1431 if (dir
== 0 || dir
== 1) { // horz
1435 if (dir
== 0 || dir
== 2) { // vert
1437 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1441 long unsigned n
= 4;
1443 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1444 otk::Property::atoms
.cardinal
, &n
,
1445 (long unsigned**) &dimensions
)) {
1447 if (dir
== 0 || dir
== 1) { // horz
1448 x
= (signed int)dimensions
[0];
1449 w
= (signed int)dimensions
[2];
1451 if (dir
== 0 || dir
== 2) { // vert
1452 y
= (signed int)dimensions
[1];
1453 h
= (signed int)dimensions
[3];
1458 // pick some fallbacks...
1459 if (dir
== 0 || dir
== 1) { // horz
1460 x
= a
.x() + a
.width() / 4;
1463 if (dir
== 0 || dir
== 2) { // vert
1464 y
= a
.y() + a
.height() / 4;
1470 if (dir
== 0 || dir
== 1) // horz
1472 if (dir
== 0 || dir
== 2) // vert
1475 if (!_max_horz
&& !_max_vert
)
1476 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1478 changeState(); // change the state hints on the client
1480 frame
->frameGravity(x
, y
); // figure out where the client should be going
1481 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1485 void Client::fullscreen(bool fs
, bool savearea
)
1487 static FunctionFlags saved_func
;
1488 static DecorationFlags saved_decor
;
1490 if (!(_functions
& Func_Fullscreen
) || // can't
1491 _fullscreen
== fs
) return; // already done
1494 changeState(); // change the state hints on the client
1496 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1499 // save the functions and remove them
1500 saved_func
= _functions
;
1501 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1502 // save the decorations and remove them
1503 saved_decor
= _decorations
;
1507 dimensions
[0] = _area
.x();
1508 dimensions
[1] = _area
.y();
1509 dimensions
[2] = _area
.width();
1510 dimensions
[3] = _area
.height();
1511 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1512 otk::Property::atoms
.cardinal
,
1513 (long unsigned*)dimensions
, 4);
1515 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1518 w
= info
->size().width();
1519 h
= info
->size().height();
1521 _functions
= saved_func
;
1522 _decorations
= saved_decor
;
1525 long unsigned n
= 4;
1527 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1528 otk::Property::atoms
.cardinal
, &n
,
1529 (long unsigned**) &dimensions
)) {
1538 // pick some fallbacks...
1539 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1540 x
= a
.x() + a
.width() / 4;
1541 y
= a
.y() + a
.height() / 4;
1547 changeAllowedActions(); // based on the new _functions
1549 // when fullscreening, don't obey things like increments, fill the screen
1550 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1552 // raise (back) into our stacking layer
1553 openbox
->screen(_screen
)->raiseWindow(this);
1555 // try focus us when we go into fullscreen mode
1560 void Client::iconify(bool iconic
, bool curdesk
)
1562 if (_iconic
== iconic
) return; // nothing to do
1565 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1571 _wmstate
= IconicState
;
1573 // we unmap the client itself so that we can get MapRequest events, and
1574 // because the ICCCM tells us to!
1575 XUnmapWindow(**otk::display
, _window
);
1578 setDesktop(openbox
->screen(_screen
)->desktop());
1579 _wmstate
= NormalState
;
1580 XMapWindow(**otk::display
, _window
);
1586 openbox
->screen(_screen
)->updateStruts();
1590 void Client::disableDecorations(DecorationFlags flags
)
1592 _disabled_decorations
= flags
;
1593 setupDecorAndFunctions();
1597 void Client::installColormap(bool install
) const
1599 XWindowAttributes wa
;
1600 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1602 XInstallColormap(**otk::display
, wa
.colormap
);
1604 XUninstallColormap(**otk::display
, wa
.colormap
);
1609 // recursively searches the client 'tree' for a modal client, always skips the
1610 // topmost node (the window you're starting with)
1611 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1613 List::const_iterator it
, end
= node
->_transients
.end();
1616 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1617 if (*it
== skip
) continue; // circular?
1618 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1619 if ((*it
)->_modal
) return *it
; // got one
1624 Client
*Client::findModalChild()
1626 return searchModalTree(this, this);
1630 bool Client::focus()
1632 // if we have a modal child, then focus it, not us
1633 Client
*c
= findModalChild();
1634 if (c
) return c
->focus();
1636 // won't try focus if the client doesn't want it, or if the window isn't
1637 // visible on the screen
1638 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1640 if (_focused
) return true;
1642 // do a check to see if the window has already been unmapped or destroyed
1643 // do this intelligently while watching out for unmaps we've generated
1644 // (ignore_unmaps > 0)
1646 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1647 XPutBackEvent(**otk::display
, &ev
);
1650 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1651 if (ignore_unmaps
) {
1652 unmapHandler(ev
.xunmap
);
1654 XPutBackEvent(**otk::display
, &ev
);
1660 XSetInputFocus(**otk::display
, _window
,
1661 RevertToNone
, CurrentTime
);
1663 if (_focus_notify
) {
1665 ce
.xclient
.type
= ClientMessage
;
1666 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1667 ce
.xclient
.display
= **otk::display
;
1668 ce
.xclient
.window
= _window
;
1669 ce
.xclient
.format
= 32;
1670 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1671 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1672 ce
.xclient
.data
.l
[2] = 0l;
1673 ce
.xclient
.data
.l
[3] = 0l;
1674 ce
.xclient
.data
.l
[4] = 0l;
1675 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1678 XSync(**otk::display
, False
);
1683 void Client::unfocus() const
1685 if (!_focused
) return;
1687 assert(openbox
->focusedClient() == this);
1688 openbox
->setFocusedClient(0);
1692 void Client::focusHandler(const XFocusChangeEvent
&e
)
1695 // printf("FocusIn for 0x%lx\n", e.window);
1698 otk::EventHandler::focusHandler(e
);
1701 frame
->adjustFocus();
1703 openbox
->setFocusedClient(this);
1707 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1710 // printf("FocusOut for 0x%lx\n", e.window);
1713 otk::EventHandler::unfocusHandler(e
);
1716 frame
->adjustFocus();
1718 if (openbox
->focusedClient() == this)
1719 openbox
->setFocusedClient(0);
1723 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1726 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1729 otk::EventHandler::configureRequestHandler(ec
);
1732 XConfigureRequestEvent e
= ec
;
1734 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1736 // XXX if this causes bad things.. we can compress config req's with the
1738 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1739 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1740 e
.x
= ev
.xconfigurerequest
.x
;
1741 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1742 e
.y
= ev
.xconfigurerequest
.y
;
1743 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1744 e
.width
= ev
.xconfigurerequest
.width
;
1745 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1746 e
.height
= ev
.xconfigurerequest
.height
;
1747 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1748 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1749 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1750 e
.detail
= ev
.xconfigurerequest
.detail
;
1753 // if we are iconic (or shaded (fvwm does this)) ignore the event
1754 if (_iconic
|| _shaded
) return;
1756 if (e
.value_mask
& CWBorderWidth
)
1757 _border_width
= e
.border_width
;
1759 // resize, then move, as specified in the EWMH section 7.7
1760 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1761 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1762 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1766 case NorthEastGravity
:
1770 case SouthWestGravity
:
1772 corner
= BottomLeft
;
1774 case SouthEastGravity
:
1775 corner
= BottomRight
;
1777 default: // NorthWest, Static, etc
1781 // if moving AND resizing ...
1782 if (e
.value_mask
& (CWX
| CWY
)) {
1783 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1784 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1785 internal_resize(corner
, w
, h
, false, x
, y
);
1786 } else // if JUST resizing...
1787 internal_resize(corner
, w
, h
, false);
1788 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1789 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1790 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1791 internal_move(x
, y
);
1794 if (e
.value_mask
& CWStackMode
) {
1798 openbox
->screen(_screen
)->lowerWindow(this);
1804 openbox
->screen(_screen
)->raiseWindow(this);
1811 void Client::unmapHandler(const XUnmapEvent
&e
)
1813 if (ignore_unmaps
) {
1815 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1822 printf("UnmapNotify for 0x%lx\n", e
.window
);
1825 otk::EventHandler::unmapHandler(e
);
1827 // this deletes us etc
1828 openbox
->screen(_screen
)->unmanageWindow(this);
1832 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1835 printf("DestroyNotify for 0x%lx\n", e
.window
);
1838 otk::EventHandler::destroyHandler(e
);
1840 // this deletes us etc
1841 openbox
->screen(_screen
)->unmanageWindow(this);
1845 void Client::reparentHandler(const XReparentEvent
&e
)
1847 // this is when the client is first taken captive in the frame
1848 if (e
.parent
== frame
->plate()) return;
1851 printf("ReparentNotify for 0x%lx\n", e
.window
);
1854 otk::EventHandler::reparentHandler(e
);
1857 This event is quite rare and is usually handled in unmapHandler.
1858 However, if the window is unmapped when the reparent event occurs,
1859 the window manager never sees it because an unmap event is not sent
1860 to an already unmapped window.
1863 // we don't want the reparent event, put it back on the stack for the X
1864 // server to deal with after we unmanage the window
1867 XPutBackEvent(**otk::display
, &ev
);
1869 // this deletes us etc
1870 openbox
->screen(_screen
)->unmanageWindow(this);
1873 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1876 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1879 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1881 // move to the current desktop (uniconify)
1883 // XXX: should we focus/raise the window? (basically a net_wm_active_window)