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>
21 #define _(str) gettext(str)
26 Client::Client(int screen
, Window window
)
27 : otk::EventHandler(),
28 WidgetBase(WidgetBase::Type_Client
),
29 frame(0), _screen(screen
), _window(window
)
36 // update EVERYTHING the first time!!
38 // the state is kinda assumed to be normal. is this right? XXX
39 _wmstate
= NormalState
; _iconic
= false;
40 // no default decors or functions, each has to be enabled
41 _decorations
= _functions
= 0;
44 // not a transient by default of course
46 // pick a layer to start from
47 _layer
= Layer_Normal
;
56 setupDecorAndFunctions();
75 const otk::Property
*property
= openbox
->property();
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 property
->erase(_window
, otk::Property::net_wm_desktop
);
90 property
->erase(_window
, otk::Property::net_wm_state
);
95 void Client::getDesktop()
97 const otk::Property
*property
= openbox
->property();
99 // defaults to the current desktop
100 _desktop
= openbox
->screen(_screen
)->desktop();
102 if (!property
->get(_window
, otk::Property::net_wm_desktop
,
103 otk::Property::Atom_Cardinal
,
104 (long unsigned*)&_desktop
)) {
105 // make sure the hint exists
106 openbox
->property()->set(_window
,
107 otk::Property::net_wm_desktop
,
108 otk::Property::Atom_Cardinal
,
114 void Client::getType()
116 const otk::Property
*property
= openbox
->property();
118 _type
= (WindowType
) -1;
121 unsigned long num
= (unsigned) -1;
122 if (property
->get(_window
, otk::Property::net_wm_window_type
,
123 otk::Property::Atom_Atom
,
125 // use the first value that we know about in the array
126 for (unsigned long i
= 0; i
< num
; ++i
) {
128 property
->atom(otk::Property::net_wm_window_type_desktop
))
129 _type
= Type_Desktop
;
131 property
->atom(otk::Property::net_wm_window_type_dock
))
134 property
->atom(otk::Property::net_wm_window_type_toolbar
))
135 _type
= Type_Toolbar
;
137 property
->atom(otk::Property::net_wm_window_type_menu
))
140 property
->atom(otk::Property::net_wm_window_type_utility
))
141 _type
= Type_Utility
;
143 property
->atom(otk::Property::net_wm_window_type_splash
))
146 property
->atom(otk::Property::net_wm_window_type_dialog
))
149 property
->atom(otk::Property::net_wm_window_type_normal
))
151 // else if (val[i] ==
152 // property->atom(otk::Property::kde_net_wm_window_type_override))
153 // mwm_decorations = 0; // prevent this window from getting any decor
154 // XXX: make this work again
155 if (_type
!= (WindowType
) -1)
156 break; // grab the first known type
161 if (_type
== (WindowType
) -1) {
163 * the window type hint was not set, which means we either classify ourself
164 * as a normal window or a dialog, depending on if we are a transient.
174 void Client::setupDecorAndFunctions()
176 // start with everything
177 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
178 Decor_Iconify
| Decor_Maximize
;
179 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
183 // normal windows retain all of the possible decorations and
187 // dialogs cannot be maximized
188 _decorations
&= ~Decor_Maximize
;
189 _functions
&= ~Func_Maximize
;
195 // these windows get less functionality
196 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
197 _functions
&= ~(Func_Iconify
| Func_Resize
);
203 // none of these windows are manipulated by the window manager
209 // Mwm Hints are applied subtractively to what has already been chosen for
210 // decor and functionality
211 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
212 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
213 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
214 _decorations
&= ~Decor_Border
;
215 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
216 _decorations
&= ~Decor_Handle
;
217 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
218 _decorations
&= ~Decor_Titlebar
;
219 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
220 _decorations
&= ~Decor_Iconify
;
221 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
222 _decorations
&= ~Decor_Maximize
;
226 if (_mwmhints
.flags
& MwmFlag_Functions
) {
227 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
228 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
229 _functions
&= ~Func_Resize
;
230 if (! (_mwmhints
.functions
& MwmFunc_Move
))
231 _functions
&= ~Func_Move
;
232 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
233 _functions
&= ~Func_Iconify
;
234 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
235 _functions
&= ~Func_Maximize
;
236 // dont let mwm hints kill the close button
237 //if (! (_mwmhints.functions & MwmFunc_Close))
238 // _functions &= ~Func_Close;
242 // XXX: changeAllowedActions();
246 void Client::getMwmHints()
248 const otk::Property
*property
= openbox
->property();
250 unsigned long num
= MwmHints::elements
;
251 unsigned long *hints
;
253 _mwmhints
.flags
= 0; // default to none
255 if (!property
->get(_window
, otk::Property::motif_wm_hints
,
256 otk::Property::motif_wm_hints
, &num
,
257 (unsigned long **)&hints
))
260 if (num
>= MwmHints::elements
) {
261 // retrieved the hints
262 _mwmhints
.flags
= hints
[0];
263 _mwmhints
.functions
= hints
[1];
264 _mwmhints
.decorations
= hints
[2];
271 void Client::getArea()
273 XWindowAttributes wattrib
;
276 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
277 assert(ret
!= BadWindow
);
279 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
280 _border_width
= wattrib
.border_width
;
284 void Client::getState()
286 const otk::Property
*property
= openbox
->property();
288 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
289 _skip_taskbar
= _skip_pager
= false;
291 unsigned long *state
;
292 unsigned long num
= (unsigned) -1;
294 if (property
->get(_window
, otk::Property::net_wm_state
,
295 otk::Property::Atom_Atom
, &num
, &state
)) {
296 for (unsigned long i
= 0; i
< num
; ++i
) {
297 if (state
[i
] == property
->atom(otk::Property::net_wm_state_modal
))
300 property
->atom(otk::Property::net_wm_state_shaded
)) {
302 _wmstate
= IconicState
;
303 } else if (state
[i
] ==
304 property
->atom(otk::Property::net_wm_state_skip_taskbar
))
305 _skip_taskbar
= true;
307 property
->atom(otk::Property::net_wm_state_skip_pager
))
310 property
->atom(otk::Property::net_wm_state_fullscreen
))
313 property
->atom(otk::Property::net_wm_state_maximized_vert
))
316 property
->atom(otk::Property::net_wm_state_maximized_horz
))
319 property
->atom(otk::Property::net_wm_state_above
))
322 property
->atom(otk::Property::net_wm_state_below
))
331 void Client::getShaped()
335 if (otk::display
->shape()) {
340 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
342 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
343 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
350 void Client::calcLayer() {
353 if (_iconic
) l
= Layer_Icon
;
354 else if (_fullscreen
) l
= Layer_Fullscreen
;
355 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
356 else if (_type
== Type_Dock
) {
357 if (!_below
) l
= Layer_Top
;
358 else l
= Layer_Normal
;
360 else if (_above
) l
= Layer_Above
;
361 else if (_below
) l
= Layer_Below
;
362 else l
= Layer_Normal
;
368 if we don't have a frame, then we aren't mapped yet (and this would
371 openbox
->screen(_screen
)->restack(true, this); // raise
377 void Client::updateProtocols()
379 const otk::Property
*property
= openbox
->property();
384 _focus_notify
= false;
385 _decorations
&= ~Decor_Close
;
386 _functions
&= ~Func_Close
;
388 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
389 for (int i
= 0; i
< num_return
; ++i
) {
390 if (proto
[i
] == property
->atom(otk::Property::wm_delete_window
)) {
391 _decorations
|= Decor_Close
;
392 _functions
|= Func_Close
;
394 frame
->adjustSize(); // update the decorations
395 } else if (proto
[i
] == property
->atom(otk::Property::wm_take_focus
))
396 // if this protocol is requested, then the window will be notified
397 // by the window manager whenever it receives focus
398 _focus_notify
= true;
405 void Client::updateNormalHints()
409 int oldgravity
= _gravity
;
412 _gravity
= NorthWestGravity
;
413 _size_inc
.setPoint(1, 1);
414 _base_size
.setPoint(0, 0);
415 _min_size
.setPoint(0, 0);
416 _max_size
.setPoint(INT_MAX
, INT_MAX
);
418 // XXX: might want to cancel any interactive resizing of the window at this
421 // get the hints from the window
422 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
423 _positioned
= (size
.flags
& (PPosition
|USPosition
));
425 if (size
.flags
& PWinGravity
)
426 _gravity
= size
.win_gravity
;
428 if (size
.flags
& PMinSize
)
429 _min_size
.setPoint(size
.min_width
, size
.min_height
);
431 if (size
.flags
& PMaxSize
)
432 _max_size
.setPoint(size
.max_width
, size
.max_height
);
434 if (size
.flags
& PBaseSize
)
435 _base_size
.setPoint(size
.base_width
, size
.base_height
);
437 if (size
.flags
& PResizeInc
)
438 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
441 // if the client has a frame, i.e. has already been mapped and is
442 // changing its gravity
443 if (frame
&& _gravity
!= oldgravity
) {
444 // move our idea of the client's position based on its new gravity
446 frame
->frameGravity(x
, y
);
452 void Client::updateWMHints()
456 // assume a window takes input if it doesnt specify
460 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
461 if (hints
->flags
& InputHint
)
462 _can_focus
= hints
->input
;
464 if (hints
->flags
& XUrgencyHint
)
467 if (hints
->flags
& WindowGroupHint
) {
468 if (hints
->window_group
!= _group
) {
469 // XXX: remove from the old group if there was one
470 _group
= hints
->window_group
;
471 // XXX: do stuff with the group
481 void Client::updateTitle()
483 const otk::Property
*property
= openbox
->property();
488 if (! property
->get(_window
, otk::Property::net_wm_name
,
489 otk::Property::utf8
, &_title
)) {
491 property
->get(_window
, otk::Property::wm_name
,
492 otk::Property::ascii
, &_title
);
496 _title
= _("Unnamed Window");
499 frame
->setTitle(_title
);
503 void Client::updateIconTitle()
505 const otk::Property
*property
= openbox
->property();
510 if (! property
->get(_window
, otk::Property::net_wm_icon_name
,
511 otk::Property::utf8
, &_icon_title
)) {
513 property
->get(_window
, otk::Property::wm_icon_name
,
514 otk::Property::ascii
, &_icon_title
);
518 _icon_title
= _("Unnamed Window");
522 void Client::updateClass()
524 const otk::Property
*property
= openbox
->property();
527 _app_name
= _app_class
= _role
= "";
529 otk::Property::StringVect v
;
530 unsigned long num
= 2;
532 if (property
->get(_window
, otk::Property::wm_class
,
533 otk::Property::ascii
, &num
, &v
)) {
534 if (num
> 0) _app_name
= v
[0].c_str();
535 if (num
> 1) _app_class
= v
[1].c_str();
540 if (property
->get(_window
, otk::Property::wm_window_role
,
541 otk::Property::ascii
, &num
, &v
)) {
542 if (num
> 0) _role
= v
[0].c_str();
547 void Client::updateStrut()
549 unsigned long num
= 4;
551 if (!openbox
->property()->get(_window
,
552 otk::Property::net_wm_strut
,
553 otk::Property::Atom_Cardinal
,
558 _strut
.left
= data
[0];
559 _strut
.right
= data
[1];
560 _strut
.top
= data
[2];
561 _strut
.bottom
= data
[3];
563 openbox
->screen(_screen
)->updateStrut();
570 void Client::updateTransientFor()
575 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
576 t
!= _window
) { // cant be transient to itself!
577 c
= openbox
->findClient(t
);
578 assert(c
!= this); // if this happens then we need to check for it
580 if (!c
/*XXX: && _group*/) {
581 // not transient to a client, see if it is transient for a group
582 if (//t == _group->leader() ||
584 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
585 // window is a transient for its group!
586 // XXX: for now this is treated as non-transient.
587 // this needs to be fixed!
592 // if anything has changed...
593 if (c
!= _transient_for
) {
595 _transient_for
->_transients
.remove(this); // remove from old parent
598 _transient_for
->_transients
.push_back(this); // add to new parent
600 // XXX: change decor status?
605 void Client::propertyHandler(const XPropertyEvent
&e
)
607 otk::EventHandler::propertyHandler(e
);
609 const otk::Property
*property
= openbox
->property();
611 // compress changes to a single property into a single change
613 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
614 // XXX: it would be nice to compress ALL changes to a property, not just
615 // changes in a row without other props between.
616 if (ce
.xproperty
.atom
!= e
.atom
) {
617 XPutBackEvent(**otk::display
, &ce
);
622 if (e
.atom
== XA_WM_NORMAL_HINTS
)
624 else if (e
.atom
== XA_WM_HINTS
)
626 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
627 updateTransientFor();
629 calcLayer(); // type may have changed, so update the layer
630 setupDecorAndFunctions();
631 frame
->adjustSize(); // this updates the frame for any new decor settings
633 else if (e
.atom
== property
->atom(otk::Property::net_wm_name
) ||
634 e
.atom
== property
->atom(otk::Property::wm_name
))
636 else if (e
.atom
== property
->atom(otk::Property::net_wm_icon_name
) ||
637 e
.atom
== property
->atom(otk::Property::wm_icon_name
))
639 else if (e
.atom
== property
->atom(otk::Property::wm_class
))
641 else if (e
.atom
== property
->atom(otk::Property::wm_protocols
))
643 else if (e
.atom
== property
->atom(otk::Property::net_wm_strut
))
648 void Client::setWMState(long state
)
650 if (state
== _wmstate
) return; // no change
655 // XXX: cause it to iconify
658 // XXX: cause it to uniconify
664 void Client::setDesktop(long target
)
666 if (target
== _desktop
) return;
668 printf("Setting desktop %ld\n", target
);
670 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
674 openbox
->property()->set(_window
,
675 otk::Property::net_wm_desktop
,
676 otk::Property::Atom_Cardinal
,
679 // 'move' the window to the new desktop
680 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
681 _desktop
== (signed)0xffffffff)
688 void Client::setState(StateAction action
, long data1
, long data2
)
690 const otk::Property
*property
= openbox
->property();
691 bool shadestate
= _shaded
;
693 if (!(action
== State_Add
|| action
== State_Remove
||
694 action
== State_Toggle
))
695 return; // an invalid action was passed to the client message, ignore it
697 for (int i
= 0; i
< 2; ++i
) {
698 Atom state
= i
== 0 ? data1
: data2
;
700 if (! state
) continue;
702 // if toggling, then pick whether we're adding or removing
703 if (action
== State_Toggle
) {
704 if (state
== property
->atom(otk::Property::net_wm_state_modal
))
705 action
= _modal
? State_Remove
: State_Add
;
707 property
->atom(otk::Property::net_wm_state_maximized_vert
))
708 action
= _max_vert
? State_Remove
: State_Add
;
710 property
->atom(otk::Property::net_wm_state_maximized_horz
))
711 action
= _max_horz
? State_Remove
: State_Add
;
712 else if (state
== property
->atom(otk::Property::net_wm_state_shaded
))
713 action
= _shaded
? State_Remove
: State_Add
;
715 property
->atom(otk::Property::net_wm_state_skip_taskbar
))
716 action
= _skip_taskbar
? State_Remove
: State_Add
;
718 property
->atom(otk::Property::net_wm_state_skip_pager
))
719 action
= _skip_pager
? State_Remove
: State_Add
;
721 property
->atom(otk::Property::net_wm_state_fullscreen
))
722 action
= _fullscreen
? State_Remove
: State_Add
;
723 else if (state
== property
->atom(otk::Property::net_wm_state_above
))
724 action
= _above
? State_Remove
: State_Add
;
725 else if (state
== property
->atom(otk::Property::net_wm_state_below
))
726 action
= _below
? State_Remove
: State_Add
;
729 if (action
== State_Add
) {
730 if (state
== property
->atom(otk::Property::net_wm_state_modal
)) {
731 if (_modal
) continue;
733 // XXX: give it focus if another window has focus that shouldnt now
735 property
->atom(otk::Property::net_wm_state_maximized_vert
)){
736 if (_max_vert
) continue;
738 // XXX: resize the window etc
740 property
->atom(otk::Property::net_wm_state_maximized_horz
)){
741 if (_max_horz
) continue;
743 // XXX: resize the window etc
745 property
->atom(otk::Property::net_wm_state_shaded
)) {
746 if (_shaded
) continue;
747 // shade when we're all thru here
750 property
->atom(otk::Property::net_wm_state_skip_taskbar
)) {
751 _skip_taskbar
= true;
753 property
->atom(otk::Property::net_wm_state_skip_pager
)) {
756 property
->atom(otk::Property::net_wm_state_fullscreen
)) {
757 if (_fullscreen
) continue;
760 property
->atom(otk::Property::net_wm_state_above
)) {
761 if (_above
) continue;
764 property
->atom(otk::Property::net_wm_state_below
)) {
765 if (_below
) continue;
769 } else { // action == State_Remove
770 if (state
== property
->atom(otk::Property::net_wm_state_modal
)) {
771 if (!_modal
) continue;
774 property
->atom(otk::Property::net_wm_state_maximized_vert
)){
775 if (!_max_vert
) continue;
777 // XXX: resize the window etc
779 property
->atom(otk::Property::net_wm_state_maximized_horz
)){
780 if (!_max_horz
) continue;
782 // XXX: resize the window etc
784 property
->atom(otk::Property::net_wm_state_shaded
)) {
785 if (!_shaded
) continue;
786 // unshade when we're all thru here
789 property
->atom(otk::Property::net_wm_state_skip_taskbar
)) {
790 _skip_taskbar
= false;
792 property
->atom(otk::Property::net_wm_state_skip_pager
)) {
795 property
->atom(otk::Property::net_wm_state_fullscreen
)) {
796 if (!_fullscreen
) continue;
799 property
->atom(otk::Property::net_wm_state_above
)) {
800 if (!_above
) continue;
803 property
->atom(otk::Property::net_wm_state_below
)) {
804 if (!_below
) continue;
809 if (shadestate
!= _shaded
)
815 void Client::toggleClientBorder(bool addborder
)
817 // adjust our idea of where the client is, based on its border. When the
818 // border is removed, the client should now be considered to be in a
819 // different position.
820 // when re-adding the border to the client, the same operation needs to be
822 int x
= _area
.x(), y
= _area
.y();
824 case NorthWestGravity
:
826 case SouthWestGravity
:
828 case NorthEastGravity
:
830 case SouthEastGravity
:
831 if (addborder
) x
-= _border_width
* 2;
832 else x
+= _border_width
* 2;
836 case NorthWestGravity
:
838 case NorthEastGravity
:
840 case SouthWestGravity
:
842 case SouthEastGravity
:
843 if (addborder
) y
-= _border_width
* 2;
844 else y
+= _border_width
* 2;
847 // no change for StaticGravity etc.
853 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
855 // move the client so it is back it the right spot _with_ its border!
856 XMoveWindow(**otk::display
, _window
, x
, y
);
858 XSetWindowBorderWidth(**otk::display
, _window
, 0);
862 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
864 otk::EventHandler::clientMessageHandler(e
);
866 if (e
.format
!= 32) return;
868 const otk::Property
*property
= openbox
->property();
870 if (e
.message_type
== property
->atom(otk::Property::wm_change_state
)) {
871 // compress changes into a single change
872 bool compress
= false;
874 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
875 // XXX: it would be nice to compress ALL messages of a type, not just
876 // messages in a row without other message types between.
877 if (ce
.xclient
.message_type
!= e
.message_type
) {
878 XPutBackEvent(**otk::display
, &ce
);
884 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
886 setWMState(e
.data
.l
[0]); // use the original event
887 } else if (e
.message_type
==
888 property
->atom(otk::Property::net_wm_desktop
)) {
889 // compress changes into a single change
890 bool compress
= false;
892 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
893 // XXX: it would be nice to compress ALL messages of a type, not just
894 // messages in a row without other message types between.
895 if (ce
.xclient
.message_type
!= e
.message_type
) {
896 XPutBackEvent(**otk::display
, &ce
);
902 setDesktop(e
.data
.l
[0]); // use the found event
904 setDesktop(e
.data
.l
[0]); // use the original event
905 } else if (e
.message_type
== property
->atom(otk::Property::net_wm_state
)) {
906 // can't compress these
908 printf("net_wm_state %s %ld %ld for 0x%lx\n",
909 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
910 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
911 e
.data
.l
[1], e
.data
.l
[2], _window
);
913 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
914 } else if (e
.message_type
==
915 property
->atom(otk::Property::net_close_window
)) {
917 printf("net_close_window for 0x%lx\n", _window
);
920 } else if (e
.message_type
==
921 property
->atom(otk::Property::net_active_window
)) {
923 printf("net_active_window for 0x%lx\n", _window
);
929 openbox
->screen(_screen
)->restack(true, this); // raise
935 void Client::shapeHandler(const XShapeEvent
&e
)
937 otk::EventHandler::shapeHandler(e
);
939 if (e
.kind
== ShapeBounding
) {
941 frame
->adjustShape();
947 void Client::resize(Corner anchor
, int w
, int h
, int x
, int y
)
952 // for interactive resizing. have to move half an increment in each
954 w
+= _size_inc
.x() / 2;
955 h
+= _size_inc
.y() / 2;
957 // is the window resizable? if it is not, then don't check its sizes, the
958 // client can do what it wants and the user can't change it anyhow
959 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
960 // smaller than min size or bigger than max size?
961 if (w
< _min_size
.x()) w
= _min_size
.x();
962 else if (w
> _max_size
.x()) w
= _max_size
.x();
963 if (h
< _min_size
.y()) h
= _min_size
.y();
964 else if (h
> _max_size
.y()) h
= _max_size
.y();
967 // keep to the increments
971 // you cannot resize to nothing
975 // store the logical size
976 _logical_size
.setPoint(w
, h
);
984 if (x
== INT_MIN
|| y
== INT_MIN
) {
991 x
-= w
- _area
.width();
994 y
-= h
- _area
.height();
997 x
-= w
- _area
.width();
998 y
-= h
- _area
.height();
1003 _area
.setSize(w
, h
);
1005 XResizeWindow(**otk::display
, _window
, w
, h
);
1007 // resize the frame to match the request
1008 frame
->adjustSize();
1013 void Client::move(int x
, int y
)
1017 // move the frame to be in the requested position
1018 if (frame
) { // this can be called while mapping, before frame exists
1019 frame
->adjustPosition();
1021 // send synthetic configure notify (we don't need to if we aren't mapped
1024 event
.type
= ConfigureNotify
;
1025 event
.xconfigure
.display
= **otk::display
;
1026 event
.xconfigure
.event
= _window
;
1027 event
.xconfigure
.window
= _window
;
1028 event
.xconfigure
.x
= x
;
1029 event
.xconfigure
.y
= y
;
1030 event
.xconfigure
.width
= _area
.width();
1031 event
.xconfigure
.height
= _area
.height();
1032 event
.xconfigure
.border_width
= _border_width
;
1033 event
.xconfigure
.above
= frame
->window();
1034 event
.xconfigure
.override_redirect
= False
;
1035 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1036 StructureNotifyMask
, &event
);
1041 void Client::close()
1044 const otk::Property
*property
= openbox
->property();
1046 if (!(_functions
& Func_Close
)) return;
1048 // XXX: itd be cool to do timeouts and shit here for killing the client's
1050 // like... if the window is around after 5 seconds, then the close button
1051 // turns a nice red, and if this function is called again, the client is
1052 // explicitly killed.
1054 ce
.xclient
.type
= ClientMessage
;
1055 ce
.xclient
.message_type
= property
->atom(otk::Property::wm_protocols
);
1056 ce
.xclient
.display
= **otk::display
;
1057 ce
.xclient
.window
= _window
;
1058 ce
.xclient
.format
= 32;
1059 ce
.xclient
.data
.l
[0] = property
->atom(otk::Property::wm_delete_window
);
1060 ce
.xclient
.data
.l
[1] = CurrentTime
;
1061 ce
.xclient
.data
.l
[2] = 0l;
1062 ce
.xclient
.data
.l
[3] = 0l;
1063 ce
.xclient
.data
.l
[4] = 0l;
1064 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1068 void Client::changeState()
1070 const otk::Property
*property
= openbox
->property();
1072 unsigned long state
[2];
1073 state
[0] = _wmstate
;
1075 property
->set(_window
, otk::Property::wm_state
, otk::Property::wm_state
,
1081 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_modal
);
1083 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_shaded
);
1085 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_hidden
);
1088 property
->atom(otk::Property::net_wm_state_skip_taskbar
);
1090 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_skip_pager
);
1092 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_fullscreen
);
1095 property
->atom(otk::Property::net_wm_state_maximized_vert
);
1098 property
->atom(otk::Property::net_wm_state_maximized_horz
);
1100 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_above
);
1102 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_below
);
1103 property
->set(_window
, otk::Property::net_wm_state
,
1104 otk::Property::Atom_Atom
, netstate
, num
);
1110 void Client::shade(bool shade
)
1112 if (shade
== _shaded
) return; // already done
1114 _wmstate
= shade
? IconicState
: NormalState
;
1117 frame
->adjustSize();
1121 bool Client::focus() const
1123 // won't try focus if the client doesn't want it, or if the window isn't
1124 // visible on the screen
1125 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1127 if (_focused
) return true;
1130 XSetInputFocus(**otk::display
, _window
,
1131 RevertToNone
, CurrentTime
);
1133 if (_focus_notify
) {
1135 const otk::Property
*property
= openbox
->property();
1137 ce
.xclient
.type
= ClientMessage
;
1138 ce
.xclient
.message_type
= property
->atom(otk::Property::wm_protocols
);
1139 ce
.xclient
.display
= **otk::display
;
1140 ce
.xclient
.window
= _window
;
1141 ce
.xclient
.format
= 32;
1142 ce
.xclient
.data
.l
[0] = property
->atom(otk::Property::wm_take_focus
);
1143 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1144 ce
.xclient
.data
.l
[2] = 0l;
1145 ce
.xclient
.data
.l
[3] = 0l;
1146 ce
.xclient
.data
.l
[4] = 0l;
1147 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1154 void Client::unfocus() const
1156 if (!_focused
) return;
1158 assert(openbox
->focusedClient() == this);
1159 openbox
->setFocusedClient(0);
1163 void Client::focusHandler(const XFocusChangeEvent
&e
)
1166 // printf("FocusIn for 0x%lx\n", e.window);
1169 otk::EventHandler::focusHandler(e
);
1174 openbox
->setFocusedClient(this);
1178 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1181 // printf("FocusOut for 0x%lx\n", e.window);
1184 otk::EventHandler::unfocusHandler(e
);
1189 if (openbox
->focusedClient() == this)
1190 openbox
->setFocusedClient(0);
1194 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1197 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1200 otk::EventHandler::configureRequestHandler(e
);
1202 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1204 if (e
.value_mask
& CWBorderWidth
)
1205 _border_width
= e
.border_width
;
1207 // resize, then move, as specified in the EWMH section 7.7
1208 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1209 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1210 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1214 case NorthEastGravity
:
1218 case SouthWestGravity
:
1220 corner
= BottomLeft
;
1222 case SouthEastGravity
:
1223 corner
= BottomRight
;
1225 default: // NorthWest, Static, etc
1229 // if moving AND resizing ...
1230 if (e
.value_mask
& (CWX
| CWY
)) {
1231 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1232 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1233 resize(corner
, w
, h
, x
, y
);
1234 } else // if JUST resizing...
1235 resize(corner
, w
, h
);
1236 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1237 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1238 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1242 if (e
.value_mask
& CWStackMode
) {
1246 openbox
->screen(_screen
)->restack(false, this); // lower
1252 openbox
->screen(_screen
)->restack(true, this); // raise
1259 void Client::unmapHandler(const XUnmapEvent
&e
)
1261 if (ignore_unmaps
) {
1263 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1270 printf("UnmapNotify for 0x%lx\n", e
.window
);
1273 otk::EventHandler::unmapHandler(e
);
1275 // this deletes us etc
1276 openbox
->screen(_screen
)->unmanageWindow(this);
1280 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1283 printf("DestroyNotify for 0x%lx\n", e
.window
);
1286 otk::EventHandler::destroyHandler(e
);
1288 // this deletes us etc
1289 openbox
->screen(_screen
)->unmanageWindow(this);
1293 void Client::reparentHandler(const XReparentEvent
&e
)
1295 // this is when the client is first taken captive in the frame
1296 if (e
.parent
== frame
->plate()) return;
1299 printf("ReparentNotify for 0x%lx\n", e
.window
);
1302 otk::EventHandler::reparentHandler(e
);
1305 This event is quite rare and is usually handled in unmapHandler.
1306 However, if the window is unmapped when the reparent event occurs,
1307 the window manager never sees it because an unmap event is not sent
1308 to an already unmapped window.
1311 // we don't want the reparent event, put it back on the stack for the X
1312 // server to deal with after we unmanage the window
1315 XPutBackEvent(**otk::display
, &ev
);
1317 // this deletes us etc
1318 openbox
->screen(_screen
)->unmanageWindow(this);