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
;
114 void Client::getDesktop()
116 // defaults to the current desktop
117 _desktop
= openbox
->screen(_screen
)->desktop();
119 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
120 otk::Property::atoms
.cardinal
,
121 (long unsigned*)&_desktop
)) {
122 // make sure the hint exists
123 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
124 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
129 void Client::getType()
131 _type
= (WindowType
) -1;
134 unsigned long num
= (unsigned) -1;
135 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
136 otk::Property::atoms
.atom
, &num
, &val
)) {
137 // use the first value that we know about in the array
138 for (unsigned long i
= 0; i
< num
; ++i
) {
139 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
140 _type
= Type_Desktop
;
141 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
143 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
144 _type
= Type_Toolbar
;
145 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
147 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
148 _type
= Type_Utility
;
149 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
151 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
153 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
155 // XXX: make this work again
156 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
157 // mwm_decorations = 0; // prevent this window from getting any decor
158 if (_type
!= (WindowType
) -1)
159 break; // grab the first known type
164 if (_type
== (WindowType
) -1) {
166 * the window type hint was not set, which means we either classify ourself
167 * as a normal window or a dialog, depending on if we are a transient.
177 void Client::setupDecorAndFunctions()
179 // start with everything
180 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
181 Decor_Iconify
| Decor_Maximize
;
182 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
186 // normal windows retain all of the possible decorations and
190 // dialogs cannot be maximized
191 _decorations
&= ~Decor_Maximize
;
192 _functions
&= ~Func_Maximize
;
198 // these windows get less functionality
199 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
200 _functions
&= ~(Func_Iconify
| Func_Resize
);
206 // none of these windows are manipulated by the window manager
212 // Mwm Hints are applied subtractively to what has already been chosen for
213 // decor and functionality
214 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
215 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
216 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
217 _decorations
&= ~Decor_Border
;
218 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
219 _decorations
&= ~Decor_Handle
;
220 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
221 _decorations
&= ~Decor_Titlebar
;
222 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
223 _decorations
&= ~Decor_Iconify
;
224 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
225 _decorations
&= ~Decor_Maximize
;
229 if (_mwmhints
.flags
& MwmFlag_Functions
) {
230 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
231 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
232 _functions
&= ~Func_Resize
;
233 if (! (_mwmhints
.functions
& MwmFunc_Move
))
234 _functions
&= ~Func_Move
;
235 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
236 _functions
&= ~Func_Iconify
;
237 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
238 _functions
&= ~Func_Maximize
;
239 // dont let mwm hints kill the close button
240 //if (! (_mwmhints.functions & MwmFunc_Close))
241 // _functions &= ~Func_Close;
245 // XXX: changeAllowedActions();
249 void Client::getMwmHints()
251 unsigned long num
= MwmHints::elements
;
252 unsigned long *hints
;
254 _mwmhints
.flags
= 0; // default to none
256 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
257 otk::Property::atoms
.motif_wm_hints
, &num
,
258 (unsigned long **)&hints
))
261 if (num
>= MwmHints::elements
) {
262 // retrieved the hints
263 _mwmhints
.flags
= hints
[0];
264 _mwmhints
.functions
= hints
[1];
265 _mwmhints
.decorations
= hints
[2];
272 void Client::getArea()
274 XWindowAttributes wattrib
;
277 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
278 assert(ret
!= BadWindow
);
280 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
281 _border_width
= wattrib
.border_width
;
285 void Client::getState()
287 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
288 _skip_taskbar
= _skip_pager
= false;
290 unsigned long *state
;
291 unsigned long num
= (unsigned) -1;
293 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
294 otk::Property::atoms
.atom
, &num
, &state
)) {
295 for (unsigned long i
= 0; i
< num
; ++i
) {
296 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
298 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
) {
300 _wmstate
= IconicState
;
301 } else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
302 _skip_taskbar
= true;
303 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
305 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
307 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
309 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
311 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
313 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
322 void Client::getShaped()
326 if (otk::display
->shape()) {
331 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
333 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
334 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
341 void Client::calcLayer() {
344 if (_iconic
) l
= Layer_Icon
;
345 else if (_fullscreen
) l
= Layer_Fullscreen
;
346 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
347 else if (_type
== Type_Dock
) {
348 if (!_below
) l
= Layer_Top
;
349 else l
= Layer_Normal
;
351 else if (_above
) l
= Layer_Above
;
352 else if (_below
) l
= Layer_Below
;
353 else l
= Layer_Normal
;
359 if we don't have a frame, then we aren't mapped yet (and this would
362 openbox
->screen(_screen
)->raiseWindow(this);
368 void Client::updateProtocols()
373 _focus_notify
= false;
374 _decorations
&= ~Decor_Close
;
375 _functions
&= ~Func_Close
;
377 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
378 for (int i
= 0; i
< num_return
; ++i
) {
379 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
380 _decorations
|= Decor_Close
;
381 _functions
|= Func_Close
;
383 frame
->adjustSize(); // update the decorations
384 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
385 // if this protocol is requested, then the window will be notified
386 // by the window manager whenever it receives focus
387 _focus_notify
= true;
394 void Client::updateNormalHints()
398 int oldgravity
= _gravity
;
401 _size_inc
.setPoint(1, 1);
402 _base_size
.setPoint(0, 0);
403 _min_size
.setPoint(0, 0);
404 _max_size
.setPoint(INT_MAX
, INT_MAX
);
406 // XXX: might want to cancel any interactive resizing of the window at this
409 // get the hints from the window
410 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
411 _positioned
= (size
.flags
& (PPosition
|USPosition
));
413 if (size
.flags
& PWinGravity
) {
414 _gravity
= size
.win_gravity
;
416 // if the client has a frame, i.e. has already been mapped and is
417 // changing its gravity
418 if (_gravity
!= oldgravity
) {
419 // move our idea of the client's position based on its new gravity
421 frame
->frameGravity(x
, y
);
426 if (size
.flags
& PMinSize
)
427 _min_size
.setPoint(size
.min_width
, size
.min_height
);
429 if (size
.flags
& PMaxSize
)
430 _max_size
.setPoint(size
.max_width
, size
.max_height
);
432 if (size
.flags
& PBaseSize
)
433 _base_size
.setPoint(size
.base_width
, size
.base_height
);
435 if (size
.flags
& PResizeInc
)
436 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
441 void Client::updateWMHints()
445 // assume a window takes input if it doesnt specify
449 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
450 if (hints
->flags
& InputHint
)
451 _can_focus
= hints
->input
;
453 if (hints
->flags
& XUrgencyHint
)
456 if (hints
->flags
& WindowGroupHint
) {
457 if (hints
->window_group
!= _group
) {
458 // XXX: remove from the old group if there was one
459 _group
= hints
->window_group
;
460 // XXX: do stuff with the group
470 void Client::updateTitle()
475 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
476 otk::Property::utf8
, &_title
)) {
478 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
479 otk::Property::ascii
, &_title
);
483 _title
= _("Unnamed Window");
486 frame
->setTitle(_title
);
490 void Client::updateIconTitle()
495 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
496 otk::Property::utf8
, &_icon_title
)) {
498 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
499 otk::Property::ascii
, &_icon_title
);
503 _icon_title
= _("Unnamed Window");
507 void Client::updateClass()
510 _app_name
= _app_class
= _role
= "";
512 otk::Property::StringVect v
;
513 unsigned long num
= 2;
515 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
516 otk::Property::ascii
, &num
, &v
)) {
517 if (num
> 0) _app_name
= v
[0].c_str();
518 if (num
> 1) _app_class
= v
[1].c_str();
523 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
524 otk::Property::ascii
, &num
, &v
)) {
525 if (num
> 0) _role
= v
[0].c_str();
530 void Client::updateStrut()
532 unsigned long num
= 4;
534 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
535 otk::Property::atoms
.cardinal
, &num
, &data
))
539 _strut
.left
= data
[0];
540 _strut
.right
= data
[1];
541 _strut
.top
= data
[2];
542 _strut
.bottom
= data
[3];
544 openbox
->screen(_screen
)->updateStrut();
551 void Client::updateTransientFor()
556 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
557 t
!= _window
) { // cant be transient to itself!
558 c
= openbox
->findClient(t
);
559 assert(c
!= this); // if this happens then we need to check for it
561 if (!c
/*XXX: && _group*/) {
562 // not transient to a client, see if it is transient for a group
563 if (//t == _group->leader() ||
565 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
566 // window is a transient for its group!
567 // XXX: for now this is treated as non-transient.
568 // this needs to be fixed!
573 // if anything has changed...
574 if (c
!= _transient_for
) {
576 _transient_for
->_transients
.remove(this); // remove from old parent
579 _transient_for
->_transients
.push_back(this); // add to new parent
581 // XXX: change decor status?
586 void Client::propertyHandler(const XPropertyEvent
&e
)
588 otk::EventHandler::propertyHandler(e
);
590 // compress changes to a single property into a single change
592 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
593 // XXX: it would be nice to compress ALL changes to a property, not just
594 // changes in a row without other props between.
595 if (ce
.xproperty
.atom
!= e
.atom
) {
596 XPutBackEvent(**otk::display
, &ce
);
601 if (e
.atom
== XA_WM_NORMAL_HINTS
)
603 else if (e
.atom
== XA_WM_HINTS
)
605 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
606 updateTransientFor();
608 calcLayer(); // type may have changed, so update the layer
609 setupDecorAndFunctions();
610 frame
->adjustSize(); // this updates the frame for any new decor settings
612 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
613 e
.atom
== otk::Property::atoms
.wm_name
)
615 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
616 e
.atom
== otk::Property::atoms
.wm_icon_name
)
618 else if (e
.atom
== otk::Property::atoms
.wm_class
)
620 else if (e
.atom
== otk::Property::atoms
.wm_protocols
)
622 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
627 void Client::setWMState(long state
)
629 if (state
== _wmstate
) return; // no change
634 // XXX: cause it to iconify
637 // XXX: cause it to uniconify
643 void Client::setDesktop(long target
)
645 if (target
== _desktop
) return;
647 printf("Setting desktop %ld\n", target
);
649 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
653 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
654 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
656 // 'move' the window to the new desktop
657 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
658 _desktop
== (signed)0xffffffff)
665 void Client::setState(StateAction action
, long data1
, long data2
)
667 bool shadestate
= _shaded
;
669 if (!(action
== State_Add
|| action
== State_Remove
||
670 action
== State_Toggle
))
671 return; // an invalid action was passed to the client message, ignore it
673 for (int i
= 0; i
< 2; ++i
) {
674 Atom state
= i
== 0 ? data1
: data2
;
676 if (! state
) continue;
678 // if toggling, then pick whether we're adding or removing
679 if (action
== State_Toggle
) {
680 if (state
== otk::Property::atoms
.net_wm_state_modal
)
681 action
= _modal
? State_Remove
: State_Add
;
682 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
683 action
= _max_vert
? State_Remove
: State_Add
;
684 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
685 action
= _max_horz
? State_Remove
: State_Add
;
686 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
687 action
= _shaded
? State_Remove
: State_Add
;
688 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
689 action
= _skip_taskbar
? State_Remove
: State_Add
;
690 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
691 action
= _skip_pager
? State_Remove
: State_Add
;
692 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
693 action
= _fullscreen
? State_Remove
: State_Add
;
694 else if (state
== otk::Property::atoms
.net_wm_state_above
)
695 action
= _above
? State_Remove
: State_Add
;
696 else if (state
== otk::Property::atoms
.net_wm_state_below
)
697 action
= _below
? State_Remove
: State_Add
;
700 if (action
== State_Add
) {
701 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
702 if (_modal
) continue;
704 // XXX: give it focus if another window has focus that shouldnt now
705 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
706 if (_max_vert
) continue;
708 // XXX: resize the window etc
709 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
710 if (_max_horz
) continue;
712 // XXX: resize the window etc
713 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
714 if (_shaded
) continue;
715 // shade when we're all thru here
717 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
718 _skip_taskbar
= true;
719 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
721 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
722 if (_fullscreen
) continue;
724 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
725 if (_above
) continue;
727 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
728 if (_below
) continue;
732 } else { // action == State_Remove
733 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
734 if (!_modal
) continue;
736 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
737 if (!_max_vert
) continue;
739 // XXX: resize the window etc
740 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
741 if (!_max_horz
) continue;
743 // XXX: resize the window etc
744 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
745 if (!_shaded
) continue;
746 // unshade when we're all thru here
748 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
749 _skip_taskbar
= false;
750 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
752 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
753 if (!_fullscreen
) continue;
755 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
756 if (!_above
) continue;
758 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
759 if (!_below
) continue;
764 if (shadestate
!= _shaded
)
770 void Client::toggleClientBorder(bool addborder
)
772 // adjust our idea of where the client is, based on its border. When the
773 // border is removed, the client should now be considered to be in a
774 // different position.
775 // when re-adding the border to the client, the same operation needs to be
777 int x
= _area
.x(), y
= _area
.y();
780 case NorthWestGravity
:
782 case SouthWestGravity
:
787 case NorthEastGravity
:
789 case SouthEastGravity
:
790 if (addborder
) x
-= _border_width
* 2;
791 else x
+= _border_width
* 2;
795 if (addborder
) x
-= _border_width
;
796 else x
+= _border_width
;
801 case NorthWestGravity
:
805 case NorthEastGravity
:
808 case SouthWestGravity
:
810 case SouthEastGravity
:
811 if (addborder
) y
-= _border_width
* 2;
812 else y
+= _border_width
* 2;
816 if (addborder
) y
-= _border_width
;
817 else y
+= _border_width
;
823 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
825 // move the client so it is back it the right spot _with_ its border!
826 XMoveWindow(**otk::display
, _window
, x
, y
);
828 XSetWindowBorderWidth(**otk::display
, _window
, 0);
832 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
834 otk::EventHandler::clientMessageHandler(e
);
836 if (e
.format
!= 32) return;
838 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
839 // compress changes into a single change
840 bool compress
= false;
842 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
843 // XXX: it would be nice to compress ALL messages of a type, not just
844 // messages in a row without other message types between.
845 if (ce
.xclient
.message_type
!= e
.message_type
) {
846 XPutBackEvent(**otk::display
, &ce
);
852 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
854 setWMState(e
.data
.l
[0]); // use the original event
855 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
856 // compress changes into a single change
857 bool compress
= false;
859 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
860 // XXX: it would be nice to compress ALL messages of a type, not just
861 // messages in a row without other message types between.
862 if (ce
.xclient
.message_type
!= e
.message_type
) {
863 XPutBackEvent(**otk::display
, &ce
);
869 setDesktop(e
.data
.l
[0]); // use the found event
871 setDesktop(e
.data
.l
[0]); // use the original event
872 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
873 // can't compress these
875 printf("net_wm_state %s %ld %ld for 0x%lx\n",
876 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
877 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
878 e
.data
.l
[1], e
.data
.l
[2], _window
);
880 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
881 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
883 printf("net_close_window for 0x%lx\n", _window
);
886 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
888 printf("net_active_window for 0x%lx\n", _window
);
894 openbox
->screen(_screen
)->raiseWindow(this);
900 void Client::shapeHandler(const XShapeEvent
&e
)
902 otk::EventHandler::shapeHandler(e
);
904 if (e
.kind
== ShapeBounding
) {
906 frame
->adjustShape();
912 void Client::resize(Corner anchor
, int w
, int h
, int x
, int y
)
917 // for interactive resizing. have to move half an increment in each
919 w
+= _size_inc
.x() / 2;
920 h
+= _size_inc
.y() / 2;
922 // is the window resizable? if it is not, then don't check its sizes, the
923 // client can do what it wants and the user can't change it anyhow
924 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
925 // smaller than min size or bigger than max size?
926 if (w
< _min_size
.x()) w
= _min_size
.x();
927 else if (w
> _max_size
.x()) w
= _max_size
.x();
928 if (h
< _min_size
.y()) h
= _min_size
.y();
929 else if (h
> _max_size
.y()) h
= _max_size
.y();
932 // keep to the increments
936 // you cannot resize to nothing
940 // store the logical size
941 _logical_size
.setPoint(w
, h
);
949 if (x
== INT_MIN
|| y
== INT_MIN
) {
956 x
-= w
- _area
.width();
959 y
-= h
- _area
.height();
962 x
-= w
- _area
.width();
963 y
-= h
- _area
.height();
970 XResizeWindow(**otk::display
, _window
, w
, h
);
972 // resize the frame to match the request
978 void Client::move(int x
, int y
)
982 // move the frame to be in the requested position
983 if (frame
) { // this can be called while mapping, before frame exists
984 frame
->adjustPosition();
986 // send synthetic configure notify (we don't need to if we aren't mapped
989 event
.type
= ConfigureNotify
;
990 event
.xconfigure
.display
= **otk::display
;
991 event
.xconfigure
.event
= _window
;
992 event
.xconfigure
.window
= _window
;
993 event
.xconfigure
.x
= x
;
994 event
.xconfigure
.y
= y
;
995 event
.xconfigure
.width
= _area
.width();
996 event
.xconfigure
.height
= _area
.height();
997 event
.xconfigure
.border_width
= _border_width
;
998 event
.xconfigure
.above
= frame
->window();
999 event
.xconfigure
.override_redirect
= False
;
1000 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1001 StructureNotifyMask
, &event
);
1006 void Client::close()
1010 if (!(_functions
& Func_Close
)) return;
1012 // XXX: itd be cool to do timeouts and shit here for killing the client's
1014 // like... if the window is around after 5 seconds, then the close button
1015 // turns a nice red, and if this function is called again, the client is
1016 // explicitly killed.
1018 ce
.xclient
.type
= ClientMessage
;
1019 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1020 ce
.xclient
.display
= **otk::display
;
1021 ce
.xclient
.window
= _window
;
1022 ce
.xclient
.format
= 32;
1023 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1024 ce
.xclient
.data
.l
[1] = CurrentTime
;
1025 ce
.xclient
.data
.l
[2] = 0l;
1026 ce
.xclient
.data
.l
[3] = 0l;
1027 ce
.xclient
.data
.l
[4] = 0l;
1028 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1032 void Client::changeState()
1034 unsigned long state
[2];
1035 state
[0] = _wmstate
;
1037 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1038 otk::Property::atoms
.wm_state
, state
, 2);
1043 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1045 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1047 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1049 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1051 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1053 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1055 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1057 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1059 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1061 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1062 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1063 otk::Property::atoms
.atom
, netstate
, num
);
1069 void Client::shade(bool shade
)
1071 if (shade
== _shaded
) return; // already done
1073 _wmstate
= shade
? IconicState
: NormalState
;
1076 frame
->adjustSize();
1080 bool Client::focus() const
1082 // won't try focus if the client doesn't want it, or if the window isn't
1083 // visible on the screen
1084 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1086 if (_focused
) return true;
1089 XSetInputFocus(**otk::display
, _window
,
1090 RevertToNone
, CurrentTime
);
1092 if (_focus_notify
) {
1094 ce
.xclient
.type
= ClientMessage
;
1095 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1096 ce
.xclient
.display
= **otk::display
;
1097 ce
.xclient
.window
= _window
;
1098 ce
.xclient
.format
= 32;
1099 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1100 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1101 ce
.xclient
.data
.l
[2] = 0l;
1102 ce
.xclient
.data
.l
[3] = 0l;
1103 ce
.xclient
.data
.l
[4] = 0l;
1104 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1111 void Client::unfocus() const
1113 if (!_focused
) return;
1115 assert(openbox
->focusedClient() == this);
1116 openbox
->setFocusedClient(0);
1120 void Client::focusHandler(const XFocusChangeEvent
&e
)
1123 // printf("FocusIn for 0x%lx\n", e.window);
1126 otk::EventHandler::focusHandler(e
);
1131 openbox
->setFocusedClient(this);
1135 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1138 // printf("FocusOut for 0x%lx\n", e.window);
1141 otk::EventHandler::unfocusHandler(e
);
1146 if (openbox
->focusedClient() == this)
1147 openbox
->setFocusedClient(0);
1151 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1154 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1157 otk::EventHandler::configureRequestHandler(e
);
1159 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1161 if (e
.value_mask
& CWBorderWidth
)
1162 _border_width
= e
.border_width
;
1164 // resize, then move, as specified in the EWMH section 7.7
1165 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1166 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1167 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1171 case NorthEastGravity
:
1175 case SouthWestGravity
:
1177 corner
= BottomLeft
;
1179 case SouthEastGravity
:
1180 corner
= BottomRight
;
1182 default: // NorthWest, Static, etc
1186 // if moving AND resizing ...
1187 if (e
.value_mask
& (CWX
| CWY
)) {
1188 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1189 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1190 resize(corner
, w
, h
, x
, y
);
1191 } else // if JUST resizing...
1192 resize(corner
, w
, h
);
1193 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1194 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1195 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1199 if (e
.value_mask
& CWStackMode
) {
1203 openbox
->screen(_screen
)->lowerWindow(this);
1209 openbox
->screen(_screen
)->raiseWindow(this);
1216 void Client::unmapHandler(const XUnmapEvent
&e
)
1218 if (ignore_unmaps
) {
1220 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1227 printf("UnmapNotify for 0x%lx\n", e
.window
);
1230 otk::EventHandler::unmapHandler(e
);
1232 // this deletes us etc
1233 openbox
->screen(_screen
)->unmanageWindow(this);
1237 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1240 printf("DestroyNotify for 0x%lx\n", e
.window
);
1243 otk::EventHandler::destroyHandler(e
);
1245 // this deletes us etc
1246 openbox
->screen(_screen
)->unmanageWindow(this);
1250 void Client::reparentHandler(const XReparentEvent
&e
)
1252 // this is when the client is first taken captive in the frame
1253 if (e
.parent
== frame
->plate()) return;
1256 printf("ReparentNotify for 0x%lx\n", e
.window
);
1259 otk::EventHandler::reparentHandler(e
);
1262 This event is quite rare and is usually handled in unmapHandler.
1263 However, if the window is unmapped when the reparent event occurs,
1264 the window manager never sees it because an unmap event is not sent
1265 to an already unmapped window.
1268 // we don't want the reparent event, put it back on the stack for the X
1269 // server to deal with after we unmanage the window
1272 XPutBackEvent(**otk::display
, &ev
);
1274 // this deletes us etc
1275 openbox
->screen(_screen
)->unmanageWindow(this);