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)
22 #include <cstring> // for memcpy
29 Client::Client(int screen
, Window window
)
30 : otk::EventHandler(),
31 frame(0), _screen(screen
), _window(window
)
38 // update EVERYTHING the first time!!
41 _wmstate
= NormalState
;
44 _layer
= Layer_Normal
;
47 _disabled_decorations
= 0;
54 getState(); // do this before updateTransientFor! (for _modal)
59 getType(); // this can change the mwmhints for special cases
63 getGravity(); // get the attribute gravity
64 updateNormalHints(); // this may override the attribute gravity
66 // got the type, the mwmhints, the protocols, and the normal hints (min/max
67 // sizes), so we're ready to set up
68 // the decorations/functions
69 setupDecorAndFunctions();
71 // also get the initial_state and set _iconic if we aren't "starting"
72 // when we're "starting" that means we should use whatever state was already
73 // on the window over the initial map state, because it was already mapped
74 updateWMHints(openbox
->state() != Openbox::State_Starting
);
82 // this makes sure that these windows appear on all desktops
83 if (/*_type == Type_Dock ||*/ _type
== Type_Desktop
)
84 _desktop
= 0xffffffff;
86 // set the desktop hint, to make sure that it always exists, and to reflect
87 // any changes we've made here
88 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
89 otk::Property::atoms
.cardinal
, (unsigned)_desktop
);
96 assert(_nicons
> 0); // there should always be a default..
97 for (int j
= 0; j
< _nicons
; ++j
)
98 delete [] _icons
[j
].data
;
101 // clean up childrens' references
102 while (!_transients
.empty()) {
103 _transients
.front()->_transient_for
= 0;
104 _transients
.pop_front();
107 // clean up parents reference to this
109 _transient_for
->_transients
.remove(this); // remove from old parent
111 if (openbox
->state() != Openbox::State_Exiting
) {
112 // these values should not be persisted across a window unmapping/mapping
113 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_desktop
);
114 otk::Property::erase(_window
, otk::Property::atoms
.net_wm_state
);
116 // if we're left in an iconic state, the client wont be mapped. this is
117 // bad, since we will no longer be managing the window on restart
119 XMapWindow(**otk::display
, _window
);
124 bool Client::validate() const
126 XSync(**otk::display
, false); // get all events on the server
129 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &e
) ||
130 XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &e
)) {
131 XPutBackEvent(**otk::display
, &e
);
139 void Client::getGravity()
141 XWindowAttributes wattrib
;
144 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
145 assert(ret
!= BadWindow
);
146 _gravity
= wattrib
.win_gravity
;
150 void Client::getDesktop()
152 // defaults to the current desktop
153 _desktop
= openbox
->screen(_screen
)->desktop();
156 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
157 otk::Property::atoms
.cardinal
,
158 (long unsigned*)&d
)) {
159 if (d
>= openbox
->screen(_screen
)->numDesktops() &&
161 d
= openbox
->screen(_screen
)->numDesktops() - 1;
164 // printf("Window requested desktop: %ld\n", _desktop);
170 void Client::getType()
172 _type
= (WindowType
) -1;
176 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
177 otk::Property::atoms
.atom
, &num
, &val
)) {
178 // use the first value that we know about in the array
179 for (unsigned long i
= 0; i
< num
; ++i
) {
180 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
181 _type
= Type_Desktop
;
182 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
184 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
185 _type
= Type_Toolbar
;
186 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
188 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
189 _type
= Type_Utility
;
190 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
192 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
194 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
196 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
197 // prevent this window from getting any decor or functionality
198 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
199 _mwmhints
.decorations
= 0;
200 _mwmhints
.functions
= 0;
202 if (_type
!= (WindowType
) -1)
203 break; // grab the first known type
208 if (_type
== (WindowType
) -1) {
210 * the window type hint was not set, which means we either classify ourself
211 * as a normal window or a dialog, depending on if we are a transient.
221 void Client::setupDecorAndFunctions()
223 // start with everything (cept fullscreen)
224 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
| Decor_Icon
|
225 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
226 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
228 if (_delete_window
) {
229 _decorations
|= Decor_Close
;
230 _functions
|= Func_Close
;
233 if (!(_min_size
.width() < _max_size
.width() ||
234 _min_size
.height() < _max_size
.height())) {
235 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
236 _functions
&= ~(Func_Resize
| Func_Maximize
);
241 // normal windows retain all of the possible decorations and
242 // functionality, and are the only windows that you can fullscreen
243 _functions
|= Func_Fullscreen
;
247 // dialogs cannot be maximized
248 _decorations
&= ~Decor_Maximize
;
249 _functions
&= ~Func_Maximize
;
255 // these windows get less functionality
256 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
257 _functions
&= ~(Func_Iconify
| Func_Resize
);
263 // none of these windows are manipulated by the window manager
269 // Mwm Hints are applied subtractively to what has already been chosen for
270 // decor and functionality
271 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
272 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
273 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
274 _decorations
&= ~Decor_Border
;
275 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
276 _decorations
&= ~Decor_Handle
;
277 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
278 _decorations
&= ~Decor_Titlebar
;
279 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
280 _decorations
&= ~Decor_Iconify
;
281 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
282 _decorations
&= ~Decor_Maximize
;
286 if (_mwmhints
.flags
& MwmFlag_Functions
) {
287 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
288 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
289 _functions
&= ~Func_Resize
;
290 if (! (_mwmhints
.functions
& MwmFunc_Move
))
291 _functions
&= ~Func_Move
;
292 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
293 _functions
&= ~Func_Iconify
;
294 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
295 _functions
&= ~Func_Maximize
;
296 // dont let mwm hints kill the close button
297 //if (! (_mwmhints.functions & MwmFunc_Close))
298 // _functions &= ~Func_Close;
302 // can't maximize without moving/resizing
303 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
304 _functions
&= ~Func_Maximize
;
306 // finally, user specified disabled decorations are applied to subtract
308 if (_disabled_decorations
& Decor_Titlebar
)
309 _decorations
&= ~Decor_Titlebar
;
310 if (_disabled_decorations
& Decor_Handle
)
311 _decorations
&= ~Decor_Handle
;
312 if (_disabled_decorations
& Decor_Border
)
313 _decorations
&= ~Decor_Border
;
314 if (_disabled_decorations
& Decor_Iconify
)
315 _decorations
&= ~Decor_Iconify
;
316 if (_disabled_decorations
& Decor_Maximize
)
317 _decorations
&= ~Decor_Maximize
;
318 if (_disabled_decorations
& Decor_AllDesktops
)
319 _decorations
&= ~Decor_AllDesktops
;
320 if (_disabled_decorations
& Decor_Close
)
321 _decorations
&= ~Decor_Close
;
323 // if we don't have a titlebar, then we cannot shade!
324 if (!(_decorations
& Decor_Titlebar
))
325 _functions
&= ~Func_Shade
;
327 changeAllowedActions();
330 frame
->adjustSize(); // change the decors on the frame
331 frame
->adjustPosition(); // with more/less decorations, we may need to be
333 remaximize(); // with new decor, the window's maximized size may change
338 void Client::getMwmHints()
340 unsigned long num
= MwmHints::elements
;
341 unsigned long *hints
;
343 _mwmhints
.flags
= 0; // default to none
345 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
346 otk::Property::atoms
.motif_wm_hints
, &num
,
347 (unsigned long **)&hints
))
350 if (num
>= MwmHints::elements
) {
351 // retrieved the hints
352 _mwmhints
.flags
= hints
[0];
353 _mwmhints
.functions
= hints
[1];
354 _mwmhints
.decorations
= hints
[2];
361 void Client::getArea()
363 XWindowAttributes wattrib
;
366 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
367 assert(ret
!= BadWindow
);
369 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
370 _border_width
= wattrib
.border_width
;
374 void Client::getState()
376 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
377 _iconic
= _skip_taskbar
= _skip_pager
= false;
379 unsigned long *state
;
382 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
383 otk::Property::atoms
.atom
, &num
, &state
)) {
384 for (unsigned long i
= 0; i
< num
; ++i
) {
385 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
391 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
392 _skip_taskbar
= true;
393 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
395 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
397 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
399 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
401 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
403 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
411 void Client::getShaped()
415 if (otk::display
->shape()) {
420 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
422 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
423 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
429 Client
*Client::searchFocusTree(Client
*node
, Client
*skip
)
431 List::const_iterator it
, end
= node
->_transients
.end();
434 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
435 if (*it
== skip
) continue; // circular?
436 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
437 if ((*it
)->_focused
) return *it
; // got one
442 void Client::calcLayer() {
446 // are we fullscreen, or do we have a fullscreen transient parent?
449 if (c
->_fullscreen
) {
453 c
= c
->_transient_for
;
455 if (!fs
&& _fullscreen
) {
456 // is one of our transients focused?
457 c
= searchFocusTree(this, this);
461 if (_iconic
) l
= Layer_Icon
;
462 else if (fs
) l
= Layer_Fullscreen
;
463 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
464 else if (_type
== Type_Dock
) {
465 if (!_below
) l
= Layer_Top
;
466 else l
= Layer_Normal
;
468 else if (_above
) l
= Layer_Above
;
469 else if (_below
) l
= Layer_Below
;
470 else l
= Layer_Normal
;
476 if we don't have a frame, then we aren't mapped yet (and this would
479 openbox
->screen(_screen
)->raiseWindow(this);
484 void Client::updateProtocols()
489 _focus_notify
= false;
490 _delete_window
= false;
492 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
493 for (int i
= 0; i
< num_return
; ++i
) {
494 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
495 // this means we can request the window to close
496 _delete_window
= true;
497 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
498 // if this protocol is requested, then the window will be notified
499 // by the window manager whenever it receives focus
500 _focus_notify
= true;
506 void Client::updateNormalHints()
510 int oldgravity
= _gravity
;
515 _size_inc
= otk::Size(1, 1);
516 _base_size
= otk::Size(0, 0);
517 _min_size
= otk::Size(0, 0);
518 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
520 // get the hints from the window
521 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
522 _positioned
= (size
.flags
& (PPosition
|USPosition
));
524 if (size
.flags
& PWinGravity
) {
525 _gravity
= size
.win_gravity
;
527 // if the client has a frame, i.e. has already been mapped and is
528 // changing its gravity
529 if (frame
&& _gravity
!= oldgravity
) {
530 // move our idea of the client's position based on its new gravity
531 int x
= frame
->area().x(), y
= frame
->area().y();
532 frame
->frameGravity(x
, y
);
533 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
537 if (size
.flags
& PAspect
) {
538 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
539 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
542 if (size
.flags
& PMinSize
)
543 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
545 if (size
.flags
& PMaxSize
)
546 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
548 if (size
.flags
& PBaseSize
)
549 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
551 if (size
.flags
& PResizeInc
)
552 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
556 void Client::updateWMHints(bool initstate
)
560 // assume a window takes input if it doesnt specify
564 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
565 if (hints
->flags
& InputHint
)
566 _can_focus
= hints
->input
;
568 // only do this when initstate is true!
569 if (initstate
&& (hints
->flags
& StateHint
))
570 _iconic
= hints
->initial_state
== IconicState
;
572 if (hints
->flags
& XUrgencyHint
)
575 if (hints
->flags
& WindowGroupHint
) {
576 if (hints
->window_group
!= _group
) {
577 // XXX: remove from the old group if there was one
578 _group
= hints
->window_group
;
579 // XXX: do stuff with the group
584 if (hints
->flags
& IconPixmapHint
) {
585 updateKwmIcon(); // try get the kwm icon first, this is a fallback only
586 if (_pixmap_icon
== None
) {
587 _pixmap_icon
= hints
->icon_pixmap
;
588 if (hints
->flags
& IconMaskHint
)
589 _pixmap_icon_mask
= hints
->icon_mask
;
591 _pixmap_icon_mask
= None
;
601 printf("Urgent Hint for 0x%lx: %s\n",
602 (long)_window
, _urgent
? "ON" : "OFF");
604 // fire the urgent callback if we're mapped, otherwise, wait until after
611 void Client::updateTitle()
616 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
617 otk::Property::utf8
, &_title
)) {
619 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
620 otk::Property::ascii
, &_title
);
624 _title
= _("Unnamed Window");
627 frame
->adjustTitle();
630 void Client::updateIconTitle()
635 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
636 otk::Property::utf8
, &_icon_title
)) {
638 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
639 otk::Property::ascii
, &_icon_title
);
643 _icon_title
= _("Unnamed Window");
646 void Client::updateClass()
649 _app_name
= _app_class
= _role
= "";
651 otk::Property::StringVect v
;
652 unsigned long num
= 2;
654 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
655 otk::Property::ascii
, &num
, &v
)) {
656 if (num
> 0) _app_name
= v
[0].c_str();
657 if (num
> 1) _app_class
= v
[1].c_str();
662 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
663 otk::Property::ascii
, &num
, &v
)) {
664 if (num
> 0) _role
= v
[0].c_str();
668 void Client::updateStrut()
670 unsigned long num
= 4;
672 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
673 otk::Property::atoms
.cardinal
, &num
, &data
))
677 _strut
.left
= data
[0];
678 _strut
.right
= data
[1];
679 _strut
.top
= data
[2];
680 _strut
.bottom
= data
[3];
682 // updating here is pointless while we're being mapped cuz we're not in
683 // the screen's client list yet
685 openbox
->screen(_screen
)->updateStruts();
691 void Client::updateTransientFor()
696 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
697 t
!= _window
) { // cant be transient to itself!
698 c
= openbox
->findClient(t
);
699 assert(c
!= this); // if this happens then we need to check for it
701 if (!c
/*XXX: && _group*/) {
702 // not transient to a client, see if it is transient for a group
703 if (//t == _group->leader() ||
705 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
706 // window is a transient for its group!
707 // XXX: for now this is treated as non-transient.
708 // this needs to be fixed!
713 // if anything has changed...
714 if (c
!= _transient_for
) {
716 _transient_for
->_transients
.remove(this); // remove from old parent
719 _transient_for
->_transients
.push_back(this); // add to new parent
723 void Client::updateIcons()
727 unsigned long w
, h
, i
= 0;
729 for (int j
= 0; j
< _nicons
; ++j
)
730 delete [] _icons
[j
].data
;
735 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon
,
736 otk::Property::atoms
.cardinal
, &num
, &data
)) {
737 // figure out how man valid icons are in here
738 while (num
- i
> 2) {
746 _icons
= new Icon
[_nicons
];
750 for (int j
= 0; j
< _nicons
; ++j
) {
751 w
= _icons
[j
].w
= data
[i
++];
752 h
= _icons
[j
].h
= data
[i
++];
753 _icons
[j
].data
= new unsigned long[w
* h
];
754 ::memcpy(_icons
[j
].data
, &data
[i
], w
* h
* sizeof(unsigned long));
764 _icons
= new Icon
[1];
770 assert(_nicons
> 0); // there should always be a default..
772 if (frame
) frame
->adjustIcon();
775 void Client::updateKwmIcon()
777 _pixmap_icon
= _pixmap_icon_mask
= None
;
779 unsigned long num
= 2;
781 if (otk::Property::get(_window
, otk::Property::atoms
.kwm_win_icon
,
782 otk::Property::atoms
.kwm_win_icon
, &num
, &data
)) {
784 _pixmap_icon
= data
[0];
785 _pixmap_icon_mask
= data
[1];
791 void Client::propertyHandler(const XPropertyEvent
&e
)
793 otk::EventHandler::propertyHandler(e
);
795 // validate cuz we query stuff off the client here
796 if (!validate()) return;
798 // compress changes to a single property into a single change
800 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
801 // XXX: it would be nice to compress ALL changes to a property, not just
802 // changes in a row without other props between.
803 if (ce
.xproperty
.atom
!= e
.atom
) {
804 XPutBackEvent(**otk::display
, &ce
);
809 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
811 setupDecorAndFunctions(); // normal hints can make a window non-resizable
812 } else if (e
.atom
== XA_WM_HINTS
)
814 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
815 updateTransientFor();
817 calcLayer(); // type may have changed, so update the layer
818 setupDecorAndFunctions();
820 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
821 e
.atom
== otk::Property::atoms
.wm_name
)
823 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
824 e
.atom
== otk::Property::atoms
.wm_icon_name
)
826 else if (e
.atom
== otk::Property::atoms
.wm_class
)
828 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
830 setupDecorAndFunctions();
832 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
834 else if (e
.atom
== otk::Property::atoms
.net_wm_icon
)
836 else if (e
.atom
== otk::Property::atoms
.kwm_win_icon
)
840 void Client::setWMState(long state
)
842 if (state
== _wmstate
) return; // no change
854 void Client::setDesktop(unsigned int target
)
856 if (target
== _desktop
) return;
858 printf("Setting desktop %u\n", target
);
860 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
861 target
== 0xffffffff))
865 // set the desktop hint
866 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
867 otk::Property::atoms
.cardinal
, _desktop
);
868 frame
->adjustState(); // the frame can display the current desktop state
869 // 'move' the window to the new desktop
871 openbox
->screen(_screen
)->updateStruts();
874 void Client::showhide()
877 Screen
*s
= openbox
->screen(_screen
);
879 if (_iconic
) show
= false;
880 else if (!(_desktop
== s
->desktop() ||
881 _desktop
== 0xffffffff)) show
= false;
882 else if (normal() && s
->showingDesktop()) show
= false;
885 if (show
) frame
->show();
889 void Client::setState(Atom action
, long data1
, long data2
)
891 bool shadestate
= _shaded
;
892 bool fsstate
= _fullscreen
;
893 bool maxh
= _max_horz
;
894 bool maxv
= _max_vert
;
896 if (!(action
== otk::Property::atoms
.net_wm_state_add
||
897 action
== otk::Property::atoms
.net_wm_state_remove
||
898 action
== otk::Property::atoms
.net_wm_state_toggle
))
899 return; // an invalid action was passed to the client message, ignore it
901 for (int i
= 0; i
< 2; ++i
) {
902 Atom state
= i
== 0 ? data1
: data2
;
904 if (! state
) continue;
906 // if toggling, then pick whether we're adding or removing
907 if (action
== otk::Property::atoms
.net_wm_state_toggle
) {
908 if (state
== otk::Property::atoms
.net_wm_state_modal
)
909 action
= _modal
? otk::Property::atoms
.net_wm_state_remove
:
910 otk::Property::atoms
.net_wm_state_add
;
911 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
912 action
= _max_vert
? otk::Property::atoms
.net_wm_state_remove
:
913 otk::Property::atoms
.net_wm_state_add
;
914 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
915 action
= _max_horz
? otk::Property::atoms
.net_wm_state_remove
:
916 otk::Property::atoms
.net_wm_state_add
;
917 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
918 action
= _shaded
? otk::Property::atoms
.net_wm_state_remove
:
919 otk::Property::atoms
.net_wm_state_add
;
920 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
921 action
= _skip_taskbar
? otk::Property::atoms
.net_wm_state_remove
:
922 otk::Property::atoms
.net_wm_state_add
;
923 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
924 action
= _skip_pager
? otk::Property::atoms
.net_wm_state_remove
:
925 otk::Property::atoms
.net_wm_state_add
;
926 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
927 action
= _fullscreen
? otk::Property::atoms
.net_wm_state_remove
:
928 otk::Property::atoms
.net_wm_state_add
;
929 else if (state
== otk::Property::atoms
.net_wm_state_above
)
930 action
= _above
? otk::Property::atoms
.net_wm_state_remove
:
931 otk::Property::atoms
.net_wm_state_add
;
932 else if (state
== otk::Property::atoms
.net_wm_state_below
)
933 action
= _below
? otk::Property::atoms
.net_wm_state_remove
:
934 otk::Property::atoms
.net_wm_state_add
;
937 if (action
== otk::Property::atoms
.net_wm_state_add
) {
938 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
939 if (_modal
) continue;
941 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
943 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
944 if (_max_horz
) continue;
946 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
948 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
949 _skip_taskbar
= true;
950 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
952 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
954 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
955 if (_above
) continue;
957 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
958 if (_below
) continue;
962 } else { // action == otk::Property::atoms.net_wm_state_remove
963 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
964 if (!_modal
) continue;
966 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
968 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
970 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
972 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
973 _skip_taskbar
= false;
974 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
976 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
978 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
979 if (!_above
) continue;
981 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
982 if (!_below
) continue;
987 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
988 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
989 if (maxh
== maxv
) { // both going the same way
990 maximize(maxh
, 0, true);
992 maximize(maxh
, 1, true);
993 maximize(maxv
, 2, true);
995 } else { // toggling one
996 if (maxh
!= _max_horz
)
997 maximize(maxh
, 1, true);
999 maximize(maxv
, 2, true);
1002 // change fullscreen state before shading, as it will affect if the window
1004 if (fsstate
!= _fullscreen
)
1005 fullscreen(fsstate
, true);
1006 if (shadestate
!= _shaded
)
1009 changeState(); // change the hint to relect these changes
1012 void Client::toggleClientBorder(bool addborder
)
1014 // adjust our idea of where the client is, based on its border. When the
1015 // border is removed, the client should now be considered to be in a
1016 // different position.
1017 // when re-adding the border to the client, the same operation needs to be
1019 int oldx
= _area
.x(), oldy
= _area
.y();
1020 int x
= oldx
, y
= oldy
;
1023 case NorthWestGravity
:
1025 case SouthWestGravity
:
1027 case NorthEastGravity
:
1029 case SouthEastGravity
:
1030 if (addborder
) x
-= _border_width
* 2;
1031 else x
+= _border_width
* 2;
1038 if (addborder
) x
-= _border_width
;
1039 else x
+= _border_width
;
1044 case NorthWestGravity
:
1046 case NorthEastGravity
:
1048 case SouthWestGravity
:
1050 case SouthEastGravity
:
1051 if (addborder
) y
-= _border_width
* 2;
1052 else y
+= _border_width
* 2;
1059 if (addborder
) y
-= _border_width
;
1060 else y
+= _border_width
;
1063 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1066 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1068 // move the client so it is back it the right spot _with_ its border!
1069 if (x
!= oldx
|| y
!= oldy
)
1070 XMoveWindow(**otk::display
, _window
, x
, y
);
1072 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1075 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1077 otk::EventHandler::clientMessageHandler(e
);
1079 // validate cuz we query stuff off the client here
1080 if (!validate()) return;
1082 if (e
.format
!= 32) return;
1084 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1085 // compress changes into a single change
1086 bool compress
= false;
1088 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1089 // XXX: it would be nice to compress ALL messages of a type, not just
1090 // messages in a row without other message types between.
1091 if (ce
.xclient
.message_type
!= e
.message_type
) {
1092 XPutBackEvent(**otk::display
, &ce
);
1098 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1100 setWMState(e
.data
.l
[0]); // use the original event
1101 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1102 // compress changes into a single change
1103 bool compress
= false;
1105 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1106 // XXX: it would be nice to compress ALL messages of a type, not just
1107 // messages in a row without other message types between.
1108 if (ce
.xclient
.message_type
!= e
.message_type
) {
1109 XPutBackEvent(**otk::display
, &ce
);
1115 setDesktop(e
.data
.l
[0]); // use the found event
1117 setDesktop(e
.data
.l
[0]); // use the original event
1118 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1119 // can't compress these
1121 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1122 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1123 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1124 e
.data
.l
[1], e
.data
.l
[2], _window
);
1126 setState(e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1127 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1129 printf("net_close_window for 0x%lx\n", _window
);
1132 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1134 printf("net_active_window for 0x%lx\n", _window
);
1136 if (openbox
->screen(_screen
)->showingDesktop())
1137 openbox
->screen(_screen
)->showDesktop(false);
1140 else if (!frame
->visible()) // if its not visible for other reasons, then
1141 return; // don't mess with it
1145 openbox
->screen(_screen
)->raiseWindow(this);
1146 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1147 if (openbox
->screen(_screen
)->showingDesktop())
1148 openbox
->screen(_screen
)->showDesktop(false);
1151 else if (!frame
->visible()) // if its not visible for other reasons, then
1152 return; // don't mess with it
1153 if (e
.data
.l
[0] && _shaded
)
1157 openbox
->screen(_screen
)->raiseWindow(this);
1158 } else if (e
.message_type
== otk::Property::atoms
.openbox_restack_window
) {
1160 printf("openbox_restack_window for 0x%lx\n", _window
);
1162 if (e
.data
.l
[0] == 0)
1163 openbox
->screen(_screen
)->raiseWindow(this);
1164 else if (e
.data
.l
[0] == 1)
1165 openbox
->screen(_screen
)->lowerWindow(this);
1170 void Client::shapeHandler(const XShapeEvent
&e
)
1172 otk::EventHandler::shapeHandler(e
);
1174 if (e
.kind
== ShapeBounding
) {
1176 frame
->adjustShape();
1181 void Client::resize(Corner anchor
, int w
, int h
)
1183 if (!(_functions
& Func_Resize
)) return;
1184 internal_resize(anchor
, w
, h
);
1187 void Client::internal_resize(Corner anchor
, int w
, int h
,
1188 bool user
, int x
, int y
)
1190 w
-= _base_size
.width();
1191 h
-= _base_size
.height();
1194 // for interactive resizing. have to move half an increment in each
1196 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1197 int mh
= h
% _size_inc
.height();
1198 int aw
= _size_inc
.width() / 2; // amount to add
1199 int ah
= _size_inc
.height() / 2;
1200 // don't let us move into a new size increment
1201 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1202 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1206 // if this is a user-requested resize, then check against min/max sizes
1207 // and aspect ratios
1209 // smaller than min size or bigger than max size?
1210 if (w
> _max_size
.width()) w
= _max_size
.width();
1211 if (w
< _min_size
.width()) w
= _min_size
.width();
1212 if (h
> _max_size
.height()) h
= _max_size
.height();
1213 if (h
< _min_size
.height()) h
= _min_size
.height();
1215 // adjust the height ot match the width for the aspect ratios
1217 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1219 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1222 // keep to the increments
1223 w
/= _size_inc
.width();
1224 h
/= _size_inc
.height();
1226 // you cannot resize to nothing
1230 // store the logical size
1231 _logical_size
= otk::Size(w
, h
);
1233 w
*= _size_inc
.width();
1234 h
*= _size_inc
.height();
1236 w
+= _base_size
.width();
1237 h
+= _base_size
.height();
1239 if (x
== INT_MIN
|| y
== INT_MIN
) {
1246 x
-= w
- _area
.width();
1249 y
-= h
- _area
.height();
1252 x
-= w
- _area
.width();
1253 y
-= h
- _area
.height();
1258 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1260 XResizeWindow(**otk::display
, _window
, w
, h
);
1262 // resize the frame to match the request
1263 frame
->adjustSize();
1264 internal_move(x
, y
);
1267 const Icon
*Client::icon(const otk::Size
&s
) const
1269 unsigned long req
= s
.width() * s
.height();
1270 // si is the smallest image >= req
1271 // li is the largest image < req
1272 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1274 assert(_nicons
> 0); // there should always be a default..
1275 for (int i
= 0; i
< _nicons
; ++i
) {
1276 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1277 if (size
< smallest
&& size
>= req
) {
1281 if (size
> largest
&& size
<= req
) {
1286 if (largest
== 0) // didnt find one smaller than the requested size
1291 void Client::move(int x
, int y
, bool final
)
1293 if (!(_functions
& Func_Move
)) return;
1294 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1296 internal_move(x
, y
, final
);
1299 void Client::internal_move(int x
, int y
, bool final
)
1301 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1303 // move the frame to be in the requested position
1304 if (frame
) { // this can be called while mapping, before frame exists
1305 frame
->adjustPosition();
1307 // send synthetic configure notify (we don't need to if we aren't mapped
1311 event
.type
= ConfigureNotify
;
1312 event
.xconfigure
.display
= **otk::display
;
1313 event
.xconfigure
.event
= _window
;
1314 event
.xconfigure
.window
= _window
;
1316 // root window coords with border in mind
1317 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1318 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1320 event
.xconfigure
.width
= _area
.width();
1321 event
.xconfigure
.height
= _area
.height();
1322 event
.xconfigure
.border_width
= _border_width
;
1323 event
.xconfigure
.above
= frame
->plate();
1324 event
.xconfigure
.override_redirect
= False
;
1325 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1326 StructureNotifyMask
, &event
);
1328 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1329 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1330 event
.xconfigure
.height
, event
.xconfigure
.window
);
1336 void Client::close()
1340 if (!(_functions
& Func_Close
)) return;
1342 // XXX: itd be cool to do timeouts and shit here for killing the client's
1344 // like... if the window is around after 5 seconds, then the close button
1345 // turns a nice red, and if this function is called again, the client is
1346 // explicitly killed.
1348 ce
.xclient
.type
= ClientMessage
;
1349 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1350 ce
.xclient
.display
= **otk::display
;
1351 ce
.xclient
.window
= _window
;
1352 ce
.xclient
.format
= 32;
1353 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1354 ce
.xclient
.data
.l
[1] = CurrentTime
;
1355 ce
.xclient
.data
.l
[2] = 0l;
1356 ce
.xclient
.data
.l
[3] = 0l;
1357 ce
.xclient
.data
.l
[4] = 0l;
1358 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1361 void Client::changeState()
1363 unsigned long state
[2];
1364 state
[0] = _wmstate
;
1366 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1367 otk::Property::atoms
.wm_state
, state
, 2);
1372 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1374 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1376 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1378 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1380 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1382 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1384 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1386 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1388 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1390 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1391 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1392 otk::Property::atoms
.atom
, netstate
, num
);
1397 frame
->adjustState();
1400 void Client::changeAllowedActions(void)
1405 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1407 if (_functions
& Func_Shade
)
1408 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1409 if (_functions
& Func_Close
)
1410 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1411 if (_functions
& Func_Move
)
1412 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1413 if (_functions
& Func_Iconify
)
1414 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1415 if (_functions
& Func_Resize
)
1416 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1417 if (_functions
& Func_Fullscreen
)
1418 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1419 if (_functions
& Func_Maximize
) {
1420 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1421 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1424 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1425 otk::Property::atoms
.atom
, actions
, num
);
1427 // make sure the window isn't breaking any rules now
1429 if (!(_functions
& Func_Shade
) && _shaded
)
1430 if (frame
) shade(false);
1431 else _shaded
= false;
1432 if (!(_functions
& Func_Iconify
) && _iconic
)
1433 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1434 else _iconic
= false;
1435 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1436 if (frame
) fullscreen(false);
1437 else _fullscreen
= false;
1438 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1439 if (frame
) maximize(false, 0);
1440 else _max_vert
= _max_horz
= false;
1443 void Client::remaximize()
1446 if (_max_horz
&& _max_vert
)
1453 return; // not maximized
1454 _max_horz
= _max_vert
= false;
1455 maximize(true, dir
, false);
1458 void Client::applyStartupState()
1460 // these are in a carefully crafted order..
1467 _fullscreen
= false;
1468 fullscreen(true, false);
1477 if (_max_vert
&& _max_horz
) {
1478 _max_vert
= _max_horz
= false;
1479 maximize(true, 0, false);
1480 } else if (_max_vert
) {
1482 maximize(true, 2, false);
1483 } else if (_max_horz
) {
1485 maximize(true, 1, false);
1488 if (_skip_taskbar
); // nothing to do for this
1489 if (_skip_pager
); // nothing to do for this
1490 if (_modal
); // nothing to do for this
1491 if (_above
); // nothing to do for this
1492 if (_below
); // nothing to do for this
1495 void Client::fireUrgent()
1497 // call the python UrgentWindow callbacks
1498 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1499 openbox
->bindings()->fireEvent(&data
);
1502 void Client::shade(bool shade
)
1504 if (!(_functions
& Func_Shade
) || // can't
1505 _shaded
== shade
) return; // already done
1507 // when we're iconic, don't change the wmstate
1509 _wmstate
= shade
? IconicState
: NormalState
;
1512 frame
->adjustSize();
1515 void Client::maximize(bool max
, int dir
, bool savearea
)
1517 assert(dir
== 0 || dir
== 1 || dir
== 2);
1518 if (!(_functions
& Func_Maximize
)) return; // can't
1520 // check if already done
1522 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1523 if (dir
== 1 && _max_horz
) return;
1524 if (dir
== 2 && _max_vert
) return;
1526 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1527 if (dir
== 1 && !_max_horz
) return;
1528 if (dir
== 2 && !_max_vert
) return;
1531 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1532 int x
= frame
->area().x(), y
= frame
->area().y(),
1533 w
= _area
.width(), h
= _area
.height();
1539 unsigned long n
= 4;
1546 // get the property off the window and use it for the dimentions we are
1548 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1549 otk::Property::atoms
.cardinal
, &n
,
1550 (long unsigned**) &readdim
)) {
1553 dimensions
[0] = readdim
[0];
1554 dimensions
[2] = readdim
[2];
1557 dimensions
[1] = readdim
[1];
1558 dimensions
[3] = readdim
[3];
1564 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1565 otk::Property::atoms
.cardinal
,
1566 (long unsigned*)dimensions
, 4);
1568 if (dir
== 0 || dir
== 1) { // horz
1572 if (dir
== 0 || dir
== 2) { // vert
1574 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1578 long unsigned n
= 4;
1580 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1581 otk::Property::atoms
.cardinal
, &n
,
1582 (long unsigned**) &dimensions
)) {
1584 if (dir
== 0 || dir
== 1) { // horz
1585 x
= (signed int)dimensions
[0];
1586 w
= (signed int)dimensions
[2];
1588 if (dir
== 0 || dir
== 2) { // vert
1589 y
= (signed int)dimensions
[1];
1590 h
= (signed int)dimensions
[3];
1595 // pick some fallbacks...
1596 if (dir
== 0 || dir
== 1) { // horz
1597 x
= a
.x() + a
.width() / 4;
1600 if (dir
== 0 || dir
== 2) { // vert
1601 y
= a
.y() + a
.height() / 4;
1607 if (dir
== 0 || dir
== 1) // horz
1609 if (dir
== 0 || dir
== 2) // vert
1612 if (!_max_horz
&& !_max_vert
)
1613 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1615 changeState(); // change the state hints on the client
1617 frame
->frameGravity(x
, y
); // figure out where the client should be going
1618 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1621 void Client::fullscreen(bool fs
, bool savearea
)
1623 static FunctionFlags saved_func
;
1624 static DecorationFlags saved_decor
;
1626 if (!(_functions
& Func_Fullscreen
) || // can't
1627 _fullscreen
== fs
) return; // already done
1630 changeState(); // change the state hints on the client
1632 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1635 // save the functions and remove them
1636 saved_func
= _functions
;
1637 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1638 // save the decorations and remove them
1639 saved_decor
= _decorations
;
1643 dimensions
[0] = _area
.x();
1644 dimensions
[1] = _area
.y();
1645 dimensions
[2] = _area
.width();
1646 dimensions
[3] = _area
.height();
1647 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1648 otk::Property::atoms
.cardinal
,
1649 (long unsigned*)dimensions
, 4);
1651 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1654 w
= info
->size().width();
1655 h
= info
->size().height();
1657 _functions
= saved_func
;
1658 _decorations
= saved_decor
;
1661 long unsigned n
= 4;
1663 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1664 otk::Property::atoms
.cardinal
, &n
,
1665 (long unsigned**) &dimensions
)) {
1674 // pick some fallbacks...
1675 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1676 x
= a
.x() + a
.width() / 4;
1677 y
= a
.y() + a
.height() / 4;
1683 changeAllowedActions(); // based on the new _functions
1685 // when fullscreening, don't obey things like increments, fill the screen
1686 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1688 // raise (back) into our stacking layer
1689 openbox
->screen(_screen
)->raiseWindow(this);
1691 // try focus us when we go into fullscreen mode
1695 void Client::iconify(bool iconic
, bool curdesk
)
1697 if (_iconic
== iconic
) return; // nothing to do
1700 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1706 _wmstate
= IconicState
;
1708 // we unmap the client itself so that we can get MapRequest events, and
1709 // because the ICCCM tells us to!
1710 XUnmapWindow(**otk::display
, _window
);
1713 setDesktop(openbox
->screen(_screen
)->desktop());
1714 _wmstate
= NormalState
;
1715 XMapWindow(**otk::display
, _window
);
1719 openbox
->screen(_screen
)->updateStruts();
1722 void Client::disableDecorations(DecorationFlags flags
)
1724 _disabled_decorations
= flags
;
1725 setupDecorAndFunctions();
1728 void Client::installColormap(bool install
) const
1730 XWindowAttributes wa
;
1731 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1733 XInstallColormap(**otk::display
, wa
.colormap
);
1735 XUninstallColormap(**otk::display
, wa
.colormap
);
1739 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1741 List::const_iterator it
, end
= node
->_transients
.end();
1744 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1745 if (*it
== skip
) continue; // circular?
1746 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1747 if ((*it
)->_modal
) return *it
; // got one
1752 Client
*Client::findModalChild()
1754 return searchModalTree(this, this);
1758 bool Client::focus()
1760 // if we have a modal child, then focus it, not us
1761 Client
*c
= findModalChild();
1762 if (c
) return c
->focus();
1764 // won't try focus if the client doesn't want it, or if the window isn't
1765 // visible on the screen
1766 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1768 // do a check to see if the window has already been unmapped or destroyed
1769 // do this intelligently while watching out for unmaps we've generated
1770 // (ignore_unmaps > 0)
1772 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1773 XPutBackEvent(**otk::display
, &ev
);
1776 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1777 if (ignore_unmaps
) {
1778 unmapHandler(ev
.xunmap
);
1780 XPutBackEvent(**otk::display
, &ev
);
1786 XSetInputFocus(**otk::display
, _window
,
1787 RevertToNone
, CurrentTime
);
1789 if (_focus_notify
) {
1791 ce
.xclient
.type
= ClientMessage
;
1792 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1793 ce
.xclient
.display
= **otk::display
;
1794 ce
.xclient
.window
= _window
;
1795 ce
.xclient
.format
= 32;
1796 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1797 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1798 ce
.xclient
.data
.l
[2] = 0l;
1799 ce
.xclient
.data
.l
[3] = 0l;
1800 ce
.xclient
.data
.l
[4] = 0l;
1801 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1804 XSync(**otk::display
, False
);
1809 void Client::unfocus() const
1811 assert(openbox
->focusedClient() == this);
1812 openbox
->setFocusedClient(0);
1816 void Client::focusHandler(const XFocusChangeEvent
&e
)
1819 // printf("FocusIn for 0x%lx\n", e.window);
1822 otk::EventHandler::focusHandler(e
);
1825 frame
->adjustFocus();
1827 calcLayer(); // focus state can affect the stacking layer
1829 openbox
->setFocusedClient(this);
1833 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1836 // printf("FocusOut for 0x%lx\n", e.window);
1839 otk::EventHandler::unfocusHandler(e
);
1842 frame
->adjustFocus();
1844 calcLayer(); // focus state can affect the stacking layer
1846 if (openbox
->focusedClient() == this)
1847 openbox
->setFocusedClient(0);
1851 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1854 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1857 otk::EventHandler::configureRequestHandler(ec
);
1860 XConfigureRequestEvent e
= ec
;
1862 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1864 // XXX if this causes bad things.. we can compress config req's with the
1866 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1867 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1868 e
.x
= ev
.xconfigurerequest
.x
;
1869 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1870 e
.y
= ev
.xconfigurerequest
.y
;
1871 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1872 e
.width
= ev
.xconfigurerequest
.width
;
1873 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1874 e
.height
= ev
.xconfigurerequest
.height
;
1875 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1876 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1877 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1878 e
.detail
= ev
.xconfigurerequest
.detail
;
1881 // if we are iconic (or shaded (fvwm does this)) ignore the event
1882 if (_iconic
|| _shaded
) return;
1884 if (e
.value_mask
& CWBorderWidth
)
1885 _border_width
= e
.border_width
;
1887 // resize, then move, as specified in the EWMH section 7.7
1888 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1889 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1890 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1894 case NorthEastGravity
:
1898 case SouthWestGravity
:
1900 corner
= BottomLeft
;
1902 case SouthEastGravity
:
1903 corner
= BottomRight
;
1905 default: // NorthWest, Static, etc
1909 // if moving AND resizing ...
1910 if (e
.value_mask
& (CWX
| CWY
)) {
1911 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1912 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1913 internal_resize(corner
, w
, h
, false, x
, y
);
1914 } else // if JUST resizing...
1915 internal_resize(corner
, w
, h
, false);
1916 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1917 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1918 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1919 internal_move(x
, y
);
1922 if (e
.value_mask
& CWStackMode
) {
1926 openbox
->screen(_screen
)->lowerWindow(this);
1932 openbox
->screen(_screen
)->raiseWindow(this);
1939 void Client::unmapHandler(const XUnmapEvent
&e
)
1941 if (ignore_unmaps
) {
1943 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1950 printf("UnmapNotify for 0x%lx\n", e
.window
);
1953 otk::EventHandler::unmapHandler(e
);
1955 // this deletes us etc
1956 openbox
->screen(_screen
)->unmanageWindow(this);
1960 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1963 printf("DestroyNotify for 0x%lx\n", e
.window
);
1966 otk::EventHandler::destroyHandler(e
);
1968 // this deletes us etc
1969 openbox
->screen(_screen
)->unmanageWindow(this);
1973 void Client::reparentHandler(const XReparentEvent
&e
)
1975 // this is when the client is first taken captive in the frame
1976 if (e
.parent
== frame
->plate()) return;
1979 printf("ReparentNotify for 0x%lx\n", e
.window
);
1982 otk::EventHandler::reparentHandler(e
);
1985 This event is quite rare and is usually handled in unmapHandler.
1986 However, if the window is unmapped when the reparent event occurs,
1987 the window manager never sees it because an unmap event is not sent
1988 to an already unmapped window.
1991 // we don't want the reparent event, put it back on the stack for the X
1992 // server to deal with after we unmanage the window
1995 XPutBackEvent(**otk::display
, &ev
);
1997 // this deletes us etc
1998 openbox
->screen(_screen
)->unmanageWindow(this);
2001 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
2004 printf("MapRequest for already managed 0x%lx\n", e
.window
);
2007 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
2009 // move to the current desktop (uniconify)
2011 // XXX: should we focus/raise the window? (basically a net_wm_active_window)