1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
11 #include "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
23 #define _(str) gettext(str)
30 Client::Client(int screen
, Window window
)
31 : otk::EventHandler(),
32 WidgetBase(WidgetBase::Type_Client
),
33 frame(0), _screen(screen
), _window(window
)
40 // update EVERYTHING the first time!!
42 // we default to NormalState, visible
43 _wmstate
= NormalState
;
46 // not a transient by default of course
48 // pick a layer to start from
49 _layer
= Layer_Normal
;
50 // default to not urgent
65 // got the type, the mwmhints, and the protocols, so we're ready to set up
66 // the decorations/functions
67 setupDecorAndFunctions();
69 getGravity(); // get the attribute gravity
70 updateNormalHints(); // this may override the attribute gravity
71 // also get the initial_state and set _iconic if we aren't "starting"
72 // when we're "starting" that means we should use whatever state was already
73 // on the window over the initial map state, because it was already mapped
74 updateWMHints(openbox
->state() != Openbox::State_Starting
);
80 // this makes sure that these windows appear on all desktops
81 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
82 _desktop
= 0xffffffff;
84 // set the desktop hint, to make sure that it always exists, and to reflect
85 // any changes we've made here
86 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
87 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
95 // clean up childrens' references
96 while (!_transients
.empty()) {
97 _transients
.front()->_transient_for
= 0;
98 _transients
.pop_front();
101 // clean up parents reference to this
103 _transient_for
->_transients
.remove(this); // remove from old parent
105 if (openbox
->state() != Openbox::State_Exiting
) {
106 // these values should not be persisted across a window unmapping/mapping
107 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
108 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
110 // if we're left in an iconic state, the client wont be mapped. this is
111 // bad, since we will no longer be managing the window on restart
113 XMapWindow(**otk::display
, _window
);
118 void Client::getGravity()
120 XWindowAttributes wattrib
;
123 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
124 assert(ret
!= BadWindow
);
125 _gravity
= wattrib
.win_gravity
;
129 void Client::getDesktop()
131 // defaults to the current desktop
132 _desktop
= openbox
->screen(_screen
)->desktop();
134 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
135 otk::Property::atoms
.cardinal
,
136 (long unsigned*)&_desktop
)) {
138 // printf("Window requested desktop: %ld\n", _desktop);
144 void Client::getType()
146 _type
= (WindowType
) -1;
149 unsigned long num
= (unsigned) -1;
150 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
151 otk::Property::atoms
.atom
, &num
, &val
)) {
152 // use the first value that we know about in the array
153 for (unsigned long i
= 0; i
< num
; ++i
) {
154 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
155 _type
= Type_Desktop
;
156 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
158 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
159 _type
= Type_Toolbar
;
160 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
162 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
163 _type
= Type_Utility
;
164 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
166 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
168 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
170 // XXX: make this work again
171 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
172 // mwm_decorations = 0; // prevent this window from getting any decor
173 if (_type
!= (WindowType
) -1)
174 break; // grab the first known type
179 if (_type
== (WindowType
) -1) {
181 * the window type hint was not set, which means we either classify ourself
182 * as a normal window or a dialog, depending on if we are a transient.
192 void Client::setupDecorAndFunctions()
194 // start with everything (cept fullscreen)
195 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
196 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
197 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
199 if (_delete_window
) {
200 _decorations
|= Decor_Close
;
201 _functions
|= Func_Close
;
204 if (_min_size
.x() > _max_size
.x() || _min_size
.y() > _max_size
.y()) {
205 _decorations
&= ~Decor_Maximize
;
206 _functions
&= ~(Func_Resize
| Func_Maximize
);
211 // normal windows retain all of the possible decorations and
212 // functionality, and are the only windows that you can fullscreen
213 _functions
|= Func_Fullscreen
;
216 // dialogs cannot be maximized
217 _decorations
&= ~Decor_Maximize
;
218 _functions
&= ~Func_Maximize
;
224 // these windows get less functionality
225 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
226 _functions
&= ~(Func_Iconify
| Func_Resize
);
232 // none of these windows are manipulated by the window manager
238 // Mwm Hints are applied subtractively to what has already been chosen for
239 // decor and functionality
240 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
241 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
242 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
243 _decorations
&= ~Decor_Border
;
244 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
245 _decorations
&= ~Decor_Handle
;
246 if (! (_mwmhints
.decorations
& MwmDecor_Title
)) {
247 _decorations
&= ~Decor_Titlebar
;
248 // if we don't have a titlebar, then we cannot shade!
249 _functions
&= ~Func_Shade
;
251 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
252 _decorations
&= ~Decor_Iconify
;
253 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
254 _decorations
&= ~Decor_Maximize
;
258 if (_mwmhints
.flags
& MwmFlag_Functions
) {
259 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
260 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
261 _functions
&= ~Func_Resize
;
262 if (! (_mwmhints
.functions
& MwmFunc_Move
))
263 _functions
&= ~Func_Move
;
264 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
265 _functions
&= ~Func_Iconify
;
266 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
267 _functions
&= ~Func_Maximize
;
268 // dont let mwm hints kill the close button
269 //if (! (_mwmhints.functions & MwmFunc_Close))
270 // _functions &= ~Func_Close;
274 changeAllowedActions();
278 void Client::getMwmHints()
280 unsigned long num
= MwmHints::elements
;
281 unsigned long *hints
;
283 _mwmhints
.flags
= 0; // default to none
285 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
286 otk::Property::atoms
.motif_wm_hints
, &num
,
287 (unsigned long **)&hints
))
290 if (num
>= MwmHints::elements
) {
291 // retrieved the hints
292 _mwmhints
.flags
= hints
[0];
293 _mwmhints
.functions
= hints
[1];
294 _mwmhints
.decorations
= hints
[2];
301 void Client::getArea()
303 XWindowAttributes wattrib
;
306 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
307 assert(ret
!= BadWindow
);
309 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
310 _border_width
= wattrib
.border_width
;
314 void Client::getState()
316 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
317 _iconic
= _skip_taskbar
= _skip_pager
= false;
319 unsigned long *state
;
320 unsigned long num
= (unsigned) -1;
322 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
323 otk::Property::atoms
.atom
, &num
, &state
)) {
324 for (unsigned long i
= 0; i
< num
; ++i
) {
325 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
327 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
329 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
331 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
332 _skip_taskbar
= true;
333 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
335 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
337 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
339 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
341 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
343 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
352 void Client::getShaped()
356 if (otk::display
->shape()) {
361 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
363 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
364 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
371 void Client::calcLayer() {
374 if (_iconic
) l
= Layer_Icon
;
375 else if (_fullscreen
) l
= Layer_Fullscreen
;
376 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
377 else if (_type
== Type_Dock
) {
378 if (!_below
) l
= Layer_Top
;
379 else l
= Layer_Normal
;
381 else if (_above
) l
= Layer_Above
;
382 else if (_below
) l
= Layer_Below
;
383 else l
= Layer_Normal
;
389 if we don't have a frame, then we aren't mapped yet (and this would
392 openbox
->screen(_screen
)->raiseWindow(this);
398 void Client::updateProtocols()
403 _focus_notify
= false;
404 _delete_window
= false;
406 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
407 for (int i
= 0; i
< num_return
; ++i
) {
408 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
409 // this means we can request the window to close
410 _delete_window
= true;
411 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
412 // if this protocol is requested, then the window will be notified
413 // by the window manager whenever it receives focus
414 _focus_notify
= true;
421 void Client::updateNormalHints()
425 int oldgravity
= _gravity
;
430 _size_inc
.setPoint(1, 1);
431 _base_size
.setPoint(0, 0);
432 _min_size
.setPoint(0, 0);
433 _max_size
.setPoint(INT_MAX
, INT_MAX
);
435 // XXX: might want to cancel any interactive resizing of the window at this
438 // get the hints from the window
439 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
440 _positioned
= (size
.flags
& (PPosition
|USPosition
));
442 if (size
.flags
& PWinGravity
) {
443 _gravity
= size
.win_gravity
;
445 // if the client has a frame, i.e. has already been mapped and is
446 // changing its gravity
447 if (frame
&& _gravity
!= oldgravity
) {
448 // move our idea of the client's position based on its new gravity
450 frame
->frameGravity(x
, y
);
455 if (size
.flags
& PAspect
) {
456 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
457 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
460 if (size
.flags
& PMinSize
)
461 _min_size
.setPoint(size
.min_width
, size
.min_height
);
463 if (size
.flags
& PMaxSize
)
464 _max_size
.setPoint(size
.max_width
, size
.max_height
);
466 if (size
.flags
& PBaseSize
)
467 _base_size
.setPoint(size
.base_width
, size
.base_height
);
469 if (size
.flags
& PResizeInc
)
470 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
475 void Client::updateWMHints(bool initstate
)
479 // assume a window takes input if it doesnt specify
483 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
484 if (hints
->flags
& InputHint
)
485 _can_focus
= hints
->input
;
487 // only do this when initstate is true!
488 if (initstate
&& (hints
->flags
& StateHint
))
489 _iconic
= hints
->initial_state
== IconicState
;
491 if (hints
->flags
& XUrgencyHint
)
494 if (hints
->flags
& WindowGroupHint
) {
495 if (hints
->window_group
!= _group
) {
496 // XXX: remove from the old group if there was one
497 _group
= hints
->window_group
;
498 // XXX: do stuff with the group
509 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
510 (long)_window
, _urgent
? "ON" : "OFF");
512 // fire the urgent callback if we're mapped, otherwise, wait until after
520 void Client::updateTitle()
525 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
526 otk::Property::utf8
, &_title
)) {
528 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
529 otk::Property::ascii
, &_title
);
533 _title
= _("Unnamed Window");
536 frame
->setTitle(_title
);
540 void Client::updateIconTitle()
545 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
546 otk::Property::utf8
, &_icon_title
)) {
548 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
549 otk::Property::ascii
, &_icon_title
);
553 _icon_title
= _("Unnamed Window");
557 void Client::updateClass()
560 _app_name
= _app_class
= _role
= "";
562 otk::Property::StringVect v
;
563 unsigned long num
= 2;
565 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
566 otk::Property::ascii
, &num
, &v
)) {
567 if (num
> 0) _app_name
= v
[0].c_str();
568 if (num
> 1) _app_class
= v
[1].c_str();
573 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
574 otk::Property::ascii
, &num
, &v
)) {
575 if (num
> 0) _role
= v
[0].c_str();
580 void Client::updateStrut()
582 unsigned long num
= 4;
584 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
585 otk::Property::atoms
.cardinal
, &num
, &data
))
589 _strut
.left
= data
[0];
590 _strut
.right
= data
[1];
591 _strut
.top
= data
[2];
592 _strut
.bottom
= data
[3];
594 openbox
->screen(_screen
)->updateStrut();
601 void Client::updateTransientFor()
606 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
607 t
!= _window
) { // cant be transient to itself!
608 c
= openbox
->findClient(t
);
609 assert(c
!= this); // if this happens then we need to check for it
611 if (!c
/*XXX: && _group*/) {
612 // not transient to a client, see if it is transient for a group
613 if (//t == _group->leader() ||
615 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
616 // window is a transient for its group!
617 // XXX: for now this is treated as non-transient.
618 // this needs to be fixed!
623 // if anything has changed...
624 if (c
!= _transient_for
) {
626 _transient_for
->_transients
.remove(this); // remove from old parent
629 _transient_for
->_transients
.push_back(this); // add to new parent
631 // XXX: change decor status?
636 void Client::propertyHandler(const XPropertyEvent
&e
)
638 otk::EventHandler::propertyHandler(e
);
640 // compress changes to a single property into a single change
642 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
643 // XXX: it would be nice to compress ALL changes to a property, not just
644 // changes in a row without other props between.
645 if (ce
.xproperty
.atom
!= e
.atom
) {
646 XPutBackEvent(**otk::display
, &ce
);
651 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
653 setupDecorAndFunctions(); // normal hints can make a window non-resizable
654 } else if (e
.atom
== XA_WM_HINTS
)
656 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
657 updateTransientFor();
659 calcLayer(); // type may have changed, so update the layer
660 setupDecorAndFunctions();
661 frame
->adjustSize(); // this updates the frame for any new decor settings
663 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
664 e
.atom
== otk::Property::atoms
.wm_name
)
666 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
667 e
.atom
== otk::Property::atoms
.wm_icon_name
)
669 else if (e
.atom
== otk::Property::atoms
.wm_class
)
671 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
673 setupDecorAndFunctions();
674 frame
->adjustSize(); // update the decorations
676 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
681 void Client::setWMState(long state
)
683 if (state
== _wmstate
) return; // no change
687 setDesktop(ICONIC_DESKTOP
);
690 setDesktop(openbox
->screen(_screen
)->desktop());
696 void Client::setDesktop(long target
)
698 if (target
== _desktop
) return;
700 printf("Setting desktop %ld\n", target
);
702 if (!(target
>= 0 || target
== (signed)0xffffffff ||
703 target
== ICONIC_DESKTOP
))
708 // set the desktop hint, but not if we're iconifying
709 if (_desktop
!= ICONIC_DESKTOP
)
710 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
711 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
713 // 'move' the window to the new desktop
714 if (_desktop
== openbox
->screen(_screen
)->desktop() ||
715 _desktop
== (signed)0xffffffff)
720 // Handle Iconic state. Iconic state is maintained by the client being a
721 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
722 // uniconifying happen.
723 bool i
= _desktop
== ICONIC_DESKTOP
;
724 if (i
!= _iconic
) { // has the state changed?
727 _wmstate
= IconicState
;
729 // we unmap the client itself so that we can get MapRequest events, and
730 // because the ICCCM tells us to!
731 XUnmapWindow(**otk::display
, _window
);
733 _wmstate
= NormalState
;
734 XMapWindow(**otk::display
, _window
);
739 frame
->adjustState();
743 void Client::setState(StateAction action
, long data1
, long data2
)
745 bool shadestate
= _shaded
;
746 bool fsstate
= _fullscreen
;
748 if (!(action
== State_Add
|| action
== State_Remove
||
749 action
== State_Toggle
))
750 return; // an invalid action was passed to the client message, ignore it
752 for (int i
= 0; i
< 2; ++i
) {
753 Atom state
= i
== 0 ? data1
: data2
;
755 if (! state
) continue;
757 // if toggling, then pick whether we're adding or removing
758 if (action
== State_Toggle
) {
759 if (state
== otk::Property::atoms
.net_wm_state_modal
)
760 action
= _modal
? State_Remove
: State_Add
;
761 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
762 action
= _max_vert
? State_Remove
: State_Add
;
763 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
764 action
= _max_horz
? State_Remove
: State_Add
;
765 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
766 action
= _shaded
? State_Remove
: State_Add
;
767 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
768 action
= _skip_taskbar
? State_Remove
: State_Add
;
769 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
770 action
= _skip_pager
? State_Remove
: State_Add
;
771 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
772 action
= _fullscreen
? State_Remove
: State_Add
;
773 else if (state
== otk::Property::atoms
.net_wm_state_above
)
774 action
= _above
? State_Remove
: State_Add
;
775 else if (state
== otk::Property::atoms
.net_wm_state_below
)
776 action
= _below
? State_Remove
: State_Add
;
779 if (action
== State_Add
) {
780 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
781 if (_modal
) continue;
783 // XXX: give it focus if another window has focus that shouldnt now
784 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
785 if (_max_vert
) continue;
787 // XXX: resize the window etc
788 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
789 if (_max_horz
) continue;
791 // XXX: resize the window etc
792 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
794 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
795 _skip_taskbar
= true;
796 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
798 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
800 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
801 if (_above
) continue;
803 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
804 if (_below
) continue;
808 } else { // action == State_Remove
809 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
810 if (!_modal
) continue;
812 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
813 if (!_max_vert
) continue;
815 // XXX: resize the window etc
816 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
817 if (!_max_horz
) continue;
819 // XXX: resize the window etc
820 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
822 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
823 _skip_taskbar
= false;
824 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
826 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
828 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
829 if (!_above
) continue;
831 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
832 if (!_below
) continue;
837 // change fullscreen state before shading, as it will affect if the window
839 if (fsstate
!= _fullscreen
)
841 if (shadestate
!= _shaded
)
847 void Client::toggleClientBorder(bool addborder
)
849 // adjust our idea of where the client is, based on its border. When the
850 // border is removed, the client should now be considered to be in a
851 // different position.
852 // when re-adding the border to the client, the same operation needs to be
854 int x
= _area
.x(), y
= _area
.y();
857 case NorthWestGravity
:
859 case SouthWestGravity
:
861 case NorthEastGravity
:
863 case SouthEastGravity
:
864 if (addborder
) x
-= _border_width
* 2;
865 else x
+= _border_width
* 2;
872 if (addborder
) x
-= _border_width
;
873 else x
+= _border_width
;
878 case NorthWestGravity
:
880 case NorthEastGravity
:
882 case SouthWestGravity
:
884 case SouthEastGravity
:
885 if (addborder
) y
-= _border_width
* 2;
886 else y
+= _border_width
* 2;
893 if (addborder
) y
-= _border_width
;
894 else y
+= _border_width
;
900 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
902 // move the client so it is back it the right spot _with_ its border!
903 XMoveWindow(**otk::display
, _window
, x
, y
);
905 XSetWindowBorderWidth(**otk::display
, _window
, 0);
909 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
911 otk::EventHandler::clientMessageHandler(e
);
913 if (e
.format
!= 32) return;
915 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
916 // compress changes into a single change
917 bool compress
= false;
919 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
920 // XXX: it would be nice to compress ALL messages of a type, not just
921 // messages in a row without other message types between.
922 if (ce
.xclient
.message_type
!= e
.message_type
) {
923 XPutBackEvent(**otk::display
, &ce
);
929 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
931 setWMState(e
.data
.l
[0]); // use the original event
932 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
933 // compress changes into a single change
934 bool compress
= false;
936 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
937 // XXX: it would be nice to compress ALL messages of a type, not just
938 // messages in a row without other message types between.
939 if (ce
.xclient
.message_type
!= e
.message_type
) {
940 XPutBackEvent(**otk::display
, &ce
);
946 setDesktop(e
.data
.l
[0]); // use the found event
948 setDesktop(e
.data
.l
[0]); // use the original event
949 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
950 // can't compress these
952 printf("net_wm_state %s %ld %ld for 0x%lx\n",
953 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
954 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
955 e
.data
.l
[1], e
.data
.l
[2], _window
);
957 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
958 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
960 printf("net_close_window for 0x%lx\n", _window
);
963 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
965 printf("net_active_window for 0x%lx\n", _window
);
968 setDesktop(openbox
->screen(_screen
)->desktop());
973 openbox
->screen(_screen
)->raiseWindow(this);
979 void Client::shapeHandler(const XShapeEvent
&e
)
981 otk::EventHandler::shapeHandler(e
);
983 if (e
.kind
== ShapeBounding
) {
985 frame
->adjustShape();
991 void Client::resize(Corner anchor
, int w
, int h
)
993 if (!(_functions
& Func_Resize
)) return;
994 internal_resize(anchor
, w
, h
);
998 void Client::internal_resize(Corner anchor
, int w
, int h
, bool user
,
1001 w
-= _base_size
.x();
1002 h
-= _base_size
.y();
1004 // for interactive resizing. have to move half an increment in each
1006 w
+= _size_inc
.x() / 2;
1007 h
+= _size_inc
.y() / 2;
1010 // if this is a user-requested resize, then check against min/max sizes
1011 // and aspect ratios
1013 // smaller than min size or bigger than max size?
1014 if (w
< _min_size
.x()) w
= _min_size
.x();
1015 else if (w
> _max_size
.x()) w
= _max_size
.x();
1016 if (h
< _min_size
.y()) h
= _min_size
.y();
1017 else if (h
> _max_size
.y()) h
= _max_size
.y();
1019 // adjust the height ot match the width for the aspect ratios
1021 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1023 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1026 // keep to the increments
1030 // you cannot resize to nothing
1034 // store the logical size
1035 _logical_size
.setPoint(w
, h
);
1040 w
+= _base_size
.x();
1041 h
+= _base_size
.y();
1043 if (x
== INT_MIN
|| y
== INT_MIN
) {
1050 x
-= w
- _area
.width();
1053 y
-= h
- _area
.height();
1056 x
-= w
- _area
.width();
1057 y
-= h
- _area
.height();
1062 _area
.setSize(w
, h
);
1064 XResizeWindow(**otk::display
, _window
, w
, h
);
1066 // resize the frame to match the request
1067 frame
->adjustSize();
1068 internal_move(x
, y
);
1072 void Client::move(int x
, int y
)
1074 if (!(_functions
& Func_Move
)) return;
1075 internal_move(x
, y
);
1079 void Client::internal_move(int x
, int y
)
1083 // move the frame to be in the requested position
1084 if (frame
) { // this can be called while mapping, before frame exists
1085 frame
->adjustPosition();
1087 // send synthetic configure notify (we don't need to if we aren't mapped
1090 event
.type
= ConfigureNotify
;
1091 event
.xconfigure
.display
= **otk::display
;
1092 event
.xconfigure
.event
= _window
;
1093 event
.xconfigure
.window
= _window
;
1094 event
.xconfigure
.x
= x
;
1095 event
.xconfigure
.y
= y
;
1096 event
.xconfigure
.width
= _area
.width();
1097 event
.xconfigure
.height
= _area
.height();
1098 event
.xconfigure
.border_width
= _border_width
;
1099 event
.xconfigure
.above
= frame
->window();
1100 event
.xconfigure
.override_redirect
= False
;
1101 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1102 StructureNotifyMask
, &event
);
1107 void Client::close()
1111 if (!(_functions
& Func_Close
)) return;
1113 // XXX: itd be cool to do timeouts and shit here for killing the client's
1115 // like... if the window is around after 5 seconds, then the close button
1116 // turns a nice red, and if this function is called again, the client is
1117 // explicitly killed.
1119 ce
.xclient
.type
= ClientMessage
;
1120 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1121 ce
.xclient
.display
= **otk::display
;
1122 ce
.xclient
.window
= _window
;
1123 ce
.xclient
.format
= 32;
1124 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1125 ce
.xclient
.data
.l
[1] = CurrentTime
;
1126 ce
.xclient
.data
.l
[2] = 0l;
1127 ce
.xclient
.data
.l
[3] = 0l;
1128 ce
.xclient
.data
.l
[4] = 0l;
1129 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1133 void Client::changeState()
1135 unsigned long state
[2];
1136 state
[0] = _wmstate
;
1138 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1139 otk::Property::atoms
.wm_state
, state
, 2);
1144 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1146 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1148 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1150 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1152 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1154 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1156 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1158 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1160 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1162 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1163 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1164 otk::Property::atoms
.atom
, netstate
, num
);
1169 frame
->adjustState();
1173 void Client::changeAllowedActions(void)
1178 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1180 if (_functions
& Func_Shade
)
1181 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1182 if (_functions
& Func_Close
)
1183 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1184 if (_functions
& Func_Move
)
1185 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1186 if (_functions
& Func_Iconify
)
1187 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1188 if (_functions
& Func_Resize
)
1189 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1190 if (_functions
& Func_Fullscreen
)
1191 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1192 if (_functions
& Func_Maximize
) {
1193 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1194 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1197 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1198 otk::Property::atoms
.atom
, actions
, num
);
1202 void Client::applyStartupState()
1204 // these are in a carefully crafted order..
1208 setDesktop(ICONIC_DESKTOP
);
1211 _fullscreen
= false;
1221 if (_max_vert
); // XXX: incomplete
1222 if (_max_horz
); // XXX: incomplete
1224 if (_skip_taskbar
); // nothing to do for this
1225 if (_skip_pager
); // nothing to do for this
1226 if (_modal
); // nothing to do for this
1227 if (_above
); // nothing to do for this
1228 if (_below
); // nothing to do for this
1232 void Client::fireUrgent()
1234 // call the python UrgentWindow callbacks
1235 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1236 openbox
->bindings()->fireEvent(&data
);
1240 void Client::shade(bool shade
)
1242 if (!(_functions
& Func_Shade
) || // can't
1243 _shaded
== shade
) return; // already done
1245 // when we're iconic, don't change the wmstate
1247 _wmstate
= shade
? IconicState
: NormalState
;
1250 frame
->adjustSize();
1254 void Client::fullscreen(bool fs
)
1256 static FunctionFlags saved_func
;
1257 static DecorationFlags saved_decor
;
1258 static otk::Rect saved_area
;
1259 static otk::Point saved_logical_size
;
1261 if (!(_functions
& Func_Fullscreen
) || // can't
1262 _fullscreen
== fs
) return; // already done
1265 changeState(); // change the state hints on the client
1268 // save the functions and remove them
1269 saved_func
= _functions
;
1270 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1271 // save the decorations and remove them
1272 saved_decor
= _decorations
;
1274 // save the area and adjust it (we don't call internal resize here for
1275 // constraints on the size, etc, we just make it fullscreen).
1277 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1278 _area
.setRect(0, 0, info
->width(), info
->height());
1279 saved_logical_size
= _logical_size
;
1280 _logical_size
.setPoint((info
->width() - _base_size
.x()) / _size_inc
.x(),
1281 (info
->height() - _base_size
.y()) / _size_inc
.y());
1283 _functions
= saved_func
;
1284 _decorations
= saved_decor
;
1286 _logical_size
= saved_logical_size
;
1289 changeAllowedActions(); // based on the new _functions
1291 frame
->adjustSize(); // drop/replace the decor's and resize
1292 frame
->adjustPosition(); // get (back) in position!
1294 // raise (back) into our stacking layer
1295 openbox
->screen(_screen
)->raiseWindow(this);
1297 // try focus us when we go into fullscreen mode
1302 bool Client::focus()
1304 // won't try focus if the client doesn't want it, or if the window isn't
1305 // visible on the screen
1306 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1308 if (_focused
) return true;
1310 // do a check to see if the window has already been unmapped or destroyed
1311 // do this intelligently while watching out for unmaps we've generated
1312 // (ignore_unmaps > 0)
1314 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1315 XPutBackEvent(**otk::display
, &ev
);
1318 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1319 if (ignore_unmaps
) {
1320 unmapHandler(ev
.xunmap
);
1322 XPutBackEvent(**otk::display
, &ev
);
1328 XSetInputFocus(**otk::display
, _window
,
1329 RevertToNone
, CurrentTime
);
1331 if (_focus_notify
) {
1333 ce
.xclient
.type
= ClientMessage
;
1334 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1335 ce
.xclient
.display
= **otk::display
;
1336 ce
.xclient
.window
= _window
;
1337 ce
.xclient
.format
= 32;
1338 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1339 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1340 ce
.xclient
.data
.l
[2] = 0l;
1341 ce
.xclient
.data
.l
[3] = 0l;
1342 ce
.xclient
.data
.l
[4] = 0l;
1343 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1350 void Client::unfocus() const
1352 if (!_focused
) return;
1354 assert(openbox
->focusedClient() == this);
1355 openbox
->setFocusedClient(0);
1359 void Client::focusHandler(const XFocusChangeEvent
&e
)
1362 // printf("FocusIn for 0x%lx\n", e.window);
1365 otk::EventHandler::focusHandler(e
);
1370 openbox
->setFocusedClient(this);
1374 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1377 // printf("FocusOut for 0x%lx\n", e.window);
1380 otk::EventHandler::unfocusHandler(e
);
1385 if (openbox
->focusedClient() == this)
1386 openbox
->setFocusedClient(0);
1390 void Client::configureRequestHandler(const XConfigureRequestEvent
&e
)
1393 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1396 otk::EventHandler::configureRequestHandler(e
);
1398 // if we are iconic (or shaded (fvwm does this)) ignore the event
1399 if (_iconic
|| _shaded
) return;
1401 if (e
.value_mask
& CWBorderWidth
)
1402 _border_width
= e
.border_width
;
1404 // resize, then move, as specified in the EWMH section 7.7
1405 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1406 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1407 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1411 case NorthEastGravity
:
1415 case SouthWestGravity
:
1417 corner
= BottomLeft
;
1419 case SouthEastGravity
:
1420 corner
= BottomRight
;
1422 default: // NorthWest, Static, etc
1426 // if moving AND resizing ...
1427 if (e
.value_mask
& (CWX
| CWY
)) {
1428 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1429 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1430 internal_resize(corner
, w
, h
, false, x
, y
);
1431 } else // if JUST resizing...
1432 internal_resize(corner
, w
, h
, false);
1433 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1434 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1435 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1436 internal_move(x
, y
);
1439 if (e
.value_mask
& CWStackMode
) {
1443 openbox
->screen(_screen
)->lowerWindow(this);
1449 openbox
->screen(_screen
)->raiseWindow(this);
1456 void Client::unmapHandler(const XUnmapEvent
&e
)
1458 if (ignore_unmaps
) {
1460 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1467 printf("UnmapNotify for 0x%lx\n", e
.window
);
1470 otk::EventHandler::unmapHandler(e
);
1472 // this deletes us etc
1473 openbox
->screen(_screen
)->unmanageWindow(this);
1477 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1480 printf("DestroyNotify for 0x%lx\n", e
.window
);
1483 otk::EventHandler::destroyHandler(e
);
1485 // this deletes us etc
1486 openbox
->screen(_screen
)->unmanageWindow(this);
1490 void Client::reparentHandler(const XReparentEvent
&e
)
1492 // this is when the client is first taken captive in the frame
1493 if (e
.parent
== frame
->plate()) return;
1496 printf("ReparentNotify for 0x%lx\n", e
.window
);
1499 otk::EventHandler::reparentHandler(e
);
1502 This event is quite rare and is usually handled in unmapHandler.
1503 However, if the window is unmapped when the reparent event occurs,
1504 the window manager never sees it because an unmap event is not sent
1505 to an already unmapped window.
1508 // we don't want the reparent event, put it back on the stack for the X
1509 // server to deal with after we unmanage the window
1512 XPutBackEvent(**otk::display
, &ev
);
1514 // this deletes us etc
1515 openbox
->screen(_screen
)->unmanageWindow(this);
1518 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1521 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1524 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1526 // move to the current desktop (uniconify)
1527 setDesktop(openbox
->screen(_screen
)->desktop());
1528 // XXX: should we focus/raise the window? (basically a net_wm_active_window)