1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
10 #include "otk/display.hh"
11 #include "otk/property.hh"
15 #include <X11/Xutil.h>
16 #include <X11/Xatom.h>
19 #define _(str) gettext(str)
28 Client::Client(int screen
, Window window
)
29 : otk::EventHandler(),
30 frame(0), _screen(screen
), _window(window
)
37 // update EVERYTHING the first time!!
40 _wmstate
= NormalState
;
43 _layer
= Layer_Normal
;
46 _disabled_decorations
= 0;
52 getState(); // do this before updateTransientFor! (for _modal)
57 getType(); // this can change the mwmhints for special cases
61 getGravity(); // get the attribute gravity
62 updateNormalHints(); // this may override the attribute gravity
64 // got the type, the mwmhints, the protocols, and the normal hints (min/max
65 // sizes), so we're ready to set up
66 // the decorations/functions
67 setupDecorAndFunctions();
69 // also get the initial_state and set _iconic if we aren't "starting"
70 // when we're "starting" that means we should use whatever state was already
71 // on the window over the initial map state, because it was already mapped
72 updateWMHints(openbox
->state() != Openbox::State_Starting
);
78 // this makes sure that these windows appear on all desktops
79 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
80 _desktop
= 0xffffffff;
82 // set the desktop hint, to make sure that it always exists, and to reflect
83 // any changes we've made here
84 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
85 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
93 // clean up childrens' references
94 while (!_transients
.empty()) {
95 _transients
.front()->_transient_for
= 0;
96 _transients
.pop_front();
99 // clean up parents reference to this
101 _transient_for
->_transients
.remove(this); // remove from old parent
103 if (openbox
->state() != Openbox::State_Exiting
) {
104 // these values should not be persisted across a window unmapping/mapping
105 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
106 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
108 // if we're left in an iconic state, the client wont be mapped. this is
109 // bad, since we will no longer be managing the window on restart
111 XMapWindow(**otk::display
, _window
);
116 bool Client::validate() const
118 XSync(**otk::display
, false); // get all events on the server
121 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
122 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
123 XPutBackEvent(**otk::display
, &e
);
131 void Client::getGravity()
133 XWindowAttributes wattrib
;
136 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
137 assert(ret
!= BadWindow
);
138 _gravity
= wattrib
.win_gravity
;
142 void Client::getDesktop()
144 // defaults to the current desktop
145 _desktop
= openbox
->screen(_screen
)->desktop();
147 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
148 otk::Property::atoms
.cardinal
,
149 (long unsigned*)&_desktop
)) {
151 // printf("Window requested desktop: %ld\n", _desktop);
157 void Client::getType()
159 _type
= (WindowType
) -1;
162 unsigned long num
= (unsigned) -1;
163 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
164 otk::Property::atoms
.atom
, &num
, &val
)) {
165 // use the first value that we know about in the array
166 for (unsigned long i
= 0; i
< num
; ++i
) {
167 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
168 _type
= Type_Desktop
;
169 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
171 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
172 _type
= Type_Toolbar
;
173 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
175 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
176 _type
= Type_Utility
;
177 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
179 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
181 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
183 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
184 // prevent this window from getting any decor or functionality
185 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
186 _mwmhints
.decorations
= 0;
187 _mwmhints
.functions
= 0;
189 if (_type
!= (WindowType
) -1)
190 break; // grab the first known type
195 if (_type
== (WindowType
) -1) {
197 * the window type hint was not set, which means we either classify ourself
198 * as a normal window or a dialog, depending on if we are a transient.
208 void Client::setupDecorAndFunctions()
210 // start with everything (cept fullscreen)
211 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
212 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
213 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
215 if (_delete_window
) {
216 _decorations
|= Decor_Close
;
217 _functions
|= Func_Close
;
220 if (!(_min_size
.width() < _max_size
.width() ||
221 _min_size
.height() < _max_size
.height())) {
222 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
223 _functions
&= ~(Func_Resize
| Func_Maximize
);
228 // normal windows retain all of the possible decorations and
229 // functionality, and are the only windows that you can fullscreen
230 _functions
|= Func_Fullscreen
;
234 // dialogs cannot be maximized
235 _decorations
&= ~Decor_Maximize
;
236 _functions
&= ~Func_Maximize
;
242 // these windows get less functionality
243 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
244 _functions
&= ~(Func_Iconify
| Func_Resize
);
250 // none of these windows are manipulated by the window manager
256 // Mwm Hints are applied subtractively to what has already been chosen for
257 // decor and functionality
258 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
259 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
260 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
261 _decorations
&= ~Decor_Border
;
262 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
263 _decorations
&= ~Decor_Handle
;
264 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
265 _decorations
&= ~Decor_Titlebar
;
266 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
267 _decorations
&= ~Decor_Iconify
;
268 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
269 _decorations
&= ~Decor_Maximize
;
273 if (_mwmhints
.flags
& MwmFlag_Functions
) {
274 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
275 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
276 _functions
&= ~Func_Resize
;
277 if (! (_mwmhints
.functions
& MwmFunc_Move
))
278 _functions
&= ~Func_Move
;
279 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
280 _functions
&= ~Func_Iconify
;
281 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
282 _functions
&= ~Func_Maximize
;
283 // dont let mwm hints kill the close button
284 //if (! (_mwmhints.functions & MwmFunc_Close))
285 // _functions &= ~Func_Close;
289 // can't maximize without moving/resizing
290 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
291 _functions
&= ~Func_Maximize
;
293 // finally, user specified disabled decorations are applied to subtract
295 if (_disabled_decorations
& Decor_Titlebar
)
296 _decorations
&= ~Decor_Titlebar
;
297 if (_disabled_decorations
& Decor_Handle
)
298 _decorations
&= ~Decor_Handle
;
299 if (_disabled_decorations
& Decor_Border
)
300 _decorations
&= ~Decor_Border
;
301 if (_disabled_decorations
& Decor_Iconify
)
302 _decorations
&= ~Decor_Iconify
;
303 if (_disabled_decorations
& Decor_Maximize
)
304 _decorations
&= ~Decor_Maximize
;
305 if (_disabled_decorations
& Decor_AllDesktops
)
306 _decorations
&= ~Decor_AllDesktops
;
307 if (_disabled_decorations
& Decor_Close
)
308 _decorations
&= ~Decor_Close
;
310 // if we don't have a titlebar, then we cannot shade!
311 if (!(_decorations
& Decor_Titlebar
))
312 _functions
&= ~Func_Shade
;
314 changeAllowedActions();
317 frame
->adjustSize(); // change the decors on the frame
318 frame
->adjustPosition(); // with more/less decorations, we may need to be
320 remaximize(); // with new decor, the window's maximized size may change
325 void Client::getMwmHints()
327 unsigned long num
= MwmHints::elements
;
328 unsigned long *hints
;
330 _mwmhints
.flags
= 0; // default to none
332 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
333 otk::Property::atoms
.motif_wm_hints
, &num
,
334 (unsigned long **)&hints
))
337 if (num
>= MwmHints::elements
) {
338 // retrieved the hints
339 _mwmhints
.flags
= hints
[0];
340 _mwmhints
.functions
= hints
[1];
341 _mwmhints
.decorations
= hints
[2];
348 void Client::getArea()
350 XWindowAttributes wattrib
;
353 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
354 assert(ret
!= BadWindow
);
356 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
357 _border_width
= wattrib
.border_width
;
361 void Client::getState()
363 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
364 _iconic
= _skip_taskbar
= _skip_pager
= false;
366 unsigned long *state
;
367 unsigned long num
= (unsigned) -1;
369 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
370 otk::Property::atoms
.atom
, &num
, &state
)) {
371 for (unsigned long i
= 0; i
< num
; ++i
) {
372 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
374 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
376 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
378 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
379 _skip_taskbar
= true;
380 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
382 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
384 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
386 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
388 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
390 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
399 void Client::getShaped()
403 if (otk::display
->shape()) {
408 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
410 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
411 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
418 void Client::calcLayer() {
421 if (_iconic
) l
= Layer_Icon
;
422 else if (_fullscreen
) l
= Layer_Fullscreen
;
423 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
424 else if (_type
== Type_Dock
) {
425 if (!_below
) l
= Layer_Top
;
426 else l
= Layer_Normal
;
428 else if (_above
) l
= Layer_Above
;
429 else if (_below
) l
= Layer_Below
;
430 else l
= Layer_Normal
;
436 if we don't have a frame, then we aren't mapped yet (and this would
439 openbox
->screen(_screen
)->raiseWindow(this);
445 void Client::updateProtocols()
450 _focus_notify
= false;
451 _delete_window
= false;
453 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
454 for (int i
= 0; i
< num_return
; ++i
) {
455 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
456 // this means we can request the window to close
457 _delete_window
= true;
458 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
459 // if this protocol is requested, then the window will be notified
460 // by the window manager whenever it receives focus
461 _focus_notify
= true;
468 void Client::updateNormalHints()
472 int oldgravity
= _gravity
;
477 _size_inc
= otk::Size(1, 1);
478 _base_size
= otk::Size(0, 0);
479 _min_size
= otk::Size(0, 0);
480 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
482 // get the hints from the window
483 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
484 _positioned
= (size
.flags
& (PPosition
|USPosition
));
486 if (size
.flags
& PWinGravity
) {
487 _gravity
= size
.win_gravity
;
489 // if the client has a frame, i.e. has already been mapped and is
490 // changing its gravity
491 if (frame
&& _gravity
!= oldgravity
) {
492 // move our idea of the client's position based on its new gravity
493 int x
= frame
->area().x(), y
= frame
->area().y();
494 frame
->frameGravity(x
, y
);
495 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
499 if (size
.flags
& PAspect
) {
500 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
501 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
504 if (size
.flags
& PMinSize
)
505 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
507 if (size
.flags
& PMaxSize
)
508 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
510 if (size
.flags
& PBaseSize
)
511 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
513 if (size
.flags
& PResizeInc
)
514 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
519 void Client::updateWMHints(bool initstate
)
523 // assume a window takes input if it doesnt specify
527 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
528 if (hints
->flags
& InputHint
)
529 _can_focus
= hints
->input
;
531 // only do this when initstate is true!
532 if (initstate
&& (hints
->flags
& StateHint
))
533 _iconic
= hints
->initial_state
== IconicState
;
535 if (hints
->flags
& XUrgencyHint
)
538 if (hints
->flags
& WindowGroupHint
) {
539 if (hints
->window_group
!= _group
) {
540 // XXX: remove from the old group if there was one
541 _group
= hints
->window_group
;
542 // XXX: do stuff with the group
553 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
554 (long)_window
, _urgent
? "ON" : "OFF");
556 // fire the urgent callback if we're mapped, otherwise, wait until after
564 void Client::updateTitle()
569 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
570 otk::Property::utf8
, &_title
)) {
572 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
573 otk::Property::ascii
, &_title
);
577 _title
= _("Unnamed Window");
580 frame
->adjustTitle();
584 void Client::updateIconTitle()
589 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
590 otk::Property::utf8
, &_icon_title
)) {
592 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
593 otk::Property::ascii
, &_icon_title
);
597 _icon_title
= _("Unnamed Window");
601 void Client::updateClass()
604 _app_name
= _app_class
= _role
= "";
606 otk::Property::StringVect v
;
607 unsigned long num
= 2;
609 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
610 otk::Property::ascii
, &num
, &v
)) {
611 if (num
> 0) _app_name
= v
[0].c_str();
612 if (num
> 1) _app_class
= v
[1].c_str();
617 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
618 otk::Property::ascii
, &num
, &v
)) {
619 if (num
> 0) _role
= v
[0].c_str();
624 void Client::updateStrut()
626 unsigned long num
= 4;
628 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
629 otk::Property::atoms
.cardinal
, &num
, &data
))
633 _strut
.left
= data
[0];
634 _strut
.right
= data
[1];
635 _strut
.top
= data
[2];
636 _strut
.bottom
= data
[3];
638 // updating here is pointless while we're being mapped cuz we're not in
639 // the screen's client list yet
641 openbox
->screen(_screen
)->updateStruts();
648 void Client::updateTransientFor()
653 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
654 t
!= _window
) { // cant be transient to itself!
655 c
= openbox
->findClient(t
);
656 assert(c
!= this); // if this happens then we need to check for it
658 if (!c
/*XXX: && _group*/) {
659 // not transient to a client, see if it is transient for a group
660 if (//t == _group->leader() ||
662 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
663 // window is a transient for its group!
664 // XXX: for now this is treated as non-transient.
665 // this needs to be fixed!
670 // if anything has changed...
671 if (c
!= _transient_for
) {
673 _transient_for
->_transients
.remove(this); // remove from old parent
676 _transient_for
->_transients
.push_back(this); // add to new parent
681 void Client::propertyHandler(const XPropertyEvent
&e
)
683 otk::EventHandler::propertyHandler(e
);
685 // validate cuz we query stuff off the client here
686 if (!validate()) return;
688 // compress changes to a single property into a single change
690 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
691 // XXX: it would be nice to compress ALL changes to a property, not just
692 // changes in a row without other props between.
693 if (ce
.xproperty
.atom
!= e
.atom
) {
694 XPutBackEvent(**otk::display
, &ce
);
699 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
701 setupDecorAndFunctions(); // normal hints can make a window non-resizable
702 } else if (e
.atom
== XA_WM_HINTS
)
704 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
705 updateTransientFor();
707 calcLayer(); // type may have changed, so update the layer
708 setupDecorAndFunctions();
710 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
711 e
.atom
== otk::Property::atoms
.wm_name
)
713 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
714 e
.atom
== otk::Property::atoms
.wm_icon_name
)
716 else if (e
.atom
== otk::Property::atoms
.wm_class
)
718 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
720 setupDecorAndFunctions();
722 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
727 void Client::setWMState(long state
)
729 if (state
== _wmstate
) return; // no change
742 void Client::setDesktop(unsigned int target
)
744 if (target
== _desktop
) return;
746 printf("Setting desktop %u\n", target
);
748 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
749 target
== 0xffffffff))
754 // set the desktop hint
755 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
756 otk::Property::atoms
.cardinal
, _desktop
);
758 // 'move' the window to the new desktop
761 openbox
->screen(_screen
)->updateStruts();
765 void Client::showhide()
768 (_desktop
== openbox
->screen(_screen
)->desktop() ||
769 _desktop
== 0xffffffff))
776 void Client::setState(StateAction action
, long data1
, long data2
)
778 bool shadestate
= _shaded
;
779 bool fsstate
= _fullscreen
;
780 bool maxh
= _max_horz
;
781 bool maxv
= _max_vert
;
783 if (!(action
== State_Add
|| action
== State_Remove
||
784 action
== State_Toggle
))
785 return; // an invalid action was passed to the client message, ignore it
787 for (int i
= 0; i
< 2; ++i
) {
788 Atom state
= i
== 0 ? data1
: data2
;
790 if (! state
) continue;
792 // if toggling, then pick whether we're adding or removing
793 if (action
== State_Toggle
) {
794 if (state
== otk::Property::atoms
.net_wm_state_modal
)
795 action
= _modal
? State_Remove
: State_Add
;
796 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
797 action
= _max_vert
? State_Remove
: State_Add
;
798 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
799 action
= _max_horz
? State_Remove
: State_Add
;
800 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
801 action
= _shaded
? State_Remove
: State_Add
;
802 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
803 action
= _skip_taskbar
? State_Remove
: State_Add
;
804 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
805 action
= _skip_pager
? State_Remove
: State_Add
;
806 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
807 action
= _fullscreen
? State_Remove
: State_Add
;
808 else if (state
== otk::Property::atoms
.net_wm_state_above
)
809 action
= _above
? State_Remove
: State_Add
;
810 else if (state
== otk::Property::atoms
.net_wm_state_below
)
811 action
= _below
? State_Remove
: State_Add
;
814 if (action
== State_Add
) {
815 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
816 if (_modal
) continue;
818 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
820 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
821 if (_max_horz
) continue;
823 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
825 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
826 _skip_taskbar
= true;
827 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
829 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
831 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
832 if (_above
) continue;
834 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
835 if (_below
) continue;
839 } else { // action == State_Remove
840 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
841 if (!_modal
) continue;
843 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
845 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
847 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
849 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
850 _skip_taskbar
= false;
851 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
853 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
855 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
856 if (!_above
) continue;
858 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
859 if (!_below
) continue;
864 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
865 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
866 if (maxh
== maxv
) { // both going the same way
867 maximize(maxh
, 0, true);
869 maximize(maxh
, 1, true);
870 maximize(maxv
, 2, true);
872 } else { // toggling one
873 if (maxh
!= _max_horz
)
874 maximize(maxh
, 1, true);
876 maximize(maxv
, 2, true);
879 // change fullscreen state before shading, as it will affect if the window
881 if (fsstate
!= _fullscreen
)
882 fullscreen(fsstate
, true);
883 if (shadestate
!= _shaded
)
886 changeState(); // change the hint to relect these changes
890 void Client::toggleClientBorder(bool addborder
)
892 // adjust our idea of where the client is, based on its border. When the
893 // border is removed, the client should now be considered to be in a
894 // different position.
895 // when re-adding the border to the client, the same operation needs to be
897 int oldx
= _area
.x(), oldy
= _area
.y();
898 int x
= oldx
, y
= oldy
;
901 case NorthWestGravity
:
903 case SouthWestGravity
:
905 case NorthEastGravity
:
907 case SouthEastGravity
:
908 if (addborder
) x
-= _border_width
* 2;
909 else x
+= _border_width
* 2;
916 if (addborder
) x
-= _border_width
;
917 else x
+= _border_width
;
922 case NorthWestGravity
:
924 case NorthEastGravity
:
926 case SouthWestGravity
:
928 case SouthEastGravity
:
929 if (addborder
) y
-= _border_width
* 2;
930 else y
+= _border_width
* 2;
937 if (addborder
) y
-= _border_width
;
938 else y
+= _border_width
;
941 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
944 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
946 // move the client so it is back it the right spot _with_ its border!
947 if (x
!= oldx
|| y
!= oldy
)
948 XMoveWindow(**otk::display
, _window
, x
, y
);
950 XSetWindowBorderWidth(**otk::display
, _window
, 0);
954 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
956 otk::EventHandler::clientMessageHandler(e
);
958 // validate cuz we query stuff off the client here
959 if (!validate()) return;
961 if (e
.format
!= 32) return;
963 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
964 // compress changes into a single change
965 bool compress
= false;
967 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
968 // XXX: it would be nice to compress ALL messages of a type, not just
969 // messages in a row without other message types between.
970 if (ce
.xclient
.message_type
!= e
.message_type
) {
971 XPutBackEvent(**otk::display
, &ce
);
977 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
979 setWMState(e
.data
.l
[0]); // use the original event
980 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
981 // compress changes into a single change
982 bool compress
= false;
984 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
985 // XXX: it would be nice to compress ALL messages of a type, not just
986 // messages in a row without other message types between.
987 if (ce
.xclient
.message_type
!= e
.message_type
) {
988 XPutBackEvent(**otk::display
, &ce
);
994 setDesktop(e
.data
.l
[0]); // use the found event
996 setDesktop(e
.data
.l
[0]); // use the original event
997 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
998 // can't compress these
1000 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1001 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1002 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1003 e
.data
.l
[1], e
.data
.l
[2], _window
);
1005 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1006 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1008 printf("net_close_window for 0x%lx\n", _window
);
1011 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1013 printf("net_active_window for 0x%lx\n", _window
);
1020 openbox
->screen(_screen
)->raiseWindow(this);
1021 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1024 if (e
.data
.l
[0] && _shaded
)
1028 openbox
->screen(_screen
)->raiseWindow(this);
1034 void Client::shapeHandler(const XShapeEvent
&e
)
1036 otk::EventHandler::shapeHandler(e
);
1038 if (e
.kind
== ShapeBounding
) {
1040 frame
->adjustShape();
1046 void Client::resize(Corner anchor
, int w
, int h
)
1048 if (!(_functions
& Func_Resize
)) return;
1049 internal_resize(anchor
, w
, h
);
1053 void Client::internal_resize(Corner anchor
, int w
, int h
,
1054 bool user
, int x
, int y
)
1056 w
-= _base_size
.width();
1057 h
-= _base_size
.height();
1060 // for interactive resizing. have to move half an increment in each
1062 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1063 int mh
= h
% _size_inc
.height();
1064 int aw
= _size_inc
.width() / 2; // amount to add
1065 int ah
= _size_inc
.height() / 2;
1066 // don't let us move into a new size increment
1067 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1068 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1072 // if this is a user-requested resize, then check against min/max sizes
1073 // and aspect ratios
1075 // smaller than min size or bigger than max size?
1076 if (w
> _max_size
.width()) w
= _max_size
.width();
1077 if (w
< _min_size
.width()) w
= _min_size
.width();
1078 if (h
> _max_size
.height()) h
= _max_size
.height();
1079 if (h
< _min_size
.height()) h
= _min_size
.height();
1081 // adjust the height ot match the width for the aspect ratios
1083 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1085 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1088 // keep to the increments
1089 w
/= _size_inc
.width();
1090 h
/= _size_inc
.height();
1092 // you cannot resize to nothing
1096 // store the logical size
1097 _logical_size
= otk::Size(w
, h
);
1099 w
*= _size_inc
.width();
1100 h
*= _size_inc
.height();
1102 w
+= _base_size
.width();
1103 h
+= _base_size
.height();
1105 if (x
== INT_MIN
|| y
== INT_MIN
) {
1112 x
-= w
- _area
.width();
1115 y
-= h
- _area
.height();
1118 x
-= w
- _area
.width();
1119 y
-= h
- _area
.height();
1124 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1126 XResizeWindow(**otk::display
, _window
, w
, h
);
1128 // resize the frame to match the request
1129 frame
->adjustSize();
1130 internal_move(x
, y
);
1134 void Client::move(int x
, int y
)
1136 if (!(_functions
& Func_Move
)) return;
1137 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1139 internal_move(x
, y
);
1143 void Client::internal_move(int x
, int y
)
1145 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1147 // move the frame to be in the requested position
1148 if (frame
) { // this can be called while mapping, before frame exists
1149 frame
->adjustPosition();
1151 // send synthetic configure notify (we don't need to if we aren't mapped
1154 event
.type
= ConfigureNotify
;
1155 event
.xconfigure
.display
= **otk::display
;
1156 event
.xconfigure
.event
= _window
;
1157 event
.xconfigure
.window
= _window
;
1159 // root window coords with border in mind
1160 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1161 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1163 event
.xconfigure
.width
= _area
.width();
1164 event
.xconfigure
.height
= _area
.height();
1165 event
.xconfigure
.border_width
= _border_width
;
1166 event
.xconfigure
.above
= frame
->plate();
1167 event
.xconfigure
.override_redirect
= False
;
1168 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1169 StructureNotifyMask
, &event
);
1171 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1172 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1173 event
.xconfigure
.height
, event
.xconfigure
.window
);
1179 void Client::close()
1183 if (!(_functions
& Func_Close
)) return;
1185 // XXX: itd be cool to do timeouts and shit here for killing the client's
1187 // like... if the window is around after 5 seconds, then the close button
1188 // turns a nice red, and if this function is called again, the client is
1189 // explicitly killed.
1191 ce
.xclient
.type
= ClientMessage
;
1192 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1193 ce
.xclient
.display
= **otk::display
;
1194 ce
.xclient
.window
= _window
;
1195 ce
.xclient
.format
= 32;
1196 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1197 ce
.xclient
.data
.l
[1] = CurrentTime
;
1198 ce
.xclient
.data
.l
[2] = 0l;
1199 ce
.xclient
.data
.l
[3] = 0l;
1200 ce
.xclient
.data
.l
[4] = 0l;
1201 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1205 void Client::changeState()
1207 unsigned long state
[2];
1208 state
[0] = _wmstate
;
1210 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1211 otk::Property::atoms
.wm_state
, state
, 2);
1216 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1218 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1220 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1222 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1224 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1226 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1228 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1230 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1232 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1234 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1235 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1236 otk::Property::atoms
.atom
, netstate
, num
);
1241 frame
->adjustState();
1245 void Client::changeAllowedActions(void)
1250 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1252 if (_functions
& Func_Shade
)
1253 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1254 if (_functions
& Func_Close
)
1255 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1256 if (_functions
& Func_Move
)
1257 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1258 if (_functions
& Func_Iconify
)
1259 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1260 if (_functions
& Func_Resize
)
1261 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1262 if (_functions
& Func_Fullscreen
)
1263 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1264 if (_functions
& Func_Maximize
) {
1265 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1266 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1269 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1270 otk::Property::atoms
.atom
, actions
, num
);
1272 // make sure the window isn't breaking any rules now
1274 if (!(_functions
& Func_Shade
) && _shaded
)
1275 if (frame
) shade(false);
1276 else _shaded
= false;
1277 if (!(_functions
& Func_Iconify
) && _iconic
)
1278 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1279 else _iconic
= false;
1280 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1281 if (frame
) fullscreen(false);
1282 else _fullscreen
= false;
1283 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1284 if (frame
) maximize(false, 0);
1285 else _max_vert
= _max_horz
= false;
1289 void Client::remaximize()
1291 printf("REMAXIMIZE!!!!!!!!!!!!!!!!!!!\n");
1293 if (_max_horz
&& _max_vert
)
1300 return; // not maximized
1301 _max_horz
= _max_vert
= false;
1302 maximize(true, dir
, false);
1306 void Client::applyStartupState()
1308 // these are in a carefully crafted order..
1315 _fullscreen
= false;
1316 fullscreen(true, false);
1325 if (_max_vert
&& _max_horz
) {
1326 _max_vert
= _max_horz
= false;
1327 maximize(true, 0, false);
1328 } else if (_max_vert
) {
1330 maximize(true, 2, false);
1331 } else if (_max_horz
) {
1333 maximize(true, 1, false);
1336 if (_skip_taskbar
); // nothing to do for this
1337 if (_skip_pager
); // nothing to do for this
1338 if (_modal
); // nothing to do for this
1339 if (_above
); // nothing to do for this
1340 if (_below
); // nothing to do for this
1344 void Client::fireUrgent()
1346 // call the python UrgentWindow callbacks
1347 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1348 openbox
->bindings()->fireEvent(&data
);
1352 void Client::shade(bool shade
)
1354 if (!(_functions
& Func_Shade
) || // can't
1355 _shaded
== shade
) return; // already done
1357 // when we're iconic, don't change the wmstate
1359 _wmstate
= shade
? IconicState
: NormalState
;
1362 frame
->adjustSize();
1366 void Client::maximize(bool max
, int dir
, bool savearea
)
1368 assert(dir
== 0 || dir
== 1 || dir
== 2);
1369 if (!(_functions
& Func_Maximize
)) return; // can't
1371 // check if already done
1373 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1374 if (dir
== 1 && _max_horz
) return;
1375 if (dir
== 2 && _max_vert
) return;
1377 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1378 if (dir
== 1 && !_max_horz
) return;
1379 if (dir
== 2 && !_max_vert
) return;
1382 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1383 int x
= frame
->area().x(), y
= frame
->area().y(),
1384 w
= _area
.width(), h
= _area
.height();
1390 unsigned long n
= 4;
1397 // get the property off the window and use it for the dimentions we are
1399 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1400 otk::Property::atoms
.cardinal
, &n
,
1401 (long unsigned**) &readdim
)) {
1404 dimensions
[0] = readdim
[0];
1405 dimensions
[2] = readdim
[2];
1408 dimensions
[1] = readdim
[1];
1409 dimensions
[3] = readdim
[3];
1415 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1416 otk::Property::atoms
.cardinal
,
1417 (long unsigned*)dimensions
, 4);
1419 if (dir
== 0 || dir
== 1) { // horz
1423 if (dir
== 0 || dir
== 2) { // vert
1425 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1429 long unsigned n
= 4;
1431 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1432 otk::Property::atoms
.cardinal
, &n
,
1433 (long unsigned**) &dimensions
)) {
1435 if (dir
== 0 || dir
== 1) { // horz
1436 x
= (signed int)dimensions
[0];
1437 w
= (signed int)dimensions
[2];
1439 if (dir
== 0 || dir
== 2) { // vert
1440 y
= (signed int)dimensions
[1];
1441 h
= (signed int)dimensions
[3];
1446 // pick some fallbacks...
1447 if (dir
== 0 || dir
== 1) { // horz
1448 x
= a
.x() + a
.width() / 4;
1451 if (dir
== 0 || dir
== 2) { // vert
1452 y
= a
.y() + a
.height() / 4;
1458 if (dir
== 0 || dir
== 1) // horz
1460 if (dir
== 0 || dir
== 2) // vert
1463 if (!_max_horz
&& !_max_vert
)
1464 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1466 changeState(); // change the state hints on the client
1468 frame
->frameGravity(x
, y
); // figure out where the client should be going
1469 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1473 void Client::fullscreen(bool fs
, bool savearea
)
1475 static FunctionFlags saved_func
;
1476 static DecorationFlags saved_decor
;
1478 if (!(_functions
& Func_Fullscreen
) || // can't
1479 _fullscreen
== fs
) return; // already done
1482 changeState(); // change the state hints on the client
1484 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1487 // save the functions and remove them
1488 saved_func
= _functions
;
1489 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1490 // save the decorations and remove them
1491 saved_decor
= _decorations
;
1495 dimensions
[0] = _area
.x();
1496 dimensions
[1] = _area
.y();
1497 dimensions
[2] = _area
.width();
1498 dimensions
[3] = _area
.height();
1499 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1500 otk::Property::atoms
.cardinal
,
1501 (long unsigned*)dimensions
, 4);
1503 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1506 w
= info
->size().width();
1507 h
= info
->size().height();
1509 _functions
= saved_func
;
1510 _decorations
= saved_decor
;
1513 long unsigned n
= 4;
1515 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1516 otk::Property::atoms
.cardinal
, &n
,
1517 (long unsigned**) &dimensions
)) {
1526 // pick some fallbacks...
1527 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1528 x
= a
.x() + a
.width() / 4;
1529 y
= a
.y() + a
.height() / 4;
1535 changeAllowedActions(); // based on the new _functions
1537 // when fullscreening, don't obey things like increments, fill the screen
1538 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1540 // raise (back) into our stacking layer
1541 openbox
->screen(_screen
)->raiseWindow(this);
1543 // try focus us when we go into fullscreen mode
1548 void Client::iconify(bool iconic
, bool curdesk
)
1550 if (_iconic
== iconic
) return; // nothing to do
1555 _wmstate
= IconicState
;
1557 // we unmap the client itself so that we can get MapRequest events, and
1558 // because the ICCCM tells us to!
1559 XUnmapWindow(**otk::display
, _window
);
1562 setDesktop(openbox
->screen(_screen
)->desktop());
1563 _wmstate
= NormalState
;
1564 XMapWindow(**otk::display
, _window
);
1570 openbox
->screen(_screen
)->updateStruts();
1574 void Client::disableDecorations(DecorationFlags flags
)
1576 _disabled_decorations
= flags
;
1577 setupDecorAndFunctions();
1581 void Client::installColormap(bool install
) const
1583 XWindowAttributes wa
;
1584 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1586 XInstallColormap(**otk::display
, wa
.colormap
);
1588 XUninstallColormap(**otk::display
, wa
.colormap
);
1593 // recursively searches the client 'tree' for a modal client, always skips the
1594 // topmost node (the window you're starting with)
1595 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1597 List::const_iterator it
, end
= node
->_transients
.end();
1600 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1601 if (*it
== skip
) continue; // circular?
1602 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1603 if ((*it
)->_modal
) return *it
; // got one
1608 Client
*Client::findModalChild()
1610 return searchModalTree(this, this);
1614 bool Client::focus()
1616 // if we have a modal child, then focus it, not us
1617 Client
*c
= findModalChild();
1618 if (c
) return c
->focus();
1620 // won't try focus if the client doesn't want it, or if the window isn't
1621 // visible on the screen
1622 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1624 if (_focused
) return true;
1626 // do a check to see if the window has already been unmapped or destroyed
1627 // do this intelligently while watching out for unmaps we've generated
1628 // (ignore_unmaps > 0)
1630 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1631 XPutBackEvent(**otk::display
, &ev
);
1634 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1635 if (ignore_unmaps
) {
1636 unmapHandler(ev
.xunmap
);
1638 XPutBackEvent(**otk::display
, &ev
);
1644 XSetInputFocus(**otk::display
, _window
,
1645 RevertToNone
, CurrentTime
);
1647 if (_focus_notify
) {
1649 ce
.xclient
.type
= ClientMessage
;
1650 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1651 ce
.xclient
.display
= **otk::display
;
1652 ce
.xclient
.window
= _window
;
1653 ce
.xclient
.format
= 32;
1654 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1655 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1656 ce
.xclient
.data
.l
[2] = 0l;
1657 ce
.xclient
.data
.l
[3] = 0l;
1658 ce
.xclient
.data
.l
[4] = 0l;
1659 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1662 XSync(**otk::display
, False
);
1667 void Client::unfocus() const
1669 if (!_focused
) return;
1671 assert(openbox
->focusedClient() == this);
1672 openbox
->setFocusedClient(0);
1676 void Client::focusHandler(const XFocusChangeEvent
&e
)
1679 // printf("FocusIn for 0x%lx\n", e.window);
1682 otk::EventHandler::focusHandler(e
);
1685 frame
->adjustFocus();
1687 openbox
->setFocusedClient(this);
1691 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1694 // printf("FocusOut for 0x%lx\n", e.window);
1697 otk::EventHandler::unfocusHandler(e
);
1700 frame
->adjustFocus();
1702 if (openbox
->focusedClient() == this)
1703 openbox
->setFocusedClient(0);
1707 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1710 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1713 otk::EventHandler::configureRequestHandler(ec
);
1716 XConfigureRequestEvent e
= ec
;
1718 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1720 // XXX if this causes bad things.. we can compress config req's with the
1722 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1723 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1724 e
.x
= ev
.xconfigurerequest
.x
;
1725 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1726 e
.y
= ev
.xconfigurerequest
.y
;
1727 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1728 e
.width
= ev
.xconfigurerequest
.width
;
1729 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1730 e
.height
= ev
.xconfigurerequest
.height
;
1731 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1732 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1733 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1734 e
.detail
= ev
.xconfigurerequest
.detail
;
1737 // if we are iconic (or shaded (fvwm does this)) ignore the event
1738 if (_iconic
|| _shaded
) return;
1740 if (e
.value_mask
& CWBorderWidth
)
1741 _border_width
= e
.border_width
;
1743 // resize, then move, as specified in the EWMH section 7.7
1744 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1745 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1746 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1750 case NorthEastGravity
:
1754 case SouthWestGravity
:
1756 corner
= BottomLeft
;
1758 case SouthEastGravity
:
1759 corner
= BottomRight
;
1761 default: // NorthWest, Static, etc
1765 // if moving AND resizing ...
1766 if (e
.value_mask
& (CWX
| CWY
)) {
1767 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1768 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1769 internal_resize(corner
, w
, h
, false, x
, y
);
1770 } else // if JUST resizing...
1771 internal_resize(corner
, w
, h
, false);
1772 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1773 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1774 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1775 internal_move(x
, y
);
1778 if (e
.value_mask
& CWStackMode
) {
1782 openbox
->screen(_screen
)->lowerWindow(this);
1788 openbox
->screen(_screen
)->raiseWindow(this);
1795 void Client::unmapHandler(const XUnmapEvent
&e
)
1797 if (ignore_unmaps
) {
1799 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1806 printf("UnmapNotify for 0x%lx\n", e
.window
);
1809 otk::EventHandler::unmapHandler(e
);
1811 // this deletes us etc
1812 openbox
->screen(_screen
)->unmanageWindow(this);
1816 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1819 printf("DestroyNotify for 0x%lx\n", e
.window
);
1822 otk::EventHandler::destroyHandler(e
);
1824 // this deletes us etc
1825 openbox
->screen(_screen
)->unmanageWindow(this);
1829 void Client::reparentHandler(const XReparentEvent
&e
)
1831 // this is when the client is first taken captive in the frame
1832 if (e
.parent
== frame
->plate()) return;
1835 printf("ReparentNotify for 0x%lx\n", e
.window
);
1838 otk::EventHandler::reparentHandler(e
);
1841 This event is quite rare and is usually handled in unmapHandler.
1842 However, if the window is unmapped when the reparent event occurs,
1843 the window manager never sees it because an unmap event is not sent
1844 to an already unmapped window.
1847 // we don't want the reparent event, put it back on the stack for the X
1848 // server to deal with after we unmanage the window
1851 XPutBackEvent(**otk::display
, &ev
);
1853 // this deletes us etc
1854 openbox
->screen(_screen
)->unmanageWindow(this);
1857 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1860 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1863 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1865 // move to the current desktop (uniconify)
1867 // XXX: should we focus/raise the window? (basically a net_wm_active_window)