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()
96 XWindowAttributes wattrib
;
99 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
100 assert(ret
!= BadWindow
);
102 _gravity
= wattrib
.win_gravity
;
105 void Client::getDesktop()
107 // defaults to the current desktop
108 _desktop
= openbox
->screen(_screen
)->desktop();
110 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
111 otk::Property::atoms
.cardinal
,
112 (long unsigned*)&_desktop
)) {
113 // make sure the hint exists
114 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
115 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
120 void Client::getType()
122 _type
= (WindowType
) -1;
125 unsigned long num
= (unsigned) -1;
126 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
127 otk::Property::atoms
.atom
, &num
, &val
)) {
128 // use the first value that we know about in the array
129 for (unsigned long i
= 0; i
< num
; ++i
) {
130 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
131 _type
= Type_Desktop
;
132 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
134 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
135 _type
= Type_Toolbar
;
136 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
138 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
139 _type
= Type_Utility
;
140 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
142 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
144 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
146 // XXX: make this work again
147 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
148 // mwm_decorations = 0; // prevent this window from getting any decor
149 if (_type
!= (WindowType
) -1)
150 break; // grab the first known type
155 if (_type
== (WindowType
) -1) {
157 * the window type hint was not set, which means we either classify ourself
158 * as a normal window or a dialog, depending on if we are a transient.
168 void Client::setupDecorAndFunctions()
170 // start with everything
171 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
172 Decor_Iconify
| Decor_Maximize
;
173 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
177 // normal windows retain all of the possible decorations and
181 // dialogs cannot be maximized
182 _decorations
&= ~Decor_Maximize
;
183 _functions
&= ~Func_Maximize
;
189 // these windows get less functionality
190 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
191 _functions
&= ~(Func_Iconify
| Func_Resize
);
197 // none of these windows are manipulated by the window manager
203 // Mwm Hints are applied subtractively to what has already been chosen for
204 // decor and functionality
205 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
206 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
207 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
208 _decorations
&= ~Decor_Border
;
209 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
210 _decorations
&= ~Decor_Handle
;
211 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
212 _decorations
&= ~Decor_Titlebar
;
213 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
214 _decorations
&= ~Decor_Iconify
;
215 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
216 _decorations
&= ~Decor_Maximize
;
220 if (_mwmhints
.flags
& MwmFlag_Functions
) {
221 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
222 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
223 _functions
&= ~Func_Resize
;
224 if (! (_mwmhints
.functions
& MwmFunc_Move
))
225 _functions
&= ~Func_Move
;
226 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
227 _functions
&= ~Func_Iconify
;
228 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
229 _functions
&= ~Func_Maximize
;
230 // dont let mwm hints kill the close button
231 //if (! (_mwmhints.functions & MwmFunc_Close))
232 // _functions &= ~Func_Close;
236 // XXX: changeAllowedActions();
240 void Client::getMwmHints()
242 unsigned long num
= MwmHints::elements
;
243 unsigned long *hints
;
245 _mwmhints
.flags
= 0; // default to none
247 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
248 otk::Property::atoms
.motif_wm_hints
, &num
,
249 (unsigned long **)&hints
))
252 if (num
>= MwmHints::elements
) {
253 // retrieved the hints
254 _mwmhints
.flags
= hints
[0];
255 _mwmhints
.functions
= hints
[1];
256 _mwmhints
.decorations
= hints
[2];
263 void Client::getArea()
265 XWindowAttributes wattrib
;
268 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
269 assert(ret
!= BadWindow
);
271 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
272 _border_width
= wattrib
.border_width
;
276 void Client::getState()
278 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
279 _skip_taskbar
= _skip_pager
= false;
281 unsigned long *state
;
282 unsigned long num
= (unsigned) -1;
284 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
285 otk::Property::atoms
.atom
, &num
, &state
)) {
286 for (unsigned long i
= 0; i
< num
; ++i
) {
287 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
289 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
) {
291 _wmstate
= IconicState
;
292 } else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
293 _skip_taskbar
= true;
294 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
296 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
298 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
300 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
302 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
304 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
313 void Client::getShaped()
317 if (otk::display
->shape()) {
322 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
324 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
325 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
332 void Client::calcLayer() {
335 if (_iconic
) l
= Layer_Icon
;
336 else if (_fullscreen
) l
= Layer_Fullscreen
;
337 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
338 else if (_type
== Type_Dock
) {
339 if (!_below
) l
= Layer_Top
;
340 else l
= Layer_Normal
;
342 else if (_above
) l
= Layer_Above
;
343 else if (_below
) l
= Layer_Below
;
344 else l
= Layer_Normal
;
350 if we don't have a frame, then we aren't mapped yet (and this would
353 openbox
->screen(_screen
)->raiseWindow(this);
359 void Client::updateProtocols()
364 _focus_notify
= false;
365 _decorations
&= ~Decor_Close
;
366 _functions
&= ~Func_Close
;
368 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
369 for (int i
= 0; i
< num_return
; ++i
) {
370 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
371 _decorations
|= Decor_Close
;
372 _functions
|= Func_Close
;
374 frame
->adjustSize(); // update the decorations
375 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
376 // if this protocol is requested, then the window will be notified
377 // by the window manager whenever it receives focus
378 _focus_notify
= true;
385 void Client::updateNormalHints()
389 int oldgravity
= _gravity
;
392 _size_inc
.setPoint(1, 1);
393 _base_size
.setPoint(0, 0);
394 _min_size
.setPoint(0, 0);
395 _max_size
.setPoint(INT_MAX
, INT_MAX
);
397 // XXX: might want to cancel any interactive resizing of the window at this
400 // get the hints from the window
401 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
402 _positioned
= (size
.flags
& (PPosition
|USPosition
));
404 if (size
.flags
& PWinGravity
) {
405 _gravity
= size
.win_gravity
;
407 // if the client has a frame, i.e. has already been mapped and is
408 // changing its gravity
409 if (frame
&& _gravity
!= oldgravity
) {
410 // move our idea of the client's position based on its new gravity
412 frame
->frameGravity(x
, y
);
417 if (size
.flags
& PMinSize
)
418 _min_size
.setPoint(size
.min_width
, size
.min_height
);
420 if (size
.flags
& PMaxSize
)
421 _max_size
.setPoint(size
.max_width
, size
.max_height
);
423 if (size
.flags
& PBaseSize
)
424 _base_size
.setPoint(size
.base_width
, size
.base_height
);
426 if (size
.flags
& PResizeInc
)
427 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
432 void Client::updateWMHints()
436 // assume a window takes input if it doesnt specify
440 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
441 if (hints
->flags
& InputHint
)
442 _can_focus
= hints
->input
;
444 if (hints
->flags
& XUrgencyHint
)
447 if (hints
->flags
& WindowGroupHint
) {
448 if (hints
->window_group
!= _group
) {
449 // XXX: remove from the old group if there was one
450 _group
= hints
->window_group
;
451 // XXX: do stuff with the group
461 void Client::updateTitle()
466 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
467 otk::Property::utf8
, &_title
)) {
469 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
470 otk::Property::ascii
, &_title
);
474 _title
= _("Unnamed Window");
477 frame
->setTitle(_title
);
481 void Client::updateIconTitle()
486 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
487 otk::Property::utf8
, &_icon_title
)) {
489 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
490 otk::Property::ascii
, &_icon_title
);
494 _icon_title
= _("Unnamed Window");
498 void Client::updateClass()
501 _app_name
= _app_class
= _role
= "";
503 otk::Property::StringVect v
;
504 unsigned long num
= 2;
506 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
507 otk::Property::ascii
, &num
, &v
)) {
508 if (num
> 0) _app_name
= v
[0].c_str();
509 if (num
> 1) _app_class
= v
[1].c_str();
514 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
515 otk::Property::ascii
, &num
, &v
)) {
516 if (num
> 0) _role
= v
[0].c_str();
521 void Client::updateStrut()
523 unsigned long num
= 4;
525 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
526 otk::Property::atoms
.cardinal
, &num
, &data
))
530 _strut
.left
= data
[0];
531 _strut
.right
= data
[1];
532 _strut
.top
= data
[2];
533 _strut
.bottom
= data
[3];
535 openbox
->screen(_screen
)->updateStrut();
542 void Client::updateTransientFor()
547 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
548 t
!= _window
) { // cant be transient to itself!
549 c
= openbox
->findClient(t
);
550 assert(c
!= this); // if this happens then we need to check for it
552 if (!c
/*XXX: && _group*/) {
553 // not transient to a client, see if it is transient for a group
554 if (//t == _group->leader() ||
556 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
557 // window is a transient for its group!
558 // XXX: for now this is treated as non-transient.
559 // this needs to be fixed!
564 // if anything has changed...
565 if (c
!= _transient_for
) {
567 _transient_for
->_transients
.remove(this); // remove from old parent
570 _transient_for
->_transients
.push_back(this); // add to new parent
572 // XXX: change decor status?
577 void Client::propertyHandler(const XPropertyEvent
&e
)
579 otk::EventHandler::propertyHandler(e
);
581 // compress changes to a single property into a single change
583 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
584 // XXX: it would be nice to compress ALL changes to a property, not just
585 // changes in a row without other props between.
586 if (ce
.xproperty
.atom
!= e
.atom
) {
587 XPutBackEvent(**otk::display
, &ce
);
592 if (e
.atom
== XA_WM_NORMAL_HINTS
)
594 else if (e
.atom
== XA_WM_HINTS
)
596 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
597 updateTransientFor();
599 calcLayer(); // type may have changed, so update the layer
600 setupDecorAndFunctions();
601 frame
->adjustSize(); // this updates the frame for any new decor settings
603 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
604 e
.atom
== otk::Property::atoms
.wm_name
)
606 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
607 e
.atom
== otk::Property::atoms
.wm_icon_name
)
609 else if (e
.atom
== otk::Property::atoms
.wm_class
)
611 else if (e
.atom
== otk::Property::atoms
.wm_protocols
)
613 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
618 void Client::setWMState(long state
)
620 if (state
== _wmstate
) return; // no change
625 // XXX: cause it to iconify
628 // XXX: cause it to uniconify
634 void Client::setDesktop(long target
)
636 if (target
== _desktop
) return;
638 printf("Setting desktop %ld\n", target
);
640 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
644 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
645 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
647 // 'move' the window to the new desktop
648 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
649 _desktop
== (signed)0xffffffff)
656 void Client::setState(StateAction action
, long data1
, long data2
)
658 bool shadestate
= _shaded
;
660 if (!(action
== State_Add
|| action
== State_Remove
||
661 action
== State_Toggle
))
662 return; // an invalid action was passed to the client message, ignore it
664 for (int i
= 0; i
< 2; ++i
) {
665 Atom state
= i
== 0 ? data1
: data2
;
667 if (! state
) continue;
669 // if toggling, then pick whether we're adding or removing
670 if (action
== State_Toggle
) {
671 if (state
== otk::Property::atoms
.net_wm_state_modal
)
672 action
= _modal
? State_Remove
: State_Add
;
673 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
674 action
= _max_vert
? State_Remove
: State_Add
;
675 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
676 action
= _max_horz
? State_Remove
: State_Add
;
677 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
678 action
= _shaded
? State_Remove
: State_Add
;
679 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
680 action
= _skip_taskbar
? State_Remove
: State_Add
;
681 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
682 action
= _skip_pager
? State_Remove
: State_Add
;
683 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
684 action
= _fullscreen
? State_Remove
: State_Add
;
685 else if (state
== otk::Property::atoms
.net_wm_state_above
)
686 action
= _above
? State_Remove
: State_Add
;
687 else if (state
== otk::Property::atoms
.net_wm_state_below
)
688 action
= _below
? State_Remove
: State_Add
;
691 if (action
== State_Add
) {
692 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
693 if (_modal
) continue;
695 // XXX: give it focus if another window has focus that shouldnt now
696 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
697 if (_max_vert
) continue;
699 // XXX: resize the window etc
700 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
701 if (_max_horz
) continue;
703 // XXX: resize the window etc
704 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
705 if (_shaded
) continue;
706 // shade when we're all thru here
708 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
709 _skip_taskbar
= true;
710 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
712 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
713 if (_fullscreen
) continue;
715 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
716 if (_above
) continue;
718 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
719 if (_below
) continue;
723 } else { // action == State_Remove
724 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
725 if (!_modal
) continue;
727 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
728 if (!_max_vert
) continue;
730 // XXX: resize the window etc
731 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
732 if (!_max_horz
) continue;
734 // XXX: resize the window etc
735 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
736 if (!_shaded
) continue;
737 // unshade when we're all thru here
739 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
740 _skip_taskbar
= false;
741 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
743 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
744 if (!_fullscreen
) continue;
746 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
747 if (!_above
) continue;
749 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
750 if (!_below
) continue;
755 if (shadestate
!= _shaded
)
761 void Client::toggleClientBorder(bool addborder
)
763 // adjust our idea of where the client is, based on its border. When the
764 // border is removed, the client should now be considered to be in a
765 // different position.
766 // when re-adding the border to the client, the same operation needs to be
768 int x
= _area
.x(), y
= _area
.y();
771 case NorthWestGravity
:
773 case SouthWestGravity
:
778 case NorthEastGravity
:
780 case SouthEastGravity
:
781 if (addborder
) x
-= _border_width
* 2;
782 else x
+= _border_width
* 2;
786 if (addborder
) x
-= _border_width
;
787 else x
+= _border_width
;
792 case NorthWestGravity
:
796 case NorthEastGravity
:
799 case SouthWestGravity
:
801 case SouthEastGravity
:
802 if (addborder
) y
-= _border_width
* 2;
803 else y
+= _border_width
* 2;
807 if (addborder
) y
-= _border_width
;
808 else y
+= _border_width
;
814 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
816 // move the client so it is back it the right spot _with_ its border!
817 XMoveWindow(**otk::display
, _window
, x
, y
);
819 XSetWindowBorderWidth(**otk::display
, _window
, 0);
823 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
825 otk::EventHandler::clientMessageHandler(e
);
827 if (e
.format
!= 32) return;
829 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
830 // compress changes into a single change
831 bool compress
= false;
833 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
834 // XXX: it would be nice to compress ALL messages of a type, not just
835 // messages in a row without other message types between.
836 if (ce
.xclient
.message_type
!= e
.message_type
) {
837 XPutBackEvent(**otk::display
, &ce
);
843 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
845 setWMState(e
.data
.l
[0]); // use the original event
846 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
847 // compress changes into a single change
848 bool compress
= false;
850 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
851 // XXX: it would be nice to compress ALL messages of a type, not just
852 // messages in a row without other message types between.
853 if (ce
.xclient
.message_type
!= e
.message_type
) {
854 XPutBackEvent(**otk::display
, &ce
);
860 setDesktop(e
.data
.l
[0]); // use the found event
862 setDesktop(e
.data
.l
[0]); // use the original event
863 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
864 // can't compress these
866 printf("net_wm_state %s %ld %ld for 0x%lx\n",
867 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
868 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
869 e
.data
.l
[1], e
.data
.l
[2], _window
);
871 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
872 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
874 printf("net_close_window for 0x%lx\n", _window
);
877 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
879 printf("net_active_window for 0x%lx\n", _window
);
885 openbox
->screen(_screen
)->raiseWindow(this);
891 void Client::shapeHandler(const XShapeEvent
&e
)
893 otk::EventHandler::shapeHandler(e
);
895 if (e
.kind
== ShapeBounding
) {
897 frame
->adjustShape();
903 void Client::resize(Corner anchor
, int w
, int h
, int x
, int y
)
908 // for interactive resizing. have to move half an increment in each
910 w
+= _size_inc
.x() / 2;
911 h
+= _size_inc
.y() / 2;
913 // is the window resizable? if it is not, then don't check its sizes, the
914 // client can do what it wants and the user can't change it anyhow
915 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
916 // smaller than min size or bigger than max size?
917 if (w
< _min_size
.x()) w
= _min_size
.x();
918 else if (w
> _max_size
.x()) w
= _max_size
.x();
919 if (h
< _min_size
.y()) h
= _min_size
.y();
920 else if (h
> _max_size
.y()) h
= _max_size
.y();
923 // keep to the increments
927 // you cannot resize to nothing
931 // store the logical size
932 _logical_size
.setPoint(w
, h
);
940 if (x
== INT_MIN
|| y
== INT_MIN
) {
947 x
-= w
- _area
.width();
950 y
-= h
- _area
.height();
953 x
-= w
- _area
.width();
954 y
-= h
- _area
.height();
961 XResizeWindow(**otk::display
, _window
, w
, h
);
963 // resize the frame to match the request
969 void Client::move(int x
, int y
)
973 // move the frame to be in the requested position
974 if (frame
) { // this can be called while mapping, before frame exists
975 frame
->adjustPosition();
977 // send synthetic configure notify (we don't need to if we aren't mapped
980 event
.type
= ConfigureNotify
;
981 event
.xconfigure
.display
= **otk::display
;
982 event
.xconfigure
.event
= _window
;
983 event
.xconfigure
.window
= _window
;
984 event
.xconfigure
.x
= x
;
985 event
.xconfigure
.y
= y
;
986 event
.xconfigure
.width
= _area
.width();
987 event
.xconfigure
.height
= _area
.height();
988 event
.xconfigure
.border_width
= _border_width
;
989 event
.xconfigure
.above
= frame
->window();
990 event
.xconfigure
.override_redirect
= False
;
991 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
992 StructureNotifyMask
, &event
);
1001 if (!(_functions
& Func_Close
)) return;
1003 // XXX: itd be cool to do timeouts and shit here for killing the client's
1005 // like... if the window is around after 5 seconds, then the close button
1006 // turns a nice red, and if this function is called again, the client is
1007 // explicitly killed.
1009 ce
.xclient
.type
= ClientMessage
;
1010 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1011 ce
.xclient
.display
= **otk::display
;
1012 ce
.xclient
.window
= _window
;
1013 ce
.xclient
.format
= 32;
1014 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1015 ce
.xclient
.data
.l
[1] = CurrentTime
;
1016 ce
.xclient
.data
.l
[2] = 0l;
1017 ce
.xclient
.data
.l
[3] = 0l;
1018 ce
.xclient
.data
.l
[4] = 0l;
1019 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1023 void Client::changeState()
1025 unsigned long state
[2];
1026 state
[0] = _wmstate
;
1028 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1029 otk::Property::atoms
.wm_state
, state
, 2);
1034 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1036 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1038 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1040 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1042 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1044 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1046 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1048 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1050 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1052 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1053 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1054 otk::Property::atoms
.atom
, netstate
, num
);
1060 void Client::shade(bool shade
)
1062 if (shade
== _shaded
) return; // already done
1064 _wmstate
= shade
? IconicState
: NormalState
;
1067 frame
->adjustSize();
1071 bool Client::focus() const
1073 // won't try focus if the client doesn't want it, or if the window isn't
1074 // visible on the screen
1075 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1077 if (_focused
) return true;
1080 XSetInputFocus(**otk::display
, _window
,
1081 RevertToNone
, CurrentTime
);
1083 if (_focus_notify
) {
1085 ce
.xclient
.type
= ClientMessage
;
1086 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1087 ce
.xclient
.display
= **otk::display
;
1088 ce
.xclient
.window
= _window
;
1089 ce
.xclient
.format
= 32;
1090 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1091 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1092 ce
.xclient
.data
.l
[2] = 0l;
1093 ce
.xclient
.data
.l
[3] = 0l;
1094 ce
.xclient
.data
.l
[4] = 0l;
1095 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1102 void Client::unfocus() const
1104 if (!_focused
) return;
1106 assert(openbox
->focusedClient() == this);
1107 openbox
->setFocusedClient(0);
1111 void Client::focusHandler(const XFocusChangeEvent
&e
)
1114 // printf("FocusIn for 0x%lx\n", e.window);
1117 otk::EventHandler::focusHandler(e
);
1122 openbox
->setFocusedClient(this);
1126 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1129 // printf("FocusOut for 0x%lx\n", e.window);
1132 otk::EventHandler::unfocusHandler(e
);
1137 if (openbox
->focusedClient() == this)
1138 openbox
->setFocusedClient(0);
1142 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1145 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1148 otk::EventHandler::configureRequestHandler(e
);
1150 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1152 if (e
.value_mask
& CWBorderWidth
)
1153 _border_width
= e
.border_width
;
1155 // resize, then move, as specified in the EWMH section 7.7
1156 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1157 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1158 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1162 case NorthEastGravity
:
1166 case SouthWestGravity
:
1168 corner
= BottomLeft
;
1170 case SouthEastGravity
:
1171 corner
= BottomRight
;
1173 default: // NorthWest, Static, etc
1177 // if moving AND resizing ...
1178 if (e
.value_mask
& (CWX
| CWY
)) {
1179 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1180 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1181 resize(corner
, w
, h
, x
, y
);
1182 } else // if JUST resizing...
1183 resize(corner
, w
, h
);
1184 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1185 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1186 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1190 if (e
.value_mask
& CWStackMode
) {
1194 openbox
->screen(_screen
)->lowerWindow(this);
1200 openbox
->screen(_screen
)->raiseWindow(this);
1207 void Client::unmapHandler(const XUnmapEvent
&e
)
1209 if (ignore_unmaps
) {
1211 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1218 printf("UnmapNotify for 0x%lx\n", e
.window
);
1221 otk::EventHandler::unmapHandler(e
);
1223 // this deletes us etc
1224 openbox
->screen(_screen
)->unmanageWindow(this);
1228 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1231 printf("DestroyNotify for 0x%lx\n", e
.window
);
1234 otk::EventHandler::destroyHandler(e
);
1236 // this deletes us etc
1237 openbox
->screen(_screen
)->unmanageWindow(this);
1241 void Client::reparentHandler(const XReparentEvent
&e
)
1243 // this is when the client is first taken captive in the frame
1244 if (e
.parent
== frame
->plate()) return;
1247 printf("ReparentNotify for 0x%lx\n", e
.window
);
1250 otk::EventHandler::reparentHandler(e
);
1253 This event is quite rare and is usually handled in unmapHandler.
1254 However, if the window is unmapped when the reparent event occurs,
1255 the window manager never sees it because an unmap event is not sent
1256 to an already unmapped window.
1259 // we don't want the reparent event, put it back on the stack for the X
1260 // server to deal with after we unmanage the window
1263 XPutBackEvent(**otk::display
, &ev
);
1265 // this deletes us etc
1266 openbox
->screen(_screen
)->unmanageWindow(this);