1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
11 #include "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
23 #define _(str) gettext(str)
28 Client::Client(int screen
, Window window
)
29 : otk::EventHandler(),
30 WidgetBase(WidgetBase::Type_Client
),
31 frame(0), _screen(screen
), _window(window
)
38 // update EVERYTHING the first time!!
40 // we default to NormalState, visible
41 _wmstate
= NormalState
;
44 // not a transient by default of course
46 // pick a layer to start from
47 _layer
= Layer_Normal
;
48 // default to not urgent
63 // got the type, the mwmhints, and the protocols, so we're ready to set up
64 // the decorations/functions
65 setupDecorAndFunctions();
67 getGravity(); // get the attribute gravity
68 updateNormalHints(); // this may override the attribute gravity
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 void Client::getGravity()
118 XWindowAttributes wattrib
;
121 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
122 assert(ret
!= BadWindow
);
123 _gravity
= wattrib
.win_gravity
;
127 void Client::getDesktop()
129 // defaults to the current desktop
130 _desktop
= openbox
->screen(_screen
)->desktop();
132 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
133 otk::Property::atoms
.cardinal
,
134 (long unsigned*)&_desktop
)) {
136 printf("DEBUG: Window requested desktop: %ld\n", _desktop
);
142 void Client::getType()
144 _type
= (WindowType
) -1;
147 unsigned long num
= (unsigned) -1;
148 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
149 otk::Property::atoms
.atom
, &num
, &val
)) {
150 // use the first value that we know about in the array
151 for (unsigned long i
= 0; i
< num
; ++i
) {
152 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
153 _type
= Type_Desktop
;
154 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
156 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
157 _type
= Type_Toolbar
;
158 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
160 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
161 _type
= Type_Utility
;
162 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
164 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
166 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
168 // XXX: make this work again
169 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
170 // mwm_decorations = 0; // prevent this window from getting any decor
171 if (_type
!= (WindowType
) -1)
172 break; // grab the first known type
177 if (_type
== (WindowType
) -1) {
179 * the window type hint was not set, which means we either classify ourself
180 * as a normal window or a dialog, depending on if we are a transient.
190 void Client::setupDecorAndFunctions()
192 // start with everything (cept fullscreen)
193 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
194 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
195 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
197 if (_delete_window
) {
198 _decorations
|= Decor_Close
;
199 _functions
|= Func_Close
;
204 // normal windows retain all of the possible decorations and
205 // functionality, and are the only windows that you can fullscreen
206 _functions
|= Func_Fullscreen
;
209 // dialogs cannot be maximized
210 _decorations
&= ~Decor_Maximize
;
211 _functions
&= ~Func_Maximize
;
217 // these windows get less functionality
218 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
219 _functions
&= ~(Func_Iconify
| Func_Resize
);
225 // none of these windows are manipulated by the window manager
231 // Mwm Hints are applied subtractively to what has already been chosen for
232 // decor and functionality
233 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
234 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
235 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
236 _decorations
&= ~Decor_Border
;
237 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
238 _decorations
&= ~Decor_Handle
;
239 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
240 _decorations
&= ~Decor_Titlebar
;
241 // if we don't have a titlebar, then we cannot shade!
242 _functions
&= ~Func_Shade
;
244 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
245 _decorations
&= ~Decor_Iconify
;
246 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
247 _decorations
&= ~Decor_Maximize
;
251 if (_mwmhints
.flags
& MwmFlag_Functions
) {
252 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
253 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
254 _functions
&= ~Func_Resize
;
255 if (! (_mwmhints
.functions
& MwmFunc_Move
))
256 _functions
&= ~Func_Move
;
257 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
258 _functions
&= ~Func_Iconify
;
259 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
260 _functions
&= ~Func_Maximize
;
261 // dont let mwm hints kill the close button
262 //if (! (_mwmhints.functions & MwmFunc_Close))
263 // _functions &= ~Func_Close;
267 changeAllowedActions();
271 void Client::getMwmHints()
273 unsigned long num
= MwmHints::elements
;
274 unsigned long *hints
;
276 _mwmhints
.flags
= 0; // default to none
278 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
279 otk::Property::atoms
.motif_wm_hints
, &num
,
280 (unsigned long **)&hints
))
283 if (num
>= MwmHints::elements
) {
284 // retrieved the hints
285 _mwmhints
.flags
= hints
[0];
286 _mwmhints
.functions
= hints
[1];
287 _mwmhints
.decorations
= hints
[2];
294 void Client::getArea()
296 XWindowAttributes wattrib
;
299 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
300 assert(ret
!= BadWindow
);
302 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
303 _border_width
= wattrib
.border_width
;
307 void Client::getState()
309 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
310 _iconic
= _skip_taskbar
= _skip_pager
= false;
312 unsigned long *state
;
313 unsigned long num
= (unsigned) -1;
315 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
316 otk::Property::atoms
.atom
, &num
, &state
)) {
317 for (unsigned long i
= 0; i
< num
; ++i
) {
318 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
320 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
322 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
324 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
325 _skip_taskbar
= true;
326 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
328 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
330 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
332 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
334 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
336 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
345 void Client::getShaped()
349 if (otk::display
->shape()) {
354 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
356 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
357 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
364 void Client::calcLayer() {
367 if (_iconic
) l
= Layer_Icon
;
368 else if (_fullscreen
) l
= Layer_Fullscreen
;
369 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
370 else if (_type
== Type_Dock
) {
371 if (!_below
) l
= Layer_Top
;
372 else l
= Layer_Normal
;
374 else if (_above
) l
= Layer_Above
;
375 else if (_below
) l
= Layer_Below
;
376 else l
= Layer_Normal
;
382 if we don't have a frame, then we aren't mapped yet (and this would
385 openbox
->screen(_screen
)->raiseWindow(this);
391 void Client::updateProtocols()
396 _focus_notify
= false;
397 _delete_window
= false;
399 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
400 for (int i
= 0; i
< num_return
; ++i
) {
401 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
402 // this means we can request the window to close
403 _delete_window
= true;
404 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
405 // if this protocol is requested, then the window will be notified
406 // by the window manager whenever it receives focus
407 _focus_notify
= true;
414 void Client::updateNormalHints()
418 int oldgravity
= _gravity
;
421 _size_inc
.setPoint(1, 1);
422 _base_size
.setPoint(0, 0);
423 _min_size
.setPoint(0, 0);
424 _max_size
.setPoint(INT_MAX
, INT_MAX
);
426 // XXX: might want to cancel any interactive resizing of the window at this
429 // get the hints from the window
430 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
431 _positioned
= (size
.flags
& (PPosition
|USPosition
));
433 if (size
.flags
& PWinGravity
) {
434 _gravity
= size
.win_gravity
;
436 // if the client has a frame, i.e. has already been mapped and is
437 // changing its gravity
438 if (frame
&& _gravity
!= oldgravity
) {
439 // move our idea of the client's position based on its new gravity
441 frame
->frameGravity(x
, y
);
446 if (size
.flags
& PMinSize
)
447 _min_size
.setPoint(size
.min_width
, size
.min_height
);
449 if (size
.flags
& PMaxSize
)
450 _max_size
.setPoint(size
.max_width
, size
.max_height
);
452 if (size
.flags
& PBaseSize
)
453 _base_size
.setPoint(size
.base_width
, size
.base_height
);
455 if (size
.flags
& PResizeInc
)
456 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
461 void Client::updateWMHints(bool initstate
)
465 // assume a window takes input if it doesnt specify
469 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
470 if (hints
->flags
& InputHint
)
471 _can_focus
= hints
->input
;
473 // only do this when initstate is true!
474 if (initstate
&& (hints
->flags
& StateHint
))
475 _iconic
= hints
->initial_state
== IconicState
;
477 if (hints
->flags
& XUrgencyHint
)
480 if (hints
->flags
& WindowGroupHint
) {
481 if (hints
->window_group
!= _group
) {
482 // XXX: remove from the old group if there was one
483 _group
= hints
->window_group
;
484 // XXX: do stuff with the group
495 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
496 (long)_window
, _urgent
? "ON" : "OFF");
498 // fire the urgent callback if we're mapped, otherwise, wait until after
500 if (_urgent
&& frame
)
506 void Client::updateTitle()
511 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
512 otk::Property::utf8
, &_title
)) {
514 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
515 otk::Property::ascii
, &_title
);
519 _title
= _("Unnamed Window");
522 frame
->setTitle(_title
);
526 void Client::updateIconTitle()
531 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
532 otk::Property::utf8
, &_icon_title
)) {
534 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
535 otk::Property::ascii
, &_icon_title
);
539 _icon_title
= _("Unnamed Window");
543 void Client::updateClass()
546 _app_name
= _app_class
= _role
= "";
548 otk::Property::StringVect v
;
549 unsigned long num
= 2;
551 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
552 otk::Property::ascii
, &num
, &v
)) {
553 if (num
> 0) _app_name
= v
[0].c_str();
554 if (num
> 1) _app_class
= v
[1].c_str();
559 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
560 otk::Property::ascii
, &num
, &v
)) {
561 if (num
> 0) _role
= v
[0].c_str();
566 void Client::updateStrut()
568 unsigned long num
= 4;
570 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
571 otk::Property::atoms
.cardinal
, &num
, &data
))
575 _strut
.left
= data
[0];
576 _strut
.right
= data
[1];
577 _strut
.top
= data
[2];
578 _strut
.bottom
= data
[3];
580 openbox
->screen(_screen
)->updateStrut();
587 void Client::updateTransientFor()
592 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
593 t
!= _window
) { // cant be transient to itself!
594 c
= openbox
->findClient(t
);
595 assert(c
!= this); // if this happens then we need to check for it
597 if (!c
/*XXX: && _group*/) {
598 // not transient to a client, see if it is transient for a group
599 if (//t == _group->leader() ||
601 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
602 // window is a transient for its group!
603 // XXX: for now this is treated as non-transient.
604 // this needs to be fixed!
609 // if anything has changed...
610 if (c
!= _transient_for
) {
612 _transient_for
->_transients
.remove(this); // remove from old parent
615 _transient_for
->_transients
.push_back(this); // add to new parent
617 // XXX: change decor status?
622 void Client::propertyHandler(const XPropertyEvent
&e
)
624 otk::EventHandler::propertyHandler(e
);
626 // compress changes to a single property into a single change
628 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
629 // XXX: it would be nice to compress ALL changes to a property, not just
630 // changes in a row without other props between.
631 if (ce
.xproperty
.atom
!= e
.atom
) {
632 XPutBackEvent(**otk::display
, &ce
);
637 if (e
.atom
== XA_WM_NORMAL_HINTS
)
639 else if (e
.atom
== XA_WM_HINTS
)
641 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
642 updateTransientFor();
644 calcLayer(); // type may have changed, so update the layer
645 setupDecorAndFunctions();
646 frame
->adjustSize(); // this updates the frame for any new decor settings
648 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
649 e
.atom
== otk::Property::atoms
.wm_name
)
651 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
652 e
.atom
== otk::Property::atoms
.wm_icon_name
)
654 else if (e
.atom
== otk::Property::atoms
.wm_class
)
656 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
658 setupDecorAndFunctions();
659 frame
->adjustSize(); // update the decorations
661 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
666 void Client::setWMState(long state
)
668 if (state
== _wmstate
) return; // no change
672 setDesktop(ICONIC_DESKTOP
);
675 setDesktop(openbox
->screen(_screen
)->desktop());
681 void Client::setDesktop(long target
)
683 if (target
== _desktop
) return;
685 printf("Setting desktop %ld\n", target
);
687 if (!(target
>= 0 || target
== (signed)0xffffffff ||
688 target
== ICONIC_DESKTOP
))
693 // set the desktop hint, but not if we're iconifying
694 if (_desktop
!= ICONIC_DESKTOP
)
695 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
696 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
698 // 'move' the window to the new desktop
699 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
700 _desktop
== (signed)0xffffffff)
705 // Handle Iconic state. Iconic state is maintained by the client being a
706 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
707 // uniconifying happen.
708 bool i
= _desktop
== ICONIC_DESKTOP
;
709 if (i
!= _iconic
) { // has the state changed?
712 _wmstate
= IconicState
;
714 // we unmap the client itself so that we can get MapRequest events, and
715 // because the ICCCM tells us to!
716 XUnmapWindow(**otk::display
, _window
);
718 _wmstate
= NormalState
;
719 XMapWindow(**otk::display
, _window
);
724 frame
->adjustState();
728 void Client::setState(StateAction action
, long data1
, long data2
)
730 bool shadestate
= _shaded
;
731 bool fsstate
= _fullscreen
;
733 if (!(action
== State_Add
|| action
== State_Remove
||
734 action
== State_Toggle
))
735 return; // an invalid action was passed to the client message, ignore it
737 for (int i
= 0; i
< 2; ++i
) {
738 Atom state
= i
== 0 ? data1
: data2
;
740 if (! state
) continue;
742 // if toggling, then pick whether we're adding or removing
743 if (action
== State_Toggle
) {
744 if (state
== otk::Property::atoms
.net_wm_state_modal
)
745 action
= _modal
? State_Remove
: State_Add
;
746 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
747 action
= _max_vert
? State_Remove
: State_Add
;
748 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
749 action
= _max_horz
? State_Remove
: State_Add
;
750 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
751 action
= _shaded
? State_Remove
: State_Add
;
752 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
753 action
= _skip_taskbar
? State_Remove
: State_Add
;
754 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
755 action
= _skip_pager
? State_Remove
: State_Add
;
756 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
757 action
= _fullscreen
? State_Remove
: State_Add
;
758 else if (state
== otk::Property::atoms
.net_wm_state_above
)
759 action
= _above
? State_Remove
: State_Add
;
760 else if (state
== otk::Property::atoms
.net_wm_state_below
)
761 action
= _below
? State_Remove
: State_Add
;
764 if (action
== State_Add
) {
765 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
766 if (_modal
) continue;
768 // XXX: give it focus if another window has focus that shouldnt now
769 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
770 if (_max_vert
) continue;
772 // XXX: resize the window etc
773 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
774 if (_max_horz
) continue;
776 // XXX: resize the window etc
777 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
779 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
780 _skip_taskbar
= true;
781 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
783 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
785 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
786 if (_above
) continue;
788 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
789 if (_below
) continue;
793 } else { // action == State_Remove
794 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
795 if (!_modal
) continue;
797 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
798 if (!_max_vert
) continue;
800 // XXX: resize the window etc
801 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
802 if (!_max_horz
) continue;
804 // XXX: resize the window etc
805 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
807 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
808 _skip_taskbar
= false;
809 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
811 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
813 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
814 if (!_above
) continue;
816 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
817 if (!_below
) continue;
822 // change fullscreen state before shading, as it will affect if the window
824 if (fsstate
!= _fullscreen
)
826 if (shadestate
!= _shaded
)
832 void Client::toggleClientBorder(bool addborder
)
834 // adjust our idea of where the client is, based on its border. When the
835 // border is removed, the client should now be considered to be in a
836 // different position.
837 // when re-adding the border to the client, the same operation needs to be
839 int x
= _area
.x(), y
= _area
.y();
842 case NorthWestGravity
:
844 case SouthWestGravity
:
846 case NorthEastGravity
:
848 case SouthEastGravity
:
849 if (addborder
) x
-= _border_width
* 2;
850 else x
+= _border_width
* 2;
857 if (addborder
) x
-= _border_width
;
858 else x
+= _border_width
;
863 case NorthWestGravity
:
865 case NorthEastGravity
:
867 case SouthWestGravity
:
869 case SouthEastGravity
:
870 if (addborder
) y
-= _border_width
* 2;
871 else y
+= _border_width
* 2;
878 if (addborder
) y
-= _border_width
;
879 else y
+= _border_width
;
885 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
887 // move the client so it is back it the right spot _with_ its border!
888 XMoveWindow(**otk::display
, _window
, x
, y
);
890 XSetWindowBorderWidth(**otk::display
, _window
, 0);
894 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
896 otk::EventHandler::clientMessageHandler(e
);
898 if (e
.format
!= 32) return;
900 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
901 // compress changes into a single change
902 bool compress
= false;
904 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
905 // XXX: it would be nice to compress ALL messages of a type, not just
906 // messages in a row without other message types between.
907 if (ce
.xclient
.message_type
!= e
.message_type
) {
908 XPutBackEvent(**otk::display
, &ce
);
914 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
916 setWMState(e
.data
.l
[0]); // use the original event
917 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
918 // compress changes into a single change
919 bool compress
= false;
921 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
922 // XXX: it would be nice to compress ALL messages of a type, not just
923 // messages in a row without other message types between.
924 if (ce
.xclient
.message_type
!= e
.message_type
) {
925 XPutBackEvent(**otk::display
, &ce
);
931 setDesktop(e
.data
.l
[0]); // use the found event
933 setDesktop(e
.data
.l
[0]); // use the original event
934 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
935 // can't compress these
937 printf("net_wm_state %s %ld %ld for 0x%lx\n",
938 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
939 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
940 e
.data
.l
[1], e
.data
.l
[2], _window
);
942 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
943 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
945 printf("net_close_window for 0x%lx\n", _window
);
948 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
950 printf("net_active_window for 0x%lx\n", _window
);
953 setDesktop(openbox
->screen(_screen
)->desktop());
958 openbox
->screen(_screen
)->raiseWindow(this);
964 void Client::shapeHandler(const XShapeEvent
&e
)
966 otk::EventHandler::shapeHandler(e
);
968 if (e
.kind
== ShapeBounding
) {
970 frame
->adjustShape();
976 void Client::resize(Corner anchor
, int w
, int h
)
978 if (!(_functions
& Func_Resize
)) return;
979 internal_resize(anchor
, w
, h
);
983 void Client::internal_resize(Corner anchor
, int w
, int h
, int x
, int y
)
988 // for interactive resizing. have to move half an increment in each
990 w
+= _size_inc
.x() / 2;
991 h
+= _size_inc
.y() / 2;
993 // is the window resizable? if it is not, then don't check its sizes, the
994 // client can do what it wants and the user can't change it anyhow
995 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
996 // smaller than min size or bigger than max size?
997 if (w
< _min_size
.x()) w
= _min_size
.x();
998 else if (w
> _max_size
.x()) w
= _max_size
.x();
999 if (h
< _min_size
.y()) h
= _min_size
.y();
1000 else if (h
> _max_size
.y()) h
= _max_size
.y();
1003 // keep to the increments
1007 // you cannot resize to nothing
1011 // store the logical size
1012 _logical_size
.setPoint(w
, h
);
1017 w
+= _base_size
.x();
1018 h
+= _base_size
.y();
1020 if (x
== INT_MIN
|| y
== INT_MIN
) {
1027 x
-= w
- _area
.width();
1030 y
-= h
- _area
.height();
1033 x
-= w
- _area
.width();
1034 y
-= h
- _area
.height();
1039 _area
.setSize(w
, h
);
1041 XResizeWindow(**otk::display
, _window
, w
, h
);
1043 // resize the frame to match the request
1044 frame
->adjustSize();
1045 internal_move(x
, y
);
1049 void Client::move(int x
, int y
)
1051 if (!(_functions
& Func_Move
)) return;
1052 internal_move(x
, y
);
1056 void Client::internal_move(int x
, int y
)
1060 // move the frame to be in the requested position
1061 if (frame
) { // this can be called while mapping, before frame exists
1062 frame
->adjustPosition();
1064 // send synthetic configure notify (we don't need to if we aren't mapped
1067 event
.type
= ConfigureNotify
;
1068 event
.xconfigure
.display
= **otk::display
;
1069 event
.xconfigure
.event
= _window
;
1070 event
.xconfigure
.window
= _window
;
1071 event
.xconfigure
.x
= x
;
1072 event
.xconfigure
.y
= y
;
1073 event
.xconfigure
.width
= _area
.width();
1074 event
.xconfigure
.height
= _area
.height();
1075 event
.xconfigure
.border_width
= _border_width
;
1076 event
.xconfigure
.above
= frame
->window();
1077 event
.xconfigure
.override_redirect
= False
;
1078 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1079 StructureNotifyMask
, &event
);
1084 void Client::close()
1088 if (!(_functions
& Func_Close
)) return;
1090 // XXX: itd be cool to do timeouts and shit here for killing the client's
1092 // like... if the window is around after 5 seconds, then the close button
1093 // turns a nice red, and if this function is called again, the client is
1094 // explicitly killed.
1096 ce
.xclient
.type
= ClientMessage
;
1097 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1098 ce
.xclient
.display
= **otk::display
;
1099 ce
.xclient
.window
= _window
;
1100 ce
.xclient
.format
= 32;
1101 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1102 ce
.xclient
.data
.l
[1] = CurrentTime
;
1103 ce
.xclient
.data
.l
[2] = 0l;
1104 ce
.xclient
.data
.l
[3] = 0l;
1105 ce
.xclient
.data
.l
[4] = 0l;
1106 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1110 void Client::changeState()
1112 unsigned long state
[2];
1113 state
[0] = _wmstate
;
1115 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1116 otk::Property::atoms
.wm_state
, state
, 2);
1121 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1123 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1125 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1127 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1129 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1131 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1133 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1135 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1137 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1139 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1140 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1141 otk::Property::atoms
.atom
, netstate
, num
);
1146 frame
->adjustState();
1150 void Client::changeAllowedActions(void)
1155 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1157 if (_functions
& Func_Shade
)
1158 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1159 if (_functions
& Func_Close
)
1160 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1161 if (_functions
& Func_Move
)
1162 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1163 if (_functions
& Func_Iconify
)
1164 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1165 if (_functions
& Func_Resize
)
1166 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1167 if (_functions
& Func_Fullscreen
)
1168 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1169 if (_functions
& Func_Maximize
) {
1170 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1171 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1174 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1175 otk::Property::atoms
.atom
, actions
, num
);
1179 void Client::applyStartupState()
1181 // these are in a carefully crafted order..
1184 printf("MAP ICONIC\n");
1186 setDesktop(ICONIC_DESKTOP
);
1189 _fullscreen
= false;
1199 if (_max_vert
); // XXX: incomplete
1200 if (_max_horz
); // XXX: incomplete
1202 if (_skip_taskbar
); // nothing to do for this
1203 if (_skip_pager
); // nothing to do for this
1204 if (_modal
); // nothing to do for this
1205 if (_above
); // nothing to do for this
1206 if (_below
); // nothing to do for this
1210 void Client::fireUrgent()
1212 // call the python UrgentNotify callbacks
1213 EventData
data(_screen
, this, EventUrgentNotify
, 0);
1214 openbox
->bindings()->fireEvent(&data
);
1218 void Client::shade(bool shade
)
1220 if (!(_functions
& Func_Shade
) || // can't
1221 _shaded
== shade
) return; // already done
1223 // when we're iconic, don't change the wmstate
1225 _wmstate
= shade
? IconicState
: NormalState
;
1228 frame
->adjustSize();
1232 void Client::fullscreen(bool fs
)
1234 static FunctionFlags saved_func
;
1235 static DecorationFlags saved_decor
;
1236 static otk::Rect saved_area
;
1237 static otk::Point saved_logical_size
;
1239 if (!(_functions
& Func_Fullscreen
) || // can't
1240 _fullscreen
== fs
) return; // already done
1243 changeState(); // change the state hints on the client
1246 // save the functions and remove them
1247 saved_func
= _functions
;
1248 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1249 // save the decorations and remove them
1250 saved_decor
= _decorations
;
1252 // save the area and adjust it (we don't call internal resize here for
1253 // constraints on the size, etc, we just make it fullscreen).
1255 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1256 _area
.setRect(0, 0, info
->width(), info
->height());
1257 saved_logical_size
= _logical_size
;
1258 _logical_size
.setPoint((info
->width() - _base_size
.x()) / _size_inc
.x(),
1259 (info
->height() - _base_size
.y()) / _size_inc
.y());
1261 _functions
= saved_func
;
1262 _decorations
= saved_decor
;
1264 _logical_size
= saved_logical_size
;
1267 changeAllowedActions(); // based on the new _functions
1269 frame
->adjustSize(); // drop/replace the decor's and resize
1270 frame
->adjustPosition(); // get (back) in position!
1272 // raise (back) into our stacking layer
1273 openbox
->screen(_screen
)->raiseWindow(this);
1275 // try focus us when we go into fullscreen mode
1280 bool Client::focus()
1282 // won't try focus if the client doesn't want it, or if the window isn't
1283 // visible on the screen
1284 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1286 if (_focused
) return true;
1288 // do a check to see if the window has already been unmapped or destroyed
1289 // do this intelligently while watching out for unmaps we've generated
1290 // (ignore_unmaps > 0)
1292 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1293 XPutBackEvent(**otk::display
, &ev
);
1296 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1297 if (ignore_unmaps
) {
1298 unmapHandler(ev
.xunmap
);
1300 XPutBackEvent(**otk::display
, &ev
);
1306 XSetInputFocus(**otk::display
, _window
,
1307 RevertToNone
, CurrentTime
);
1309 if (_focus_notify
) {
1311 ce
.xclient
.type
= ClientMessage
;
1312 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1313 ce
.xclient
.display
= **otk::display
;
1314 ce
.xclient
.window
= _window
;
1315 ce
.xclient
.format
= 32;
1316 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1317 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1318 ce
.xclient
.data
.l
[2] = 0l;
1319 ce
.xclient
.data
.l
[3] = 0l;
1320 ce
.xclient
.data
.l
[4] = 0l;
1321 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1328 void Client::unfocus() const
1330 if (!_focused
) return;
1332 assert(openbox
->focusedClient() == this);
1333 openbox
->setFocusedClient(0);
1337 void Client::focusHandler(const XFocusChangeEvent
&e
)
1340 // printf("FocusIn for 0x%lx\n", e.window);
1343 otk::EventHandler::focusHandler(e
);
1348 openbox
->setFocusedClient(this);
1352 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1355 // printf("FocusOut for 0x%lx\n", e.window);
1358 otk::EventHandler::unfocusHandler(e
);
1363 if (openbox
->focusedClient() == this)
1364 openbox
->setFocusedClient(0);
1368 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1371 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1374 otk::EventHandler::configureRequestHandler(e
);
1376 // if we are iconic (or shaded (fvwm does this)) ignore the event
1377 if (_iconic
|| _shaded
) return;
1379 if (e
.value_mask
& CWBorderWidth
)
1380 _border_width
= e
.border_width
;
1382 // resize, then move, as specified in the EWMH section 7.7
1383 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1384 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1385 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1389 case NorthEastGravity
:
1393 case SouthWestGravity
:
1395 corner
= BottomLeft
;
1397 case SouthEastGravity
:
1398 corner
= BottomRight
;
1400 default: // NorthWest, Static, etc
1404 // if moving AND resizing ...
1405 if (e
.value_mask
& (CWX
| CWY
)) {
1406 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1407 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1408 internal_resize(corner
, w
, h
, x
, y
);
1409 } else // if JUST resizing...
1410 internal_resize(corner
, w
, h
);
1411 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1412 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1413 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1414 internal_move(x
, y
);
1417 if (e
.value_mask
& CWStackMode
) {
1421 openbox
->screen(_screen
)->lowerWindow(this);
1427 openbox
->screen(_screen
)->raiseWindow(this);
1434 void Client::unmapHandler(const XUnmapEvent
&e
)
1436 if (ignore_unmaps
) {
1438 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1445 printf("UnmapNotify for 0x%lx\n", e
.window
);
1448 otk::EventHandler::unmapHandler(e
);
1450 // this deletes us etc
1451 openbox
->screen(_screen
)->unmanageWindow(this);
1455 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1458 printf("DestroyNotify for 0x%lx\n", e
.window
);
1461 otk::EventHandler::destroyHandler(e
);
1463 // this deletes us etc
1464 openbox
->screen(_screen
)->unmanageWindow(this);
1468 void Client::reparentHandler(const XReparentEvent
&e
)
1470 // this is when the client is first taken captive in the frame
1471 if (e
.parent
== frame
->plate()) return;
1474 printf("ReparentNotify for 0x%lx\n", e
.window
);
1477 otk::EventHandler::reparentHandler(e
);
1480 This event is quite rare and is usually handled in unmapHandler.
1481 However, if the window is unmapped when the reparent event occurs,
1482 the window manager never sees it because an unmap event is not sent
1483 to an already unmapped window.
1486 // we don't want the reparent event, put it back on the stack for the X
1487 // server to deal with after we unmanage the window
1490 XPutBackEvent(**otk::display
, &ev
);
1492 // this deletes us etc
1493 openbox
->screen(_screen
)->unmanageWindow(this);
1496 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1499 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1502 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1504 // move to the current desktop (uniconify)
1505 setDesktop(openbox
->screen(_screen
)->desktop());
1506 // XXX: should we focus/raise the window? (basically a net_wm_active_window)