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();
63 getGravity(); // get the attribute gravity
64 updateNormalHints(); // this may override the attribute gravity
77 // clean up childrens' references
78 while (!_transients
.empty()) {
79 _transients
.front()->_transient_for
= 0;
80 _transients
.pop_front();
83 // clean up parents reference to this
85 _transient_for
->_transients
.remove(this); // remove from old parent
87 if (openbox
->state() != Openbox::State_Exiting
) {
88 // these values should not be persisted across a window unmapping/mapping
89 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
90 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
95 void Client::getGravity()
97 XWindowAttributes wattrib
;
100 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
101 assert(ret
!= BadWindow
);
102 _gravity
= wattrib
.win_gravity
;
106 void Client::getDesktop()
108 // defaults to the current desktop
109 _desktop
= openbox
->screen(_screen
)->desktop();
111 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
112 otk::Property::atoms
.cardinal
,
113 (long unsigned*)&_desktop
)) {
114 // make sure the hint exists
115 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
116 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
121 void Client::getType()
123 _type
= (WindowType
) -1;
126 unsigned long num
= (unsigned) -1;
127 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
128 otk::Property::atoms
.atom
, &num
, &val
)) {
129 // use the first value that we know about in the array
130 for (unsigned long i
= 0; i
< num
; ++i
) {
131 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
132 _type
= Type_Desktop
;
133 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
135 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
136 _type
= Type_Toolbar
;
137 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
139 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
140 _type
= Type_Utility
;
141 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
143 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
145 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
147 // XXX: make this work again
148 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
149 // mwm_decorations = 0; // prevent this window from getting any decor
150 if (_type
!= (WindowType
) -1)
151 break; // grab the first known type
156 if (_type
== (WindowType
) -1) {
158 * the window type hint was not set, which means we either classify ourself
159 * as a normal window or a dialog, depending on if we are a transient.
169 void Client::setupDecorAndFunctions()
171 // start with everything (cept fullscreen)
172 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
173 Decor_Iconify
| Decor_Maximize
;
174 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
179 // normal windows retain all of the possible decorations and
180 // functionality, and are the only windows that you can fullscreen
181 _functions
|= Func_Fullscreen
;
184 // dialogs cannot be maximized
185 _decorations
&= ~Decor_Maximize
;
186 _functions
&= ~Func_Maximize
;
192 // these windows get less functionality
193 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
194 _functions
&= ~(Func_Iconify
| Func_Resize
);
200 // none of these windows are manipulated by the window manager
206 // Mwm Hints are applied subtractively to what has already been chosen for
207 // decor and functionality
208 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
209 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
210 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
211 _decorations
&= ~Decor_Border
;
212 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
213 _decorations
&= ~Decor_Handle
;
214 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
215 _decorations
&= ~Decor_Titlebar
;
216 // if we don't have a titlebar, then we cannot shade!
217 _functions
&= ~Func_Shade
;
219 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
220 _decorations
&= ~Decor_Iconify
;
221 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
222 _decorations
&= ~Decor_Maximize
;
226 if (_mwmhints
.flags
& MwmFlag_Functions
) {
227 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
228 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
229 _functions
&= ~Func_Resize
;
230 if (! (_mwmhints
.functions
& MwmFunc_Move
))
231 _functions
&= ~Func_Move
;
232 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
233 _functions
&= ~Func_Iconify
;
234 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
235 _functions
&= ~Func_Maximize
;
236 // dont let mwm hints kill the close button
237 //if (! (_mwmhints.functions & MwmFunc_Close))
238 // _functions &= ~Func_Close;
242 changeAllowedActions();
246 void Client::getMwmHints()
248 unsigned long num
= MwmHints::elements
;
249 unsigned long *hints
;
251 _mwmhints
.flags
= 0; // default to none
253 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
254 otk::Property::atoms
.motif_wm_hints
, &num
,
255 (unsigned long **)&hints
))
258 if (num
>= MwmHints::elements
) {
259 // retrieved the hints
260 _mwmhints
.flags
= hints
[0];
261 _mwmhints
.functions
= hints
[1];
262 _mwmhints
.decorations
= hints
[2];
269 void Client::getArea()
271 XWindowAttributes wattrib
;
274 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
275 assert(ret
!= BadWindow
);
277 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
278 _border_width
= wattrib
.border_width
;
282 void Client::getState()
284 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
285 _skip_taskbar
= _skip_pager
= false;
287 unsigned long *state
;
288 unsigned long num
= (unsigned) -1;
290 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
291 otk::Property::atoms
.atom
, &num
, &state
)) {
292 for (unsigned long i
= 0; i
< num
; ++i
) {
293 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
295 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
297 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
298 _skip_taskbar
= true;
299 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
301 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
303 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
305 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
307 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
309 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
318 void Client::getShaped()
322 if (otk::display
->shape()) {
327 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
329 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
330 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
337 void Client::calcLayer() {
340 if (_iconic
) l
= Layer_Icon
;
341 else if (_fullscreen
) l
= Layer_Fullscreen
;
342 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
343 else if (_type
== Type_Dock
) {
344 if (!_below
) l
= Layer_Top
;
345 else l
= Layer_Normal
;
347 else if (_above
) l
= Layer_Above
;
348 else if (_below
) l
= Layer_Below
;
349 else l
= Layer_Normal
;
355 if we don't have a frame, then we aren't mapped yet (and this would
358 openbox
->screen(_screen
)->raiseWindow(this);
364 void Client::updateProtocols()
369 _focus_notify
= false;
370 _decorations
&= ~Decor_Close
;
371 _functions
&= ~Func_Close
;
373 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
374 for (int i
= 0; i
< num_return
; ++i
) {
375 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
376 _decorations
|= Decor_Close
;
377 _functions
|= Func_Close
;
379 frame
->adjustSize(); // update the decorations
380 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
381 // if this protocol is requested, then the window will be notified
382 // by the window manager whenever it receives focus
383 _focus_notify
= true;
390 void Client::updateNormalHints()
394 int oldgravity
= _gravity
;
397 _size_inc
.setPoint(1, 1);
398 _base_size
.setPoint(0, 0);
399 _min_size
.setPoint(0, 0);
400 _max_size
.setPoint(INT_MAX
, INT_MAX
);
402 // XXX: might want to cancel any interactive resizing of the window at this
405 // get the hints from the window
406 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
407 _positioned
= (size
.flags
& (PPosition
|USPosition
));
409 if (size
.flags
& PWinGravity
) {
410 _gravity
= size
.win_gravity
;
412 // if the client has a frame, i.e. has already been mapped and is
413 // changing its gravity
414 if (frame
&& _gravity
!= oldgravity
) {
415 // move our idea of the client's position based on its new gravity
417 frame
->frameGravity(x
, y
);
422 if (size
.flags
& PMinSize
)
423 _min_size
.setPoint(size
.min_width
, size
.min_height
);
425 if (size
.flags
& PMaxSize
)
426 _max_size
.setPoint(size
.max_width
, size
.max_height
);
428 if (size
.flags
& PBaseSize
)
429 _base_size
.setPoint(size
.base_width
, size
.base_height
);
431 if (size
.flags
& PResizeInc
)
432 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
437 void Client::updateWMHints()
441 // assume a window takes input if it doesnt specify
445 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
446 if (hints
->flags
& InputHint
)
447 _can_focus
= hints
->input
;
449 if (hints
->flags
& XUrgencyHint
)
452 if (hints
->flags
& WindowGroupHint
) {
453 if (hints
->window_group
!= _group
) {
454 // XXX: remove from the old group if there was one
455 _group
= hints
->window_group
;
456 // XXX: do stuff with the group
466 void Client::updateTitle()
471 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
472 otk::Property::utf8
, &_title
)) {
474 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
475 otk::Property::ascii
, &_title
);
479 _title
= _("Unnamed Window");
482 frame
->setTitle(_title
);
486 void Client::updateIconTitle()
491 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
492 otk::Property::utf8
, &_icon_title
)) {
494 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
495 otk::Property::ascii
, &_icon_title
);
499 _icon_title
= _("Unnamed Window");
503 void Client::updateClass()
506 _app_name
= _app_class
= _role
= "";
508 otk::Property::StringVect v
;
509 unsigned long num
= 2;
511 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
512 otk::Property::ascii
, &num
, &v
)) {
513 if (num
> 0) _app_name
= v
[0].c_str();
514 if (num
> 1) _app_class
= v
[1].c_str();
519 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
520 otk::Property::ascii
, &num
, &v
)) {
521 if (num
> 0) _role
= v
[0].c_str();
526 void Client::updateStrut()
528 unsigned long num
= 4;
530 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
531 otk::Property::atoms
.cardinal
, &num
, &data
))
535 _strut
.left
= data
[0];
536 _strut
.right
= data
[1];
537 _strut
.top
= data
[2];
538 _strut
.bottom
= data
[3];
540 openbox
->screen(_screen
)->updateStrut();
547 void Client::updateTransientFor()
552 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
553 t
!= _window
) { // cant be transient to itself!
554 c
= openbox
->findClient(t
);
555 assert(c
!= this); // if this happens then we need to check for it
557 if (!c
/*XXX: && _group*/) {
558 // not transient to a client, see if it is transient for a group
559 if (//t == _group->leader() ||
561 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
562 // window is a transient for its group!
563 // XXX: for now this is treated as non-transient.
564 // this needs to be fixed!
569 // if anything has changed...
570 if (c
!= _transient_for
) {
572 _transient_for
->_transients
.remove(this); // remove from old parent
575 _transient_for
->_transients
.push_back(this); // add to new parent
577 // XXX: change decor status?
582 void Client::propertyHandler(const XPropertyEvent
&e
)
584 otk::EventHandler::propertyHandler(e
);
586 // compress changes to a single property into a single change
588 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
589 // XXX: it would be nice to compress ALL changes to a property, not just
590 // changes in a row without other props between.
591 if (ce
.xproperty
.atom
!= e
.atom
) {
592 XPutBackEvent(**otk::display
, &ce
);
597 if (e
.atom
== XA_WM_NORMAL_HINTS
)
599 else if (e
.atom
== XA_WM_HINTS
)
601 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
602 updateTransientFor();
604 calcLayer(); // type may have changed, so update the layer
605 setupDecorAndFunctions();
606 frame
->adjustSize(); // this updates the frame for any new decor settings
608 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
609 e
.atom
== otk::Property::atoms
.wm_name
)
611 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
612 e
.atom
== otk::Property::atoms
.wm_icon_name
)
614 else if (e
.atom
== otk::Property::atoms
.wm_class
)
616 else if (e
.atom
== otk::Property::atoms
.wm_protocols
)
618 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
623 void Client::setWMState(long state
)
625 if (state
== _wmstate
) return; // no change
630 // XXX: cause it to iconify
633 // XXX: cause it to uniconify
639 void Client::setDesktop(long target
)
641 if (target
== _desktop
) return;
643 printf("Setting desktop %ld\n", target
);
645 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
649 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
650 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
652 // 'move' the window to the new desktop
653 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
654 _desktop
== (signed)0xffffffff)
661 void Client::setState(StateAction action
, long data1
, long data2
)
663 bool shadestate
= _shaded
;
664 bool fsstate
= _fullscreen
;
666 if (!(action
== State_Add
|| action
== State_Remove
||
667 action
== State_Toggle
))
668 return; // an invalid action was passed to the client message, ignore it
670 for (int i
= 0; i
< 2; ++i
) {
671 Atom state
= i
== 0 ? data1
: data2
;
673 if (! state
) continue;
675 // if toggling, then pick whether we're adding or removing
676 if (action
== State_Toggle
) {
677 if (state
== otk::Property::atoms
.net_wm_state_modal
)
678 action
= _modal
? State_Remove
: State_Add
;
679 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
680 action
= _max_vert
? State_Remove
: State_Add
;
681 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
682 action
= _max_horz
? State_Remove
: State_Add
;
683 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
684 action
= _shaded
? State_Remove
: State_Add
;
685 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
686 action
= _skip_taskbar
? State_Remove
: State_Add
;
687 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
688 action
= _skip_pager
? State_Remove
: State_Add
;
689 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
690 action
= _fullscreen
? State_Remove
: State_Add
;
691 else if (state
== otk::Property::atoms
.net_wm_state_above
)
692 action
= _above
? State_Remove
: State_Add
;
693 else if (state
== otk::Property::atoms
.net_wm_state_below
)
694 action
= _below
? State_Remove
: State_Add
;
697 if (action
== State_Add
) {
698 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
699 if (_modal
) continue;
701 // XXX: give it focus if another window has focus that shouldnt now
702 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
703 if (_max_vert
) continue;
705 // XXX: resize the window etc
706 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
707 if (_max_horz
) continue;
709 // XXX: resize the window etc
710 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
712 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
713 _skip_taskbar
= true;
714 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
716 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
718 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
719 if (_above
) continue;
721 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
722 if (_below
) continue;
726 } else { // action == State_Remove
727 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
728 if (!_modal
) continue;
730 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
731 if (!_max_vert
) continue;
733 // XXX: resize the window etc
734 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
735 if (!_max_horz
) continue;
737 // XXX: resize the window etc
738 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
740 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
741 _skip_taskbar
= false;
742 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
744 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
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 // change fullscreen state before shading, as it will affect if the window
757 if (fsstate
!= _fullscreen
)
759 if (shadestate
!= _shaded
)
765 void Client::toggleClientBorder(bool addborder
)
767 // adjust our idea of where the client is, based on its border. When the
768 // border is removed, the client should now be considered to be in a
769 // different position.
770 // when re-adding the border to the client, the same operation needs to be
772 int x
= _area
.x(), y
= _area
.y();
775 case NorthWestGravity
:
777 case SouthWestGravity
:
779 case NorthEastGravity
:
781 case SouthEastGravity
:
782 if (addborder
) x
-= _border_width
* 2;
783 else x
+= _border_width
* 2;
790 if (addborder
) x
-= _border_width
;
791 else x
+= _border_width
;
796 case NorthWestGravity
:
798 case NorthEastGravity
:
800 case SouthWestGravity
:
802 case SouthEastGravity
:
803 if (addborder
) y
-= _border_width
* 2;
804 else y
+= _border_width
* 2;
811 if (addborder
) y
-= _border_width
;
812 else y
+= _border_width
;
818 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
820 // move the client so it is back it the right spot _with_ its border!
821 XMoveWindow(**otk::display
, _window
, x
, y
);
823 XSetWindowBorderWidth(**otk::display
, _window
, 0);
827 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
829 otk::EventHandler::clientMessageHandler(e
);
831 if (e
.format
!= 32) return;
833 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
834 // compress changes into a single change
835 bool compress
= false;
837 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
838 // XXX: it would be nice to compress ALL messages of a type, not just
839 // messages in a row without other message types between.
840 if (ce
.xclient
.message_type
!= e
.message_type
) {
841 XPutBackEvent(**otk::display
, &ce
);
847 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
849 setWMState(e
.data
.l
[0]); // use the original event
850 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
851 // compress changes into a single change
852 bool compress
= false;
854 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
855 // XXX: it would be nice to compress ALL messages of a type, not just
856 // messages in a row without other message types between.
857 if (ce
.xclient
.message_type
!= e
.message_type
) {
858 XPutBackEvent(**otk::display
, &ce
);
864 setDesktop(e
.data
.l
[0]); // use the found event
866 setDesktop(e
.data
.l
[0]); // use the original event
867 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
868 // can't compress these
870 printf("net_wm_state %s %ld %ld for 0x%lx\n",
871 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
872 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
873 e
.data
.l
[1], e
.data
.l
[2], _window
);
875 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
876 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
878 printf("net_close_window for 0x%lx\n", _window
);
881 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
883 printf("net_active_window for 0x%lx\n", _window
);
889 openbox
->screen(_screen
)->raiseWindow(this);
895 void Client::shapeHandler(const XShapeEvent
&e
)
897 otk::EventHandler::shapeHandler(e
);
899 if (e
.kind
== ShapeBounding
) {
901 frame
->adjustShape();
907 void Client::resize(Corner anchor
, int w
, int h
)
909 if (!(_functions
& Func_Resize
)) return;
910 internal_resize(anchor
, w
, h
);
914 void Client::internal_resize(Corner anchor
, int w
, int h
, int x
, int y
)
919 // for interactive resizing. have to move half an increment in each
921 w
+= _size_inc
.x() / 2;
922 h
+= _size_inc
.y() / 2;
924 // is the window resizable? if it is not, then don't check its sizes, the
925 // client can do what it wants and the user can't change it anyhow
926 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
927 // smaller than min size or bigger than max size?
928 if (w
< _min_size
.x()) w
= _min_size
.x();
929 else if (w
> _max_size
.x()) w
= _max_size
.x();
930 if (h
< _min_size
.y()) h
= _min_size
.y();
931 else if (h
> _max_size
.y()) h
= _max_size
.y();
934 // keep to the increments
938 // you cannot resize to nothing
942 // store the logical size
943 _logical_size
.setPoint(w
, h
);
951 if (x
== INT_MIN
|| y
== INT_MIN
) {
958 x
-= w
- _area
.width();
961 y
-= h
- _area
.height();
964 x
-= w
- _area
.width();
965 y
-= h
- _area
.height();
972 XResizeWindow(**otk::display
, _window
, w
, h
);
974 // resize the frame to match the request
980 void Client::move(int x
, int y
)
982 if (!(_functions
& Func_Move
)) return;
987 void Client::internal_move(int x
, int y
)
991 // move the frame to be in the requested position
992 if (frame
) { // this can be called while mapping, before frame exists
993 frame
->adjustPosition();
995 // send synthetic configure notify (we don't need to if we aren't mapped
998 event
.type
= ConfigureNotify
;
999 event
.xconfigure
.display
= **otk::display
;
1000 event
.xconfigure
.event
= _window
;
1001 event
.xconfigure
.window
= _window
;
1002 event
.xconfigure
.x
= x
;
1003 event
.xconfigure
.y
= y
;
1004 event
.xconfigure
.width
= _area
.width();
1005 event
.xconfigure
.height
= _area
.height();
1006 event
.xconfigure
.border_width
= _border_width
;
1007 event
.xconfigure
.above
= frame
->window();
1008 event
.xconfigure
.override_redirect
= False
;
1009 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1010 StructureNotifyMask
, &event
);
1015 void Client::close()
1019 if (!(_functions
& Func_Close
)) return;
1021 // XXX: itd be cool to do timeouts and shit here for killing the client's
1023 // like... if the window is around after 5 seconds, then the close button
1024 // turns a nice red, and if this function is called again, the client is
1025 // explicitly killed.
1027 ce
.xclient
.type
= ClientMessage
;
1028 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1029 ce
.xclient
.display
= **otk::display
;
1030 ce
.xclient
.window
= _window
;
1031 ce
.xclient
.format
= 32;
1032 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1033 ce
.xclient
.data
.l
[1] = CurrentTime
;
1034 ce
.xclient
.data
.l
[2] = 0l;
1035 ce
.xclient
.data
.l
[3] = 0l;
1036 ce
.xclient
.data
.l
[4] = 0l;
1037 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1041 void Client::changeState()
1043 unsigned long state
[2];
1044 state
[0] = _wmstate
;
1046 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1047 otk::Property::atoms
.wm_state
, state
, 2);
1052 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1054 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1056 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1058 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1060 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1062 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1064 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1066 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1068 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1070 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1071 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1072 otk::Property::atoms
.atom
, netstate
, num
);
1078 void Client::changeAllowedActions(void)
1083 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1085 if (_functions
& Func_Shade
)
1086 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1087 if (_functions
& Func_Close
)
1088 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1089 if (_functions
& Func_Move
)
1090 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1091 if (_functions
& Func_Iconify
)
1092 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1093 if (_functions
& Func_Resize
)
1094 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1095 if (_functions
& Func_Fullscreen
)
1096 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1097 if (_functions
& Func_Maximize
) {
1098 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1099 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1102 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1103 otk::Property::atoms
.atom
, actions
, num
);
1107 void Client::applyStartupState()
1109 // these are in a carefully crafted order..
1112 _fullscreen
= false;
1120 if (_max_vert
); // XXX: incomplete
1121 if (_max_horz
); // XXX: incomplete
1123 if (_skip_taskbar
); // nothing to do for this
1124 if (_skip_pager
); // nothing to do for this
1125 if (_modal
); // nothing to do for this
1126 if (_above
); // nothing to do for this
1127 if (_below
); // nothing to do for this
1131 void Client::shade(bool shade
)
1133 if (!(_functions
& Func_Shade
) || // can't
1134 _shaded
== shade
) return; // already done
1136 _wmstate
= shade
? IconicState
: NormalState
;
1139 frame
->adjustSize();
1143 void Client::fullscreen(bool fs
)
1145 static FunctionFlags saved_func
;
1146 static DecorationFlags saved_decor
;
1147 static otk::Rect saved_area
;
1148 static otk::Point saved_logical_size
;
1150 if (!(_functions
& Func_Fullscreen
) || // can't
1151 _fullscreen
== fs
) return; // already done
1154 changeState(); // change the state hints on the client
1157 // save the functions and remove them
1158 saved_func
= _functions
;
1159 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1160 // save the decorations and remove them
1161 saved_decor
= _decorations
;
1163 // save the area and adjust it (we don't call internal resize here for
1164 // constraints on the size, etc, we just make it fullscreen).
1166 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1167 _area
.setRect(0, 0, info
->width(), info
->height());
1168 saved_logical_size
= _logical_size
;
1169 _logical_size
.setPoint((info
->width() - _base_size
.x()) / _size_inc
.x(),
1170 (info
->height() - _base_size
.y()) / _size_inc
.y());
1172 _functions
= saved_func
;
1173 _decorations
= saved_decor
;
1175 _logical_size
= saved_logical_size
;
1178 changeAllowedActions(); // based on the new _functions
1180 frame
->adjustSize(); // drop/replace the decor's and resize
1181 frame
->adjustPosition(); // get (back) in position!
1183 // raise (back) into our stacking layer
1184 openbox
->screen(_screen
)->raiseWindow(this);
1186 // try focus us when we go into fullscreen mode
1191 bool Client::focus() const
1193 // won't try focus if the client doesn't want it, or if the window isn't
1194 // visible on the screen
1195 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1197 if (_focused
) return true;
1200 XSetInputFocus(**otk::display
, _window
,
1201 RevertToNone
, CurrentTime
);
1203 if (_focus_notify
) {
1205 ce
.xclient
.type
= ClientMessage
;
1206 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1207 ce
.xclient
.display
= **otk::display
;
1208 ce
.xclient
.window
= _window
;
1209 ce
.xclient
.format
= 32;
1210 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1211 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1212 ce
.xclient
.data
.l
[2] = 0l;
1213 ce
.xclient
.data
.l
[3] = 0l;
1214 ce
.xclient
.data
.l
[4] = 0l;
1215 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1222 void Client::unfocus() const
1224 if (!_focused
) return;
1226 assert(openbox
->focusedClient() == this);
1227 openbox
->setFocusedClient(0);
1231 void Client::focusHandler(const XFocusChangeEvent
&e
)
1234 // printf("FocusIn for 0x%lx\n", e.window);
1237 otk::EventHandler::focusHandler(e
);
1242 openbox
->setFocusedClient(this);
1246 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1249 // printf("FocusOut for 0x%lx\n", e.window);
1252 otk::EventHandler::unfocusHandler(e
);
1257 if (openbox
->focusedClient() == this)
1258 openbox
->setFocusedClient(0);
1262 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1265 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1268 otk::EventHandler::configureRequestHandler(e
);
1270 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1272 if (e
.value_mask
& CWBorderWidth
)
1273 _border_width
= e
.border_width
;
1275 // resize, then move, as specified in the EWMH section 7.7
1276 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1277 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1278 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1282 case NorthEastGravity
:
1286 case SouthWestGravity
:
1288 corner
= BottomLeft
;
1290 case SouthEastGravity
:
1291 corner
= BottomRight
;
1293 default: // NorthWest, Static, etc
1297 // if moving AND resizing ...
1298 if (e
.value_mask
& (CWX
| CWY
)) {
1299 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1300 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1301 internal_resize(corner
, w
, h
, x
, y
);
1302 } else // if JUST resizing...
1303 internal_resize(corner
, w
, h
);
1304 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1305 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1306 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1307 internal_move(x
, y
);
1310 if (e
.value_mask
& CWStackMode
) {
1314 openbox
->screen(_screen
)->lowerWindow(this);
1320 openbox
->screen(_screen
)->raiseWindow(this);
1327 void Client::unmapHandler(const XUnmapEvent
&e
)
1329 if (ignore_unmaps
) {
1331 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1338 printf("UnmapNotify for 0x%lx\n", e
.window
);
1341 otk::EventHandler::unmapHandler(e
);
1343 // this deletes us etc
1344 openbox
->screen(_screen
)->unmanageWindow(this);
1348 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1351 printf("DestroyNotify for 0x%lx\n", e
.window
);
1354 otk::EventHandler::destroyHandler(e
);
1356 // this deletes us etc
1357 openbox
->screen(_screen
)->unmanageWindow(this);
1361 void Client::reparentHandler(const XReparentEvent
&e
)
1363 // this is when the client is first taken captive in the frame
1364 if (e
.parent
== frame
->plate()) return;
1367 printf("ReparentNotify for 0x%lx\n", e
.window
);
1370 otk::EventHandler::reparentHandler(e
);
1373 This event is quite rare and is usually handled in unmapHandler.
1374 However, if the window is unmapped when the reparent event occurs,
1375 the window manager never sees it because an unmap event is not sent
1376 to an already unmapped window.
1379 // we don't want the reparent event, put it back on the stack for the X
1380 // server to deal with after we unmanage the window
1383 XPutBackEvent(**otk::display
, &ev
);
1385 // this deletes us etc
1386 openbox
->screen(_screen
)->unmanageWindow(this);