1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
11 #include "otk/display.hh"
12 #include "otk/property.hh"
16 #include <X11/Xutil.h>
17 #include <X11/Xatom.h>
22 #define _(str) gettext(str)
27 Client::Client(int screen
, Window window
)
28 : otk::EventHandler(),
29 WidgetBase(WidgetBase::Type_Client
),
30 frame(0), _screen(screen
), _window(window
)
37 // update EVERYTHING the first time!!
39 // we default to NormalState, visible
40 _wmstate
= NormalState
; _iconic
= false;
41 // no default decors or functions, each has to be enabled
42 _decorations
= _functions
= 0;
45 // not a transient by default of course
47 // pick a layer to start from
48 _layer
= Layer_Normal
;
57 getState(); // gets all the states except for iconic, which is found from
58 // the desktop == ICONIC_DESKTOP
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 updateWMHints(true); // also get the initial_state and set _iconic
75 // restores iconic state when we restart.
76 // this will override the initial_state if that was set
77 if (_desktop
== ICONIC_DESKTOP
) _iconic
= true;
85 // clean up childrens' references
86 while (!_transients
.empty()) {
87 _transients
.front()->_transient_for
= 0;
88 _transients
.pop_front();
91 // clean up parents reference to this
93 _transient_for
->_transients
.remove(this); // remove from old parent
95 if (openbox
->state() != Openbox::State_Exiting
) {
96 // these values should not be persisted across a window unmapping/mapping
97 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
98 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
100 // if we're left in an iconic state, the client wont be mapped. this is
101 // bad, since we will no longer be managing the window on restart
103 XMapWindow(**otk::display
, _window
);
108 void Client::getGravity()
110 XWindowAttributes wattrib
;
113 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
114 assert(ret
!= BadWindow
);
115 _gravity
= wattrib
.win_gravity
;
119 void Client::getDesktop()
121 // defaults to the current desktop
122 _desktop
= openbox
->screen(_screen
)->desktop();
124 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
125 otk::Property::atoms
.cardinal
,
126 (long unsigned*)&_desktop
)) {
127 // make sure the hint exists
128 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
129 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
134 void Client::getType()
136 _type
= (WindowType
) -1;
139 unsigned long num
= (unsigned) -1;
140 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
141 otk::Property::atoms
.atom
, &num
, &val
)) {
142 // use the first value that we know about in the array
143 for (unsigned long i
= 0; i
< num
; ++i
) {
144 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
145 _type
= Type_Desktop
;
146 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
148 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
149 _type
= Type_Toolbar
;
150 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
152 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
153 _type
= Type_Utility
;
154 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
156 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
158 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
160 // XXX: make this work again
161 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
162 // mwm_decorations = 0; // prevent this window from getting any decor
163 if (_type
!= (WindowType
) -1)
164 break; // grab the first known type
169 if (_type
== (WindowType
) -1) {
171 * the window type hint was not set, which means we either classify ourself
172 * as a normal window or a dialog, depending on if we are a transient.
182 void Client::setupDecorAndFunctions()
184 // start with everything (cept fullscreen)
185 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
186 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
187 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
189 if (_delete_window
) {
190 _decorations
|= Decor_Close
;
191 _functions
|= Func_Close
;
196 // normal windows retain all of the possible decorations and
197 // functionality, and are the only windows that you can fullscreen
198 _functions
|= Func_Fullscreen
;
201 // dialogs cannot be maximized
202 _decorations
&= ~Decor_Maximize
;
203 _functions
&= ~Func_Maximize
;
209 // these windows get less functionality
210 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
211 _functions
&= ~(Func_Iconify
| Func_Resize
);
217 // none of these windows are manipulated by the window manager
223 // Mwm Hints are applied subtractively to what has already been chosen for
224 // decor and functionality
225 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
226 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
227 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
228 _decorations
&= ~Decor_Border
;
229 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
230 _decorations
&= ~Decor_Handle
;
231 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
232 _decorations
&= ~Decor_Titlebar
;
233 // if we don't have a titlebar, then we cannot shade!
234 _functions
&= ~Func_Shade
;
236 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
237 _decorations
&= ~Decor_Iconify
;
238 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
239 _decorations
&= ~Decor_Maximize
;
243 if (_mwmhints
.flags
& MwmFlag_Functions
) {
244 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
245 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
246 _functions
&= ~Func_Resize
;
247 if (! (_mwmhints
.functions
& MwmFunc_Move
))
248 _functions
&= ~Func_Move
;
249 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
250 _functions
&= ~Func_Iconify
;
251 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
252 _functions
&= ~Func_Maximize
;
253 // dont let mwm hints kill the close button
254 //if (! (_mwmhints.functions & MwmFunc_Close))
255 // _functions &= ~Func_Close;
259 changeAllowedActions();
263 void Client::getMwmHints()
265 unsigned long num
= MwmHints::elements
;
266 unsigned long *hints
;
268 _mwmhints
.flags
= 0; // default to none
270 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
271 otk::Property::atoms
.motif_wm_hints
, &num
,
272 (unsigned long **)&hints
))
275 if (num
>= MwmHints::elements
) {
276 // retrieved the hints
277 _mwmhints
.flags
= hints
[0];
278 _mwmhints
.functions
= hints
[1];
279 _mwmhints
.decorations
= hints
[2];
286 void Client::getArea()
288 XWindowAttributes wattrib
;
291 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
292 assert(ret
!= BadWindow
);
294 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
295 _border_width
= wattrib
.border_width
;
299 void Client::getState()
301 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
302 _skip_taskbar
= _skip_pager
= false;
304 unsigned long *state
;
305 unsigned long num
= (unsigned) -1;
307 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
308 otk::Property::atoms
.atom
, &num
, &state
)) {
309 for (unsigned long i
= 0; i
< num
; ++i
) {
310 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
312 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
314 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
315 _skip_taskbar
= true;
316 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
318 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
320 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
322 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
324 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
326 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
335 void Client::getShaped()
339 if (otk::display
->shape()) {
344 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
346 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
347 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
354 void Client::calcLayer() {
357 if (_iconic
) l
= Layer_Icon
;
358 else if (_fullscreen
) l
= Layer_Fullscreen
;
359 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
360 else if (_type
== Type_Dock
) {
361 if (!_below
) l
= Layer_Top
;
362 else l
= Layer_Normal
;
364 else if (_above
) l
= Layer_Above
;
365 else if (_below
) l
= Layer_Below
;
366 else l
= Layer_Normal
;
372 if we don't have a frame, then we aren't mapped yet (and this would
375 openbox
->screen(_screen
)->raiseWindow(this);
381 void Client::updateProtocols()
386 _focus_notify
= false;
387 _delete_window
= false;
389 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
390 for (int i
= 0; i
< num_return
; ++i
) {
391 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
392 // this means we can request the window to close
393 _delete_window
= true;
394 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
395 // if this protocol is requested, then the window will be notified
396 // by the window manager whenever it receives focus
397 _focus_notify
= true;
404 void Client::updateNormalHints()
408 int oldgravity
= _gravity
;
411 _size_inc
.setPoint(1, 1);
412 _base_size
.setPoint(0, 0);
413 _min_size
.setPoint(0, 0);
414 _max_size
.setPoint(INT_MAX
, INT_MAX
);
416 // XXX: might want to cancel any interactive resizing of the window at this
419 // get the hints from the window
420 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
421 _positioned
= (size
.flags
& (PPosition
|USPosition
));
423 if (size
.flags
& PWinGravity
) {
424 _gravity
= size
.win_gravity
;
426 // if the client has a frame, i.e. has already been mapped and is
427 // changing its gravity
428 if (frame
&& _gravity
!= oldgravity
) {
429 // move our idea of the client's position based on its new gravity
431 frame
->frameGravity(x
, y
);
436 if (size
.flags
& PMinSize
)
437 _min_size
.setPoint(size
.min_width
, size
.min_height
);
439 if (size
.flags
& PMaxSize
)
440 _max_size
.setPoint(size
.max_width
, size
.max_height
);
442 if (size
.flags
& PBaseSize
)
443 _base_size
.setPoint(size
.base_width
, size
.base_height
);
445 if (size
.flags
& PResizeInc
)
446 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
451 void Client::updateWMHints(bool initstate
)
455 // assume a window takes input if it doesnt specify
459 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
460 if (hints
->flags
& InputHint
)
461 _can_focus
= hints
->input
;
463 // only do this when initstate is true!
464 if (initstate
&& (hints
->flags
& StateHint
))
465 _iconic
= hints
->initial_state
== IconicState
;
467 if (hints
->flags
& XUrgencyHint
)
470 if (hints
->flags
& WindowGroupHint
) {
471 if (hints
->window_group
!= _group
) {
472 // XXX: remove from the old group if there was one
473 _group
= hints
->window_group
;
474 // XXX: do stuff with the group
484 void Client::updateTitle()
489 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
490 otk::Property::utf8
, &_title
)) {
492 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
493 otk::Property::ascii
, &_title
);
497 _title
= _("Unnamed Window");
500 frame
->setTitle(_title
);
504 void Client::updateIconTitle()
509 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
510 otk::Property::utf8
, &_icon_title
)) {
512 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
513 otk::Property::ascii
, &_icon_title
);
517 _icon_title
= _("Unnamed Window");
521 void Client::updateClass()
524 _app_name
= _app_class
= _role
= "";
526 otk::Property::StringVect v
;
527 unsigned long num
= 2;
529 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
530 otk::Property::ascii
, &num
, &v
)) {
531 if (num
> 0) _app_name
= v
[0].c_str();
532 if (num
> 1) _app_class
= v
[1].c_str();
537 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
538 otk::Property::ascii
, &num
, &v
)) {
539 if (num
> 0) _role
= v
[0].c_str();
544 void Client::updateStrut()
546 unsigned long num
= 4;
548 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
549 otk::Property::atoms
.cardinal
, &num
, &data
))
553 _strut
.left
= data
[0];
554 _strut
.right
= data
[1];
555 _strut
.top
= data
[2];
556 _strut
.bottom
= data
[3];
558 openbox
->screen(_screen
)->updateStrut();
565 void Client::updateTransientFor()
570 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
571 t
!= _window
) { // cant be transient to itself!
572 c
= openbox
->findClient(t
);
573 assert(c
!= this); // if this happens then we need to check for it
575 if (!c
/*XXX: && _group*/) {
576 // not transient to a client, see if it is transient for a group
577 if (//t == _group->leader() ||
579 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
580 // window is a transient for its group!
581 // XXX: for now this is treated as non-transient.
582 // this needs to be fixed!
587 // if anything has changed...
588 if (c
!= _transient_for
) {
590 _transient_for
->_transients
.remove(this); // remove from old parent
593 _transient_for
->_transients
.push_back(this); // add to new parent
595 // XXX: change decor status?
600 void Client::propertyHandler(const XPropertyEvent
&e
)
602 otk::EventHandler::propertyHandler(e
);
604 // compress changes to a single property into a single change
606 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
607 // XXX: it would be nice to compress ALL changes to a property, not just
608 // changes in a row without other props between.
609 if (ce
.xproperty
.atom
!= e
.atom
) {
610 XPutBackEvent(**otk::display
, &ce
);
615 if (e
.atom
== XA_WM_NORMAL_HINTS
)
617 else if (e
.atom
== XA_WM_HINTS
)
619 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
620 updateTransientFor();
622 calcLayer(); // type may have changed, so update the layer
623 setupDecorAndFunctions();
624 frame
->adjustSize(); // this updates the frame for any new decor settings
626 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
627 e
.atom
== otk::Property::atoms
.wm_name
)
629 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
630 e
.atom
== otk::Property::atoms
.wm_icon_name
)
632 else if (e
.atom
== otk::Property::atoms
.wm_class
)
634 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
636 setupDecorAndFunctions();
637 frame
->adjustSize(); // update the decorations
639 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
644 void Client::setWMState(long state
)
646 if (state
== _wmstate
) return; // no change
650 setDesktop(ICONIC_DESKTOP
);
653 setDesktop(openbox
->screen(_screen
)->desktop());
659 void Client::setDesktop(long target
)
661 if (target
== _desktop
) return;
663 printf("Setting desktop %ld\n", target
);
665 if (!(target
>= 0 || target
== (signed)0xffffffff ||
666 target
== ICONIC_DESKTOP
))
671 // set the desktop hint
672 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
673 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
675 // 'move' the window to the new desktop
676 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
677 _desktop
== (signed)0xffffffff)
682 // Handle Iconic state. Iconic state is maintained by the client being a
683 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
684 // uniconifying happen.
685 bool i
= _desktop
== ICONIC_DESKTOP
;
686 if (i
!= _iconic
) { // has the state changed?
689 _wmstate
= IconicState
;
691 // we unmap the client itself so that we can get MapRequest events, and
692 // because the ICCCM tells us to!
693 XUnmapWindow(**otk::display
, _window
);
695 _wmstate
= NormalState
;
696 XMapWindow(**otk::display
, _window
);
701 frame
->adjustState();
705 void Client::setState(StateAction action
, long data1
, long data2
)
707 bool shadestate
= _shaded
;
708 bool fsstate
= _fullscreen
;
710 if (!(action
== State_Add
|| action
== State_Remove
||
711 action
== State_Toggle
))
712 return; // an invalid action was passed to the client message, ignore it
714 for (int i
= 0; i
< 2; ++i
) {
715 Atom state
= i
== 0 ? data1
: data2
;
717 if (! state
) continue;
719 // if toggling, then pick whether we're adding or removing
720 if (action
== State_Toggle
) {
721 if (state
== otk::Property::atoms
.net_wm_state_modal
)
722 action
= _modal
? State_Remove
: State_Add
;
723 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
724 action
= _max_vert
? State_Remove
: State_Add
;
725 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
726 action
= _max_horz
? State_Remove
: State_Add
;
727 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
728 action
= _shaded
? State_Remove
: State_Add
;
729 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
730 action
= _skip_taskbar
? State_Remove
: State_Add
;
731 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
732 action
= _skip_pager
? State_Remove
: State_Add
;
733 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
734 action
= _fullscreen
? State_Remove
: State_Add
;
735 else if (state
== otk::Property::atoms
.net_wm_state_above
)
736 action
= _above
? State_Remove
: State_Add
;
737 else if (state
== otk::Property::atoms
.net_wm_state_below
)
738 action
= _below
? State_Remove
: State_Add
;
741 if (action
== State_Add
) {
742 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
743 if (_modal
) continue;
745 // XXX: give it focus if another window has focus that shouldnt now
746 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
747 if (_max_vert
) continue;
749 // XXX: resize the window etc
750 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
751 if (_max_horz
) continue;
753 // XXX: resize the window etc
754 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
756 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
757 _skip_taskbar
= true;
758 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
760 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
762 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
763 if (_above
) continue;
765 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
766 if (_below
) continue;
770 } else { // action == State_Remove
771 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
772 if (!_modal
) continue;
774 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
775 if (!_max_vert
) continue;
777 // XXX: resize the window etc
778 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
779 if (!_max_horz
) continue;
781 // XXX: resize the window etc
782 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
784 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
785 _skip_taskbar
= false;
786 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
788 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
790 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
791 if (!_above
) continue;
793 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
794 if (!_below
) continue;
799 // change fullscreen state before shading, as it will affect if the window
801 if (fsstate
!= _fullscreen
)
803 if (shadestate
!= _shaded
)
809 void Client::toggleClientBorder(bool addborder
)
811 // adjust our idea of where the client is, based on its border. When the
812 // border is removed, the client should now be considered to be in a
813 // different position.
814 // when re-adding the border to the client, the same operation needs to be
816 int x
= _area
.x(), y
= _area
.y();
819 case NorthWestGravity
:
821 case SouthWestGravity
:
823 case NorthEastGravity
:
825 case SouthEastGravity
:
826 if (addborder
) x
-= _border_width
* 2;
827 else x
+= _border_width
* 2;
834 if (addborder
) x
-= _border_width
;
835 else x
+= _border_width
;
840 case NorthWestGravity
:
842 case NorthEastGravity
:
844 case SouthWestGravity
:
846 case SouthEastGravity
:
847 if (addborder
) y
-= _border_width
* 2;
848 else y
+= _border_width
* 2;
855 if (addborder
) y
-= _border_width
;
856 else y
+= _border_width
;
862 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
864 // move the client so it is back it the right spot _with_ its border!
865 XMoveWindow(**otk::display
, _window
, x
, y
);
867 XSetWindowBorderWidth(**otk::display
, _window
, 0);
871 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
873 otk::EventHandler::clientMessageHandler(e
);
875 if (e
.format
!= 32) return;
877 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
878 // compress changes into a single change
879 bool compress
= false;
881 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
882 // XXX: it would be nice to compress ALL messages of a type, not just
883 // messages in a row without other message types between.
884 if (ce
.xclient
.message_type
!= e
.message_type
) {
885 XPutBackEvent(**otk::display
, &ce
);
891 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
893 setWMState(e
.data
.l
[0]); // use the original event
894 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
895 // compress changes into a single change
896 bool compress
= false;
898 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
899 // XXX: it would be nice to compress ALL messages of a type, not just
900 // messages in a row without other message types between.
901 if (ce
.xclient
.message_type
!= e
.message_type
) {
902 XPutBackEvent(**otk::display
, &ce
);
908 setDesktop(e
.data
.l
[0]); // use the found event
910 setDesktop(e
.data
.l
[0]); // use the original event
911 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
912 // can't compress these
914 printf("net_wm_state %s %ld %ld for 0x%lx\n",
915 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
916 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
917 e
.data
.l
[1], e
.data
.l
[2], _window
);
919 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
920 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
922 printf("net_close_window for 0x%lx\n", _window
);
925 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
927 printf("net_active_window for 0x%lx\n", _window
);
930 setDesktop(openbox
->screen(_screen
)->desktop());
935 openbox
->screen(_screen
)->raiseWindow(this);
941 void Client::shapeHandler(const XShapeEvent
&e
)
943 otk::EventHandler::shapeHandler(e
);
945 if (e
.kind
== ShapeBounding
) {
947 frame
->adjustShape();
953 void Client::resize(Corner anchor
, int w
, int h
)
955 if (!(_functions
& Func_Resize
)) return;
956 internal_resize(anchor
, w
, h
);
960 void Client::internal_resize(Corner anchor
, int w
, int h
, int x
, int y
)
965 // for interactive resizing. have to move half an increment in each
967 w
+= _size_inc
.x() / 2;
968 h
+= _size_inc
.y() / 2;
970 // is the window resizable? if it is not, then don't check its sizes, the
971 // client can do what it wants and the user can't change it anyhow
972 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
973 // smaller than min size or bigger than max size?
974 if (w
< _min_size
.x()) w
= _min_size
.x();
975 else if (w
> _max_size
.x()) w
= _max_size
.x();
976 if (h
< _min_size
.y()) h
= _min_size
.y();
977 else if (h
> _max_size
.y()) h
= _max_size
.y();
980 // keep to the increments
984 // you cannot resize to nothing
988 // store the logical size
989 _logical_size
.setPoint(w
, h
);
997 if (x
== INT_MIN
|| y
== INT_MIN
) {
1004 x
-= w
- _area
.width();
1007 y
-= h
- _area
.height();
1010 x
-= w
- _area
.width();
1011 y
-= h
- _area
.height();
1016 _area
.setSize(w
, h
);
1018 XResizeWindow(**otk::display
, _window
, w
, h
);
1020 // resize the frame to match the request
1021 frame
->adjustSize();
1022 internal_move(x
, y
);
1026 void Client::move(int x
, int y
)
1028 if (!(_functions
& Func_Move
)) return;
1029 internal_move(x
, y
);
1033 void Client::internal_move(int x
, int y
)
1037 // move the frame to be in the requested position
1038 if (frame
) { // this can be called while mapping, before frame exists
1039 frame
->adjustPosition();
1041 // send synthetic configure notify (we don't need to if we aren't mapped
1044 event
.type
= ConfigureNotify
;
1045 event
.xconfigure
.display
= **otk::display
;
1046 event
.xconfigure
.event
= _window
;
1047 event
.xconfigure
.window
= _window
;
1048 event
.xconfigure
.x
= x
;
1049 event
.xconfigure
.y
= y
;
1050 event
.xconfigure
.width
= _area
.width();
1051 event
.xconfigure
.height
= _area
.height();
1052 event
.xconfigure
.border_width
= _border_width
;
1053 event
.xconfigure
.above
= frame
->window();
1054 event
.xconfigure
.override_redirect
= False
;
1055 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1056 StructureNotifyMask
, &event
);
1061 void Client::close()
1065 if (!(_functions
& Func_Close
)) return;
1067 // XXX: itd be cool to do timeouts and shit here for killing the client's
1069 // like... if the window is around after 5 seconds, then the close button
1070 // turns a nice red, and if this function is called again, the client is
1071 // explicitly killed.
1073 ce
.xclient
.type
= ClientMessage
;
1074 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1075 ce
.xclient
.display
= **otk::display
;
1076 ce
.xclient
.window
= _window
;
1077 ce
.xclient
.format
= 32;
1078 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1079 ce
.xclient
.data
.l
[1] = CurrentTime
;
1080 ce
.xclient
.data
.l
[2] = 0l;
1081 ce
.xclient
.data
.l
[3] = 0l;
1082 ce
.xclient
.data
.l
[4] = 0l;
1083 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1087 void Client::changeState()
1089 unsigned long state
[2];
1090 state
[0] = _wmstate
;
1092 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1093 otk::Property::atoms
.wm_state
, state
, 2);
1098 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1100 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1102 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1104 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1106 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1108 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1110 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1112 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1114 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1116 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1117 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1118 otk::Property::atoms
.atom
, netstate
, num
);
1123 frame
->adjustState();
1127 void Client::changeAllowedActions(void)
1132 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1134 if (_functions
& Func_Shade
)
1135 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1136 if (_functions
& Func_Close
)
1137 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1138 if (_functions
& Func_Move
)
1139 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1140 if (_functions
& Func_Iconify
)
1141 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1142 if (_functions
& Func_Resize
)
1143 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1144 if (_functions
& Func_Fullscreen
)
1145 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1146 if (_functions
& Func_Maximize
) {
1147 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1148 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1151 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1152 otk::Property::atoms
.atom
, actions
, num
);
1156 void Client::applyStartupState()
1158 // these are in a carefully crafted order..
1162 _desktop
= 0; // set some other source desktop so this goes through
1163 setDesktop(ICONIC_DESKTOP
);
1166 _fullscreen
= false;
1174 if (_max_vert
); // XXX: incomplete
1175 if (_max_horz
); // XXX: incomplete
1177 if (_skip_taskbar
); // nothing to do for this
1178 if (_skip_pager
); // nothing to do for this
1179 if (_modal
); // nothing to do for this
1180 if (_above
); // nothing to do for this
1181 if (_below
); // nothing to do for this
1185 void Client::shade(bool shade
)
1187 if (!(_functions
& Func_Shade
) || // can't
1188 _shaded
== shade
) return; // already done
1190 // when we're iconic, don't change the wmstate
1192 _wmstate
= shade
? IconicState
: NormalState
;
1195 frame
->adjustSize();
1199 void Client::fullscreen(bool fs
)
1201 static FunctionFlags saved_func
;
1202 static DecorationFlags saved_decor
;
1203 static otk::Rect saved_area
;
1204 static otk::Point saved_logical_size
;
1206 if (!(_functions
& Func_Fullscreen
) || // can't
1207 _fullscreen
== fs
) return; // already done
1210 changeState(); // change the state hints on the client
1213 // save the functions and remove them
1214 saved_func
= _functions
;
1215 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1216 // save the decorations and remove them
1217 saved_decor
= _decorations
;
1219 // save the area and adjust it (we don't call internal resize here for
1220 // constraints on the size, etc, we just make it fullscreen).
1222 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1223 _area
.setRect(0, 0, info
->width(), info
->height());
1224 saved_logical_size
= _logical_size
;
1225 _logical_size
.setPoint((info
->width() - _base_size
.x()) / _size_inc
.x(),
1226 (info
->height() - _base_size
.y()) / _size_inc
.y());
1228 _functions
= saved_func
;
1229 _decorations
= saved_decor
;
1231 _logical_size
= saved_logical_size
;
1234 changeAllowedActions(); // based on the new _functions
1236 frame
->adjustSize(); // drop/replace the decor's and resize
1237 frame
->adjustPosition(); // get (back) in position!
1239 // raise (back) into our stacking layer
1240 openbox
->screen(_screen
)->raiseWindow(this);
1242 // try focus us when we go into fullscreen mode
1247 bool Client::focus() const
1249 // won't try focus if the client doesn't want it, or if the window isn't
1250 // visible on the screen
1251 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1253 if (_focused
) return true;
1255 // do a check to see if the window has already been unmapped or destroyed
1257 if (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
) ||
1258 XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1259 XPutBackEvent(**otk::display
, &ev
);
1264 XSetInputFocus(**otk::display
, _window
,
1265 RevertToNone
, CurrentTime
);
1267 if (_focus_notify
) {
1269 ce
.xclient
.type
= ClientMessage
;
1270 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1271 ce
.xclient
.display
= **otk::display
;
1272 ce
.xclient
.window
= _window
;
1273 ce
.xclient
.format
= 32;
1274 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1275 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1276 ce
.xclient
.data
.l
[2] = 0l;
1277 ce
.xclient
.data
.l
[3] = 0l;
1278 ce
.xclient
.data
.l
[4] = 0l;
1279 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1286 void Client::unfocus() const
1288 if (!_focused
) return;
1290 assert(openbox
->focusedClient() == this);
1291 openbox
->setFocusedClient(0);
1295 void Client::focusHandler(const XFocusChangeEvent
&e
)
1298 // printf("FocusIn for 0x%lx\n", e.window);
1301 otk::EventHandler::focusHandler(e
);
1306 openbox
->setFocusedClient(this);
1310 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1313 // printf("FocusOut for 0x%lx\n", e.window);
1316 otk::EventHandler::unfocusHandler(e
);
1321 if (openbox
->focusedClient() == this)
1322 openbox
->setFocusedClient(0);
1326 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1329 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1332 otk::EventHandler::configureRequestHandler(e
);
1334 // if we are iconic (or shaded (fvwm does this)) ignore the event
1335 if (_iconic
|| _shaded
) return;
1337 if (e
.value_mask
& CWBorderWidth
)
1338 _border_width
= e
.border_width
;
1340 // resize, then move, as specified in the EWMH section 7.7
1341 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1342 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1343 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1347 case NorthEastGravity
:
1351 case SouthWestGravity
:
1353 corner
= BottomLeft
;
1355 case SouthEastGravity
:
1356 corner
= BottomRight
;
1358 default: // NorthWest, Static, etc
1362 // if moving AND resizing ...
1363 if (e
.value_mask
& (CWX
| CWY
)) {
1364 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1365 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1366 internal_resize(corner
, w
, h
, x
, y
);
1367 } else // if JUST resizing...
1368 internal_resize(corner
, w
, h
);
1369 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1370 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1371 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1372 internal_move(x
, y
);
1375 if (e
.value_mask
& CWStackMode
) {
1379 openbox
->screen(_screen
)->lowerWindow(this);
1385 openbox
->screen(_screen
)->raiseWindow(this);
1392 void Client::unmapHandler(const XUnmapEvent
&e
)
1394 if (ignore_unmaps
) {
1396 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1403 printf("UnmapNotify for 0x%lx\n", e
.window
);
1406 otk::EventHandler::unmapHandler(e
);
1408 // this deletes us etc
1409 openbox
->screen(_screen
)->unmanageWindow(this);
1413 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1416 printf("DestroyNotify for 0x%lx\n", e
.window
);
1419 otk::EventHandler::destroyHandler(e
);
1421 // this deletes us etc
1422 openbox
->screen(_screen
)->unmanageWindow(this);
1426 void Client::reparentHandler(const XReparentEvent
&e
)
1428 // this is when the client is first taken captive in the frame
1429 if (e
.parent
== frame
->plate()) return;
1432 printf("ReparentNotify for 0x%lx\n", e
.window
);
1435 otk::EventHandler::reparentHandler(e
);
1438 This event is quite rare and is usually handled in unmapHandler.
1439 However, if the window is unmapped when the reparent event occurs,
1440 the window manager never sees it because an unmap event is not sent
1441 to an already unmapped window.
1444 // we don't want the reparent event, put it back on the stack for the X
1445 // server to deal with after we unmanage the window
1448 XPutBackEvent(**otk::display
, &ev
);
1450 // this deletes us etc
1451 openbox
->screen(_screen
)->unmanageWindow(this);
1454 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1457 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1460 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1462 // move to the current desktop (uniconify)
1463 setDesktop(openbox
->screen(_screen
)->desktop());
1464 // XXX: should we focus/raise the window? (basically a net_wm_active_window)