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 // XXX: 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
, int x
, int y
)
909 // for interactive resizing. have to move half an increment in each
911 w
+= _size_inc
.x() / 2;
912 h
+= _size_inc
.y() / 2;
914 // is the window resizable? if it is not, then don't check its sizes, the
915 // client can do what it wants and the user can't change it anyhow
916 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
917 // smaller than min size or bigger than max size?
918 if (w
< _min_size
.x()) w
= _min_size
.x();
919 else if (w
> _max_size
.x()) w
= _max_size
.x();
920 if (h
< _min_size
.y()) h
= _min_size
.y();
921 else if (h
> _max_size
.y()) h
= _max_size
.y();
924 // keep to the increments
928 // you cannot resize to nothing
932 // store the logical size
933 _logical_size
.setPoint(w
, h
);
941 if (x
== INT_MIN
|| y
== INT_MIN
) {
948 x
-= w
- _area
.width();
951 y
-= h
- _area
.height();
954 x
-= w
- _area
.width();
955 y
-= h
- _area
.height();
962 XResizeWindow(**otk::display
, _window
, w
, h
);
964 // resize the frame to match the request
970 void Client::move(int x
, int y
)
974 // move the frame to be in the requested position
975 if (frame
) { // this can be called while mapping, before frame exists
976 frame
->adjustPosition();
978 // send synthetic configure notify (we don't need to if we aren't mapped
981 event
.type
= ConfigureNotify
;
982 event
.xconfigure
.display
= **otk::display
;
983 event
.xconfigure
.event
= _window
;
984 event
.xconfigure
.window
= _window
;
985 event
.xconfigure
.x
= x
;
986 event
.xconfigure
.y
= y
;
987 event
.xconfigure
.width
= _area
.width();
988 event
.xconfigure
.height
= _area
.height();
989 event
.xconfigure
.border_width
= _border_width
;
990 event
.xconfigure
.above
= frame
->window();
991 event
.xconfigure
.override_redirect
= False
;
992 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
993 StructureNotifyMask
, &event
);
1002 if (!(_functions
& Func_Close
)) return;
1004 // XXX: itd be cool to do timeouts and shit here for killing the client's
1006 // like... if the window is around after 5 seconds, then the close button
1007 // turns a nice red, and if this function is called again, the client is
1008 // explicitly killed.
1010 ce
.xclient
.type
= ClientMessage
;
1011 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1012 ce
.xclient
.display
= **otk::display
;
1013 ce
.xclient
.window
= _window
;
1014 ce
.xclient
.format
= 32;
1015 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1016 ce
.xclient
.data
.l
[1] = CurrentTime
;
1017 ce
.xclient
.data
.l
[2] = 0l;
1018 ce
.xclient
.data
.l
[3] = 0l;
1019 ce
.xclient
.data
.l
[4] = 0l;
1020 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1024 void Client::changeState()
1026 unsigned long state
[2];
1027 state
[0] = _wmstate
;
1029 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1030 otk::Property::atoms
.wm_state
, state
, 2);
1035 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1037 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1039 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1041 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1043 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1045 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1047 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1049 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1051 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1053 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1054 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1055 otk::Property::atoms
.atom
, netstate
, num
);
1061 void Client::shade(bool shade
)
1063 if (shade
== _shaded
) return; // already done
1065 _wmstate
= shade
? IconicState
: NormalState
;
1068 frame
->adjustSize();
1072 bool Client::focus() const
1074 // won't try focus if the client doesn't want it, or if the window isn't
1075 // visible on the screen
1076 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1078 if (_focused
) return true;
1081 XSetInputFocus(**otk::display
, _window
,
1082 RevertToNone
, CurrentTime
);
1084 if (_focus_notify
) {
1086 ce
.xclient
.type
= ClientMessage
;
1087 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1088 ce
.xclient
.display
= **otk::display
;
1089 ce
.xclient
.window
= _window
;
1090 ce
.xclient
.format
= 32;
1091 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1092 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1093 ce
.xclient
.data
.l
[2] = 0l;
1094 ce
.xclient
.data
.l
[3] = 0l;
1095 ce
.xclient
.data
.l
[4] = 0l;
1096 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1103 void Client::unfocus() const
1105 if (!_focused
) return;
1107 assert(openbox
->focusedClient() == this);
1108 openbox
->setFocusedClient(0);
1112 void Client::focusHandler(const XFocusChangeEvent
&e
)
1115 // printf("FocusIn for 0x%lx\n", e.window);
1118 otk::EventHandler::focusHandler(e
);
1123 openbox
->setFocusedClient(this);
1127 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1130 // printf("FocusOut for 0x%lx\n", e.window);
1133 otk::EventHandler::unfocusHandler(e
);
1138 if (openbox
->focusedClient() == this)
1139 openbox
->setFocusedClient(0);
1143 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1146 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1149 otk::EventHandler::configureRequestHandler(e
);
1151 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1153 if (e
.value_mask
& CWBorderWidth
)
1154 _border_width
= e
.border_width
;
1156 // resize, then move, as specified in the EWMH section 7.7
1157 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1158 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1159 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1163 case NorthEastGravity
:
1167 case SouthWestGravity
:
1169 corner
= BottomLeft
;
1171 case SouthEastGravity
:
1172 corner
= BottomRight
;
1174 default: // NorthWest, Static, etc
1178 // if moving AND resizing ...
1179 if (e
.value_mask
& (CWX
| CWY
)) {
1180 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1181 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1182 resize(corner
, w
, h
, x
, y
);
1183 } else // if JUST resizing...
1184 resize(corner
, w
, h
);
1185 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1186 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1187 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1191 if (e
.value_mask
& CWStackMode
) {
1195 openbox
->screen(_screen
)->lowerWindow(this);
1201 openbox
->screen(_screen
)->raiseWindow(this);
1208 void Client::unmapHandler(const XUnmapEvent
&e
)
1210 if (ignore_unmaps
) {
1212 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1219 printf("UnmapNotify for 0x%lx\n", e
.window
);
1222 otk::EventHandler::unmapHandler(e
);
1224 // this deletes us etc
1225 openbox
->screen(_screen
)->unmanageWindow(this);
1229 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1232 printf("DestroyNotify for 0x%lx\n", e
.window
);
1235 otk::EventHandler::destroyHandler(e
);
1237 // this deletes us etc
1238 openbox
->screen(_screen
)->unmanageWindow(this);
1242 void Client::reparentHandler(const XReparentEvent
&e
)
1244 // this is when the client is first taken captive in the frame
1245 if (e
.parent
== frame
->plate()) return;
1248 printf("ReparentNotify for 0x%lx\n", e
.window
);
1251 otk::EventHandler::reparentHandler(e
);
1254 This event is quite rare and is usually handled in unmapHandler.
1255 However, if the window is unmapped when the reparent event occurs,
1256 the window manager never sees it because an unmap event is not sent
1257 to an already unmapped window.
1260 // we don't want the reparent event, put it back on the stack for the X
1261 // server to deal with after we unmanage the window
1264 XPutBackEvent(**otk::display
, &ev
);
1266 // this deletes us etc
1267 openbox
->screen(_screen
)->unmanageWindow(this);