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 parents reference to this
79 _transient_for
->_transients
.remove(this); // remove from old parent
81 if (Openbox::instance
->state() != Openbox::State_Exiting
) {
82 // these values should not be persisted across a window unmapping/mapping
83 property
->erase(_window
, otk::Property::net_wm_desktop
);
84 property
->erase(_window
, otk::Property::net_wm_state
);
89 void Client::getDesktop()
91 const otk::Property
*property
= Openbox::instance
->property();
93 // defaults to the current desktop
94 _desktop
= Openbox::instance
->screen(_screen
)->desktop();
96 if (!property
->get(_window
, otk::Property::net_wm_desktop
,
97 otk::Property::Atom_Cardinal
,
98 (long unsigned*)&_desktop
)) {
99 // make sure the hint exists
100 Openbox::instance
->property()->set(_window
,
101 otk::Property::net_wm_desktop
,
102 otk::Property::Atom_Cardinal
,
108 void Client::getType()
110 const otk::Property
*property
= Openbox::instance
->property();
112 _type
= (WindowType
) -1;
115 unsigned long num
= (unsigned) -1;
116 if (property
->get(_window
, otk::Property::net_wm_window_type
,
117 otk::Property::Atom_Atom
,
119 // use the first value that we know about in the array
120 for (unsigned long i
= 0; i
< num
; ++i
) {
122 property
->atom(otk::Property::net_wm_window_type_desktop
))
123 _type
= Type_Desktop
;
125 property
->atom(otk::Property::net_wm_window_type_dock
))
128 property
->atom(otk::Property::net_wm_window_type_toolbar
))
129 _type
= Type_Toolbar
;
131 property
->atom(otk::Property::net_wm_window_type_menu
))
134 property
->atom(otk::Property::net_wm_window_type_utility
))
135 _type
= Type_Utility
;
137 property
->atom(otk::Property::net_wm_window_type_splash
))
140 property
->atom(otk::Property::net_wm_window_type_dialog
))
143 property
->atom(otk::Property::net_wm_window_type_normal
))
145 // else if (val[i] ==
146 // property->atom(otk::Property::kde_net_wm_window_type_override))
147 // mwm_decorations = 0; // prevent this window from getting any decor
148 // XXX: make this work again
149 if (_type
!= (WindowType
) -1)
150 break; // grab the first known type
155 if (_type
== (WindowType
) -1) {
157 * the window type hint was not set, which means we either classify ourself
158 * as a normal window or a dialog, depending on if we are a transient.
168 void Client::setupDecorAndFunctions()
170 // start with everything
171 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
172 Decor_Iconify
| Decor_Maximize
;
173 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
177 // normal windows retain all of the possible decorations and
181 // dialogs cannot be maximized
182 _decorations
&= ~Decor_Maximize
;
183 _functions
&= ~Func_Maximize
;
189 // these windows get less functionality
190 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
191 _functions
&= ~(Func_Iconify
| Func_Resize
);
197 // none of these windows are manipulated by the window manager
203 // Mwm Hints are applied subtractively to what has already been chosen for
204 // decor and functionality
205 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
206 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
207 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
208 _decorations
&= ~Decor_Border
;
209 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
210 _decorations
&= ~Decor_Handle
;
211 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
212 _decorations
&= ~Decor_Titlebar
;
213 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
214 _decorations
&= ~Decor_Iconify
;
215 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
216 _decorations
&= ~Decor_Maximize
;
220 if (_mwmhints
.flags
& MwmFlag_Functions
) {
221 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
222 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
223 _functions
&= ~Func_Resize
;
224 if (! (_mwmhints
.functions
& MwmFunc_Move
))
225 _functions
&= ~Func_Move
;
226 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
227 _functions
&= ~Func_Iconify
;
228 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
229 _functions
&= ~Func_Maximize
;
230 // dont let mwm hints kill the close button
231 //if (! (_mwmhints.functions & MwmFunc_Close))
232 // _functions &= ~Func_Close;
236 // XXX: changeAllowedActions();
240 void Client::getMwmHints()
242 const otk::Property
*property
= Openbox::instance
->property();
244 unsigned long num
= MwmHints::elements
;
245 unsigned long *hints
;
247 _mwmhints
.flags
= 0; // default to none
249 if (!property
->get(_window
, otk::Property::motif_wm_hints
,
250 otk::Property::motif_wm_hints
, &num
,
251 (unsigned long **)&hints
))
254 if (num
>= MwmHints::elements
) {
255 // retrieved the hints
256 _mwmhints
.flags
= hints
[0];
257 _mwmhints
.functions
= hints
[1];
258 _mwmhints
.decorations
= hints
[2];
265 void Client::getArea()
267 XWindowAttributes wattrib
;
270 ret
= XGetWindowAttributes(otk::Display::display
, _window
, &wattrib
);
271 assert(ret
!= BadWindow
);
273 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
274 _border_width
= wattrib
.border_width
;
278 void Client::getState()
280 const otk::Property
*property
= Openbox::instance
->property();
282 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
283 _skip_taskbar
= _skip_pager
= false;
285 unsigned long *state
;
286 unsigned long num
= (unsigned) -1;
288 if (property
->get(_window
, otk::Property::net_wm_state
,
289 otk::Property::Atom_Atom
, &num
, &state
)) {
290 for (unsigned long i
= 0; i
< num
; ++i
) {
291 if (state
[i
] == property
->atom(otk::Property::net_wm_state_modal
))
294 property
->atom(otk::Property::net_wm_state_shaded
)) {
296 _wmstate
= IconicState
;
297 } else if (state
[i
] ==
298 property
->atom(otk::Property::net_wm_state_skip_taskbar
))
299 _skip_taskbar
= true;
301 property
->atom(otk::Property::net_wm_state_skip_pager
))
304 property
->atom(otk::Property::net_wm_state_fullscreen
))
307 property
->atom(otk::Property::net_wm_state_maximized_vert
))
310 property
->atom(otk::Property::net_wm_state_maximized_horz
))
313 property
->atom(otk::Property::net_wm_state_above
))
316 property
->atom(otk::Property::net_wm_state_below
))
325 void Client::getShaped()
329 if (otk::Display::shape()) {
334 XShapeSelectInput(otk::Display::display
, _window
, ShapeNotifyMask
);
336 XShapeQueryExtents(otk::Display::display
, _window
, &s
, &foo
,
337 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
344 void Client::calcLayer() {
347 if (_iconic
) l
= Layer_Icon
;
348 else if (_fullscreen
) l
= Layer_Fullscreen
;
349 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
350 else if (_type
== Type_Dock
) {
351 if (!_below
) l
= Layer_Top
;
352 else l
= Layer_Normal
;
354 else if (_above
) l
= Layer_Above
;
355 else if (_below
) l
= Layer_Below
;
356 else l
= Layer_Normal
;
362 if we don't have a frame, then we aren't mapped yet (and this would
365 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
371 void Client::updateProtocols()
373 const otk::Property
*property
= Openbox::instance
->property();
378 _focus_notify
= false;
379 _decorations
&= ~Decor_Close
;
380 _functions
&= ~Func_Close
;
382 if (XGetWMProtocols(otk::Display::display
, _window
, &proto
, &num_return
)) {
383 for (int i
= 0; i
< num_return
; ++i
) {
384 if (proto
[i
] == property
->atom(otk::Property::wm_delete_window
)) {
385 _decorations
|= Decor_Close
;
386 _functions
|= Func_Close
;
388 frame
->adjustSize(); // update the decorations
389 } else if (proto
[i
] == property
->atom(otk::Property::wm_take_focus
))
390 // if this protocol is requested, then the window will be notified
391 // by the window manager whenever it receives focus
392 _focus_notify
= true;
399 void Client::updateNormalHints()
403 int oldgravity
= _gravity
;
406 _gravity
= NorthWestGravity
;
407 _size_inc
.setPoint(1, 1);
408 _base_size
.setPoint(0, 0);
409 _min_size
.setPoint(0, 0);
410 _max_size
.setPoint(INT_MAX
, INT_MAX
);
412 // XXX: might want to cancel any interactive resizing of the window at this
415 // get the hints from the window
416 if (XGetWMNormalHints(otk::Display::display
, _window
, &size
, &ret
)) {
417 _positioned
= (size
.flags
& (PPosition
|USPosition
));
419 if (size
.flags
& PWinGravity
)
420 _gravity
= size
.win_gravity
;
422 if (size
.flags
& PMinSize
)
423 _min_size
.setPoint(size
.min_width
, size
.min_height
);
425 if (size
.flags
& PMaxSize
)
426 _max_size
.setPoint(size
.max_width
, size
.max_height
);
428 if (size
.flags
& PBaseSize
)
429 _base_size
.setPoint(size
.base_width
, size
.base_height
);
431 if (size
.flags
& PResizeInc
)
432 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
435 // if the client has a frame, i.e. has already been mapped and is
436 // changing its gravity
437 if (frame
&& _gravity
!= oldgravity
) {
438 // move our idea of the client's position based on its new gravity
440 frame
->frameGravity(x
, y
);
446 void Client::updateWMHints()
450 // assume a window takes input if it doesnt specify
454 if ((hints
= XGetWMHints(otk::Display::display
, _window
)) != NULL
) {
455 if (hints
->flags
& InputHint
)
456 _can_focus
= hints
->input
;
458 if (hints
->flags
& XUrgencyHint
)
461 if (hints
->flags
& WindowGroupHint
) {
462 if (hints
->window_group
!= _group
) {
463 // XXX: remove from the old group if there was one
464 _group
= hints
->window_group
;
465 // XXX: do stuff with the group
475 void Client::updateTitle()
477 const otk::Property
*property
= Openbox::instance
->property();
482 if (! property
->get(_window
, otk::Property::net_wm_name
,
483 otk::Property::utf8
, &_title
)) {
485 property
->get(_window
, otk::Property::wm_name
,
486 otk::Property::ascii
, &_title
);
490 _title
= _("Unnamed Window");
493 frame
->setTitle(_title
);
497 void Client::updateIconTitle()
499 const otk::Property
*property
= Openbox::instance
->property();
504 if (! property
->get(_window
, otk::Property::net_wm_icon_name
,
505 otk::Property::utf8
, &_icon_title
)) {
507 property
->get(_window
, otk::Property::wm_icon_name
,
508 otk::Property::ascii
, &_icon_title
);
512 _icon_title
= _("Unnamed Window");
516 void Client::updateClass()
518 const otk::Property
*property
= Openbox::instance
->property();
521 _app_name
= _app_class
= _role
= "";
523 otk::Property::StringVect v
;
524 unsigned long num
= 2;
526 if (property
->get(_window
, otk::Property::wm_class
,
527 otk::Property::ascii
, &num
, &v
)) {
528 if (num
> 0) _app_name
= v
[0];
529 if (num
> 1) _app_class
= v
[1];
534 if (property
->get(_window
, otk::Property::wm_window_role
,
535 otk::Property::ascii
, &num
, &v
)) {
536 if (num
> 0) _role
= v
[0];
541 void Client::updateStrut()
543 unsigned long num
= 4;
545 if (!Openbox::instance
->property()->get(_window
,
546 otk::Property::net_wm_strut
,
547 otk::Property::Atom_Cardinal
,
552 _strut
.left
= data
[0];
553 _strut
.right
= data
[1];
554 _strut
.top
= data
[2];
555 _strut
.bottom
= data
[3];
557 Openbox::instance
->screen(_screen
)->updateStrut();
564 void Client::updateTransientFor()
569 if (XGetTransientForHint(otk::Display::display
, _window
, &t
) &&
570 t
!= _window
) { // cant be transient to itself!
571 c
= Openbox::instance
->findClient(t
);
572 assert(c
!= this); // if this happens then we need to check for it
574 if (!c
/*XXX: && _group*/) {
575 // not transient to a client, see if it is transient for a group
576 if (//t == _group->leader() ||
578 t
== otk::Display::screenInfo(_screen
)->rootWindow()) {
579 // window is a transient for its group!
580 // XXX: for now this is treated as non-transient.
581 // this needs to be fixed!
586 // if anything has changed...
587 if (c
!= _transient_for
) {
589 _transient_for
->_transients
.remove(this); // remove from old parent
592 _transient_for
->_transients
.push_back(this); // add to new parent
594 // XXX: change decor status?
599 void Client::propertyHandler(const XPropertyEvent
&e
)
601 otk::EventHandler::propertyHandler(e
);
603 const otk::Property
*property
= Openbox::instance
->property();
605 // compress changes to a single property into a single change
607 while (XCheckTypedEvent(otk::Display::display
, e
.type
, &ce
)) {
608 // XXX: it would be nice to compress ALL changes to a property, not just
609 // changes in a row without other props between.
610 if (ce
.xproperty
.atom
!= e
.atom
) {
611 XPutBackEvent(otk::Display::display
, &ce
);
616 if (e
.atom
== XA_WM_NORMAL_HINTS
)
618 else if (e
.atom
== XA_WM_HINTS
)
620 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
621 updateTransientFor();
623 calcLayer(); // type may have changed, so update the layer
624 setupDecorAndFunctions();
625 frame
->adjustSize(); // this updates the frame for any new decor settings
627 else if (e
.atom
== property
->atom(otk::Property::net_wm_name
) ||
628 e
.atom
== property
->atom(otk::Property::wm_name
))
630 else if (e
.atom
== property
->atom(otk::Property::net_wm_icon_name
) ||
631 e
.atom
== property
->atom(otk::Property::wm_icon_name
))
633 else if (e
.atom
== property
->atom(otk::Property::wm_class
))
635 else if (e
.atom
== property
->atom(otk::Property::wm_protocols
))
637 else if (e
.atom
== property
->atom(otk::Property::net_wm_strut
))
642 void Client::setWMState(long state
)
644 if (state
== _wmstate
) return; // no change
649 // XXX: cause it to iconify
652 // XXX: cause it to uniconify
658 void Client::setDesktop(long target
)
660 if (target
== _desktop
) return;
662 printf("Setting desktop %ld\n", target
);
664 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
668 Openbox::instance
->property()->set(_window
,
669 otk::Property::net_wm_desktop
,
670 otk::Property::Atom_Cardinal
,
673 // 'move' the window to the new desktop
674 if (_desktop
== Openbox::instance
->screen(_screen
)->desktop() ||
675 _desktop
== (signed)0xffffffff)
682 void Client::setState(StateAction action
, long data1
, long data2
)
684 const otk::Property
*property
= Openbox::instance
->property();
685 bool shadestate
= _shaded
;
687 if (!(action
== State_Add
|| action
== State_Remove
||
688 action
== State_Toggle
))
689 return; // an invalid action was passed to the client message, ignore it
691 for (int i
= 0; i
< 2; ++i
) {
692 Atom state
= i
== 0 ? data1
: data2
;
694 if (! state
) continue;
696 // if toggling, then pick whether we're adding or removing
697 if (action
== State_Toggle
) {
698 if (state
== property
->atom(otk::Property::net_wm_state_modal
))
699 action
= _modal
? State_Remove
: State_Add
;
701 property
->atom(otk::Property::net_wm_state_maximized_vert
))
702 action
= _max_vert
? State_Remove
: State_Add
;
704 property
->atom(otk::Property::net_wm_state_maximized_horz
))
705 action
= _max_horz
? State_Remove
: State_Add
;
706 else if (state
== property
->atom(otk::Property::net_wm_state_shaded
))
707 action
= _shaded
? State_Remove
: State_Add
;
709 property
->atom(otk::Property::net_wm_state_skip_taskbar
))
710 action
= _skip_taskbar
? State_Remove
: State_Add
;
712 property
->atom(otk::Property::net_wm_state_skip_pager
))
713 action
= _skip_pager
? State_Remove
: State_Add
;
715 property
->atom(otk::Property::net_wm_state_fullscreen
))
716 action
= _fullscreen
? State_Remove
: State_Add
;
717 else if (state
== property
->atom(otk::Property::net_wm_state_above
))
718 action
= _above
? State_Remove
: State_Add
;
719 else if (state
== property
->atom(otk::Property::net_wm_state_below
))
720 action
= _below
? State_Remove
: State_Add
;
723 if (action
== State_Add
) {
724 if (state
== property
->atom(otk::Property::net_wm_state_modal
)) {
725 if (_modal
) continue;
727 // XXX: give it focus if another window has focus that shouldnt now
729 property
->atom(otk::Property::net_wm_state_maximized_vert
)){
730 if (_max_vert
) continue;
732 // XXX: resize the window etc
734 property
->atom(otk::Property::net_wm_state_maximized_horz
)){
735 if (_max_horz
) continue;
737 // XXX: resize the window etc
739 property
->atom(otk::Property::net_wm_state_shaded
)) {
740 if (_shaded
) continue;
741 // shade when we're all thru here
744 property
->atom(otk::Property::net_wm_state_skip_taskbar
)) {
745 _skip_taskbar
= true;
747 property
->atom(otk::Property::net_wm_state_skip_pager
)) {
750 property
->atom(otk::Property::net_wm_state_fullscreen
)) {
751 if (_fullscreen
) continue;
754 property
->atom(otk::Property::net_wm_state_above
)) {
755 if (_above
) continue;
758 property
->atom(otk::Property::net_wm_state_below
)) {
759 if (_below
) continue;
763 } else { // action == State_Remove
764 if (state
== property
->atom(otk::Property::net_wm_state_modal
)) {
765 if (!_modal
) continue;
768 property
->atom(otk::Property::net_wm_state_maximized_vert
)){
769 if (!_max_vert
) continue;
771 // XXX: resize the window etc
773 property
->atom(otk::Property::net_wm_state_maximized_horz
)){
774 if (!_max_horz
) continue;
776 // XXX: resize the window etc
778 property
->atom(otk::Property::net_wm_state_shaded
)) {
779 if (!_shaded
) continue;
780 // unshade when we're all thru here
783 property
->atom(otk::Property::net_wm_state_skip_taskbar
)) {
784 _skip_taskbar
= false;
786 property
->atom(otk::Property::net_wm_state_skip_pager
)) {
789 property
->atom(otk::Property::net_wm_state_fullscreen
)) {
790 if (!_fullscreen
) continue;
793 property
->atom(otk::Property::net_wm_state_above
)) {
794 if (!_above
) continue;
797 property
->atom(otk::Property::net_wm_state_below
)) {
798 if (!_below
) continue;
803 if (shadestate
!= _shaded
)
809 void Client::toggleClientBorder(bool addborder
)
811 // adjust our idea of where the client is, based on its border. When the
812 // border is removed, the client should now be considered to be in a
813 // different position.
814 // when re-adding the border to the client, the same operation needs to be
816 int x
= _area
.x(), y
= _area
.y();
818 case NorthWestGravity
:
820 case SouthWestGravity
:
822 case NorthEastGravity
:
824 case SouthEastGravity
:
825 if (addborder
) x
-= _border_width
* 2;
826 else x
+= _border_width
* 2;
830 case NorthWestGravity
:
832 case NorthEastGravity
:
834 case SouthWestGravity
:
836 case SouthEastGravity
:
837 if (addborder
) y
-= _border_width
* 2;
838 else y
+= _border_width
* 2;
841 // no change for StaticGravity etc.
847 XSetWindowBorderWidth(otk::Display::display
, _window
, _border_width
);
849 // move the client so it is back it the right spot _with_ its border!
850 XMoveWindow(otk::Display::display
, _window
, x
, y
);
852 XSetWindowBorderWidth(otk::Display::display
, _window
, 0);
856 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
858 otk::EventHandler::clientMessageHandler(e
);
860 if (e
.format
!= 32) return;
862 const otk::Property
*property
= Openbox::instance
->property();
864 if (e
.message_type
== property
->atom(otk::Property::wm_change_state
)) {
865 // compress changes into a single change
866 bool compress
= false;
868 while (XCheckTypedEvent(otk::Display::display
, e
.type
, &ce
)) {
869 // XXX: it would be nice to compress ALL messages of a type, not just
870 // messages in a row without other message types between.
871 if (ce
.xclient
.message_type
!= e
.message_type
) {
872 XPutBackEvent(otk::Display::display
, &ce
);
878 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
880 setWMState(e
.data
.l
[0]); // use the original event
881 } else if (e
.message_type
==
882 property
->atom(otk::Property::net_wm_desktop
)) {
883 // compress changes into a single change
884 bool compress
= false;
886 while (XCheckTypedEvent(otk::Display::display
, e
.type
, &ce
)) {
887 // XXX: it would be nice to compress ALL messages of a type, not just
888 // messages in a row without other message types between.
889 if (ce
.xclient
.message_type
!= e
.message_type
) {
890 XPutBackEvent(otk::Display::display
, &ce
);
896 setDesktop(e
.data
.l
[0]); // use the found event
898 setDesktop(e
.data
.l
[0]); // use the original event
899 } else if (e
.message_type
== property
->atom(otk::Property::net_wm_state
)) {
900 // can't compress these
902 printf("net_wm_state %s %ld %ld for 0x%lx\n",
903 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
904 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
905 e
.data
.l
[1], e
.data
.l
[2], _window
);
907 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
908 } else if (e
.message_type
==
909 property
->atom(otk::Property::net_close_window
)) {
911 printf("net_close_window for 0x%lx\n", _window
);
914 } else if (e
.message_type
==
915 property
->atom(otk::Property::net_active_window
)) {
917 printf("net_active_window for 0x%lx\n", _window
);
923 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
929 void Client::shapeHandler(const XShapeEvent
&e
)
931 otk::EventHandler::shapeHandler(e
);
933 if (e
.kind
== ShapeBounding
) {
935 frame
->adjustShape();
941 void Client::resize(Corner anchor
, int w
, int h
, int x
, int y
)
946 // for interactive resizing. have to move half an increment in each
948 w
+= _size_inc
.x() / 2;
949 h
+= _size_inc
.y() / 2;
951 // is the window resizable? if it is not, then don't check its sizes, the
952 // client can do what it wants and the user can't change it anyhow
953 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
954 // smaller than min size or bigger than max size?
955 if (w
< _min_size
.x()) w
= _min_size
.x();
956 else if (w
> _max_size
.x()) w
= _max_size
.x();
957 if (h
< _min_size
.y()) h
= _min_size
.y();
958 else if (h
> _max_size
.y()) h
= _max_size
.y();
961 // keep to the increments
965 // store the logical size
966 _logical_size
.setPoint(w
, h
);
974 if (x
== INT_MIN
|| y
== INT_MIN
) {
981 x
-= w
- _area
.width();
984 y
-= h
- _area
.height();
987 x
-= w
- _area
.width();
988 y
-= h
- _area
.height();
995 XResizeWindow(otk::Display::display
, _window
, w
, h
);
997 // resize the frame to match the request
1003 void Client::move(int x
, int y
)
1007 // move the frame to be in the requested position
1008 if (frame
) { // this can be called while mapping, before frame exists
1009 frame
->adjustPosition();
1011 // send synthetic configure notify (we don't need to if we aren't mapped
1014 event
.type
= ConfigureNotify
;
1015 event
.xconfigure
.display
= otk::Display::display
;
1016 event
.xconfigure
.event
= _window
;
1017 event
.xconfigure
.window
= _window
;
1018 event
.xconfigure
.x
= x
;
1019 event
.xconfigure
.y
= y
;
1020 event
.xconfigure
.width
= _area
.width();
1021 event
.xconfigure
.height
= _area
.height();
1022 event
.xconfigure
.border_width
= _border_width
;
1023 event
.xconfigure
.above
= frame
->window();
1024 event
.xconfigure
.override_redirect
= False
;
1025 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1026 StructureNotifyMask
, &event
);
1031 void Client::close()
1034 const otk::Property
*property
= Openbox::instance
->property();
1036 if (!(_functions
& Func_Close
)) return;
1038 // XXX: itd be cool to do timeouts and shit here for killing the client's
1040 // like... if the window is around after 5 seconds, then the close button
1041 // turns a nice red, and if this function is called again, the client is
1042 // explicitly killed.
1044 ce
.xclient
.type
= ClientMessage
;
1045 ce
.xclient
.message_type
= property
->atom(otk::Property::wm_protocols
);
1046 ce
.xclient
.display
= otk::Display::display
;
1047 ce
.xclient
.window
= _window
;
1048 ce
.xclient
.format
= 32;
1049 ce
.xclient
.data
.l
[0] = property
->atom(otk::Property::wm_delete_window
);
1050 ce
.xclient
.data
.l
[1] = CurrentTime
;
1051 ce
.xclient
.data
.l
[2] = 0l;
1052 ce
.xclient
.data
.l
[3] = 0l;
1053 ce
.xclient
.data
.l
[4] = 0l;
1054 XSendEvent(otk::Display::display
, _window
, false, NoEventMask
, &ce
);
1058 void Client::changeState()
1060 const otk::Property
*property
= Openbox::instance
->property();
1062 unsigned long state
[2];
1063 state
[0] = _wmstate
;
1065 property
->set(_window
, otk::Property::wm_state
, otk::Property::wm_state
,
1071 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_modal
);
1073 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_shaded
);
1075 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_hidden
);
1078 property
->atom(otk::Property::net_wm_state_skip_taskbar
);
1080 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_skip_pager
);
1082 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_fullscreen
);
1085 property
->atom(otk::Property::net_wm_state_maximized_vert
);
1088 property
->atom(otk::Property::net_wm_state_maximized_horz
);
1090 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_above
);
1092 netstate
[num
++] = property
->atom(otk::Property::net_wm_state_below
);
1093 property
->set(_window
, otk::Property::net_wm_state
,
1094 otk::Property::Atom_Atom
, netstate
, num
);
1100 void Client::shade(bool shade
)
1102 if (shade
== _shaded
) return; // already done
1104 _wmstate
= shade
? IconicState
: NormalState
;
1107 frame
->adjustSize();
1111 bool Client::focus() const
1113 // won't try focus if the client doesn't want it, or if the window isn't
1114 // visible on the screen
1115 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1117 if (_focused
) return true;
1120 XSetInputFocus(otk::Display::display
, _window
,
1121 RevertToNone
, CurrentTime
);
1123 if (_focus_notify
) {
1125 const otk::Property
*property
= Openbox::instance
->property();
1127 ce
.xclient
.type
= ClientMessage
;
1128 ce
.xclient
.message_type
= property
->atom(otk::Property::wm_protocols
);
1129 ce
.xclient
.display
= otk::Display::display
;
1130 ce
.xclient
.window
= _window
;
1131 ce
.xclient
.format
= 32;
1132 ce
.xclient
.data
.l
[0] = property
->atom(otk::Property::wm_take_focus
);
1133 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1134 ce
.xclient
.data
.l
[2] = 0l;
1135 ce
.xclient
.data
.l
[3] = 0l;
1136 ce
.xclient
.data
.l
[4] = 0l;
1137 XSendEvent(otk::Display::display
, _window
, False
, NoEventMask
, &ce
);
1144 void Client::unfocus() const
1146 if (!_focused
) return;
1148 assert(Openbox::instance
->focusedClient() == this);
1149 Openbox::instance
->setFocusedClient(0);
1153 void Client::focusHandler(const XFocusChangeEvent
&e
)
1156 // printf("FocusIn for 0x%lx\n", e.window);
1159 otk::EventHandler::focusHandler(e
);
1164 Openbox::instance
->setFocusedClient(this);
1168 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1171 // printf("FocusOut for 0x%lx\n", e.window);
1174 otk::EventHandler::unfocusHandler(e
);
1179 if (Openbox::instance
->focusedClient() == this)
1180 Openbox::instance
->setFocusedClient(0);
1184 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1187 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1190 otk::EventHandler::configureRequestHandler(e
);
1192 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1194 if (e
.value_mask
& CWBorderWidth
)
1195 _border_width
= e
.border_width
;
1197 // resize, then move, as specified in the EWMH section 7.7
1198 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1199 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1200 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1204 case NorthEastGravity
:
1208 case SouthWestGravity
:
1210 corner
= BottomLeft
;
1212 case SouthEastGravity
:
1213 corner
= BottomRight
;
1215 default: // NorthWest, Static, etc
1219 // if moving AND resizing ...
1220 if (e
.value_mask
& (CWX
| CWY
)) {
1221 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1222 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1223 resize(corner
, w
, h
, x
, y
);
1224 } else // if JUST resizing...
1225 resize(corner
, w
, h
);
1226 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1227 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1228 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1232 if (e
.value_mask
& CWStackMode
) {
1236 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1242 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1249 void Client::unmapHandler(const XUnmapEvent
&e
)
1251 if (ignore_unmaps
) {
1253 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1260 printf("UnmapNotify for 0x%lx\n", e
.window
);
1263 otk::EventHandler::unmapHandler(e
);
1265 // this deletes us etc
1266 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1270 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1273 printf("DestroyNotify for 0x%lx\n", e
.window
);
1276 otk::EventHandler::destroyHandler(e
);
1278 // this deletes us etc
1279 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1283 void Client::reparentHandler(const XReparentEvent
&e
)
1285 // this is when the client is first taken captive in the frame
1286 if (e
.parent
== frame
->plate()) return;
1289 printf("ReparentNotify for 0x%lx\n", e
.window
);
1292 otk::EventHandler::reparentHandler(e
);
1295 This event is quite rare and is usually handled in unmapHandler.
1296 However, if the window is unmapped when the reparent event occurs,
1297 the window manager never sees it because an unmap event is not sent
1298 to an already unmapped window.
1301 // we don't want the reparent event, put it back on the stack for the X
1302 // server to deal with after we unmanage the window
1305 XPutBackEvent(otk::Display::display
, &ev
);
1307 // this deletes us etc
1308 Openbox::instance
->screen(_screen
)->unmanageWindow(this);