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
;
58 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::getGravity()
97 XWindowAttributes wattrib
;
101 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &junk
) &&
102 size
.flags
& PWinGravity
)
103 // first try the normal hints
104 _gravity
= size
.win_gravity
;
106 // then fall back to the attribute
107 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
108 assert(ret
!= BadWindow
);
109 _gravity
= wattrib
.win_gravity
;
113 void Client::getDesktop()
115 // defaults to the current desktop
116 _desktop
= openbox
->screen(_screen
)->desktop();
118 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
119 otk::Property::atoms
.cardinal
,
120 (long unsigned*)&_desktop
)) {
121 // make sure the hint exists
122 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
123 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
128 void Client::getType()
130 _type
= (WindowType
) -1;
133 unsigned long num
= (unsigned) -1;
134 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
135 otk::Property::atoms
.atom
, &num
, &val
)) {
136 // use the first value that we know about in the array
137 for (unsigned long i
= 0; i
< num
; ++i
) {
138 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
139 _type
= Type_Desktop
;
140 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
142 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
143 _type
= Type_Toolbar
;
144 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
146 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
147 _type
= Type_Utility
;
148 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
150 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
152 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
154 // XXX: make this work again
155 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
156 // mwm_decorations = 0; // prevent this window from getting any decor
157 if (_type
!= (WindowType
) -1)
158 break; // grab the first known type
163 if (_type
== (WindowType
) -1) {
165 * the window type hint was not set, which means we either classify ourself
166 * as a normal window or a dialog, depending on if we are a transient.
176 void Client::setupDecorAndFunctions()
178 // start with everything
179 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
180 Decor_Iconify
| Decor_Maximize
;
181 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
185 // normal windows retain all of the possible decorations and
189 // dialogs cannot be maximized
190 _decorations
&= ~Decor_Maximize
;
191 _functions
&= ~Func_Maximize
;
197 // these windows get less functionality
198 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
199 _functions
&= ~(Func_Iconify
| Func_Resize
);
205 // none of these windows are manipulated by the window manager
211 // Mwm Hints are applied subtractively to what has already been chosen for
212 // decor and functionality
213 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
214 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
215 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
216 _decorations
&= ~Decor_Border
;
217 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
218 _decorations
&= ~Decor_Handle
;
219 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
220 _decorations
&= ~Decor_Titlebar
;
221 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
222 _decorations
&= ~Decor_Iconify
;
223 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
224 _decorations
&= ~Decor_Maximize
;
228 if (_mwmhints
.flags
& MwmFlag_Functions
) {
229 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
230 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
231 _functions
&= ~Func_Resize
;
232 if (! (_mwmhints
.functions
& MwmFunc_Move
))
233 _functions
&= ~Func_Move
;
234 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
235 _functions
&= ~Func_Iconify
;
236 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
237 _functions
&= ~Func_Maximize
;
238 // dont let mwm hints kill the close button
239 //if (! (_mwmhints.functions & MwmFunc_Close))
240 // _functions &= ~Func_Close;
244 // XXX: changeAllowedActions();
248 void Client::getMwmHints()
250 unsigned long num
= MwmHints::elements
;
251 unsigned long *hints
;
253 _mwmhints
.flags
= 0; // default to none
255 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
256 otk::Property::atoms
.motif_wm_hints
, &num
,
257 (unsigned long **)&hints
))
260 if (num
>= MwmHints::elements
) {
261 // retrieved the hints
262 _mwmhints
.flags
= hints
[0];
263 _mwmhints
.functions
= hints
[1];
264 _mwmhints
.decorations
= hints
[2];
271 void Client::getArea()
273 XWindowAttributes wattrib
;
276 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
277 assert(ret
!= BadWindow
);
279 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
280 _border_width
= wattrib
.border_width
;
284 void Client::getState()
286 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
287 _skip_taskbar
= _skip_pager
= false;
289 unsigned long *state
;
290 unsigned long num
= (unsigned) -1;
292 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
293 otk::Property::atoms
.atom
, &num
, &state
)) {
294 for (unsigned long i
= 0; i
< num
; ++i
) {
295 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
297 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
) {
299 _wmstate
= IconicState
;
300 } else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
301 _skip_taskbar
= true;
302 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
304 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
306 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
308 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
310 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
312 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
321 void Client::getShaped()
325 if (otk::display
->shape()) {
330 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
332 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
333 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
340 void Client::calcLayer() {
343 if (_iconic
) l
= Layer_Icon
;
344 else if (_fullscreen
) l
= Layer_Fullscreen
;
345 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
346 else if (_type
== Type_Dock
) {
347 if (!_below
) l
= Layer_Top
;
348 else l
= Layer_Normal
;
350 else if (_above
) l
= Layer_Above
;
351 else if (_below
) l
= Layer_Below
;
352 else l
= Layer_Normal
;
358 if we don't have a frame, then we aren't mapped yet (and this would
361 openbox
->screen(_screen
)->raiseWindow(this);
367 void Client::updateProtocols()
372 _focus_notify
= false;
373 _decorations
&= ~Decor_Close
;
374 _functions
&= ~Func_Close
;
376 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
377 for (int i
= 0; i
< num_return
; ++i
) {
378 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
379 _decorations
|= Decor_Close
;
380 _functions
|= Func_Close
;
382 frame
->adjustSize(); // update the decorations
383 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
384 // if this protocol is requested, then the window will be notified
385 // by the window manager whenever it receives focus
386 _focus_notify
= true;
393 void Client::updateNormalHints()
397 int oldgravity
= _gravity
;
400 _size_inc
.setPoint(1, 1);
401 _base_size
.setPoint(0, 0);
402 _min_size
.setPoint(0, 0);
403 _max_size
.setPoint(INT_MAX
, INT_MAX
);
405 // XXX: might want to cancel any interactive resizing of the window at this
408 // get the hints from the window
409 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
410 _positioned
= (size
.flags
& (PPosition
|USPosition
));
412 if (size
.flags
& PWinGravity
) {
413 _gravity
= size
.win_gravity
;
415 // if the client has a frame, i.e. has already been mapped and is
416 // changing its gravity
417 if (_gravity
!= oldgravity
) {
418 // move our idea of the client's position based on its new gravity
420 frame
->frameGravity(x
, y
);
425 if (size
.flags
& PMinSize
)
426 _min_size
.setPoint(size
.min_width
, size
.min_height
);
428 if (size
.flags
& PMaxSize
)
429 _max_size
.setPoint(size
.max_width
, size
.max_height
);
431 if (size
.flags
& PBaseSize
)
432 _base_size
.setPoint(size
.base_width
, size
.base_height
);
434 if (size
.flags
& PResizeInc
)
435 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
440 void Client::updateWMHints()
444 // assume a window takes input if it doesnt specify
448 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
449 if (hints
->flags
& InputHint
)
450 _can_focus
= hints
->input
;
452 if (hints
->flags
& XUrgencyHint
)
455 if (hints
->flags
& WindowGroupHint
) {
456 if (hints
->window_group
!= _group
) {
457 // XXX: remove from the old group if there was one
458 _group
= hints
->window_group
;
459 // XXX: do stuff with the group
469 void Client::updateTitle()
474 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
475 otk::Property::utf8
, &_title
)) {
477 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
478 otk::Property::ascii
, &_title
);
482 _title
= _("Unnamed Window");
485 frame
->setTitle(_title
);
489 void Client::updateIconTitle()
494 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
495 otk::Property::utf8
, &_icon_title
)) {
497 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
498 otk::Property::ascii
, &_icon_title
);
502 _icon_title
= _("Unnamed Window");
506 void Client::updateClass()
509 _app_name
= _app_class
= _role
= "";
511 otk::Property::StringVect v
;
512 unsigned long num
= 2;
514 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
515 otk::Property::ascii
, &num
, &v
)) {
516 if (num
> 0) _app_name
= v
[0].c_str();
517 if (num
> 1) _app_class
= v
[1].c_str();
522 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
523 otk::Property::ascii
, &num
, &v
)) {
524 if (num
> 0) _role
= v
[0].c_str();
529 void Client::updateStrut()
531 unsigned long num
= 4;
533 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
534 otk::Property::atoms
.cardinal
, &num
, &data
))
538 _strut
.left
= data
[0];
539 _strut
.right
= data
[1];
540 _strut
.top
= data
[2];
541 _strut
.bottom
= data
[3];
543 openbox
->screen(_screen
)->updateStrut();
550 void Client::updateTransientFor()
555 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
556 t
!= _window
) { // cant be transient to itself!
557 c
= openbox
->findClient(t
);
558 assert(c
!= this); // if this happens then we need to check for it
560 if (!c
/*XXX: && _group*/) {
561 // not transient to a client, see if it is transient for a group
562 if (//t == _group->leader() ||
564 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
565 // window is a transient for its group!
566 // XXX: for now this is treated as non-transient.
567 // this needs to be fixed!
572 // if anything has changed...
573 if (c
!= _transient_for
) {
575 _transient_for
->_transients
.remove(this); // remove from old parent
578 _transient_for
->_transients
.push_back(this); // add to new parent
580 // XXX: change decor status?
585 void Client::propertyHandler(const XPropertyEvent
&e
)
587 otk::EventHandler::propertyHandler(e
);
589 // compress changes to a single property into a single change
591 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
592 // XXX: it would be nice to compress ALL changes to a property, not just
593 // changes in a row without other props between.
594 if (ce
.xproperty
.atom
!= e
.atom
) {
595 XPutBackEvent(**otk::display
, &ce
);
600 if (e
.atom
== XA_WM_NORMAL_HINTS
)
602 else if (e
.atom
== XA_WM_HINTS
)
604 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
605 updateTransientFor();
607 calcLayer(); // type may have changed, so update the layer
608 setupDecorAndFunctions();
609 frame
->adjustSize(); // this updates the frame for any new decor settings
611 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
612 e
.atom
== otk::Property::atoms
.wm_name
)
614 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
615 e
.atom
== otk::Property::atoms
.wm_icon_name
)
617 else if (e
.atom
== otk::Property::atoms
.wm_class
)
619 else if (e
.atom
== otk::Property::atoms
.wm_protocols
)
621 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
626 void Client::setWMState(long state
)
628 if (state
== _wmstate
) return; // no change
633 // XXX: cause it to iconify
636 // XXX: cause it to uniconify
642 void Client::setDesktop(long target
)
644 if (target
== _desktop
) return;
646 printf("Setting desktop %ld\n", target
);
648 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
652 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
653 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
655 // 'move' the window to the new desktop
656 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
657 _desktop
== (signed)0xffffffff)
664 void Client::setState(StateAction action
, long data1
, long data2
)
666 bool shadestate
= _shaded
;
668 if (!(action
== State_Add
|| action
== State_Remove
||
669 action
== State_Toggle
))
670 return; // an invalid action was passed to the client message, ignore it
672 for (int i
= 0; i
< 2; ++i
) {
673 Atom state
= i
== 0 ? data1
: data2
;
675 if (! state
) continue;
677 // if toggling, then pick whether we're adding or removing
678 if (action
== State_Toggle
) {
679 if (state
== otk::Property::atoms
.net_wm_state_modal
)
680 action
= _modal
? State_Remove
: State_Add
;
681 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
682 action
= _max_vert
? State_Remove
: State_Add
;
683 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
684 action
= _max_horz
? State_Remove
: State_Add
;
685 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
686 action
= _shaded
? State_Remove
: State_Add
;
687 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
688 action
= _skip_taskbar
? State_Remove
: State_Add
;
689 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
690 action
= _skip_pager
? State_Remove
: State_Add
;
691 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
692 action
= _fullscreen
? State_Remove
: State_Add
;
693 else if (state
== otk::Property::atoms
.net_wm_state_above
)
694 action
= _above
? State_Remove
: State_Add
;
695 else if (state
== otk::Property::atoms
.net_wm_state_below
)
696 action
= _below
? State_Remove
: State_Add
;
699 if (action
== State_Add
) {
700 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
701 if (_modal
) continue;
703 // XXX: give it focus if another window has focus that shouldnt now
704 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
705 if (_max_vert
) continue;
707 // XXX: resize the window etc
708 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
709 if (_max_horz
) continue;
711 // XXX: resize the window etc
712 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
713 if (_shaded
) continue;
714 // shade when we're all thru here
716 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
717 _skip_taskbar
= true;
718 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
720 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
721 if (_fullscreen
) continue;
723 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
724 if (_above
) continue;
726 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
727 if (_below
) continue;
731 } else { // action == State_Remove
732 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
733 if (!_modal
) continue;
735 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
736 if (!_max_vert
) continue;
738 // XXX: resize the window etc
739 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
740 if (!_max_horz
) continue;
742 // XXX: resize the window etc
743 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
744 if (!_shaded
) continue;
745 // unshade when we're all thru here
747 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
748 _skip_taskbar
= false;
749 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
751 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
752 if (!_fullscreen
) continue;
754 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
755 if (!_above
) continue;
757 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
758 if (!_below
) continue;
763 if (shadestate
!= _shaded
)
769 void Client::toggleClientBorder(bool addborder
)
771 // adjust our idea of where the client is, based on its border. When the
772 // border is removed, the client should now be considered to be in a
773 // different position.
774 // when re-adding the border to the client, the same operation needs to be
776 int x
= _area
.x(), y
= _area
.y();
779 case NorthWestGravity
:
781 case SouthWestGravity
:
786 case NorthEastGravity
:
788 case SouthEastGravity
:
789 if (addborder
) x
-= _border_width
* 2;
790 else x
+= _border_width
* 2;
794 if (addborder
) x
-= _border_width
;
795 else x
+= _border_width
;
800 case NorthWestGravity
:
804 case NorthEastGravity
:
807 case SouthWestGravity
:
809 case SouthEastGravity
:
810 if (addborder
) y
-= _border_width
* 2;
811 else y
+= _border_width
* 2;
815 if (addborder
) y
-= _border_width
;
816 else y
+= _border_width
;
822 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
824 // move the client so it is back it the right spot _with_ its border!
825 XMoveWindow(**otk::display
, _window
, x
, y
);
827 XSetWindowBorderWidth(**otk::display
, _window
, 0);
831 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
833 otk::EventHandler::clientMessageHandler(e
);
835 if (e
.format
!= 32) return;
837 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
838 // compress changes into a single change
839 bool compress
= false;
841 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
842 // XXX: it would be nice to compress ALL messages of a type, not just
843 // messages in a row without other message types between.
844 if (ce
.xclient
.message_type
!= e
.message_type
) {
845 XPutBackEvent(**otk::display
, &ce
);
851 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
853 setWMState(e
.data
.l
[0]); // use the original event
854 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
855 // compress changes into a single change
856 bool compress
= false;
858 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
859 // XXX: it would be nice to compress ALL messages of a type, not just
860 // messages in a row without other message types between.
861 if (ce
.xclient
.message_type
!= e
.message_type
) {
862 XPutBackEvent(**otk::display
, &ce
);
868 setDesktop(e
.data
.l
[0]); // use the found event
870 setDesktop(e
.data
.l
[0]); // use the original event
871 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
872 // can't compress these
874 printf("net_wm_state %s %ld %ld for 0x%lx\n",
875 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
876 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
877 e
.data
.l
[1], e
.data
.l
[2], _window
);
879 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
880 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
882 printf("net_close_window for 0x%lx\n", _window
);
885 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
887 printf("net_active_window for 0x%lx\n", _window
);
893 openbox
->screen(_screen
)->raiseWindow(this);
899 void Client::shapeHandler(const XShapeEvent
&e
)
901 otk::EventHandler::shapeHandler(e
);
903 if (e
.kind
== ShapeBounding
) {
905 frame
->adjustShape();
911 void Client::resize(Corner anchor
, int w
, int h
, int x
, int y
)
916 // for interactive resizing. have to move half an increment in each
918 w
+= _size_inc
.x() / 2;
919 h
+= _size_inc
.y() / 2;
921 // is the window resizable? if it is not, then don't check its sizes, the
922 // client can do what it wants and the user can't change it anyhow
923 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
924 // smaller than min size or bigger than max size?
925 if (w
< _min_size
.x()) w
= _min_size
.x();
926 else if (w
> _max_size
.x()) w
= _max_size
.x();
927 if (h
< _min_size
.y()) h
= _min_size
.y();
928 else if (h
> _max_size
.y()) h
= _max_size
.y();
931 // keep to the increments
935 // you cannot resize to nothing
939 // store the logical size
940 _logical_size
.setPoint(w
, h
);
948 if (x
== INT_MIN
|| y
== INT_MIN
) {
955 x
-= w
- _area
.width();
958 y
-= h
- _area
.height();
961 x
-= w
- _area
.width();
962 y
-= h
- _area
.height();
969 XResizeWindow(**otk::display
, _window
, w
, h
);
971 // resize the frame to match the request
977 void Client::move(int x
, int y
)
981 // move the frame to be in the requested position
982 if (frame
) { // this can be called while mapping, before frame exists
983 frame
->adjustPosition();
985 // send synthetic configure notify (we don't need to if we aren't mapped
988 event
.type
= ConfigureNotify
;
989 event
.xconfigure
.display
= **otk::display
;
990 event
.xconfigure
.event
= _window
;
991 event
.xconfigure
.window
= _window
;
992 event
.xconfigure
.x
= x
;
993 event
.xconfigure
.y
= y
;
994 event
.xconfigure
.width
= _area
.width();
995 event
.xconfigure
.height
= _area
.height();
996 event
.xconfigure
.border_width
= _border_width
;
997 event
.xconfigure
.above
= frame
->window();
998 event
.xconfigure
.override_redirect
= False
;
999 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1000 StructureNotifyMask
, &event
);
1005 void Client::close()
1009 if (!(_functions
& Func_Close
)) return;
1011 // XXX: itd be cool to do timeouts and shit here for killing the client's
1013 // like... if the window is around after 5 seconds, then the close button
1014 // turns a nice red, and if this function is called again, the client is
1015 // explicitly killed.
1017 ce
.xclient
.type
= ClientMessage
;
1018 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1019 ce
.xclient
.display
= **otk::display
;
1020 ce
.xclient
.window
= _window
;
1021 ce
.xclient
.format
= 32;
1022 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1023 ce
.xclient
.data
.l
[1] = CurrentTime
;
1024 ce
.xclient
.data
.l
[2] = 0l;
1025 ce
.xclient
.data
.l
[3] = 0l;
1026 ce
.xclient
.data
.l
[4] = 0l;
1027 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1031 void Client::changeState()
1033 unsigned long state
[2];
1034 state
[0] = _wmstate
;
1036 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1037 otk::Property::atoms
.wm_state
, state
, 2);
1042 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1044 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1046 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1048 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1050 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1052 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1054 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1056 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1058 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1060 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1061 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1062 otk::Property::atoms
.atom
, netstate
, num
);
1068 void Client::shade(bool shade
)
1070 if (shade
== _shaded
) return; // already done
1072 _wmstate
= shade
? IconicState
: NormalState
;
1075 frame
->adjustSize();
1079 bool Client::focus() const
1081 // won't try focus if the client doesn't want it, or if the window isn't
1082 // visible on the screen
1083 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1085 if (_focused
) return true;
1088 XSetInputFocus(**otk::display
, _window
,
1089 RevertToNone
, CurrentTime
);
1091 if (_focus_notify
) {
1093 ce
.xclient
.type
= ClientMessage
;
1094 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1095 ce
.xclient
.display
= **otk::display
;
1096 ce
.xclient
.window
= _window
;
1097 ce
.xclient
.format
= 32;
1098 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1099 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1100 ce
.xclient
.data
.l
[2] = 0l;
1101 ce
.xclient
.data
.l
[3] = 0l;
1102 ce
.xclient
.data
.l
[4] = 0l;
1103 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1110 void Client::unfocus() const
1112 if (!_focused
) return;
1114 assert(openbox
->focusedClient() == this);
1115 openbox
->setFocusedClient(0);
1119 void Client::focusHandler(const XFocusChangeEvent
&e
)
1122 // printf("FocusIn for 0x%lx\n", e.window);
1125 otk::EventHandler::focusHandler(e
);
1130 openbox
->setFocusedClient(this);
1134 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1137 // printf("FocusOut for 0x%lx\n", e.window);
1140 otk::EventHandler::unfocusHandler(e
);
1145 if (openbox
->focusedClient() == this)
1146 openbox
->setFocusedClient(0);
1150 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1153 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1156 otk::EventHandler::configureRequestHandler(e
);
1158 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1160 if (e
.value_mask
& CWBorderWidth
)
1161 _border_width
= e
.border_width
;
1163 // resize, then move, as specified in the EWMH section 7.7
1164 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1165 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1166 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1170 case NorthEastGravity
:
1174 case SouthWestGravity
:
1176 corner
= BottomLeft
;
1178 case SouthEastGravity
:
1179 corner
= BottomRight
;
1181 default: // NorthWest, Static, etc
1185 // if moving AND resizing ...
1186 if (e
.value_mask
& (CWX
| CWY
)) {
1187 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1188 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1189 resize(corner
, w
, h
, x
, y
);
1190 } else // if JUST resizing...
1191 resize(corner
, w
, h
);
1192 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1193 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1194 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1198 if (e
.value_mask
& CWStackMode
) {
1202 openbox
->screen(_screen
)->lowerWindow(this);
1208 openbox
->screen(_screen
)->raiseWindow(this);
1215 void Client::unmapHandler(const XUnmapEvent
&e
)
1217 if (ignore_unmaps
) {
1219 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1226 printf("UnmapNotify for 0x%lx\n", e
.window
);
1229 otk::EventHandler::unmapHandler(e
);
1231 // this deletes us etc
1232 openbox
->screen(_screen
)->unmanageWindow(this);
1236 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1239 printf("DestroyNotify for 0x%lx\n", e
.window
);
1242 otk::EventHandler::destroyHandler(e
);
1244 // this deletes us etc
1245 openbox
->screen(_screen
)->unmanageWindow(this);
1249 void Client::reparentHandler(const XReparentEvent
&e
)
1251 // this is when the client is first taken captive in the frame
1252 if (e
.parent
== frame
->plate()) return;
1255 printf("ReparentNotify for 0x%lx\n", e
.window
);
1258 otk::EventHandler::reparentHandler(e
);
1261 This event is quite rare and is usually handled in unmapHandler.
1262 However, if the window is unmapped when the reparent event occurs,
1263 the window manager never sees it because an unmap event is not sent
1264 to an already unmapped window.
1267 // we don't want the reparent event, put it back on the stack for the X
1268 // server to deal with after we unmanage the window
1271 XPutBackEvent(**otk::display
, &ev
);
1273 // this deletes us etc
1274 openbox
->screen(_screen
)->unmanageWindow(this);