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::instance
->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::instance
->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::instance
->property();
99 // defaults to the current desktop
100 _desktop
= Openbox::instance
->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::instance
->property()->set(_window
,
107 otk::Property::net_wm_desktop
,
108 otk::Property::Atom_Cardinal
,
114 void Client::getType()
116 const otk::Property
*property
= Openbox::instance
->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::instance
->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::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::instance
->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::display
, _window
, ShapeNotifyMask
);
342 XShapeQueryExtents(otk::Display::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::instance
->screen(_screen
)->restack(true, this); // raise
377 void Client::updateProtocols()
379 const otk::Property
*property
= Openbox::instance
->property();
384 _focus_notify
= false;
385 _decorations
&= ~Decor_Close
;
386 _functions
&= ~Func_Close
;
388 if (XGetWMProtocols(otk::Display::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::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::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::instance
->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::instance
->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::instance
->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::instance
->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::instance
->screen(_screen
)->updateStrut();
570 void Client::updateTransientFor()
575 if (XGetTransientForHint(otk::Display::display
, _window
, &t
) &&
576 t
!= _window
) { // cant be transient to itself!
577 c
= Openbox::instance
->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::instance
->property();
611 // compress changes to a single property into a single change
613 while (XCheckTypedEvent(otk::Display::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::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::instance
->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::instance
->screen(_screen
)->desktop() ||
681 _desktop
== (signed)0xffffffff)
688 void Client::setState(StateAction action
, long data1
, long data2
)
690 const otk::Property
*property
= Openbox::instance
->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::display
, _window
, _border_width
);
855 // move the client so it is back it the right spot _with_ its border!
856 XMoveWindow(otk::Display::display
, _window
, x
, y
);
858 XSetWindowBorderWidth(otk::Display::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::instance
->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::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::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::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::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::instance
->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 // store the logical size
972 _logical_size
.setPoint(w
, h
);
980 if (x
== INT_MIN
|| y
== INT_MIN
) {
987 x
-= w
- _area
.width();
990 y
-= h
- _area
.height();
993 x
-= w
- _area
.width();
994 y
-= h
- _area
.height();
1001 XResizeWindow(otk::Display::display
, _window
, w
, h
);
1003 // resize the frame to match the request
1004 frame
->adjustSize();
1009 void Client::move(int x
, int y
)
1013 // move the frame to be in the requested position
1014 if (frame
) { // this can be called while mapping, before frame exists
1015 frame
->adjustPosition();
1017 // send synthetic configure notify (we don't need to if we aren't mapped
1020 event
.type
= ConfigureNotify
;
1021 event
.xconfigure
.display
= otk::Display::display
;
1022 event
.xconfigure
.event
= _window
;
1023 event
.xconfigure
.window
= _window
;
1024 event
.xconfigure
.x
= x
;
1025 event
.xconfigure
.y
= y
;
1026 event
.xconfigure
.width
= _area
.width();
1027 event
.xconfigure
.height
= _area
.height();
1028 event
.xconfigure
.border_width
= _border_width
;
1029 event
.xconfigure
.above
= frame
->window();
1030 event
.xconfigure
.override_redirect
= False
;
1031 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1032 StructureNotifyMask
, &event
);
1037 void Client::close()
1040 const otk::Property
*property
= Openbox::instance
->property();
1042 if (!(_functions
& Func_Close
)) return;
1044 // XXX: itd be cool to do timeouts and shit here for killing the client's
1046 // like... if the window is around after 5 seconds, then the close button
1047 // turns a nice red, and if this function is called again, the client is
1048 // explicitly killed.
1050 ce
.xclient
.type
= ClientMessage
;
1051 ce
.xclient
.message_type
= property
->atom(otk::Property::wm_protocols
);
1052 ce
.xclient
.display
= otk::Display::display
;
1053 ce
.xclient
.window
= _window
;
1054 ce
.xclient
.format
= 32;
1055 ce
.xclient
.data
.l
[0] = property
->atom(otk::Property::wm_delete_window
);
1056 ce
.xclient
.data
.l
[1] = CurrentTime
;
1057 ce
.xclient
.data
.l
[2] = 0l;
1058 ce
.xclient
.data
.l
[3] = 0l;
1059 ce
.xclient
.data
.l
[4] = 0l;
1060 XSendEvent(otk::Display::display
, _window
, false, NoEventMask
, &ce
);
1064 void Client::changeState()
1066 const otk::Property
*property
= Openbox::instance
->property();
1068 unsigned long state
[2];
1069 state
[0] = _wmstate
;
1071 property
->set(_window
, otk::Property::wm_state
, otk::Property::wm_state
,
1077 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_modal
);
1079 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_shaded
);
1081 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_hidden
);
1084 property
->atom(otk::Property::net_wm_state_skip_taskbar
);
1086 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_skip_pager
);
1088 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_fullscreen
);
1091 property
->atom(otk::Property::net_wm_state_maximized_vert
);
1094 property
->atom(otk::Property::net_wm_state_maximized_horz
);
1096 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_above
);
1098 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_below
);
1099 property
->set(_window
, otk::Property::net_wm_state
,
1100 otk::Property::Atom_Atom
, netstate
, num
);
1106 void Client::shade(bool shade
)
1108 if (shade
== _shaded
) return; // already done
1110 _wmstate
= shade
? IconicState
: NormalState
;
1113 frame
->adjustSize();
1117 bool Client::focus() const
1119 // won't try focus if the client doesn't want it, or if the window isn't
1120 // visible on the screen
1121 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1123 if (_focused
) return true;
1126 XSetInputFocus(otk::Display::display
, _window
,
1127 RevertToNone
, CurrentTime
);
1129 if (_focus_notify
) {
1131 const otk::Property
*property
= Openbox::instance
->property();
1133 ce
.xclient
.type
= ClientMessage
;
1134 ce
.xclient
.message_type
= property
->atom(otk::Property::wm_protocols
);
1135 ce
.xclient
.display
= otk::Display::display
;
1136 ce
.xclient
.window
= _window
;
1137 ce
.xclient
.format
= 32;
1138 ce
.xclient
.data
.l
[0] = property
->atom(otk::Property::wm_take_focus
);
1139 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1140 ce
.xclient
.data
.l
[2] = 0l;
1141 ce
.xclient
.data
.l
[3] = 0l;
1142 ce
.xclient
.data
.l
[4] = 0l;
1143 XSendEvent(otk::Display::display
, _window
, False
, NoEventMask
, &ce
);
1150 void Client::unfocus() const
1152 if (!_focused
) return;
1154 assert(Openbox::instance
->focusedClient() == this);
1155 Openbox::instance
->setFocusedClient(0);
1159 void Client::focusHandler(const XFocusChangeEvent
&e
)
1162 // printf("FocusIn for 0x%lx\n", e.window);
1165 otk::EventHandler::focusHandler(e
);
1170 Openbox::instance
->setFocusedClient(this);
1174 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1177 // printf("FocusOut for 0x%lx\n", e.window);
1180 otk::EventHandler::unfocusHandler(e
);
1185 if (Openbox::instance
->focusedClient() == this)
1186 Openbox::instance
->setFocusedClient(0);
1190 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1193 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1196 otk::EventHandler::configureRequestHandler(e
);
1198 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1200 if (e
.value_mask
& CWBorderWidth
)
1201 _border_width
= e
.border_width
;
1203 // resize, then move, as specified in the EWMH section 7.7
1204 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1205 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1206 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1210 case NorthEastGravity
:
1214 case SouthWestGravity
:
1216 corner
= BottomLeft
;
1218 case SouthEastGravity
:
1219 corner
= BottomRight
;
1221 default: // NorthWest, Static, etc
1225 // if moving AND resizing ...
1226 if (e
.value_mask
& (CWX
| CWY
)) {
1227 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1228 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1229 resize(corner
, w
, h
, x
, y
);
1230 } else // if JUST resizing...
1231 resize(corner
, w
, h
);
1232 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1233 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1234 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1238 if (e
.value_mask
& CWStackMode
) {
1242 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1248 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1255 void Client::unmapHandler(const XUnmapEvent
&e
)
1257 if (ignore_unmaps
) {
1259 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1266 printf("UnmapNotify for 0x%lx\n", e
.window
);
1269 otk::EventHandler::unmapHandler(e
);
1271 // this deletes us etc
1272 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1276 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1279 printf("DestroyNotify for 0x%lx\n", e
.window
);
1282 otk::EventHandler::destroyHandler(e
);
1284 // this deletes us etc
1285 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1289 void Client::reparentHandler(const XReparentEvent
&e
)
1291 // this is when the client is first taken captive in the frame
1292 if (e
.parent
== frame
->plate()) return;
1295 printf("ReparentNotify for 0x%lx\n", e
.window
);
1298 otk::EventHandler::reparentHandler(e
);
1301 This event is quite rare and is usually handled in unmapHandler.
1302 However, if the window is unmapped when the reparent event occurs,
1303 the window manager never sees it because an unmap event is not sent
1304 to an already unmapped window.
1307 // we don't want the reparent event, put it back on the stack for the X
1308 // server to deal with after we unmanage the window
1311 XPutBackEvent(otk::Display::display
, &ev
);
1313 // this deletes us etc
1314 Openbox::instance
->screen(_screen
)->unmanageWindow(this);