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();
76 // clean up childrens' references
77 while (!_transients
.empty()) {
78 _transients
.front()->_transient_for
= 0;
79 _transients
.pop_front();
82 // clean up parents reference to this
84 _transient_for
->_transients
.remove(this); // remove from old parent
86 if (openbox
->state() != Openbox::State_Exiting
) {
87 // these values should not be persisted across a window unmapping/mapping
88 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
89 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
94 void Client::getDesktop()
96 // defaults to the current desktop
97 _desktop
= openbox
->screen(_screen
)->desktop();
99 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
100 otk::Property::atoms
.cardinal
,
101 (long unsigned*)&_desktop
)) {
102 // make sure the hint exists
103 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
104 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
109 void Client::getType()
111 _type
= (WindowType
) -1;
114 unsigned long num
= (unsigned) -1;
115 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
116 otk::Property::atoms
.atom
, &num
, &val
)) {
117 // use the first value that we know about in the array
118 for (unsigned long i
= 0; i
< num
; ++i
) {
119 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
120 _type
= Type_Desktop
;
121 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
123 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
124 _type
= Type_Toolbar
;
125 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
127 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
128 _type
= Type_Utility
;
129 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
131 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
133 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
135 // XXX: make this work again
136 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
137 // mwm_decorations = 0; // prevent this window from getting any decor
138 if (_type
!= (WindowType
) -1)
139 break; // grab the first known type
144 if (_type
== (WindowType
) -1) {
146 * the window type hint was not set, which means we either classify ourself
147 * as a normal window or a dialog, depending on if we are a transient.
157 void Client::setupDecorAndFunctions()
159 // start with everything
160 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
161 Decor_Iconify
| Decor_Maximize
;
162 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
166 // normal windows retain all of the possible decorations and
170 // dialogs cannot be maximized
171 _decorations
&= ~Decor_Maximize
;
172 _functions
&= ~Func_Maximize
;
178 // these windows get less functionality
179 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
180 _functions
&= ~(Func_Iconify
| Func_Resize
);
186 // none of these windows are manipulated by the window manager
192 // Mwm Hints are applied subtractively to what has already been chosen for
193 // decor and functionality
194 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
195 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
196 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
197 _decorations
&= ~Decor_Border
;
198 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
199 _decorations
&= ~Decor_Handle
;
200 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
201 _decorations
&= ~Decor_Titlebar
;
202 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
203 _decorations
&= ~Decor_Iconify
;
204 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
205 _decorations
&= ~Decor_Maximize
;
209 if (_mwmhints
.flags
& MwmFlag_Functions
) {
210 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
211 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
212 _functions
&= ~Func_Resize
;
213 if (! (_mwmhints
.functions
& MwmFunc_Move
))
214 _functions
&= ~Func_Move
;
215 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
216 _functions
&= ~Func_Iconify
;
217 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
218 _functions
&= ~Func_Maximize
;
219 // dont let mwm hints kill the close button
220 //if (! (_mwmhints.functions & MwmFunc_Close))
221 // _functions &= ~Func_Close;
225 // XXX: changeAllowedActions();
229 void Client::getMwmHints()
231 unsigned long num
= MwmHints::elements
;
232 unsigned long *hints
;
234 _mwmhints
.flags
= 0; // default to none
236 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
237 otk::Property::atoms
.motif_wm_hints
, &num
,
238 (unsigned long **)&hints
))
241 if (num
>= MwmHints::elements
) {
242 // retrieved the hints
243 _mwmhints
.flags
= hints
[0];
244 _mwmhints
.functions
= hints
[1];
245 _mwmhints
.decorations
= hints
[2];
252 void Client::getArea()
254 XWindowAttributes wattrib
;
257 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
258 assert(ret
!= BadWindow
);
260 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
261 _border_width
= wattrib
.border_width
;
265 void Client::getState()
267 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
268 _skip_taskbar
= _skip_pager
= false;
270 unsigned long *state
;
271 unsigned long num
= (unsigned) -1;
273 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
274 otk::Property::atoms
.atom
, &num
, &state
)) {
275 for (unsigned long i
= 0; i
< num
; ++i
) {
276 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
278 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
) {
280 _wmstate
= IconicState
;
281 } else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
282 _skip_taskbar
= true;
283 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
285 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
287 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
289 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
291 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
293 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
302 void Client::getShaped()
306 if (otk::display
->shape()) {
311 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
313 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
314 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
321 void Client::calcLayer() {
324 if (_iconic
) l
= Layer_Icon
;
325 else if (_fullscreen
) l
= Layer_Fullscreen
;
326 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
327 else if (_type
== Type_Dock
) {
328 if (!_below
) l
= Layer_Top
;
329 else l
= Layer_Normal
;
331 else if (_above
) l
= Layer_Above
;
332 else if (_below
) l
= Layer_Below
;
333 else l
= Layer_Normal
;
339 if we don't have a frame, then we aren't mapped yet (and this would
342 openbox
->screen(_screen
)->raiseWindow(this);
348 void Client::updateProtocols()
353 _focus_notify
= false;
354 _decorations
&= ~Decor_Close
;
355 _functions
&= ~Func_Close
;
357 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
358 for (int i
= 0; i
< num_return
; ++i
) {
359 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
360 _decorations
|= Decor_Close
;
361 _functions
|= Func_Close
;
363 frame
->adjustSize(); // update the decorations
364 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
365 // if this protocol is requested, then the window will be notified
366 // by the window manager whenever it receives focus
367 _focus_notify
= true;
374 void Client::updateNormalHints()
378 int oldgravity
= _gravity
;
381 _gravity
= NorthWestGravity
;
382 _size_inc
.setPoint(1, 1);
383 _base_size
.setPoint(0, 0);
384 _min_size
.setPoint(0, 0);
385 _max_size
.setPoint(INT_MAX
, INT_MAX
);
387 // XXX: might want to cancel any interactive resizing of the window at this
390 // get the hints from the window
391 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
392 _positioned
= (size
.flags
& (PPosition
|USPosition
));
394 if (size
.flags
& PWinGravity
)
395 _gravity
= size
.win_gravity
;
397 if (size
.flags
& PMinSize
)
398 _min_size
.setPoint(size
.min_width
, size
.min_height
);
400 if (size
.flags
& PMaxSize
)
401 _max_size
.setPoint(size
.max_width
, size
.max_height
);
403 if (size
.flags
& PBaseSize
)
404 _base_size
.setPoint(size
.base_width
, size
.base_height
);
406 if (size
.flags
& PResizeInc
)
407 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
410 // if the client has a frame, i.e. has already been mapped and is
411 // changing its gravity
412 if (frame
&& _gravity
!= oldgravity
) {
413 // move our idea of the client's position based on its new gravity
415 frame
->frameGravity(x
, y
);
421 void Client::updateWMHints()
425 // assume a window takes input if it doesnt specify
429 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
430 if (hints
->flags
& InputHint
)
431 _can_focus
= hints
->input
;
433 if (hints
->flags
& XUrgencyHint
)
436 if (hints
->flags
& WindowGroupHint
) {
437 if (hints
->window_group
!= _group
) {
438 // XXX: remove from the old group if there was one
439 _group
= hints
->window_group
;
440 // XXX: do stuff with the group
450 void Client::updateTitle()
455 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
456 otk::Property::utf8
, &_title
)) {
458 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
459 otk::Property::ascii
, &_title
);
463 _title
= _("Unnamed Window");
466 frame
->setTitle(_title
);
470 void Client::updateIconTitle()
475 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
476 otk::Property::utf8
, &_icon_title
)) {
478 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
479 otk::Property::ascii
, &_icon_title
);
483 _icon_title
= _("Unnamed Window");
487 void Client::updateClass()
490 _app_name
= _app_class
= _role
= "";
492 otk::Property::StringVect v
;
493 unsigned long num
= 2;
495 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
496 otk::Property::ascii
, &num
, &v
)) {
497 if (num
> 0) _app_name
= v
[0].c_str();
498 if (num
> 1) _app_class
= v
[1].c_str();
503 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
504 otk::Property::ascii
, &num
, &v
)) {
505 if (num
> 0) _role
= v
[0].c_str();
510 void Client::updateStrut()
512 unsigned long num
= 4;
514 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
515 otk::Property::atoms
.cardinal
, &num
, &data
))
519 _strut
.left
= data
[0];
520 _strut
.right
= data
[1];
521 _strut
.top
= data
[2];
522 _strut
.bottom
= data
[3];
524 openbox
->screen(_screen
)->updateStrut();
531 void Client::updateTransientFor()
536 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
537 t
!= _window
) { // cant be transient to itself!
538 c
= openbox
->findClient(t
);
539 assert(c
!= this); // if this happens then we need to check for it
541 if (!c
/*XXX: && _group*/) {
542 // not transient to a client, see if it is transient for a group
543 if (//t == _group->leader() ||
545 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
546 // window is a transient for its group!
547 // XXX: for now this is treated as non-transient.
548 // this needs to be fixed!
553 // if anything has changed...
554 if (c
!= _transient_for
) {
556 _transient_for
->_transients
.remove(this); // remove from old parent
559 _transient_for
->_transients
.push_back(this); // add to new parent
561 // XXX: change decor status?
566 void Client::propertyHandler(const XPropertyEvent
&e
)
568 otk::EventHandler::propertyHandler(e
);
570 // compress changes to a single property into a single change
572 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
573 // XXX: it would be nice to compress ALL changes to a property, not just
574 // changes in a row without other props between.
575 if (ce
.xproperty
.atom
!= e
.atom
) {
576 XPutBackEvent(**otk::display
, &ce
);
581 if (e
.atom
== XA_WM_NORMAL_HINTS
)
583 else if (e
.atom
== XA_WM_HINTS
)
585 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
586 updateTransientFor();
588 calcLayer(); // type may have changed, so update the layer
589 setupDecorAndFunctions();
590 frame
->adjustSize(); // this updates the frame for any new decor settings
592 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
593 e
.atom
== otk::Property::atoms
.wm_name
)
595 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
596 e
.atom
== otk::Property::atoms
.wm_icon_name
)
598 else if (e
.atom
== otk::Property::atoms
.wm_class
)
600 else if (e
.atom
== otk::Property::atoms
.wm_protocols
)
602 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
607 void Client::setWMState(long state
)
609 if (state
== _wmstate
) return; // no change
614 // XXX: cause it to iconify
617 // XXX: cause it to uniconify
623 void Client::setDesktop(long target
)
625 if (target
== _desktop
) return;
627 printf("Setting desktop %ld\n", target
);
629 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
633 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
634 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
636 // 'move' the window to the new desktop
637 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
638 _desktop
== (signed)0xffffffff)
645 void Client::setState(StateAction action
, long data1
, long data2
)
647 bool shadestate
= _shaded
;
649 if (!(action
== State_Add
|| action
== State_Remove
||
650 action
== State_Toggle
))
651 return; // an invalid action was passed to the client message, ignore it
653 for (int i
= 0; i
< 2; ++i
) {
654 Atom state
= i
== 0 ? data1
: data2
;
656 if (! state
) continue;
658 // if toggling, then pick whether we're adding or removing
659 if (action
== State_Toggle
) {
660 if (state
== otk::Property::atoms
.net_wm_state_modal
)
661 action
= _modal
? State_Remove
: State_Add
;
662 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
663 action
= _max_vert
? State_Remove
: State_Add
;
664 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
665 action
= _max_horz
? State_Remove
: State_Add
;
666 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
667 action
= _shaded
? State_Remove
: State_Add
;
668 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
669 action
= _skip_taskbar
? State_Remove
: State_Add
;
670 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
671 action
= _skip_pager
? State_Remove
: State_Add
;
672 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
673 action
= _fullscreen
? State_Remove
: State_Add
;
674 else if (state
== otk::Property::atoms
.net_wm_state_above
)
675 action
= _above
? State_Remove
: State_Add
;
676 else if (state
== otk::Property::atoms
.net_wm_state_below
)
677 action
= _below
? State_Remove
: State_Add
;
680 if (action
== State_Add
) {
681 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
682 if (_modal
) continue;
684 // XXX: give it focus if another window has focus that shouldnt now
685 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
686 if (_max_vert
) continue;
688 // XXX: resize the window etc
689 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
690 if (_max_horz
) continue;
692 // XXX: resize the window etc
693 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
694 if (_shaded
) continue;
695 // shade when we're all thru here
697 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
698 _skip_taskbar
= true;
699 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
701 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
702 if (_fullscreen
) continue;
704 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
705 if (_above
) continue;
707 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
708 if (_below
) continue;
712 } else { // action == State_Remove
713 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
714 if (!_modal
) continue;
716 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
717 if (!_max_vert
) continue;
719 // XXX: resize the window etc
720 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
721 if (!_max_horz
) continue;
723 // XXX: resize the window etc
724 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
725 if (!_shaded
) continue;
726 // unshade when we're all thru here
728 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
729 _skip_taskbar
= false;
730 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
732 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
733 if (!_fullscreen
) continue;
735 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
736 if (!_above
) continue;
738 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
739 if (!_below
) continue;
744 if (shadestate
!= _shaded
)
750 void Client::toggleClientBorder(bool addborder
)
752 // adjust our idea of where the client is, based on its border. When the
753 // border is removed, the client should now be considered to be in a
754 // different position.
755 // when re-adding the border to the client, the same operation needs to be
757 int x
= _area
.x(), y
= _area
.y();
759 case NorthWestGravity
:
761 case SouthWestGravity
:
763 case NorthEastGravity
:
765 case SouthEastGravity
:
766 if (addborder
) x
-= _border_width
* 2;
767 else x
+= _border_width
* 2;
771 case NorthWestGravity
:
773 case NorthEastGravity
:
775 case SouthWestGravity
:
777 case SouthEastGravity
:
778 if (addborder
) y
-= _border_width
* 2;
779 else y
+= _border_width
* 2;
782 // no change for StaticGravity etc.
788 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
790 // move the client so it is back it the right spot _with_ its border!
791 XMoveWindow(**otk::display
, _window
, x
, y
);
793 XSetWindowBorderWidth(**otk::display
, _window
, 0);
797 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
799 otk::EventHandler::clientMessageHandler(e
);
801 if (e
.format
!= 32) return;
803 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
804 // compress changes into a single change
805 bool compress
= false;
807 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
808 // XXX: it would be nice to compress ALL messages of a type, not just
809 // messages in a row without other message types between.
810 if (ce
.xclient
.message_type
!= e
.message_type
) {
811 XPutBackEvent(**otk::display
, &ce
);
817 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
819 setWMState(e
.data
.l
[0]); // use the original event
820 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
821 // compress changes into a single change
822 bool compress
= false;
824 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
825 // XXX: it would be nice to compress ALL messages of a type, not just
826 // messages in a row without other message types between.
827 if (ce
.xclient
.message_type
!= e
.message_type
) {
828 XPutBackEvent(**otk::display
, &ce
);
834 setDesktop(e
.data
.l
[0]); // use the found event
836 setDesktop(e
.data
.l
[0]); // use the original event
837 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
838 // can't compress these
840 printf("net_wm_state %s %ld %ld for 0x%lx\n",
841 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
842 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
843 e
.data
.l
[1], e
.data
.l
[2], _window
);
845 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
846 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
848 printf("net_close_window for 0x%lx\n", _window
);
851 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
853 printf("net_active_window for 0x%lx\n", _window
);
859 openbox
->screen(_screen
)->raiseWindow(this);
865 void Client::shapeHandler(const XShapeEvent
&e
)
867 otk::EventHandler::shapeHandler(e
);
869 if (e
.kind
== ShapeBounding
) {
871 frame
->adjustShape();
877 void Client::resize(Corner anchor
, int w
, int h
, int x
, int y
)
882 // for interactive resizing. have to move half an increment in each
884 w
+= _size_inc
.x() / 2;
885 h
+= _size_inc
.y() / 2;
887 // is the window resizable? if it is not, then don't check its sizes, the
888 // client can do what it wants and the user can't change it anyhow
889 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
890 // smaller than min size or bigger than max size?
891 if (w
< _min_size
.x()) w
= _min_size
.x();
892 else if (w
> _max_size
.x()) w
= _max_size
.x();
893 if (h
< _min_size
.y()) h
= _min_size
.y();
894 else if (h
> _max_size
.y()) h
= _max_size
.y();
897 // keep to the increments
901 // you cannot resize to nothing
905 // store the logical size
906 _logical_size
.setPoint(w
, h
);
914 if (x
== INT_MIN
|| y
== INT_MIN
) {
921 x
-= w
- _area
.width();
924 y
-= h
- _area
.height();
927 x
-= w
- _area
.width();
928 y
-= h
- _area
.height();
935 XResizeWindow(**otk::display
, _window
, w
, h
);
937 // resize the frame to match the request
943 void Client::move(int x
, int y
)
947 // move the frame to be in the requested position
948 if (frame
) { // this can be called while mapping, before frame exists
949 frame
->adjustPosition();
951 // send synthetic configure notify (we don't need to if we aren't mapped
954 event
.type
= ConfigureNotify
;
955 event
.xconfigure
.display
= **otk::display
;
956 event
.xconfigure
.event
= _window
;
957 event
.xconfigure
.window
= _window
;
958 event
.xconfigure
.x
= x
;
959 event
.xconfigure
.y
= y
;
960 event
.xconfigure
.width
= _area
.width();
961 event
.xconfigure
.height
= _area
.height();
962 event
.xconfigure
.border_width
= _border_width
;
963 event
.xconfigure
.above
= frame
->window();
964 event
.xconfigure
.override_redirect
= False
;
965 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
966 StructureNotifyMask
, &event
);
975 if (!(_functions
& Func_Close
)) return;
977 // XXX: itd be cool to do timeouts and shit here for killing the client's
979 // like... if the window is around after 5 seconds, then the close button
980 // turns a nice red, and if this function is called again, the client is
981 // explicitly killed.
983 ce
.xclient
.type
= ClientMessage
;
984 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
985 ce
.xclient
.display
= **otk::display
;
986 ce
.xclient
.window
= _window
;
987 ce
.xclient
.format
= 32;
988 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
989 ce
.xclient
.data
.l
[1] = CurrentTime
;
990 ce
.xclient
.data
.l
[2] = 0l;
991 ce
.xclient
.data
.l
[3] = 0l;
992 ce
.xclient
.data
.l
[4] = 0l;
993 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
997 void Client::changeState()
999 unsigned long state
[2];
1000 state
[0] = _wmstate
;
1002 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1003 otk::Property::atoms
.wm_state
, state
, 2);
1008 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1010 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1012 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1014 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1016 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1018 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1020 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1022 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1024 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1026 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1027 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1028 otk::Property::atoms
.atom
, netstate
, num
);
1034 void Client::shade(bool shade
)
1036 if (shade
== _shaded
) return; // already done
1038 _wmstate
= shade
? IconicState
: NormalState
;
1041 frame
->adjustSize();
1045 bool Client::focus() const
1047 // won't try focus if the client doesn't want it, or if the window isn't
1048 // visible on the screen
1049 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1051 if (_focused
) return true;
1054 XSetInputFocus(**otk::display
, _window
,
1055 RevertToNone
, CurrentTime
);
1057 if (_focus_notify
) {
1059 ce
.xclient
.type
= ClientMessage
;
1060 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1061 ce
.xclient
.display
= **otk::display
;
1062 ce
.xclient
.window
= _window
;
1063 ce
.xclient
.format
= 32;
1064 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1065 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1066 ce
.xclient
.data
.l
[2] = 0l;
1067 ce
.xclient
.data
.l
[3] = 0l;
1068 ce
.xclient
.data
.l
[4] = 0l;
1069 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1076 void Client::unfocus() const
1078 if (!_focused
) return;
1080 assert(openbox
->focusedClient() == this);
1081 openbox
->setFocusedClient(0);
1085 void Client::focusHandler(const XFocusChangeEvent
&e
)
1088 // printf("FocusIn for 0x%lx\n", e.window);
1091 otk::EventHandler::focusHandler(e
);
1096 openbox
->setFocusedClient(this);
1100 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1103 // printf("FocusOut for 0x%lx\n", e.window);
1106 otk::EventHandler::unfocusHandler(e
);
1111 if (openbox
->focusedClient() == this)
1112 openbox
->setFocusedClient(0);
1116 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1119 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1122 otk::EventHandler::configureRequestHandler(e
);
1124 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1126 if (e
.value_mask
& CWBorderWidth
)
1127 _border_width
= e
.border_width
;
1129 // resize, then move, as specified in the EWMH section 7.7
1130 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1131 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1132 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1136 case NorthEastGravity
:
1140 case SouthWestGravity
:
1142 corner
= BottomLeft
;
1144 case SouthEastGravity
:
1145 corner
= BottomRight
;
1147 default: // NorthWest, Static, etc
1151 // if moving AND resizing ...
1152 if (e
.value_mask
& (CWX
| CWY
)) {
1153 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1154 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1155 resize(corner
, w
, h
, x
, y
);
1156 } else // if JUST resizing...
1157 resize(corner
, w
, h
);
1158 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1159 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1160 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1164 if (e
.value_mask
& CWStackMode
) {
1168 openbox
->screen(_screen
)->lowerWindow(this);
1174 openbox
->screen(_screen
)->raiseWindow(this);
1181 void Client::unmapHandler(const XUnmapEvent
&e
)
1183 if (ignore_unmaps
) {
1185 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1192 printf("UnmapNotify for 0x%lx\n", e
.window
);
1195 otk::EventHandler::unmapHandler(e
);
1197 // this deletes us etc
1198 openbox
->screen(_screen
)->unmanageWindow(this);
1202 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1205 printf("DestroyNotify for 0x%lx\n", e
.window
);
1208 otk::EventHandler::destroyHandler(e
);
1210 // this deletes us etc
1211 openbox
->screen(_screen
)->unmanageWindow(this);
1215 void Client::reparentHandler(const XReparentEvent
&e
)
1217 // this is when the client is first taken captive in the frame
1218 if (e
.parent
== frame
->plate()) return;
1221 printf("ReparentNotify for 0x%lx\n", e
.window
);
1224 otk::EventHandler::reparentHandler(e
);
1227 This event is quite rare and is usually handled in unmapHandler.
1228 However, if the window is unmapped when the reparent event occurs,
1229 the window manager never sees it because an unmap event is not sent
1230 to an already unmapped window.
1233 // we don't want the reparent event, put it back on the stack for the X
1234 // server to deal with after we unmanage the window
1237 XPutBackEvent(**otk::display
, &ev
);
1239 // this deletes us etc
1240 openbox
->screen(_screen
)->unmanageWindow(this);