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();
760 case NorthWestGravity
:
762 case SouthWestGravity
:
767 case NorthEastGravity
:
769 case SouthEastGravity
:
770 if (addborder
) x
-= _border_width
* 2;
771 else x
+= _border_width
* 2;
775 if (addborder
) x
-= _border_width
;
776 else x
+= _border_width
;
781 case NorthWestGravity
:
785 case NorthEastGravity
:
788 case SouthWestGravity
:
790 case SouthEastGravity
:
791 if (addborder
) y
-= _border_width
* 2;
792 else y
+= _border_width
* 2;
796 if (addborder
) y
-= _border_width
;
797 else y
+= _border_width
;
803 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
805 // move the client so it is back it the right spot _with_ its border!
806 XMoveWindow(**otk::display
, _window
, x
, y
);
808 XSetWindowBorderWidth(**otk::display
, _window
, 0);
812 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
814 otk::EventHandler::clientMessageHandler(e
);
816 if (e
.format
!= 32) return;
818 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
819 // compress changes into a single change
820 bool compress
= false;
822 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
823 // XXX: it would be nice to compress ALL messages of a type, not just
824 // messages in a row without other message types between.
825 if (ce
.xclient
.message_type
!= e
.message_type
) {
826 XPutBackEvent(**otk::display
, &ce
);
832 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
834 setWMState(e
.data
.l
[0]); // use the original event
835 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
836 // compress changes into a single change
837 bool compress
= false;
839 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
840 // XXX: it would be nice to compress ALL messages of a type, not just
841 // messages in a row without other message types between.
842 if (ce
.xclient
.message_type
!= e
.message_type
) {
843 XPutBackEvent(**otk::display
, &ce
);
849 setDesktop(e
.data
.l
[0]); // use the found event
851 setDesktop(e
.data
.l
[0]); // use the original event
852 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
853 // can't compress these
855 printf("net_wm_state %s %ld %ld for 0x%lx\n",
856 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
857 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
858 e
.data
.l
[1], e
.data
.l
[2], _window
);
860 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
861 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
863 printf("net_close_window for 0x%lx\n", _window
);
866 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
868 printf("net_active_window for 0x%lx\n", _window
);
874 openbox
->screen(_screen
)->raiseWindow(this);
880 void Client::shapeHandler(const XShapeEvent
&e
)
882 otk::EventHandler::shapeHandler(e
);
884 if (e
.kind
== ShapeBounding
) {
886 frame
->adjustShape();
892 void Client::resize(Corner anchor
, int w
, int h
, int x
, int y
)
897 // for interactive resizing. have to move half an increment in each
899 w
+= _size_inc
.x() / 2;
900 h
+= _size_inc
.y() / 2;
902 // is the window resizable? if it is not, then don't check its sizes, the
903 // client can do what it wants and the user can't change it anyhow
904 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
905 // smaller than min size or bigger than max size?
906 if (w
< _min_size
.x()) w
= _min_size
.x();
907 else if (w
> _max_size
.x()) w
= _max_size
.x();
908 if (h
< _min_size
.y()) h
= _min_size
.y();
909 else if (h
> _max_size
.y()) h
= _max_size
.y();
912 // keep to the increments
916 // you cannot resize to nothing
920 // store the logical size
921 _logical_size
.setPoint(w
, h
);
929 if (x
== INT_MIN
|| y
== INT_MIN
) {
936 x
-= w
- _area
.width();
939 y
-= h
- _area
.height();
942 x
-= w
- _area
.width();
943 y
-= h
- _area
.height();
950 XResizeWindow(**otk::display
, _window
, w
, h
);
952 // resize the frame to match the request
958 void Client::move(int x
, int y
)
962 // move the frame to be in the requested position
963 if (frame
) { // this can be called while mapping, before frame exists
964 frame
->adjustPosition();
966 // send synthetic configure notify (we don't need to if we aren't mapped
969 event
.type
= ConfigureNotify
;
970 event
.xconfigure
.display
= **otk::display
;
971 event
.xconfigure
.event
= _window
;
972 event
.xconfigure
.window
= _window
;
973 event
.xconfigure
.x
= x
;
974 event
.xconfigure
.y
= y
;
975 event
.xconfigure
.width
= _area
.width();
976 event
.xconfigure
.height
= _area
.height();
977 event
.xconfigure
.border_width
= _border_width
;
978 event
.xconfigure
.above
= frame
->window();
979 event
.xconfigure
.override_redirect
= False
;
980 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
981 StructureNotifyMask
, &event
);
990 if (!(_functions
& Func_Close
)) return;
992 // XXX: itd be cool to do timeouts and shit here for killing the client's
994 // like... if the window is around after 5 seconds, then the close button
995 // turns a nice red, and if this function is called again, the client is
996 // explicitly killed.
998 ce
.xclient
.type
= ClientMessage
;
999 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1000 ce
.xclient
.display
= **otk::display
;
1001 ce
.xclient
.window
= _window
;
1002 ce
.xclient
.format
= 32;
1003 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1004 ce
.xclient
.data
.l
[1] = CurrentTime
;
1005 ce
.xclient
.data
.l
[2] = 0l;
1006 ce
.xclient
.data
.l
[3] = 0l;
1007 ce
.xclient
.data
.l
[4] = 0l;
1008 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1012 void Client::changeState()
1014 unsigned long state
[2];
1015 state
[0] = _wmstate
;
1017 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1018 otk::Property::atoms
.wm_state
, state
, 2);
1023 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1025 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1027 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1029 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1031 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1033 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1035 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1037 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1039 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1041 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1042 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1043 otk::Property::atoms
.atom
, netstate
, num
);
1049 void Client::shade(bool shade
)
1051 if (shade
== _shaded
) return; // already done
1053 _wmstate
= shade
? IconicState
: NormalState
;
1056 frame
->adjustSize();
1060 bool Client::focus() const
1062 // won't try focus if the client doesn't want it, or if the window isn't
1063 // visible on the screen
1064 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1066 if (_focused
) return true;
1069 XSetInputFocus(**otk::display
, _window
,
1070 RevertToNone
, CurrentTime
);
1072 if (_focus_notify
) {
1074 ce
.xclient
.type
= ClientMessage
;
1075 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1076 ce
.xclient
.display
= **otk::display
;
1077 ce
.xclient
.window
= _window
;
1078 ce
.xclient
.format
= 32;
1079 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1080 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1081 ce
.xclient
.data
.l
[2] = 0l;
1082 ce
.xclient
.data
.l
[3] = 0l;
1083 ce
.xclient
.data
.l
[4] = 0l;
1084 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1091 void Client::unfocus() const
1093 if (!_focused
) return;
1095 assert(openbox
->focusedClient() == this);
1096 openbox
->setFocusedClient(0);
1100 void Client::focusHandler(const XFocusChangeEvent
&e
)
1103 // printf("FocusIn for 0x%lx\n", e.window);
1106 otk::EventHandler::focusHandler(e
);
1111 openbox
->setFocusedClient(this);
1115 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1118 // printf("FocusOut for 0x%lx\n", e.window);
1121 otk::EventHandler::unfocusHandler(e
);
1126 if (openbox
->focusedClient() == this)
1127 openbox
->setFocusedClient(0);
1131 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1134 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1137 otk::EventHandler::configureRequestHandler(e
);
1139 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1141 if (e
.value_mask
& CWBorderWidth
)
1142 _border_width
= e
.border_width
;
1144 // resize, then move, as specified in the EWMH section 7.7
1145 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1146 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1147 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1151 case NorthEastGravity
:
1155 case SouthWestGravity
:
1157 corner
= BottomLeft
;
1159 case SouthEastGravity
:
1160 corner
= BottomRight
;
1162 default: // NorthWest, Static, etc
1166 // if moving AND resizing ...
1167 if (e
.value_mask
& (CWX
| CWY
)) {
1168 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1169 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1170 resize(corner
, w
, h
, x
, y
);
1171 } else // if JUST resizing...
1172 resize(corner
, w
, h
);
1173 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1174 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1175 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1179 if (e
.value_mask
& CWStackMode
) {
1183 openbox
->screen(_screen
)->lowerWindow(this);
1189 openbox
->screen(_screen
)->raiseWindow(this);
1196 void Client::unmapHandler(const XUnmapEvent
&e
)
1198 if (ignore_unmaps
) {
1200 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1207 printf("UnmapNotify for 0x%lx\n", e
.window
);
1210 otk::EventHandler::unmapHandler(e
);
1212 // this deletes us etc
1213 openbox
->screen(_screen
)->unmanageWindow(this);
1217 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1220 printf("DestroyNotify for 0x%lx\n", e
.window
);
1223 otk::EventHandler::destroyHandler(e
);
1225 // this deletes us etc
1226 openbox
->screen(_screen
)->unmanageWindow(this);
1230 void Client::reparentHandler(const XReparentEvent
&e
)
1232 // this is when the client is first taken captive in the frame
1233 if (e
.parent
== frame
->plate()) return;
1236 printf("ReparentNotify for 0x%lx\n", e
.window
);
1239 otk::EventHandler::reparentHandler(e
);
1242 This event is quite rare and is usually handled in unmapHandler.
1243 However, if the window is unmapped when the reparent event occurs,
1244 the window manager never sees it because an unmap event is not sent
1245 to an already unmapped window.
1248 // we don't want the reparent event, put it back on the stack for the X
1249 // server to deal with after we unmanage the window
1252 XPutBackEvent(**otk::display
, &ev
);
1254 // this deletes us etc
1255 openbox
->screen(_screen
)->unmanageWindow(this);