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
);
92 // clean up childrens' references
93 while (!_transients
.empty()) {
94 _transients
.front()->_transient_for
= 0;
95 _transients
.pop_front();
98 // clean up parents reference to this
100 _transient_for
->_transients
.remove(this); // remove from old parent
102 if (openbox
->state() != Openbox::State_Exiting
) {
103 // these values should not be persisted across a window unmapping/mapping
104 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
105 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
107 // if we're left in an iconic state, the client wont be mapped. this is
108 // bad, since we will no longer be managing the window on restart
110 XMapWindow(**otk::display
, _window
);
115 bool Client::validate() const
117 XSync(**otk::display
, false); // get all events on the server
120 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
121 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
122 XPutBackEvent(**otk::display
, &e
);
130 void Client::getGravity()
132 XWindowAttributes wattrib
;
135 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
136 assert(ret
!= BadWindow
);
137 _gravity
= wattrib
.win_gravity
;
141 void Client::getDesktop()
143 // defaults to the current desktop
144 _desktop
= openbox
->screen(_screen
)->desktop();
146 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
147 otk::Property::atoms
.cardinal
,
148 (long unsigned*)&_desktop
)) {
150 // printf("Window requested desktop: %ld\n", _desktop);
156 void Client::getType()
158 _type
= (WindowType
) -1;
161 unsigned long num
= (unsigned) -1;
162 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
163 otk::Property::atoms
.atom
, &num
, &val
)) {
164 // use the first value that we know about in the array
165 for (unsigned long i
= 0; i
< num
; ++i
) {
166 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
167 _type
= Type_Desktop
;
168 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
170 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
171 _type
= Type_Toolbar
;
172 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
174 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
175 _type
= Type_Utility
;
176 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
178 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
180 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
182 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
183 // prevent this window from getting any decor or functionality
184 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
185 _mwmhints
.decorations
= 0;
186 _mwmhints
.functions
= 0;
188 if (_type
!= (WindowType
) -1)
189 break; // grab the first known type
194 if (_type
== (WindowType
) -1) {
196 * the window type hint was not set, which means we either classify ourself
197 * as a normal window or a dialog, depending on if we are a transient.
207 void Client::setupDecorAndFunctions()
209 // start with everything (cept fullscreen)
210 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
211 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
212 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
214 if (_delete_window
) {
215 _decorations
|= Decor_Close
;
216 _functions
|= Func_Close
;
219 if (!(_min_size
.width() < _max_size
.width() ||
220 _min_size
.height() < _max_size
.height())) {
221 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
222 _functions
&= ~(Func_Resize
| Func_Maximize
);
227 // normal windows retain all of the possible decorations and
228 // functionality, and are the only windows that you can fullscreen
229 _functions
|= Func_Fullscreen
;
233 // dialogs cannot be maximized
234 _decorations
&= ~Decor_Maximize
;
235 _functions
&= ~Func_Maximize
;
241 // these windows get less functionality
242 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
243 _functions
&= ~(Func_Iconify
| Func_Resize
);
249 // none of these windows are manipulated by the window manager
255 // Mwm Hints are applied subtractively to what has already been chosen for
256 // decor and functionality
257 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
258 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
259 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
260 _decorations
&= ~Decor_Border
;
261 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
262 _decorations
&= ~Decor_Handle
;
263 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
264 _decorations
&= ~Decor_Titlebar
;
265 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
266 _decorations
&= ~Decor_Iconify
;
267 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
268 _decorations
&= ~Decor_Maximize
;
272 if (_mwmhints
.flags
& MwmFlag_Functions
) {
273 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
274 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
275 _functions
&= ~Func_Resize
;
276 if (! (_mwmhints
.functions
& MwmFunc_Move
))
277 _functions
&= ~Func_Move
;
278 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
279 _functions
&= ~Func_Iconify
;
280 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
281 _functions
&= ~Func_Maximize
;
282 // dont let mwm hints kill the close button
283 //if (! (_mwmhints.functions & MwmFunc_Close))
284 // _functions &= ~Func_Close;
288 // can't maximize without moving/resizing
289 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
290 _functions
&= ~Func_Maximize
;
292 // finally, user specified disabled decorations are applied to subtract
294 if (_disabled_decorations
& Decor_Titlebar
)
295 _decorations
&= ~Decor_Titlebar
;
296 if (_disabled_decorations
& Decor_Handle
)
297 _decorations
&= ~Decor_Handle
;
298 if (_disabled_decorations
& Decor_Border
)
299 _decorations
&= ~Decor_Border
;
300 if (_disabled_decorations
& Decor_Iconify
)
301 _decorations
&= ~Decor_Iconify
;
302 if (_disabled_decorations
& Decor_Maximize
)
303 _decorations
&= ~Decor_Maximize
;
304 if (_disabled_decorations
& Decor_AllDesktops
)
305 _decorations
&= ~Decor_AllDesktops
;
306 if (_disabled_decorations
& Decor_Close
)
307 _decorations
&= ~Decor_Close
;
309 // if we don't have a titlebar, then we cannot shade!
310 if (!(_decorations
& Decor_Titlebar
))
311 _functions
&= ~Func_Shade
;
313 changeAllowedActions();
316 frame
->adjustSize(); // change the decors on the frame
317 frame
->adjustPosition(); // with more/less decorations, we may need to be
319 remaximize(); // with new decor, the window's maximized size may change
324 void Client::getMwmHints()
326 unsigned long num
= MwmHints::elements
;
327 unsigned long *hints
;
329 _mwmhints
.flags
= 0; // default to none
331 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
332 otk::Property::atoms
.motif_wm_hints
, &num
,
333 (unsigned long **)&hints
))
336 if (num
>= MwmHints::elements
) {
337 // retrieved the hints
338 _mwmhints
.flags
= hints
[0];
339 _mwmhints
.functions
= hints
[1];
340 _mwmhints
.decorations
= hints
[2];
347 void Client::getArea()
349 XWindowAttributes wattrib
;
352 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
353 assert(ret
!= BadWindow
);
355 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
356 _border_width
= wattrib
.border_width
;
360 void Client::getState()
362 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
363 _iconic
= _skip_taskbar
= _skip_pager
= false;
365 unsigned long *state
;
366 unsigned long num
= (unsigned) -1;
368 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
369 otk::Property::atoms
.atom
, &num
, &state
)) {
370 for (unsigned long i
= 0; i
< num
; ++i
) {
371 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
373 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
375 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
377 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
378 _skip_taskbar
= true;
379 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
381 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
383 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
385 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
398 void Client::getShaped()
402 if (otk::display
->shape()) {
407 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
409 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
410 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
417 void Client::calcLayer() {
420 if (_iconic
) l
= Layer_Icon
;
421 else if (_fullscreen
) l
= Layer_Fullscreen
;
422 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
423 else if (_type
== Type_Dock
) {
424 if (!_below
) l
= Layer_Top
;
425 else l
= Layer_Normal
;
427 else if (_above
) l
= Layer_Above
;
428 else if (_below
) l
= Layer_Below
;
429 else l
= Layer_Normal
;
435 if we don't have a frame, then we aren't mapped yet (and this would
438 openbox
->screen(_screen
)->raiseWindow(this);
444 void Client::updateProtocols()
449 _focus_notify
= false;
450 _delete_window
= false;
452 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
453 for (int i
= 0; i
< num_return
; ++i
) {
454 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
455 // this means we can request the window to close
456 _delete_window
= true;
457 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
458 // if this protocol is requested, then the window will be notified
459 // by the window manager whenever it receives focus
460 _focus_notify
= true;
467 void Client::updateNormalHints()
471 int oldgravity
= _gravity
;
476 _size_inc
= otk::Size(1, 1);
477 _base_size
= otk::Size(0, 0);
478 _min_size
= otk::Size(0, 0);
479 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
481 // get the hints from the window
482 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
483 _positioned
= (size
.flags
& (PPosition
|USPosition
));
485 if (size
.flags
& PWinGravity
) {
486 _gravity
= size
.win_gravity
;
488 // if the client has a frame, i.e. has already been mapped and is
489 // changing its gravity
490 if (frame
&& _gravity
!= oldgravity
) {
491 // move our idea of the client's position based on its new gravity
492 int x
= frame
->area().x(), y
= frame
->area().y();
493 frame
->frameGravity(x
, y
);
494 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
498 if (size
.flags
& PAspect
) {
499 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
500 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
503 if (size
.flags
& PMinSize
)
504 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
506 if (size
.flags
& PMaxSize
)
507 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
509 if (size
.flags
& PBaseSize
)
510 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
512 if (size
.flags
& PResizeInc
)
513 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
518 void Client::updateWMHints(bool initstate
)
522 // assume a window takes input if it doesnt specify
526 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
527 if (hints
->flags
& InputHint
)
528 _can_focus
= hints
->input
;
530 // only do this when initstate is true!
531 if (initstate
&& (hints
->flags
& StateHint
))
532 _iconic
= hints
->initial_state
== IconicState
;
534 if (hints
->flags
& XUrgencyHint
)
537 if (hints
->flags
& WindowGroupHint
) {
538 if (hints
->window_group
!= _group
) {
539 // XXX: remove from the old group if there was one
540 _group
= hints
->window_group
;
541 // XXX: do stuff with the group
552 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
553 (long)_window
, _urgent
? "ON" : "OFF");
555 // fire the urgent callback if we're mapped, otherwise, wait until after
563 void Client::updateTitle()
568 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
569 otk::Property::utf8
, &_title
)) {
571 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
572 otk::Property::ascii
, &_title
);
576 _title
= _("Unnamed Window");
579 frame
->adjustTitle();
583 void Client::updateIconTitle()
588 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
589 otk::Property::utf8
, &_icon_title
)) {
591 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
592 otk::Property::ascii
, &_icon_title
);
596 _icon_title
= _("Unnamed Window");
600 void Client::updateClass()
603 _app_name
= _app_class
= _role
= "";
605 otk::Property::StringVect v
;
606 unsigned long num
= 2;
608 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
609 otk::Property::ascii
, &num
, &v
)) {
610 if (num
> 0) _app_name
= v
[0].c_str();
611 if (num
> 1) _app_class
= v
[1].c_str();
616 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
617 otk::Property::ascii
, &num
, &v
)) {
618 if (num
> 0) _role
= v
[0].c_str();
623 void Client::updateStrut()
625 unsigned long num
= 4;
627 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
628 otk::Property::atoms
.cardinal
, &num
, &data
))
632 _strut
.left
= data
[0];
633 _strut
.right
= data
[1];
634 _strut
.top
= data
[2];
635 _strut
.bottom
= data
[3];
637 // updating here is pointless while we're being mapped cuz we're not in
638 // the screen's client list yet
640 openbox
->screen(_screen
)->updateStruts();
647 void Client::updateTransientFor()
652 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
653 t
!= _window
) { // cant be transient to itself!
654 c
= openbox
->findClient(t
);
655 assert(c
!= this); // if this happens then we need to check for it
657 if (!c
/*XXX: && _group*/) {
658 // not transient to a client, see if it is transient for a group
659 if (//t == _group->leader() ||
661 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
662 // window is a transient for its group!
663 // XXX: for now this is treated as non-transient.
664 // this needs to be fixed!
669 // if anything has changed...
670 if (c
!= _transient_for
) {
672 _transient_for
->_transients
.remove(this); // remove from old parent
675 _transient_for
->_transients
.push_back(this); // add to new parent
680 void Client::propertyHandler(const XPropertyEvent
&e
)
682 otk::EventHandler::propertyHandler(e
);
684 // validate cuz we query stuff off the client here
685 if (!validate()) return;
687 // compress changes to a single property into a single change
689 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
690 // XXX: it would be nice to compress ALL changes to a property, not just
691 // changes in a row without other props between.
692 if (ce
.xproperty
.atom
!= e
.atom
) {
693 XPutBackEvent(**otk::display
, &ce
);
698 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
700 setupDecorAndFunctions(); // normal hints can make a window non-resizable
701 } else if (e
.atom
== XA_WM_HINTS
)
703 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
704 updateTransientFor();
706 calcLayer(); // type may have changed, so update the layer
707 setupDecorAndFunctions();
709 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
710 e
.atom
== otk::Property::atoms
.wm_name
)
712 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
713 e
.atom
== otk::Property::atoms
.wm_icon_name
)
715 else if (e
.atom
== otk::Property::atoms
.wm_class
)
717 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
719 setupDecorAndFunctions();
721 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
726 void Client::setWMState(long state
)
728 if (state
== _wmstate
) return; // no change
741 void Client::setDesktop(unsigned int target
)
743 if (target
== _desktop
) return;
745 printf("Setting desktop %u\n", target
);
747 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
748 target
== 0xffffffff))
752 // set the desktop hint
753 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
754 otk::Property::atoms
.cardinal
, _desktop
);
755 frame
->adjustState(); // the frame can display the current desktop state
756 // 'move' the window to the new desktop
758 openbox
->screen(_screen
)->updateStruts();
762 void Client::showhide()
765 Screen
*s
= openbox
->screen(_screen
);
767 if (_iconic
) show
= false;
768 else if (!(_desktop
== s
->desktop() ||
769 _desktop
== 0xffffffff)) show
= false;
770 else if (normal() && s
->showingDesktop()) show
= false;
773 if (show
) frame
->show();
778 void Client::setState(StateAction action
, long data1
, long data2
)
780 bool shadestate
= _shaded
;
781 bool fsstate
= _fullscreen
;
782 bool maxh
= _max_horz
;
783 bool maxv
= _max_vert
;
785 if (!(action
== State_Add
|| action
== State_Remove
||
786 action
== State_Toggle
))
787 return; // an invalid action was passed to the client message, ignore it
789 for (int i
= 0; i
< 2; ++i
) {
790 Atom state
= i
== 0 ? data1
: data2
;
792 if (! state
) continue;
794 // if toggling, then pick whether we're adding or removing
795 if (action
== State_Toggle
) {
796 if (state
== otk::Property::atoms
.net_wm_state_modal
)
797 action
= _modal
? State_Remove
: State_Add
;
798 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
799 action
= _max_vert
? State_Remove
: State_Add
;
800 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
801 action
= _max_horz
? State_Remove
: State_Add
;
802 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
803 action
= _shaded
? State_Remove
: State_Add
;
804 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
805 action
= _skip_taskbar
? State_Remove
: State_Add
;
806 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
807 action
= _skip_pager
? State_Remove
: State_Add
;
808 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
809 action
= _fullscreen
? State_Remove
: State_Add
;
810 else if (state
== otk::Property::atoms
.net_wm_state_above
)
811 action
= _above
? State_Remove
: State_Add
;
812 else if (state
== otk::Property::atoms
.net_wm_state_below
)
813 action
= _below
? State_Remove
: State_Add
;
816 if (action
== State_Add
) {
817 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
818 if (_modal
) continue;
820 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
822 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
823 if (_max_horz
) continue;
825 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
827 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
828 _skip_taskbar
= true;
829 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
831 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
833 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
834 if (_above
) continue;
836 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
837 if (_below
) continue;
841 } else { // action == State_Remove
842 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
843 if (!_modal
) continue;
845 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
847 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
849 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
851 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
852 _skip_taskbar
= false;
853 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
855 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
857 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
858 if (!_above
) continue;
860 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
861 if (!_below
) continue;
866 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
867 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
868 if (maxh
== maxv
) { // both going the same way
869 maximize(maxh
, 0, true);
871 maximize(maxh
, 1, true);
872 maximize(maxv
, 2, true);
874 } else { // toggling one
875 if (maxh
!= _max_horz
)
876 maximize(maxh
, 1, true);
878 maximize(maxv
, 2, true);
881 // change fullscreen state before shading, as it will affect if the window
883 if (fsstate
!= _fullscreen
)
884 fullscreen(fsstate
, true);
885 if (shadestate
!= _shaded
)
888 changeState(); // change the hint to relect these changes
892 void Client::toggleClientBorder(bool addborder
)
894 // adjust our idea of where the client is, based on its border. When the
895 // border is removed, the client should now be considered to be in a
896 // different position.
897 // when re-adding the border to the client, the same operation needs to be
899 int oldx
= _area
.x(), oldy
= _area
.y();
900 int x
= oldx
, y
= oldy
;
903 case NorthWestGravity
:
905 case SouthWestGravity
:
907 case NorthEastGravity
:
909 case SouthEastGravity
:
910 if (addborder
) x
-= _border_width
* 2;
911 else x
+= _border_width
* 2;
918 if (addborder
) x
-= _border_width
;
919 else x
+= _border_width
;
924 case NorthWestGravity
:
926 case NorthEastGravity
:
928 case SouthWestGravity
:
930 case SouthEastGravity
:
931 if (addborder
) y
-= _border_width
* 2;
932 else y
+= _border_width
* 2;
939 if (addborder
) y
-= _border_width
;
940 else y
+= _border_width
;
943 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
946 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
948 // move the client so it is back it the right spot _with_ its border!
949 if (x
!= oldx
|| y
!= oldy
)
950 XMoveWindow(**otk::display
, _window
, x
, y
);
952 XSetWindowBorderWidth(**otk::display
, _window
, 0);
956 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
958 otk::EventHandler::clientMessageHandler(e
);
960 // validate cuz we query stuff off the client here
961 if (!validate()) return;
963 if (e
.format
!= 32) return;
965 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
966 // compress changes into a single change
967 bool compress
= false;
969 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
970 // XXX: it would be nice to compress ALL messages of a type, not just
971 // messages in a row without other message types between.
972 if (ce
.xclient
.message_type
!= e
.message_type
) {
973 XPutBackEvent(**otk::display
, &ce
);
979 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
981 setWMState(e
.data
.l
[0]); // use the original event
982 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
983 // compress changes into a single change
984 bool compress
= false;
986 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
987 // XXX: it would be nice to compress ALL messages of a type, not just
988 // messages in a row without other message types between.
989 if (ce
.xclient
.message_type
!= e
.message_type
) {
990 XPutBackEvent(**otk::display
, &ce
);
996 setDesktop(e
.data
.l
[0]); // use the found event
998 setDesktop(e
.data
.l
[0]); // use the original event
999 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1000 // can't compress these
1002 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1003 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1004 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1005 e
.data
.l
[1], e
.data
.l
[2], _window
);
1007 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1008 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1010 printf("net_close_window for 0x%lx\n", _window
);
1013 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1015 printf("net_active_window for 0x%lx\n", _window
);
1017 if (openbox
->screen(_screen
)->showingDesktop())
1018 openbox
->screen(_screen
)->showDesktop(false);
1021 else if (!frame
->visible()) // if its not visible for other reasons, then
1022 return; // don't mess with it
1026 openbox
->screen(_screen
)->raiseWindow(this);
1027 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1028 if (openbox
->screen(_screen
)->showingDesktop())
1029 openbox
->screen(_screen
)->showDesktop(false);
1032 else if (!frame
->visible()) // if its not visible for other reasons, then
1033 return; // don't mess with it
1034 if (e
.data
.l
[0] && _shaded
)
1038 openbox
->screen(_screen
)->raiseWindow(this);
1044 void Client::shapeHandler(const XShapeEvent
&e
)
1046 otk::EventHandler::shapeHandler(e
);
1048 if (e
.kind
== ShapeBounding
) {
1050 frame
->adjustShape();
1056 void Client::resize(Corner anchor
, int w
, int h
)
1058 if (!(_functions
& Func_Resize
)) return;
1059 internal_resize(anchor
, w
, h
);
1063 void Client::internal_resize(Corner anchor
, int w
, int h
,
1064 bool user
, int x
, int y
)
1066 w
-= _base_size
.width();
1067 h
-= _base_size
.height();
1070 // for interactive resizing. have to move half an increment in each
1072 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1073 int mh
= h
% _size_inc
.height();
1074 int aw
= _size_inc
.width() / 2; // amount to add
1075 int ah
= _size_inc
.height() / 2;
1076 // don't let us move into a new size increment
1077 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1078 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1082 // if this is a user-requested resize, then check against min/max sizes
1083 // and aspect ratios
1085 // smaller than min size or bigger than max size?
1086 if (w
> _max_size
.width()) w
= _max_size
.width();
1087 if (w
< _min_size
.width()) w
= _min_size
.width();
1088 if (h
> _max_size
.height()) h
= _max_size
.height();
1089 if (h
< _min_size
.height()) h
= _min_size
.height();
1091 // adjust the height ot match the width for the aspect ratios
1093 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1095 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1098 // keep to the increments
1099 w
/= _size_inc
.width();
1100 h
/= _size_inc
.height();
1102 // you cannot resize to nothing
1106 // store the logical size
1107 _logical_size
= otk::Size(w
, h
);
1109 w
*= _size_inc
.width();
1110 h
*= _size_inc
.height();
1112 w
+= _base_size
.width();
1113 h
+= _base_size
.height();
1115 if (x
== INT_MIN
|| y
== INT_MIN
) {
1122 x
-= w
- _area
.width();
1125 y
-= h
- _area
.height();
1128 x
-= w
- _area
.width();
1129 y
-= h
- _area
.height();
1134 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1136 XResizeWindow(**otk::display
, _window
, w
, h
);
1138 // resize the frame to match the request
1139 frame
->adjustSize();
1140 internal_move(x
, y
);
1144 void Client::move(int x
, int y
)
1146 if (!(_functions
& Func_Move
)) return;
1147 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1149 internal_move(x
, y
);
1153 void Client::internal_move(int x
, int y
)
1155 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1157 // move the frame to be in the requested position
1158 if (frame
) { // this can be called while mapping, before frame exists
1159 frame
->adjustPosition();
1161 // send synthetic configure notify (we don't need to if we aren't mapped
1164 event
.type
= ConfigureNotify
;
1165 event
.xconfigure
.display
= **otk::display
;
1166 event
.xconfigure
.event
= _window
;
1167 event
.xconfigure
.window
= _window
;
1169 // root window coords with border in mind
1170 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1171 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1173 event
.xconfigure
.width
= _area
.width();
1174 event
.xconfigure
.height
= _area
.height();
1175 event
.xconfigure
.border_width
= _border_width
;
1176 event
.xconfigure
.above
= frame
->plate();
1177 event
.xconfigure
.override_redirect
= False
;
1178 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1179 StructureNotifyMask
, &event
);
1181 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1182 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1183 event
.xconfigure
.height
, event
.xconfigure
.window
);
1189 void Client::close()
1193 if (!(_functions
& Func_Close
)) return;
1195 // XXX: itd be cool to do timeouts and shit here for killing the client's
1197 // like... if the window is around after 5 seconds, then the close button
1198 // turns a nice red, and if this function is called again, the client is
1199 // explicitly killed.
1201 ce
.xclient
.type
= ClientMessage
;
1202 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1203 ce
.xclient
.display
= **otk::display
;
1204 ce
.xclient
.window
= _window
;
1205 ce
.xclient
.format
= 32;
1206 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1207 ce
.xclient
.data
.l
[1] = CurrentTime
;
1208 ce
.xclient
.data
.l
[2] = 0l;
1209 ce
.xclient
.data
.l
[3] = 0l;
1210 ce
.xclient
.data
.l
[4] = 0l;
1211 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1215 void Client::changeState()
1217 unsigned long state
[2];
1218 state
[0] = _wmstate
;
1220 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1221 otk::Property::atoms
.wm_state
, state
, 2);
1226 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1228 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1230 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1232 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1234 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1236 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1238 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1240 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1242 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1244 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1245 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1246 otk::Property::atoms
.atom
, netstate
, num
);
1251 frame
->adjustState();
1255 void Client::changeAllowedActions(void)
1260 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1262 if (_functions
& Func_Shade
)
1263 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1264 if (_functions
& Func_Close
)
1265 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1266 if (_functions
& Func_Move
)
1267 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1268 if (_functions
& Func_Iconify
)
1269 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1270 if (_functions
& Func_Resize
)
1271 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1272 if (_functions
& Func_Fullscreen
)
1273 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1274 if (_functions
& Func_Maximize
) {
1275 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1276 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1279 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1280 otk::Property::atoms
.atom
, actions
, num
);
1282 // make sure the window isn't breaking any rules now
1284 if (!(_functions
& Func_Shade
) && _shaded
)
1285 if (frame
) shade(false);
1286 else _shaded
= false;
1287 if (!(_functions
& Func_Iconify
) && _iconic
)
1288 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1289 else _iconic
= false;
1290 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1291 if (frame
) fullscreen(false);
1292 else _fullscreen
= false;
1293 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1294 if (frame
) maximize(false, 0);
1295 else _max_vert
= _max_horz
= false;
1299 void Client::remaximize()
1302 if (_max_horz
&& _max_vert
)
1309 return; // not maximized
1310 _max_horz
= _max_vert
= false;
1311 maximize(true, dir
, false);
1315 void Client::applyStartupState()
1317 // these are in a carefully crafted order..
1324 _fullscreen
= false;
1325 fullscreen(true, false);
1334 if (_max_vert
&& _max_horz
) {
1335 _max_vert
= _max_horz
= false;
1336 maximize(true, 0, false);
1337 } else if (_max_vert
) {
1339 maximize(true, 2, false);
1340 } else if (_max_horz
) {
1342 maximize(true, 1, false);
1345 if (_skip_taskbar
); // nothing to do for this
1346 if (_skip_pager
); // nothing to do for this
1347 if (_modal
); // nothing to do for this
1348 if (_above
); // nothing to do for this
1349 if (_below
); // nothing to do for this
1353 void Client::fireUrgent()
1355 // call the python UrgentWindow callbacks
1356 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1357 openbox
->bindings()->fireEvent(&data
);
1361 void Client::shade(bool shade
)
1363 if (!(_functions
& Func_Shade
) || // can't
1364 _shaded
== shade
) return; // already done
1366 // when we're iconic, don't change the wmstate
1368 _wmstate
= shade
? IconicState
: NormalState
;
1371 frame
->adjustSize();
1375 void Client::maximize(bool max
, int dir
, bool savearea
)
1377 assert(dir
== 0 || dir
== 1 || dir
== 2);
1378 if (!(_functions
& Func_Maximize
)) return; // can't
1380 // check if already done
1382 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1383 if (dir
== 1 && _max_horz
) return;
1384 if (dir
== 2 && _max_vert
) return;
1386 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1387 if (dir
== 1 && !_max_horz
) return;
1388 if (dir
== 2 && !_max_vert
) return;
1391 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1392 int x
= frame
->area().x(), y
= frame
->area().y(),
1393 w
= _area
.width(), h
= _area
.height();
1399 unsigned long n
= 4;
1406 // get the property off the window and use it for the dimentions we are
1408 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1409 otk::Property::atoms
.cardinal
, &n
,
1410 (long unsigned**) &readdim
)) {
1413 dimensions
[0] = readdim
[0];
1414 dimensions
[2] = readdim
[2];
1417 dimensions
[1] = readdim
[1];
1418 dimensions
[3] = readdim
[3];
1424 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1425 otk::Property::atoms
.cardinal
,
1426 (long unsigned*)dimensions
, 4);
1428 if (dir
== 0 || dir
== 1) { // horz
1432 if (dir
== 0 || dir
== 2) { // vert
1434 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1438 long unsigned n
= 4;
1440 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1441 otk::Property::atoms
.cardinal
, &n
,
1442 (long unsigned**) &dimensions
)) {
1444 if (dir
== 0 || dir
== 1) { // horz
1445 x
= (signed int)dimensions
[0];
1446 w
= (signed int)dimensions
[2];
1448 if (dir
== 0 || dir
== 2) { // vert
1449 y
= (signed int)dimensions
[1];
1450 h
= (signed int)dimensions
[3];
1455 // pick some fallbacks...
1456 if (dir
== 0 || dir
== 1) { // horz
1457 x
= a
.x() + a
.width() / 4;
1460 if (dir
== 0 || dir
== 2) { // vert
1461 y
= a
.y() + a
.height() / 4;
1467 if (dir
== 0 || dir
== 1) // horz
1469 if (dir
== 0 || dir
== 2) // vert
1472 if (!_max_horz
&& !_max_vert
)
1473 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1475 changeState(); // change the state hints on the client
1477 frame
->frameGravity(x
, y
); // figure out where the client should be going
1478 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1482 void Client::fullscreen(bool fs
, bool savearea
)
1484 static FunctionFlags saved_func
;
1485 static DecorationFlags saved_decor
;
1487 if (!(_functions
& Func_Fullscreen
) || // can't
1488 _fullscreen
== fs
) return; // already done
1491 changeState(); // change the state hints on the client
1493 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1496 // save the functions and remove them
1497 saved_func
= _functions
;
1498 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1499 // save the decorations and remove them
1500 saved_decor
= _decorations
;
1504 dimensions
[0] = _area
.x();
1505 dimensions
[1] = _area
.y();
1506 dimensions
[2] = _area
.width();
1507 dimensions
[3] = _area
.height();
1508 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1509 otk::Property::atoms
.cardinal
,
1510 (long unsigned*)dimensions
, 4);
1512 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1515 w
= info
->size().width();
1516 h
= info
->size().height();
1518 _functions
= saved_func
;
1519 _decorations
= saved_decor
;
1522 long unsigned n
= 4;
1524 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1525 otk::Property::atoms
.cardinal
, &n
,
1526 (long unsigned**) &dimensions
)) {
1535 // pick some fallbacks...
1536 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1537 x
= a
.x() + a
.width() / 4;
1538 y
= a
.y() + a
.height() / 4;
1544 changeAllowedActions(); // based on the new _functions
1546 // when fullscreening, don't obey things like increments, fill the screen
1547 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1549 // raise (back) into our stacking layer
1550 openbox
->screen(_screen
)->raiseWindow(this);
1552 // try focus us when we go into fullscreen mode
1557 void Client::iconify(bool iconic
, bool curdesk
)
1559 if (_iconic
== iconic
) return; // nothing to do
1562 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1568 _wmstate
= IconicState
;
1570 // we unmap the client itself so that we can get MapRequest events, and
1571 // because the ICCCM tells us to!
1572 XUnmapWindow(**otk::display
, _window
);
1575 setDesktop(openbox
->screen(_screen
)->desktop());
1576 _wmstate
= NormalState
;
1577 XMapWindow(**otk::display
, _window
);
1581 openbox
->screen(_screen
)->updateStruts();
1585 void Client::disableDecorations(DecorationFlags flags
)
1587 _disabled_decorations
= flags
;
1588 setupDecorAndFunctions();
1592 void Client::installColormap(bool install
) const
1594 XWindowAttributes wa
;
1595 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1597 XInstallColormap(**otk::display
, wa
.colormap
);
1599 XUninstallColormap(**otk::display
, wa
.colormap
);
1604 // recursively searches the client 'tree' for a modal client, always skips the
1605 // topmost node (the window you're starting with)
1606 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1608 List::const_iterator it
, end
= node
->_transients
.end();
1611 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1612 if (*it
== skip
) continue; // circular?
1613 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1614 if ((*it
)->_modal
) return *it
; // got one
1619 Client
*Client::findModalChild()
1621 return searchModalTree(this, this);
1625 bool Client::focus()
1627 // if we have a modal child, then focus it, not us
1628 Client
*c
= findModalChild();
1629 if (c
) return c
->focus();
1631 // won't try focus if the client doesn't want it, or if the window isn't
1632 // visible on the screen
1633 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1635 if (_focused
) return true;
1637 // do a check to see if the window has already been unmapped or destroyed
1638 // do this intelligently while watching out for unmaps we've generated
1639 // (ignore_unmaps > 0)
1641 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1642 XPutBackEvent(**otk::display
, &ev
);
1645 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1646 if (ignore_unmaps
) {
1647 unmapHandler(ev
.xunmap
);
1649 XPutBackEvent(**otk::display
, &ev
);
1655 XSetInputFocus(**otk::display
, _window
,
1656 RevertToNone
, CurrentTime
);
1658 if (_focus_notify
) {
1660 ce
.xclient
.type
= ClientMessage
;
1661 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1662 ce
.xclient
.display
= **otk::display
;
1663 ce
.xclient
.window
= _window
;
1664 ce
.xclient
.format
= 32;
1665 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1666 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1667 ce
.xclient
.data
.l
[2] = 0l;
1668 ce
.xclient
.data
.l
[3] = 0l;
1669 ce
.xclient
.data
.l
[4] = 0l;
1670 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1673 XSync(**otk::display
, False
);
1678 void Client::unfocus() const
1680 if (!_focused
) return;
1682 assert(openbox
->focusedClient() == this);
1683 openbox
->setFocusedClient(0);
1687 void Client::focusHandler(const XFocusChangeEvent
&e
)
1690 // printf("FocusIn for 0x%lx\n", e.window);
1693 otk::EventHandler::focusHandler(e
);
1696 frame
->adjustFocus();
1698 openbox
->setFocusedClient(this);
1702 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1705 // printf("FocusOut for 0x%lx\n", e.window);
1708 otk::EventHandler::unfocusHandler(e
);
1711 frame
->adjustFocus();
1713 if (openbox
->focusedClient() == this)
1714 openbox
->setFocusedClient(0);
1718 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1721 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1724 otk::EventHandler::configureRequestHandler(ec
);
1727 XConfigureRequestEvent e
= ec
;
1729 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1731 // XXX if this causes bad things.. we can compress config req's with the
1733 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1734 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1735 e
.x
= ev
.xconfigurerequest
.x
;
1736 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1737 e
.y
= ev
.xconfigurerequest
.y
;
1738 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1739 e
.width
= ev
.xconfigurerequest
.width
;
1740 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1741 e
.height
= ev
.xconfigurerequest
.height
;
1742 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1743 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1744 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1745 e
.detail
= ev
.xconfigurerequest
.detail
;
1748 // if we are iconic (or shaded (fvwm does this)) ignore the event
1749 if (_iconic
|| _shaded
) return;
1751 if (e
.value_mask
& CWBorderWidth
)
1752 _border_width
= e
.border_width
;
1754 // resize, then move, as specified in the EWMH section 7.7
1755 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1756 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1757 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1761 case NorthEastGravity
:
1765 case SouthWestGravity
:
1767 corner
= BottomLeft
;
1769 case SouthEastGravity
:
1770 corner
= BottomRight
;
1772 default: // NorthWest, Static, etc
1776 // if moving AND resizing ...
1777 if (e
.value_mask
& (CWX
| CWY
)) {
1778 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1779 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1780 internal_resize(corner
, w
, h
, false, x
, y
);
1781 } else // if JUST resizing...
1782 internal_resize(corner
, w
, h
, false);
1783 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1784 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1785 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1786 internal_move(x
, y
);
1789 if (e
.value_mask
& CWStackMode
) {
1793 openbox
->screen(_screen
)->lowerWindow(this);
1799 openbox
->screen(_screen
)->raiseWindow(this);
1806 void Client::unmapHandler(const XUnmapEvent
&e
)
1808 if (ignore_unmaps
) {
1810 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1817 printf("UnmapNotify for 0x%lx\n", e
.window
);
1820 otk::EventHandler::unmapHandler(e
);
1822 // this deletes us etc
1823 openbox
->screen(_screen
)->unmanageWindow(this);
1827 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1830 printf("DestroyNotify for 0x%lx\n", e
.window
);
1833 otk::EventHandler::destroyHandler(e
);
1835 // this deletes us etc
1836 openbox
->screen(_screen
)->unmanageWindow(this);
1840 void Client::reparentHandler(const XReparentEvent
&e
)
1842 // this is when the client is first taken captive in the frame
1843 if (e
.parent
== frame
->plate()) return;
1846 printf("ReparentNotify for 0x%lx\n", e
.window
);
1849 otk::EventHandler::reparentHandler(e
);
1852 This event is quite rare and is usually handled in unmapHandler.
1853 However, if the window is unmapped when the reparent event occurs,
1854 the window manager never sees it because an unmap event is not sent
1855 to an already unmapped window.
1858 // we don't want the reparent event, put it back on the stack for the X
1859 // server to deal with after we unmanage the window
1862 XPutBackEvent(**otk::display
, &ev
);
1864 // this deletes us etc
1865 openbox
->screen(_screen
)->unmanageWindow(this);
1868 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1871 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1874 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1876 // move to the current desktop (uniconify)
1878 // XXX: should we focus/raise the window? (basically a net_wm_active_window)