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
| Decor_Sticky
|
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)
659 frame
->adjustState();
663 void Client::setState(StateAction action
, long data1
, long data2
)
665 bool shadestate
= _shaded
;
666 bool fsstate
= _fullscreen
;
668 if (!(action
== State_Add
|| action
== State_Remove
||
669 action
== State_Toggle
))
670 return; // an invalid action was passed to the client message, ignore it
672 for (int i
= 0; i
< 2; ++i
) {
673 Atom state
= i
== 0 ? data1
: data2
;
675 if (! state
) continue;
677 // if toggling, then pick whether we're adding or removing
678 if (action
== State_Toggle
) {
679 if (state
== otk::Property::atoms
.net_wm_state_modal
)
680 action
= _modal
? State_Remove
: State_Add
;
681 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
682 action
= _max_vert
? State_Remove
: State_Add
;
683 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
684 action
= _max_horz
? State_Remove
: State_Add
;
685 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
686 action
= _shaded
? State_Remove
: State_Add
;
687 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
688 action
= _skip_taskbar
? State_Remove
: State_Add
;
689 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
690 action
= _skip_pager
? State_Remove
: State_Add
;
691 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
692 action
= _fullscreen
? State_Remove
: State_Add
;
693 else if (state
== otk::Property::atoms
.net_wm_state_above
)
694 action
= _above
? State_Remove
: State_Add
;
695 else if (state
== otk::Property::atoms
.net_wm_state_below
)
696 action
= _below
? State_Remove
: State_Add
;
699 if (action
== State_Add
) {
700 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
701 if (_modal
) continue;
703 // XXX: give it focus if another window has focus that shouldnt now
704 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
705 if (_max_vert
) continue;
707 // XXX: resize the window etc
708 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
709 if (_max_horz
) continue;
711 // XXX: resize the window etc
712 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
714 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
715 _skip_taskbar
= true;
716 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
718 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
720 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
721 if (_above
) continue;
723 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
724 if (_below
) continue;
728 } else { // action == State_Remove
729 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
730 if (!_modal
) continue;
732 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
733 if (!_max_vert
) continue;
735 // XXX: resize the window etc
736 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
737 if (!_max_horz
) continue;
739 // XXX: resize the window etc
740 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
742 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
743 _skip_taskbar
= false;
744 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
746 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
748 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
749 if (!_above
) continue;
751 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
752 if (!_below
) continue;
757 // change fullscreen state before shading, as it will affect if the window
759 if (fsstate
!= _fullscreen
)
761 if (shadestate
!= _shaded
)
767 void Client::toggleClientBorder(bool addborder
)
769 // adjust our idea of where the client is, based on its border. When the
770 // border is removed, the client should now be considered to be in a
771 // different position.
772 // when re-adding the border to the client, the same operation needs to be
774 int x
= _area
.x(), y
= _area
.y();
777 case NorthWestGravity
:
779 case SouthWestGravity
:
781 case NorthEastGravity
:
783 case SouthEastGravity
:
784 if (addborder
) x
-= _border_width
* 2;
785 else x
+= _border_width
* 2;
792 if (addborder
) x
-= _border_width
;
793 else x
+= _border_width
;
798 case NorthWestGravity
:
800 case NorthEastGravity
:
802 case SouthWestGravity
:
804 case SouthEastGravity
:
805 if (addborder
) y
-= _border_width
* 2;
806 else y
+= _border_width
* 2;
813 if (addborder
) y
-= _border_width
;
814 else y
+= _border_width
;
820 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
822 // move the client so it is back it the right spot _with_ its border!
823 XMoveWindow(**otk::display
, _window
, x
, y
);
825 XSetWindowBorderWidth(**otk::display
, _window
, 0);
829 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
831 otk::EventHandler::clientMessageHandler(e
);
833 if (e
.format
!= 32) return;
835 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
836 // compress changes into a single change
837 bool compress
= false;
839 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
840 // XXX: it would be nice to compress ALL messages of a type, not just
841 // messages in a row without other message types between.
842 if (ce
.xclient
.message_type
!= e
.message_type
) {
843 XPutBackEvent(**otk::display
, &ce
);
849 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
851 setWMState(e
.data
.l
[0]); // use the original event
852 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
853 // compress changes into a single change
854 bool compress
= false;
856 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
857 // XXX: it would be nice to compress ALL messages of a type, not just
858 // messages in a row without other message types between.
859 if (ce
.xclient
.message_type
!= e
.message_type
) {
860 XPutBackEvent(**otk::display
, &ce
);
866 setDesktop(e
.data
.l
[0]); // use the found event
868 setDesktop(e
.data
.l
[0]); // use the original event
869 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
870 // can't compress these
872 printf("net_wm_state %s %ld %ld for 0x%lx\n",
873 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
874 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
875 e
.data
.l
[1], e
.data
.l
[2], _window
);
877 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
878 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
880 printf("net_close_window for 0x%lx\n", _window
);
883 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
885 printf("net_active_window for 0x%lx\n", _window
);
891 openbox
->screen(_screen
)->raiseWindow(this);
897 void Client::shapeHandler(const XShapeEvent
&e
)
899 otk::EventHandler::shapeHandler(e
);
901 if (e
.kind
== ShapeBounding
) {
903 frame
->adjustShape();
909 void Client::resize(Corner anchor
, int w
, int h
)
911 if (!(_functions
& Func_Resize
)) return;
912 internal_resize(anchor
, w
, h
);
916 void Client::internal_resize(Corner anchor
, int w
, int h
, int x
, int y
)
921 // for interactive resizing. have to move half an increment in each
923 w
+= _size_inc
.x() / 2;
924 h
+= _size_inc
.y() / 2;
926 // is the window resizable? if it is not, then don't check its sizes, the
927 // client can do what it wants and the user can't change it anyhow
928 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
929 // smaller than min size or bigger than max size?
930 if (w
< _min_size
.x()) w
= _min_size
.x();
931 else if (w
> _max_size
.x()) w
= _max_size
.x();
932 if (h
< _min_size
.y()) h
= _min_size
.y();
933 else if (h
> _max_size
.y()) h
= _max_size
.y();
936 // keep to the increments
940 // you cannot resize to nothing
944 // store the logical size
945 _logical_size
.setPoint(w
, h
);
953 if (x
== INT_MIN
|| y
== INT_MIN
) {
960 x
-= w
- _area
.width();
963 y
-= h
- _area
.height();
966 x
-= w
- _area
.width();
967 y
-= h
- _area
.height();
974 XResizeWindow(**otk::display
, _window
, w
, h
);
976 // resize the frame to match the request
982 void Client::move(int x
, int y
)
984 if (!(_functions
& Func_Move
)) return;
989 void Client::internal_move(int x
, int y
)
993 // move the frame to be in the requested position
994 if (frame
) { // this can be called while mapping, before frame exists
995 frame
->adjustPosition();
997 // send synthetic configure notify (we don't need to if we aren't mapped
1000 event
.type
= ConfigureNotify
;
1001 event
.xconfigure
.display
= **otk::display
;
1002 event
.xconfigure
.event
= _window
;
1003 event
.xconfigure
.window
= _window
;
1004 event
.xconfigure
.x
= x
;
1005 event
.xconfigure
.y
= y
;
1006 event
.xconfigure
.width
= _area
.width();
1007 event
.xconfigure
.height
= _area
.height();
1008 event
.xconfigure
.border_width
= _border_width
;
1009 event
.xconfigure
.above
= frame
->window();
1010 event
.xconfigure
.override_redirect
= False
;
1011 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1012 StructureNotifyMask
, &event
);
1017 void Client::close()
1021 if (!(_functions
& Func_Close
)) return;
1023 // XXX: itd be cool to do timeouts and shit here for killing the client's
1025 // like... if the window is around after 5 seconds, then the close button
1026 // turns a nice red, and if this function is called again, the client is
1027 // explicitly killed.
1029 ce
.xclient
.type
= ClientMessage
;
1030 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1031 ce
.xclient
.display
= **otk::display
;
1032 ce
.xclient
.window
= _window
;
1033 ce
.xclient
.format
= 32;
1034 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1035 ce
.xclient
.data
.l
[1] = CurrentTime
;
1036 ce
.xclient
.data
.l
[2] = 0l;
1037 ce
.xclient
.data
.l
[3] = 0l;
1038 ce
.xclient
.data
.l
[4] = 0l;
1039 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1043 void Client::changeState()
1045 unsigned long state
[2];
1046 state
[0] = _wmstate
;
1048 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1049 otk::Property::atoms
.wm_state
, state
, 2);
1054 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1056 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1058 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1060 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1062 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1064 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1066 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1068 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1070 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1072 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1073 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1074 otk::Property::atoms
.atom
, netstate
, num
);
1079 frame
->adjustState();
1083 void Client::changeAllowedActions(void)
1088 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1090 if (_functions
& Func_Shade
)
1091 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1092 if (_functions
& Func_Close
)
1093 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1094 if (_functions
& Func_Move
)
1095 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1096 if (_functions
& Func_Iconify
)
1097 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1098 if (_functions
& Func_Resize
)
1099 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1100 if (_functions
& Func_Fullscreen
)
1101 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1102 if (_functions
& Func_Maximize
) {
1103 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1104 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1107 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1108 otk::Property::atoms
.atom
, actions
, num
);
1112 void Client::applyStartupState()
1114 // these are in a carefully crafted order..
1117 _fullscreen
= false;
1125 if (_max_vert
); // XXX: incomplete
1126 if (_max_horz
); // XXX: incomplete
1128 if (_skip_taskbar
); // nothing to do for this
1129 if (_skip_pager
); // nothing to do for this
1130 if (_modal
); // nothing to do for this
1131 if (_above
); // nothing to do for this
1132 if (_below
); // nothing to do for this
1136 void Client::shade(bool shade
)
1138 if (!(_functions
& Func_Shade
) || // can't
1139 _shaded
== shade
) return; // already done
1141 _wmstate
= shade
? IconicState
: NormalState
;
1144 frame
->adjustSize();
1148 void Client::fullscreen(bool fs
)
1150 static FunctionFlags saved_func
;
1151 static DecorationFlags saved_decor
;
1152 static otk::Rect saved_area
;
1153 static otk::Point saved_logical_size
;
1155 if (!(_functions
& Func_Fullscreen
) || // can't
1156 _fullscreen
== fs
) return; // already done
1159 changeState(); // change the state hints on the client
1162 // save the functions and remove them
1163 saved_func
= _functions
;
1164 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1165 // save the decorations and remove them
1166 saved_decor
= _decorations
;
1168 // save the area and adjust it (we don't call internal resize here for
1169 // constraints on the size, etc, we just make it fullscreen).
1171 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1172 _area
.setRect(0, 0, info
->width(), info
->height());
1173 saved_logical_size
= _logical_size
;
1174 _logical_size
.setPoint((info
->width() - _base_size
.x()) / _size_inc
.x(),
1175 (info
->height() - _base_size
.y()) / _size_inc
.y());
1177 _functions
= saved_func
;
1178 _decorations
= saved_decor
;
1180 _logical_size
= saved_logical_size
;
1183 changeAllowedActions(); // based on the new _functions
1185 frame
->adjustSize(); // drop/replace the decor's and resize
1186 frame
->adjustPosition(); // get (back) in position!
1188 // raise (back) into our stacking layer
1189 openbox
->screen(_screen
)->raiseWindow(this);
1191 // try focus us when we go into fullscreen mode
1196 bool Client::focus() const
1198 // won't try focus if the client doesn't want it, or if the window isn't
1199 // visible on the screen
1200 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1202 if (_focused
) return true;
1204 // do a check to see if the window has already been unmapped or destroyed
1206 if (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
) ||
1207 XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1208 XPutBackEvent(**otk::display
, &ev
);
1213 XSetInputFocus(**otk::display
, _window
,
1214 RevertToNone
, CurrentTime
);
1216 if (_focus_notify
) {
1218 ce
.xclient
.type
= ClientMessage
;
1219 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1220 ce
.xclient
.display
= **otk::display
;
1221 ce
.xclient
.window
= _window
;
1222 ce
.xclient
.format
= 32;
1223 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1224 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1225 ce
.xclient
.data
.l
[2] = 0l;
1226 ce
.xclient
.data
.l
[3] = 0l;
1227 ce
.xclient
.data
.l
[4] = 0l;
1228 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1235 void Client::unfocus() const
1237 if (!_focused
) return;
1239 assert(openbox
->focusedClient() == this);
1240 openbox
->setFocusedClient(0);
1244 void Client::focusHandler(const XFocusChangeEvent
&e
)
1247 // printf("FocusIn for 0x%lx\n", e.window);
1250 otk::EventHandler::focusHandler(e
);
1255 openbox
->setFocusedClient(this);
1259 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1262 // printf("FocusOut for 0x%lx\n", e.window);
1265 otk::EventHandler::unfocusHandler(e
);
1270 if (openbox
->focusedClient() == this)
1271 openbox
->setFocusedClient(0);
1275 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1278 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1281 otk::EventHandler::configureRequestHandler(e
);
1283 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1285 if (e
.value_mask
& CWBorderWidth
)
1286 _border_width
= e
.border_width
;
1288 // resize, then move, as specified in the EWMH section 7.7
1289 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1290 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1291 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1295 case NorthEastGravity
:
1299 case SouthWestGravity
:
1301 corner
= BottomLeft
;
1303 case SouthEastGravity
:
1304 corner
= BottomRight
;
1306 default: // NorthWest, Static, etc
1310 // if moving AND resizing ...
1311 if (e
.value_mask
& (CWX
| CWY
)) {
1312 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1313 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1314 internal_resize(corner
, w
, h
, x
, y
);
1315 } else // if JUST resizing...
1316 internal_resize(corner
, w
, h
);
1317 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1318 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1319 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1320 internal_move(x
, y
);
1323 if (e
.value_mask
& CWStackMode
) {
1327 openbox
->screen(_screen
)->lowerWindow(this);
1333 openbox
->screen(_screen
)->raiseWindow(this);
1340 void Client::unmapHandler(const XUnmapEvent
&e
)
1342 if (ignore_unmaps
) {
1344 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1351 printf("UnmapNotify for 0x%lx\n", e
.window
);
1354 otk::EventHandler::unmapHandler(e
);
1356 // this deletes us etc
1357 openbox
->screen(_screen
)->unmanageWindow(this);
1361 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1364 printf("DestroyNotify for 0x%lx\n", e
.window
);
1367 otk::EventHandler::destroyHandler(e
);
1369 // this deletes us etc
1370 openbox
->screen(_screen
)->unmanageWindow(this);
1374 void Client::reparentHandler(const XReparentEvent
&e
)
1376 // this is when the client is first taken captive in the frame
1377 if (e
.parent
== frame
->plate()) return;
1380 printf("ReparentNotify for 0x%lx\n", e
.window
);
1383 otk::EventHandler::reparentHandler(e
);
1386 This event is quite rare and is usually handled in unmapHandler.
1387 However, if the window is unmapped when the reparent event occurs,
1388 the window manager never sees it because an unmap event is not sent
1389 to an already unmapped window.
1392 // we don't want the reparent event, put it back on the stack for the X
1393 // server to deal with after we unmanage the window
1396 XPutBackEvent(**otk::display
, &ev
);
1398 // this deletes us etc
1399 openbox
->screen(_screen
)->unmanageWindow(this);