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 // the state is kinda assumed to be normal. is this right? XXX
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 setupDecorAndFunctions();
63 getGravity(); // get the attribute gravity
64 updateNormalHints(); // this may override the attribute gravity
77 // clean up childrens' references
78 while (!_transients
.empty()) {
79 _transients
.front()->_transient_for
= 0;
80 _transients
.pop_front();
83 // clean up parents reference to this
85 _transient_for
->_transients
.remove(this); // remove from old parent
87 if (openbox
->state() != Openbox::State_Exiting
) {
88 // these values should not be persisted across a window unmapping/mapping
89 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
90 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
95 void Client::getGravity()
97 XWindowAttributes wattrib
;
100 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
101 assert(ret
!= BadWindow
);
102 _gravity
= wattrib
.win_gravity
;
106 void Client::getDesktop()
108 // defaults to the current desktop
109 _desktop
= openbox
->screen(_screen
)->desktop();
111 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
112 otk::Property::atoms
.cardinal
,
113 (long unsigned*)&_desktop
)) {
114 // make sure the hint exists
115 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
116 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
121 void Client::getType()
123 _type
= (WindowType
) -1;
126 unsigned long num
= (unsigned) -1;
127 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
128 otk::Property::atoms
.atom
, &num
, &val
)) {
129 // use the first value that we know about in the array
130 for (unsigned long i
= 0; i
< num
; ++i
) {
131 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
132 _type
= Type_Desktop
;
133 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
135 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
136 _type
= Type_Toolbar
;
137 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
139 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
140 _type
= Type_Utility
;
141 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
143 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
145 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
147 // XXX: make this work again
148 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
149 // mwm_decorations = 0; // prevent this window from getting any decor
150 if (_type
!= (WindowType
) -1)
151 break; // grab the first known type
156 if (_type
== (WindowType
) -1) {
158 * the window type hint was not set, which means we either classify ourself
159 * as a normal window or a dialog, depending on if we are a transient.
169 void Client::setupDecorAndFunctions()
171 // start with everything
172 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
173 Decor_Iconify
| Decor_Maximize
;
174 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
178 // normal windows retain all of the possible decorations and
182 // dialogs cannot be maximized
183 _decorations
&= ~Decor_Maximize
;
184 _functions
&= ~Func_Maximize
;
190 // these windows get less functionality
191 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
192 _functions
&= ~(Func_Iconify
| Func_Resize
);
198 // none of these windows are manipulated by the window manager
204 // Mwm Hints are applied subtractively to what has already been chosen for
205 // decor and functionality
206 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
207 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
208 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
209 _decorations
&= ~Decor_Border
;
210 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
211 _decorations
&= ~Decor_Handle
;
212 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
213 _decorations
&= ~Decor_Titlebar
;
214 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
215 _decorations
&= ~Decor_Iconify
;
216 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
217 _decorations
&= ~Decor_Maximize
;
221 if (_mwmhints
.flags
& MwmFlag_Functions
) {
222 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
223 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
224 _functions
&= ~Func_Resize
;
225 if (! (_mwmhints
.functions
& MwmFunc_Move
))
226 _functions
&= ~Func_Move
;
227 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
228 _functions
&= ~Func_Iconify
;
229 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
230 _functions
&= ~Func_Maximize
;
231 // dont let mwm hints kill the close button
232 //if (! (_mwmhints.functions & MwmFunc_Close))
233 // _functions &= ~Func_Close;
237 changeAllowedActions();
241 void Client::getMwmHints()
243 unsigned long num
= MwmHints::elements
;
244 unsigned long *hints
;
246 _mwmhints
.flags
= 0; // default to none
248 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
249 otk::Property::atoms
.motif_wm_hints
, &num
,
250 (unsigned long **)&hints
))
253 if (num
>= MwmHints::elements
) {
254 // retrieved the hints
255 _mwmhints
.flags
= hints
[0];
256 _mwmhints
.functions
= hints
[1];
257 _mwmhints
.decorations
= hints
[2];
264 void Client::getArea()
266 XWindowAttributes wattrib
;
269 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
270 assert(ret
!= BadWindow
);
272 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
273 _border_width
= wattrib
.border_width
;
277 void Client::getState()
279 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
280 _skip_taskbar
= _skip_pager
= false;
282 unsigned long *state
;
283 unsigned long num
= (unsigned) -1;
285 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
286 otk::Property::atoms
.atom
, &num
, &state
)) {
287 for (unsigned long i
= 0; i
< num
; ++i
) {
288 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
290 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
) {
292 _wmstate
= IconicState
;
293 } else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
294 _skip_taskbar
= true;
295 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
297 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
299 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
301 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
303 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
305 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
314 void Client::getShaped()
318 if (otk::display
->shape()) {
323 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
325 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
326 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
333 void Client::calcLayer() {
336 if (_iconic
) l
= Layer_Icon
;
337 else if (_fullscreen
) l
= Layer_Fullscreen
;
338 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
339 else if (_type
== Type_Dock
) {
340 if (!_below
) l
= Layer_Top
;
341 else l
= Layer_Normal
;
343 else if (_above
) l
= Layer_Above
;
344 else if (_below
) l
= Layer_Below
;
345 else l
= Layer_Normal
;
351 if we don't have a frame, then we aren't mapped yet (and this would
354 openbox
->screen(_screen
)->raiseWindow(this);
360 void Client::updateProtocols()
365 _focus_notify
= false;
366 _decorations
&= ~Decor_Close
;
367 _functions
&= ~Func_Close
;
369 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
370 for (int i
= 0; i
< num_return
; ++i
) {
371 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
372 _decorations
|= Decor_Close
;
373 _functions
|= Func_Close
;
375 frame
->adjustSize(); // update the decorations
376 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
377 // if this protocol is requested, then the window will be notified
378 // by the window manager whenever it receives focus
379 _focus_notify
= true;
386 void Client::updateNormalHints()
390 int oldgravity
= _gravity
;
393 _size_inc
.setPoint(1, 1);
394 _base_size
.setPoint(0, 0);
395 _min_size
.setPoint(0, 0);
396 _max_size
.setPoint(INT_MAX
, INT_MAX
);
398 // XXX: might want to cancel any interactive resizing of the window at this
401 // get the hints from the window
402 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
403 _positioned
= (size
.flags
& (PPosition
|USPosition
));
405 if (size
.flags
& PWinGravity
) {
406 _gravity
= size
.win_gravity
;
408 // if the client has a frame, i.e. has already been mapped and is
409 // changing its gravity
410 if (frame
&& _gravity
!= oldgravity
) {
411 // move our idea of the client's position based on its new gravity
413 frame
->frameGravity(x
, y
);
418 if (size
.flags
& PMinSize
)
419 _min_size
.setPoint(size
.min_width
, size
.min_height
);
421 if (size
.flags
& PMaxSize
)
422 _max_size
.setPoint(size
.max_width
, size
.max_height
);
424 if (size
.flags
& PBaseSize
)
425 _base_size
.setPoint(size
.base_width
, size
.base_height
);
427 if (size
.flags
& PResizeInc
)
428 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
433 void Client::updateWMHints()
437 // assume a window takes input if it doesnt specify
441 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
442 if (hints
->flags
& InputHint
)
443 _can_focus
= hints
->input
;
445 if (hints
->flags
& XUrgencyHint
)
448 if (hints
->flags
& WindowGroupHint
) {
449 if (hints
->window_group
!= _group
) {
450 // XXX: remove from the old group if there was one
451 _group
= hints
->window_group
;
452 // XXX: do stuff with the group
462 void Client::updateTitle()
467 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
468 otk::Property::utf8
, &_title
)) {
470 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
471 otk::Property::ascii
, &_title
);
475 _title
= _("Unnamed Window");
478 frame
->setTitle(_title
);
482 void Client::updateIconTitle()
487 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
488 otk::Property::utf8
, &_icon_title
)) {
490 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
491 otk::Property::ascii
, &_icon_title
);
495 _icon_title
= _("Unnamed Window");
499 void Client::updateClass()
502 _app_name
= _app_class
= _role
= "";
504 otk::Property::StringVect v
;
505 unsigned long num
= 2;
507 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
508 otk::Property::ascii
, &num
, &v
)) {
509 if (num
> 0) _app_name
= v
[0].c_str();
510 if (num
> 1) _app_class
= v
[1].c_str();
515 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
516 otk::Property::ascii
, &num
, &v
)) {
517 if (num
> 0) _role
= v
[0].c_str();
522 void Client::updateStrut()
524 unsigned long num
= 4;
526 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
527 otk::Property::atoms
.cardinal
, &num
, &data
))
531 _strut
.left
= data
[0];
532 _strut
.right
= data
[1];
533 _strut
.top
= data
[2];
534 _strut
.bottom
= data
[3];
536 openbox
->screen(_screen
)->updateStrut();
543 void Client::updateTransientFor()
548 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
549 t
!= _window
) { // cant be transient to itself!
550 c
= openbox
->findClient(t
);
551 assert(c
!= this); // if this happens then we need to check for it
553 if (!c
/*XXX: && _group*/) {
554 // not transient to a client, see if it is transient for a group
555 if (//t == _group->leader() ||
557 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
558 // window is a transient for its group!
559 // XXX: for now this is treated as non-transient.
560 // this needs to be fixed!
565 // if anything has changed...
566 if (c
!= _transient_for
) {
568 _transient_for
->_transients
.remove(this); // remove from old parent
571 _transient_for
->_transients
.push_back(this); // add to new parent
573 // XXX: change decor status?
578 void Client::propertyHandler(const XPropertyEvent
&e
)
580 otk::EventHandler::propertyHandler(e
);
582 // compress changes to a single property into a single change
584 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
585 // XXX: it would be nice to compress ALL changes to a property, not just
586 // changes in a row without other props between.
587 if (ce
.xproperty
.atom
!= e
.atom
) {
588 XPutBackEvent(**otk::display
, &ce
);
593 if (e
.atom
== XA_WM_NORMAL_HINTS
)
595 else if (e
.atom
== XA_WM_HINTS
)
597 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
598 updateTransientFor();
600 calcLayer(); // type may have changed, so update the layer
601 setupDecorAndFunctions();
602 frame
->adjustSize(); // this updates the frame for any new decor settings
604 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
605 e
.atom
== otk::Property::atoms
.wm_name
)
607 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
608 e
.atom
== otk::Property::atoms
.wm_icon_name
)
610 else if (e
.atom
== otk::Property::atoms
.wm_class
)
612 else if (e
.atom
== otk::Property::atoms
.wm_protocols
)
614 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
619 void Client::setWMState(long state
)
621 if (state
== _wmstate
) return; // no change
626 // XXX: cause it to iconify
629 // XXX: cause it to uniconify
635 void Client::setDesktop(long target
)
637 if (target
== _desktop
) return;
639 printf("Setting desktop %ld\n", target
);
641 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
645 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
646 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
648 // 'move' the window to the new desktop
649 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
650 _desktop
== (signed)0xffffffff)
657 void Client::setState(StateAction action
, long data1
, long data2
)
659 bool shadestate
= _shaded
;
661 if (!(action
== State_Add
|| action
== State_Remove
||
662 action
== State_Toggle
))
663 return; // an invalid action was passed to the client message, ignore it
665 for (int i
= 0; i
< 2; ++i
) {
666 Atom state
= i
== 0 ? data1
: data2
;
668 if (! state
) continue;
670 // if toggling, then pick whether we're adding or removing
671 if (action
== State_Toggle
) {
672 if (state
== otk::Property::atoms
.net_wm_state_modal
)
673 action
= _modal
? State_Remove
: State_Add
;
674 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
675 action
= _max_vert
? State_Remove
: State_Add
;
676 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
677 action
= _max_horz
? State_Remove
: State_Add
;
678 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
679 action
= _shaded
? State_Remove
: State_Add
;
680 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
681 action
= _skip_taskbar
? State_Remove
: State_Add
;
682 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
683 action
= _skip_pager
? State_Remove
: State_Add
;
684 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
685 action
= _fullscreen
? State_Remove
: State_Add
;
686 else if (state
== otk::Property::atoms
.net_wm_state_above
)
687 action
= _above
? State_Remove
: State_Add
;
688 else if (state
== otk::Property::atoms
.net_wm_state_below
)
689 action
= _below
? State_Remove
: State_Add
;
692 if (action
== State_Add
) {
693 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
694 if (_modal
) continue;
696 // XXX: give it focus if another window has focus that shouldnt now
697 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
698 if (_max_vert
) continue;
700 // XXX: resize the window etc
701 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
702 if (_max_horz
) continue;
704 // XXX: resize the window etc
705 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
706 if (_shaded
) continue;
707 // shade when we're all thru here
709 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
710 _skip_taskbar
= true;
711 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
713 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
714 if (_fullscreen
) continue;
716 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
717 if (_above
) continue;
719 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
720 if (_below
) continue;
724 } else { // action == State_Remove
725 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
726 if (!_modal
) continue;
728 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
729 if (!_max_vert
) continue;
731 // XXX: resize the window etc
732 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
733 if (!_max_horz
) continue;
735 // XXX: resize the window etc
736 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
737 if (!_shaded
) continue;
738 // unshade when we're all thru here
740 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
741 _skip_taskbar
= false;
742 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
744 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
745 if (!_fullscreen
) continue;
747 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
748 if (!_above
) continue;
750 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
751 if (!_below
) continue;
756 if (shadestate
!= _shaded
)
762 void Client::toggleClientBorder(bool addborder
)
764 // adjust our idea of where the client is, based on its border. When the
765 // border is removed, the client should now be considered to be in a
766 // different position.
767 // when re-adding the border to the client, the same operation needs to be
769 int x
= _area
.x(), y
= _area
.y();
772 case NorthWestGravity
:
774 case SouthWestGravity
:
776 case NorthEastGravity
:
778 case SouthEastGravity
:
779 if (addborder
) x
-= _border_width
* 2;
780 else x
+= _border_width
* 2;
787 if (addborder
) x
-= _border_width
;
788 else x
+= _border_width
;
793 case NorthWestGravity
:
795 case NorthEastGravity
:
797 case SouthWestGravity
:
799 case SouthEastGravity
:
800 if (addborder
) y
-= _border_width
* 2;
801 else y
+= _border_width
* 2;
808 if (addborder
) y
-= _border_width
;
809 else y
+= _border_width
;
815 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
817 // move the client so it is back it the right spot _with_ its border!
818 XMoveWindow(**otk::display
, _window
, x
, y
);
820 XSetWindowBorderWidth(**otk::display
, _window
, 0);
824 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
826 otk::EventHandler::clientMessageHandler(e
);
828 if (e
.format
!= 32) return;
830 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
831 // compress changes into a single change
832 bool compress
= false;
834 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
835 // XXX: it would be nice to compress ALL messages of a type, not just
836 // messages in a row without other message types between.
837 if (ce
.xclient
.message_type
!= e
.message_type
) {
838 XPutBackEvent(**otk::display
, &ce
);
844 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
846 setWMState(e
.data
.l
[0]); // use the original event
847 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
848 // compress changes into a single change
849 bool compress
= false;
851 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
852 // XXX: it would be nice to compress ALL messages of a type, not just
853 // messages in a row without other message types between.
854 if (ce
.xclient
.message_type
!= e
.message_type
) {
855 XPutBackEvent(**otk::display
, &ce
);
861 setDesktop(e
.data
.l
[0]); // use the found event
863 setDesktop(e
.data
.l
[0]); // use the original event
864 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
865 // can't compress these
867 printf("net_wm_state %s %ld %ld for 0x%lx\n",
868 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
869 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
870 e
.data
.l
[1], e
.data
.l
[2], _window
);
872 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
873 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
875 printf("net_close_window for 0x%lx\n", _window
);
878 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
880 printf("net_active_window for 0x%lx\n", _window
);
886 openbox
->screen(_screen
)->raiseWindow(this);
892 void Client::shapeHandler(const XShapeEvent
&e
)
894 otk::EventHandler::shapeHandler(e
);
896 if (e
.kind
== ShapeBounding
) {
898 frame
->adjustShape();
904 void Client::resize(Corner anchor
, int w
, int h
)
906 if (!(_functions
& Func_Resize
)) return;
907 internal_resize(anchor
, w
, h
);
911 void Client::internal_resize(Corner anchor
, int w
, int h
, int x
, int y
)
916 // for interactive resizing. have to move half an increment in each
918 w
+= _size_inc
.x() / 2;
919 h
+= _size_inc
.y() / 2;
921 // is the window resizable? if it is not, then don't check its sizes, the
922 // client can do what it wants and the user can't change it anyhow
923 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
924 // smaller than min size or bigger than max size?
925 if (w
< _min_size
.x()) w
= _min_size
.x();
926 else if (w
> _max_size
.x()) w
= _max_size
.x();
927 if (h
< _min_size
.y()) h
= _min_size
.y();
928 else if (h
> _max_size
.y()) h
= _max_size
.y();
931 // keep to the increments
935 // you cannot resize to nothing
939 // store the logical size
940 _logical_size
.setPoint(w
, h
);
948 if (x
== INT_MIN
|| y
== INT_MIN
) {
955 x
-= w
- _area
.width();
958 y
-= h
- _area
.height();
961 x
-= w
- _area
.width();
962 y
-= h
- _area
.height();
969 XResizeWindow(**otk::display
, _window
, w
, h
);
971 // resize the frame to match the request
977 void Client::move(int x
, int y
)
979 if (!(_functions
& Func_Move
)) return;
984 void Client::internal_move(int x
, int y
)
988 // move the frame to be in the requested position
989 if (frame
) { // this can be called while mapping, before frame exists
990 frame
->adjustPosition();
992 // send synthetic configure notify (we don't need to if we aren't mapped
995 event
.type
= ConfigureNotify
;
996 event
.xconfigure
.display
= **otk::display
;
997 event
.xconfigure
.event
= _window
;
998 event
.xconfigure
.window
= _window
;
999 event
.xconfigure
.x
= x
;
1000 event
.xconfigure
.y
= y
;
1001 event
.xconfigure
.width
= _area
.width();
1002 event
.xconfigure
.height
= _area
.height();
1003 event
.xconfigure
.border_width
= _border_width
;
1004 event
.xconfigure
.above
= frame
->window();
1005 event
.xconfigure
.override_redirect
= False
;
1006 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1007 StructureNotifyMask
, &event
);
1012 void Client::close()
1016 if (!(_functions
& Func_Close
)) return;
1018 // XXX: itd be cool to do timeouts and shit here for killing the client's
1020 // like... if the window is around after 5 seconds, then the close button
1021 // turns a nice red, and if this function is called again, the client is
1022 // explicitly killed.
1024 ce
.xclient
.type
= ClientMessage
;
1025 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1026 ce
.xclient
.display
= **otk::display
;
1027 ce
.xclient
.window
= _window
;
1028 ce
.xclient
.format
= 32;
1029 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1030 ce
.xclient
.data
.l
[1] = CurrentTime
;
1031 ce
.xclient
.data
.l
[2] = 0l;
1032 ce
.xclient
.data
.l
[3] = 0l;
1033 ce
.xclient
.data
.l
[4] = 0l;
1034 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1038 void Client::changeState()
1040 unsigned long state
[2];
1041 state
[0] = _wmstate
;
1043 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1044 otk::Property::atoms
.wm_state
, state
, 2);
1049 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1051 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1053 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1055 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1057 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1059 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1061 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1063 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1065 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1067 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1068 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1069 otk::Property::atoms
.atom
, netstate
, num
);
1075 void Client::changeAllowedActions(void)
1080 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1081 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1083 if (_functions
& Func_Close
)
1084 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1085 if (_functions
& Func_Move
)
1086 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1087 if (_functions
& Func_Resize
)
1088 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1089 if (_functions
& Func_Maximize
) {
1090 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1091 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1094 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1095 otk::Property::atoms
.atom
, actions
, num
);
1099 void Client::shade(bool shade
)
1101 if (shade
== _shaded
) return; // already done
1103 _wmstate
= shade
? IconicState
: NormalState
;
1106 frame
->adjustSize();
1110 bool Client::focus() const
1112 // won't try focus if the client doesn't want it, or if the window isn't
1113 // visible on the screen
1114 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1116 if (_focused
) return true;
1119 XSetInputFocus(**otk::display
, _window
,
1120 RevertToNone
, CurrentTime
);
1122 if (_focus_notify
) {
1124 ce
.xclient
.type
= ClientMessage
;
1125 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1126 ce
.xclient
.display
= **otk::display
;
1127 ce
.xclient
.window
= _window
;
1128 ce
.xclient
.format
= 32;
1129 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1130 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1131 ce
.xclient
.data
.l
[2] = 0l;
1132 ce
.xclient
.data
.l
[3] = 0l;
1133 ce
.xclient
.data
.l
[4] = 0l;
1134 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1141 void Client::unfocus() const
1143 if (!_focused
) return;
1145 assert(openbox
->focusedClient() == this);
1146 openbox
->setFocusedClient(0);
1150 void Client::focusHandler(const XFocusChangeEvent
&e
)
1153 // printf("FocusIn for 0x%lx\n", e.window);
1156 otk::EventHandler::focusHandler(e
);
1161 openbox
->setFocusedClient(this);
1165 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1168 // printf("FocusOut for 0x%lx\n", e.window);
1171 otk::EventHandler::unfocusHandler(e
);
1176 if (openbox
->focusedClient() == this)
1177 openbox
->setFocusedClient(0);
1181 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1184 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1187 otk::EventHandler::configureRequestHandler(e
);
1189 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1191 if (e
.value_mask
& CWBorderWidth
)
1192 _border_width
= e
.border_width
;
1194 // resize, then move, as specified in the EWMH section 7.7
1195 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1196 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1197 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1201 case NorthEastGravity
:
1205 case SouthWestGravity
:
1207 corner
= BottomLeft
;
1209 case SouthEastGravity
:
1210 corner
= BottomRight
;
1212 default: // NorthWest, Static, etc
1216 // if moving AND resizing ...
1217 if (e
.value_mask
& (CWX
| CWY
)) {
1218 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1219 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1220 internal_resize(corner
, w
, h
, x
, y
);
1221 } else // if JUST resizing...
1222 internal_resize(corner
, w
, h
);
1223 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1224 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1225 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1226 internal_move(x
, y
);
1229 if (e
.value_mask
& CWStackMode
) {
1233 openbox
->screen(_screen
)->lowerWindow(this);
1239 openbox
->screen(_screen
)->raiseWindow(this);
1246 void Client::unmapHandler(const XUnmapEvent
&e
)
1248 if (ignore_unmaps
) {
1250 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1257 printf("UnmapNotify for 0x%lx\n", e
.window
);
1260 otk::EventHandler::unmapHandler(e
);
1262 // this deletes us etc
1263 openbox
->screen(_screen
)->unmanageWindow(this);
1267 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1270 printf("DestroyNotify for 0x%lx\n", e
.window
);
1273 otk::EventHandler::destroyHandler(e
);
1275 // this deletes us etc
1276 openbox
->screen(_screen
)->unmanageWindow(this);
1280 void Client::reparentHandler(const XReparentEvent
&e
)
1282 // this is when the client is first taken captive in the frame
1283 if (e
.parent
== frame
->plate()) return;
1286 printf("ReparentNotify for 0x%lx\n", e
.window
);
1289 otk::EventHandler::reparentHandler(e
);
1292 This event is quite rare and is usually handled in unmapHandler.
1293 However, if the window is unmapped when the reparent event occurs,
1294 the window manager never sees it because an unmap event is not sent
1295 to an already unmapped window.
1298 // we don't want the reparent event, put it back on the stack for the X
1299 // server to deal with after we unmanage the window
1302 XPutBackEvent(**otk::display
, &ev
);
1304 // this deletes us etc
1305 openbox
->screen(_screen
)->unmanageWindow(this);