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 d
< openbox
->screen(_screen
)->numDesktops()) {
162 // printf("Window requested desktop: %ld\n", _desktop);
168 void Client::getType()
170 _type
= (WindowType
) -1;
174 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
175 otk::Property::atoms
.atom
, &num
, &val
)) {
176 // use the first value that we know about in the array
177 for (unsigned long i
= 0; i
< num
; ++i
) {
178 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
179 _type
= Type_Desktop
;
180 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
182 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
183 _type
= Type_Toolbar
;
184 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
186 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
187 _type
= Type_Utility
;
188 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
190 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
192 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
194 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
195 // prevent this window from getting any decor or functionality
196 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
197 _mwmhints
.decorations
= 0;
198 _mwmhints
.functions
= 0;
200 if (_type
!= (WindowType
) -1)
201 break; // grab the first known type
206 if (_type
== (WindowType
) -1) {
208 * the window type hint was not set, which means we either classify ourself
209 * as a normal window or a dialog, depending on if we are a transient.
219 void Client::setupDecorAndFunctions()
221 // start with everything (cept fullscreen)
222 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
| Decor_Icon
|
223 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
224 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
226 if (_delete_window
) {
227 _decorations
|= Decor_Close
;
228 _functions
|= Func_Close
;
231 if (!(_min_size
.width() < _max_size
.width() ||
232 _min_size
.height() < _max_size
.height())) {
233 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
234 _functions
&= ~(Func_Resize
| Func_Maximize
);
239 // normal windows retain all of the possible decorations and
240 // functionality, and are the only windows that you can fullscreen
241 _functions
|= Func_Fullscreen
;
245 // dialogs cannot be maximized
246 _decorations
&= ~Decor_Maximize
;
247 _functions
&= ~Func_Maximize
;
253 // these windows get less functionality
254 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
255 _functions
&= ~(Func_Iconify
| Func_Resize
);
261 // none of these windows are manipulated by the window manager
267 // Mwm Hints are applied subtractively to what has already been chosen for
268 // decor and functionality
269 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
270 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
271 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
272 _decorations
&= ~Decor_Border
;
273 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
274 _decorations
&= ~Decor_Handle
;
275 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
276 _decorations
&= ~Decor_Titlebar
;
277 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
278 _decorations
&= ~Decor_Iconify
;
279 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
280 _decorations
&= ~Decor_Maximize
;
284 if (_mwmhints
.flags
& MwmFlag_Functions
) {
285 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
286 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
287 _functions
&= ~Func_Resize
;
288 if (! (_mwmhints
.functions
& MwmFunc_Move
))
289 _functions
&= ~Func_Move
;
290 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
291 _functions
&= ~Func_Iconify
;
292 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
293 _functions
&= ~Func_Maximize
;
294 // dont let mwm hints kill the close button
295 //if (! (_mwmhints.functions & MwmFunc_Close))
296 // _functions &= ~Func_Close;
300 // can't maximize without moving/resizing
301 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
302 _functions
&= ~Func_Maximize
;
304 // finally, user specified disabled decorations are applied to subtract
306 if (_disabled_decorations
& Decor_Titlebar
)
307 _decorations
&= ~Decor_Titlebar
;
308 if (_disabled_decorations
& Decor_Handle
)
309 _decorations
&= ~Decor_Handle
;
310 if (_disabled_decorations
& Decor_Border
)
311 _decorations
&= ~Decor_Border
;
312 if (_disabled_decorations
& Decor_Iconify
)
313 _decorations
&= ~Decor_Iconify
;
314 if (_disabled_decorations
& Decor_Maximize
)
315 _decorations
&= ~Decor_Maximize
;
316 if (_disabled_decorations
& Decor_AllDesktops
)
317 _decorations
&= ~Decor_AllDesktops
;
318 if (_disabled_decorations
& Decor_Close
)
319 _decorations
&= ~Decor_Close
;
321 // if we don't have a titlebar, then we cannot shade!
322 if (!(_decorations
& Decor_Titlebar
))
323 _functions
&= ~Func_Shade
;
325 changeAllowedActions();
328 frame
->adjustSize(); // change the decors on the frame
329 frame
->adjustPosition(); // with more/less decorations, we may need to be
331 remaximize(); // with new decor, the window's maximized size may change
336 void Client::getMwmHints()
338 unsigned long num
= MwmHints::elements
;
339 unsigned long *hints
;
341 _mwmhints
.flags
= 0; // default to none
343 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
344 otk::Property::atoms
.motif_wm_hints
, &num
,
345 (unsigned long **)&hints
))
348 if (num
>= MwmHints::elements
) {
349 // retrieved the hints
350 _mwmhints
.flags
= hints
[0];
351 _mwmhints
.functions
= hints
[1];
352 _mwmhints
.decorations
= hints
[2];
359 void Client::getArea()
361 XWindowAttributes wattrib
;
364 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
365 assert(ret
!= BadWindow
);
367 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
368 _border_width
= wattrib
.border_width
;
372 void Client::getState()
374 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
375 _iconic
= _skip_taskbar
= _skip_pager
= false;
377 unsigned long *state
;
380 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
381 otk::Property::atoms
.atom
, &num
, &state
)) {
382 for (unsigned long i
= 0; i
< num
; ++i
) {
383 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
385 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
387 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
389 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
390 _skip_taskbar
= true;
391 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
393 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
395 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
397 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
399 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
401 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
409 void Client::getShaped()
413 if (otk::display
->shape()) {
418 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
420 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
421 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
427 Client
*Client::searchFocusTree(Client
*node
, Client
*skip
)
429 List::const_iterator it
, end
= node
->_transients
.end();
432 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
433 if (*it
== skip
) continue; // circular?
434 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
435 if ((*it
)->_focused
) return *it
; // got one
440 void Client::calcLayer() {
444 // are we fullscreen, or do we have a fullscreen transient parent?
447 if (c
->_fullscreen
) {
451 c
= c
->_transient_for
;
453 if (!fs
&& _fullscreen
) {
454 // is one of our transients focused?
455 c
= searchFocusTree(this, this);
459 if (_iconic
) l
= Layer_Icon
;
460 else if (fs
) l
= Layer_Fullscreen
;
461 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
462 else if (_type
== Type_Dock
) {
463 if (!_below
) l
= Layer_Top
;
464 else l
= Layer_Normal
;
466 else if (_above
) l
= Layer_Above
;
467 else if (_below
) l
= Layer_Below
;
468 else l
= Layer_Normal
;
474 if we don't have a frame, then we aren't mapped yet (and this would
477 openbox
->screen(_screen
)->raiseWindow(this);
482 void Client::updateProtocols()
487 _focus_notify
= false;
488 _delete_window
= false;
490 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
491 for (int i
= 0; i
< num_return
; ++i
) {
492 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
493 // this means we can request the window to close
494 _delete_window
= true;
495 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
496 // if this protocol is requested, then the window will be notified
497 // by the window manager whenever it receives focus
498 _focus_notify
= true;
504 void Client::updateNormalHints()
508 int oldgravity
= _gravity
;
513 _size_inc
= otk::Size(1, 1);
514 _base_size
= otk::Size(0, 0);
515 _min_size
= otk::Size(0, 0);
516 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
518 // get the hints from the window
519 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
520 _positioned
= (size
.flags
& (PPosition
|USPosition
));
522 if (size
.flags
& PWinGravity
) {
523 _gravity
= size
.win_gravity
;
525 // if the client has a frame, i.e. has already been mapped and is
526 // changing its gravity
527 if (frame
&& _gravity
!= oldgravity
) {
528 // move our idea of the client's position based on its new gravity
529 int x
= frame
->area().x(), y
= frame
->area().y();
530 frame
->frameGravity(x
, y
);
531 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
535 if (size
.flags
& PAspect
) {
536 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
537 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
540 if (size
.flags
& PMinSize
)
541 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
543 if (size
.flags
& PMaxSize
)
544 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
546 if (size
.flags
& PBaseSize
)
547 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
549 if (size
.flags
& PResizeInc
)
550 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
554 void Client::updateWMHints(bool initstate
)
558 // assume a window takes input if it doesnt specify
562 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
563 if (hints
->flags
& InputHint
)
564 _can_focus
= hints
->input
;
566 // only do this when initstate is true!
567 if (initstate
&& (hints
->flags
& StateHint
))
568 _iconic
= hints
->initial_state
== IconicState
;
570 if (hints
->flags
& XUrgencyHint
)
573 if (hints
->flags
& WindowGroupHint
) {
574 if (hints
->window_group
!= _group
) {
575 // XXX: remove from the old group if there was one
576 _group
= hints
->window_group
;
577 // XXX: do stuff with the group
582 if (hints
->flags
& IconPixmapHint
) {
583 updateKwmIcon(); // try get the kwm icon first, this is a fallback only
584 if (_pixmap_icon
== None
) {
585 _pixmap_icon
= hints
->icon_pixmap
;
586 if (hints
->flags
& IconMaskHint
)
587 _pixmap_icon_mask
= hints
->icon_mask
;
589 _pixmap_icon_mask
= None
;
599 printf("Urgent Hint for 0x%lx: %s\n",
600 (long)_window
, _urgent
? "ON" : "OFF");
602 // fire the urgent callback if we're mapped, otherwise, wait until after
609 void Client::updateTitle()
614 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
615 otk::Property::utf8
, &_title
)) {
617 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
618 otk::Property::ascii
, &_title
);
622 _title
= _("Unnamed Window");
625 frame
->adjustTitle();
628 void Client::updateIconTitle()
633 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
634 otk::Property::utf8
, &_icon_title
)) {
636 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
637 otk::Property::ascii
, &_icon_title
);
641 _icon_title
= _("Unnamed Window");
644 void Client::updateClass()
647 _app_name
= _app_class
= _role
= "";
649 otk::Property::StringVect v
;
650 unsigned long num
= 2;
652 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
653 otk::Property::ascii
, &num
, &v
)) {
654 if (num
> 0) _app_name
= v
[0].c_str();
655 if (num
> 1) _app_class
= v
[1].c_str();
660 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
661 otk::Property::ascii
, &num
, &v
)) {
662 if (num
> 0) _role
= v
[0].c_str();
666 void Client::updateStrut()
668 unsigned long num
= 4;
670 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
671 otk::Property::atoms
.cardinal
, &num
, &data
))
675 _strut
.left
= data
[0];
676 _strut
.right
= data
[1];
677 _strut
.top
= data
[2];
678 _strut
.bottom
= data
[3];
680 // updating here is pointless while we're being mapped cuz we're not in
681 // the screen's client list yet
683 openbox
->screen(_screen
)->updateStruts();
689 void Client::updateTransientFor()
694 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
695 t
!= _window
) { // cant be transient to itself!
696 c
= openbox
->findClient(t
);
697 assert(c
!= this); // if this happens then we need to check for it
699 if (!c
/*XXX: && _group*/) {
700 // not transient to a client, see if it is transient for a group
701 if (//t == _group->leader() ||
703 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
704 // window is a transient for its group!
705 // XXX: for now this is treated as non-transient.
706 // this needs to be fixed!
711 // if anything has changed...
712 if (c
!= _transient_for
) {
714 _transient_for
->_transients
.remove(this); // remove from old parent
717 _transient_for
->_transients
.push_back(this); // add to new parent
721 void Client::updateIcons()
725 unsigned long w
, h
, i
= 0;
727 for (int j
= 0; j
< _nicons
; ++j
)
728 delete [] _icons
[j
].data
;
733 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon
,
734 otk::Property::atoms
.cardinal
, &num
, &data
)) {
735 // figure out how man valid icons are in here
736 while (num
- i
> 2) {
744 _icons
= new Icon
[_nicons
];
748 for (int j
= 0; j
< _nicons
; ++j
) {
749 w
= _icons
[j
].w
= data
[i
++];
750 h
= _icons
[j
].h
= data
[i
++];
751 _icons
[j
].data
= new unsigned long[w
* h
];
752 ::memcpy(_icons
[j
].data
, &data
[i
], w
* h
* sizeof(unsigned long));
762 _icons
= new Icon
[1];
768 assert(_nicons
> 0); // there should always be a default..
770 if (frame
) frame
->adjustIcon();
773 void Client::updateKwmIcon()
775 _pixmap_icon
= _pixmap_icon_mask
= None
;
777 unsigned long num
= 2;
779 if (otk::Property::get(_window
, otk::Property::atoms
.kwm_win_icon
,
780 otk::Property::atoms
.kwm_win_icon
, &num
, &data
)) {
782 _pixmap_icon
= data
[0];
783 _pixmap_icon_mask
= data
[1];
789 void Client::propertyHandler(const XPropertyEvent
&e
)
791 otk::EventHandler::propertyHandler(e
);
793 // validate cuz we query stuff off the client here
794 if (!validate()) return;
796 // compress changes to a single property into a single change
798 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
799 // XXX: it would be nice to compress ALL changes to a property, not just
800 // changes in a row without other props between.
801 if (ce
.xproperty
.atom
!= e
.atom
) {
802 XPutBackEvent(**otk::display
, &ce
);
807 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
809 setupDecorAndFunctions(); // normal hints can make a window non-resizable
810 } else if (e
.atom
== XA_WM_HINTS
)
812 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
813 updateTransientFor();
815 calcLayer(); // type may have changed, so update the layer
816 setupDecorAndFunctions();
818 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
819 e
.atom
== otk::Property::atoms
.wm_name
)
821 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
822 e
.atom
== otk::Property::atoms
.wm_icon_name
)
824 else if (e
.atom
== otk::Property::atoms
.wm_class
)
826 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
828 setupDecorAndFunctions();
830 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
832 else if (e
.atom
== otk::Property::atoms
.net_wm_icon
)
834 else if (e
.atom
== otk::Property::atoms
.kwm_win_icon
)
838 void Client::setWMState(long state
)
840 if (state
== _wmstate
) return; // no change
852 void Client::setDesktop(unsigned int target
)
854 if (target
== _desktop
) return;
856 printf("Setting desktop %u\n", target
);
858 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
859 target
== 0xffffffff))
863 // set the desktop hint
864 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
865 otk::Property::atoms
.cardinal
, _desktop
);
866 frame
->adjustState(); // the frame can display the current desktop state
867 // 'move' the window to the new desktop
869 openbox
->screen(_screen
)->updateStruts();
872 void Client::showhide()
875 Screen
*s
= openbox
->screen(_screen
);
877 if (_iconic
) show
= false;
878 else if (!(_desktop
== s
->desktop() ||
879 _desktop
== 0xffffffff)) show
= false;
880 else if (normal() && s
->showingDesktop()) show
= false;
883 if (show
) frame
->show();
887 void Client::setState(Atom action
, long data1
, long data2
)
889 bool shadestate
= _shaded
;
890 bool fsstate
= _fullscreen
;
891 bool maxh
= _max_horz
;
892 bool maxv
= _max_vert
;
894 if (!(action
== otk::Property::atoms
.net_wm_state_add
||
895 action
== otk::Property::atoms
.net_wm_state_remove
||
896 action
== otk::Property::atoms
.net_wm_state_toggle
))
897 return; // an invalid action was passed to the client message, ignore it
899 for (int i
= 0; i
< 2; ++i
) {
900 Atom state
= i
== 0 ? data1
: data2
;
902 if (! state
) continue;
904 // if toggling, then pick whether we're adding or removing
905 if (action
== otk::Property::atoms
.net_wm_state_toggle
) {
906 if (state
== otk::Property::atoms
.net_wm_state_modal
)
907 action
= _modal
? otk::Property::atoms
.net_wm_state_remove
:
908 otk::Property::atoms
.net_wm_state_add
;
909 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
910 action
= _max_vert
? otk::Property::atoms
.net_wm_state_remove
:
911 otk::Property::atoms
.net_wm_state_add
;
912 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
913 action
= _max_horz
? otk::Property::atoms
.net_wm_state_remove
:
914 otk::Property::atoms
.net_wm_state_add
;
915 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
916 action
= _shaded
? otk::Property::atoms
.net_wm_state_remove
:
917 otk::Property::atoms
.net_wm_state_add
;
918 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
919 action
= _skip_taskbar
? otk::Property::atoms
.net_wm_state_remove
:
920 otk::Property::atoms
.net_wm_state_add
;
921 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
922 action
= _skip_pager
? otk::Property::atoms
.net_wm_state_remove
:
923 otk::Property::atoms
.net_wm_state_add
;
924 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
925 action
= _fullscreen
? otk::Property::atoms
.net_wm_state_remove
:
926 otk::Property::atoms
.net_wm_state_add
;
927 else if (state
== otk::Property::atoms
.net_wm_state_above
)
928 action
= _above
? otk::Property::atoms
.net_wm_state_remove
:
929 otk::Property::atoms
.net_wm_state_add
;
930 else if (state
== otk::Property::atoms
.net_wm_state_below
)
931 action
= _below
? otk::Property::atoms
.net_wm_state_remove
:
932 otk::Property::atoms
.net_wm_state_add
;
935 if (action
== otk::Property::atoms
.net_wm_state_add
) {
936 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
937 if (_modal
) continue;
939 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
941 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
942 if (_max_horz
) continue;
944 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
946 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
947 _skip_taskbar
= true;
948 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
950 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
952 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
953 if (_above
) continue;
955 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
956 if (_below
) continue;
960 } else { // action == otk::Property::atoms.net_wm_state_remove
961 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
962 if (!_modal
) continue;
964 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
966 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
968 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
970 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
971 _skip_taskbar
= false;
972 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
974 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
976 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
977 if (!_above
) continue;
979 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
980 if (!_below
) continue;
985 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
986 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
987 if (maxh
== maxv
) { // both going the same way
988 maximize(maxh
, 0, true);
990 maximize(maxh
, 1, true);
991 maximize(maxv
, 2, true);
993 } else { // toggling one
994 if (maxh
!= _max_horz
)
995 maximize(maxh
, 1, true);
997 maximize(maxv
, 2, true);
1000 // change fullscreen state before shading, as it will affect if the window
1002 if (fsstate
!= _fullscreen
)
1003 fullscreen(fsstate
, true);
1004 if (shadestate
!= _shaded
)
1007 changeState(); // change the hint to relect these changes
1010 void Client::toggleClientBorder(bool addborder
)
1012 // adjust our idea of where the client is, based on its border. When the
1013 // border is removed, the client should now be considered to be in a
1014 // different position.
1015 // when re-adding the border to the client, the same operation needs to be
1017 int oldx
= _area
.x(), oldy
= _area
.y();
1018 int x
= oldx
, y
= oldy
;
1021 case NorthWestGravity
:
1023 case SouthWestGravity
:
1025 case NorthEastGravity
:
1027 case SouthEastGravity
:
1028 if (addborder
) x
-= _border_width
* 2;
1029 else x
+= _border_width
* 2;
1036 if (addborder
) x
-= _border_width
;
1037 else x
+= _border_width
;
1042 case NorthWestGravity
:
1044 case NorthEastGravity
:
1046 case SouthWestGravity
:
1048 case SouthEastGravity
:
1049 if (addborder
) y
-= _border_width
* 2;
1050 else y
+= _border_width
* 2;
1057 if (addborder
) y
-= _border_width
;
1058 else y
+= _border_width
;
1061 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1064 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1066 // move the client so it is back it the right spot _with_ its border!
1067 if (x
!= oldx
|| y
!= oldy
)
1068 XMoveWindow(**otk::display
, _window
, x
, y
);
1070 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1073 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1075 otk::EventHandler::clientMessageHandler(e
);
1077 // validate cuz we query stuff off the client here
1078 if (!validate()) return;
1080 if (e
.format
!= 32) return;
1082 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1083 // compress changes into a single change
1084 bool compress
= false;
1086 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1087 // XXX: it would be nice to compress ALL messages of a type, not just
1088 // messages in a row without other message types between.
1089 if (ce
.xclient
.message_type
!= e
.message_type
) {
1090 XPutBackEvent(**otk::display
, &ce
);
1096 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1098 setWMState(e
.data
.l
[0]); // use the original event
1099 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1100 // compress changes into a single change
1101 bool compress
= false;
1103 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1104 // XXX: it would be nice to compress ALL messages of a type, not just
1105 // messages in a row without other message types between.
1106 if (ce
.xclient
.message_type
!= e
.message_type
) {
1107 XPutBackEvent(**otk::display
, &ce
);
1113 setDesktop(e
.data
.l
[0]); // use the found event
1115 setDesktop(e
.data
.l
[0]); // use the original event
1116 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1117 // can't compress these
1119 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1120 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1121 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1122 e
.data
.l
[1], e
.data
.l
[2], _window
);
1124 setState(e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1125 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1127 printf("net_close_window for 0x%lx\n", _window
);
1130 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1132 printf("net_active_window for 0x%lx\n", _window
);
1134 if (openbox
->screen(_screen
)->showingDesktop())
1135 openbox
->screen(_screen
)->showDesktop(false);
1138 else if (!frame
->visible()) // if its not visible for other reasons, then
1139 return; // don't mess with it
1143 openbox
->screen(_screen
)->raiseWindow(this);
1144 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1145 if (openbox
->screen(_screen
)->showingDesktop())
1146 openbox
->screen(_screen
)->showDesktop(false);
1149 else if (!frame
->visible()) // if its not visible for other reasons, then
1150 return; // don't mess with it
1151 if (e
.data
.l
[0] && _shaded
)
1155 openbox
->screen(_screen
)->raiseWindow(this);
1156 } else if (e
.message_type
== otk::Property::atoms
.openbox_restack_window
) {
1158 printf("openbox_restack_window for 0x%lx\n", _window
);
1160 if (e
.data
.l
[0] == 0)
1161 openbox
->screen(_screen
)->raiseWindow(this);
1162 else if (e
.data
.l
[0] == 1)
1163 openbox
->screen(_screen
)->lowerWindow(this);
1168 void Client::shapeHandler(const XShapeEvent
&e
)
1170 otk::EventHandler::shapeHandler(e
);
1172 if (e
.kind
== ShapeBounding
) {
1174 frame
->adjustShape();
1179 void Client::resize(Corner anchor
, int w
, int h
)
1181 if (!(_functions
& Func_Resize
)) return;
1182 internal_resize(anchor
, w
, h
);
1185 void Client::internal_resize(Corner anchor
, int w
, int h
,
1186 bool user
, int x
, int y
)
1188 w
-= _base_size
.width();
1189 h
-= _base_size
.height();
1192 // for interactive resizing. have to move half an increment in each
1194 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1195 int mh
= h
% _size_inc
.height();
1196 int aw
= _size_inc
.width() / 2; // amount to add
1197 int ah
= _size_inc
.height() / 2;
1198 // don't let us move into a new size increment
1199 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1200 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1204 // if this is a user-requested resize, then check against min/max sizes
1205 // and aspect ratios
1207 // smaller than min size or bigger than max size?
1208 if (w
> _max_size
.width()) w
= _max_size
.width();
1209 if (w
< _min_size
.width()) w
= _min_size
.width();
1210 if (h
> _max_size
.height()) h
= _max_size
.height();
1211 if (h
< _min_size
.height()) h
= _min_size
.height();
1213 // adjust the height ot match the width for the aspect ratios
1215 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1217 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1220 // keep to the increments
1221 w
/= _size_inc
.width();
1222 h
/= _size_inc
.height();
1224 // you cannot resize to nothing
1228 // store the logical size
1229 _logical_size
= otk::Size(w
, h
);
1231 w
*= _size_inc
.width();
1232 h
*= _size_inc
.height();
1234 w
+= _base_size
.width();
1235 h
+= _base_size
.height();
1237 if (x
== INT_MIN
|| y
== INT_MIN
) {
1244 x
-= w
- _area
.width();
1247 y
-= h
- _area
.height();
1250 x
-= w
- _area
.width();
1251 y
-= h
- _area
.height();
1256 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1258 XResizeWindow(**otk::display
, _window
, w
, h
);
1260 // resize the frame to match the request
1261 frame
->adjustSize();
1262 internal_move(x
, y
);
1265 const Icon
*Client::icon(const otk::Size
&s
) const
1267 unsigned long req
= s
.width() * s
.height();
1268 // si is the smallest image >= req
1269 // li is the largest image < req
1270 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1272 assert(_nicons
> 0); // there should always be a default..
1273 for (int i
= 0; i
< _nicons
; ++i
) {
1274 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1275 if (size
< smallest
&& size
>= req
) {
1279 if (size
> largest
&& size
<= req
) {
1284 if (largest
== 0) // didnt find one smaller than the requested size
1289 void Client::move(int x
, int y
, bool final
)
1291 if (!(_functions
& Func_Move
)) return;
1292 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1294 internal_move(x
, y
, final
);
1297 void Client::internal_move(int x
, int y
, bool final
)
1299 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1301 // move the frame to be in the requested position
1302 if (frame
) { // this can be called while mapping, before frame exists
1303 frame
->adjustPosition();
1305 // send synthetic configure notify (we don't need to if we aren't mapped
1309 event
.type
= ConfigureNotify
;
1310 event
.xconfigure
.display
= **otk::display
;
1311 event
.xconfigure
.event
= _window
;
1312 event
.xconfigure
.window
= _window
;
1314 // root window coords with border in mind
1315 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1316 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1318 event
.xconfigure
.width
= _area
.width();
1319 event
.xconfigure
.height
= _area
.height();
1320 event
.xconfigure
.border_width
= _border_width
;
1321 event
.xconfigure
.above
= frame
->plate();
1322 event
.xconfigure
.override_redirect
= False
;
1323 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1324 StructureNotifyMask
, &event
);
1326 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1327 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1328 event
.xconfigure
.height
, event
.xconfigure
.window
);
1334 void Client::close()
1338 if (!(_functions
& Func_Close
)) return;
1340 // XXX: itd be cool to do timeouts and shit here for killing the client's
1342 // like... if the window is around after 5 seconds, then the close button
1343 // turns a nice red, and if this function is called again, the client is
1344 // explicitly killed.
1346 ce
.xclient
.type
= ClientMessage
;
1347 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1348 ce
.xclient
.display
= **otk::display
;
1349 ce
.xclient
.window
= _window
;
1350 ce
.xclient
.format
= 32;
1351 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1352 ce
.xclient
.data
.l
[1] = CurrentTime
;
1353 ce
.xclient
.data
.l
[2] = 0l;
1354 ce
.xclient
.data
.l
[3] = 0l;
1355 ce
.xclient
.data
.l
[4] = 0l;
1356 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1359 void Client::changeState()
1361 unsigned long state
[2];
1362 state
[0] = _wmstate
;
1364 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1365 otk::Property::atoms
.wm_state
, state
, 2);
1370 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1372 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1374 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1376 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1378 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1380 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1382 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1384 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1386 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1388 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1389 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1390 otk::Property::atoms
.atom
, netstate
, num
);
1395 frame
->adjustState();
1398 void Client::changeAllowedActions(void)
1403 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1405 if (_functions
& Func_Shade
)
1406 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1407 if (_functions
& Func_Close
)
1408 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1409 if (_functions
& Func_Move
)
1410 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1411 if (_functions
& Func_Iconify
)
1412 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1413 if (_functions
& Func_Resize
)
1414 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1415 if (_functions
& Func_Fullscreen
)
1416 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1417 if (_functions
& Func_Maximize
) {
1418 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1419 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1422 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1423 otk::Property::atoms
.atom
, actions
, num
);
1425 // make sure the window isn't breaking any rules now
1427 if (!(_functions
& Func_Shade
) && _shaded
)
1428 if (frame
) shade(false);
1429 else _shaded
= false;
1430 if (!(_functions
& Func_Iconify
) && _iconic
)
1431 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1432 else _iconic
= false;
1433 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1434 if (frame
) fullscreen(false);
1435 else _fullscreen
= false;
1436 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1437 if (frame
) maximize(false, 0);
1438 else _max_vert
= _max_horz
= false;
1441 void Client::remaximize()
1444 if (_max_horz
&& _max_vert
)
1451 return; // not maximized
1452 _max_horz
= _max_vert
= false;
1453 maximize(true, dir
, false);
1456 void Client::applyStartupState()
1458 // these are in a carefully crafted order..
1465 _fullscreen
= false;
1466 fullscreen(true, false);
1475 if (_max_vert
&& _max_horz
) {
1476 _max_vert
= _max_horz
= false;
1477 maximize(true, 0, false);
1478 } else if (_max_vert
) {
1480 maximize(true, 2, false);
1481 } else if (_max_horz
) {
1483 maximize(true, 1, false);
1486 if (_skip_taskbar
); // nothing to do for this
1487 if (_skip_pager
); // nothing to do for this
1488 if (_modal
); // nothing to do for this
1489 if (_above
); // nothing to do for this
1490 if (_below
); // nothing to do for this
1493 void Client::fireUrgent()
1495 // call the python UrgentWindow callbacks
1496 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1497 openbox
->bindings()->fireEvent(&data
);
1500 void Client::shade(bool shade
)
1502 if (!(_functions
& Func_Shade
) || // can't
1503 _shaded
== shade
) return; // already done
1505 // when we're iconic, don't change the wmstate
1507 _wmstate
= shade
? IconicState
: NormalState
;
1510 frame
->adjustSize();
1513 void Client::maximize(bool max
, int dir
, bool savearea
)
1515 assert(dir
== 0 || dir
== 1 || dir
== 2);
1516 if (!(_functions
& Func_Maximize
)) return; // can't
1518 // check if already done
1520 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1521 if (dir
== 1 && _max_horz
) return;
1522 if (dir
== 2 && _max_vert
) return;
1524 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1525 if (dir
== 1 && !_max_horz
) return;
1526 if (dir
== 2 && !_max_vert
) return;
1529 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1530 int x
= frame
->area().x(), y
= frame
->area().y(),
1531 w
= _area
.width(), h
= _area
.height();
1537 unsigned long n
= 4;
1544 // get the property off the window and use it for the dimentions we are
1546 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1547 otk::Property::atoms
.cardinal
, &n
,
1548 (long unsigned**) &readdim
)) {
1551 dimensions
[0] = readdim
[0];
1552 dimensions
[2] = readdim
[2];
1555 dimensions
[1] = readdim
[1];
1556 dimensions
[3] = readdim
[3];
1562 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1563 otk::Property::atoms
.cardinal
,
1564 (long unsigned*)dimensions
, 4);
1566 if (dir
== 0 || dir
== 1) { // horz
1570 if (dir
== 0 || dir
== 2) { // vert
1572 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1576 long unsigned n
= 4;
1578 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1579 otk::Property::atoms
.cardinal
, &n
,
1580 (long unsigned**) &dimensions
)) {
1582 if (dir
== 0 || dir
== 1) { // horz
1583 x
= (signed int)dimensions
[0];
1584 w
= (signed int)dimensions
[2];
1586 if (dir
== 0 || dir
== 2) { // vert
1587 y
= (signed int)dimensions
[1];
1588 h
= (signed int)dimensions
[3];
1593 // pick some fallbacks...
1594 if (dir
== 0 || dir
== 1) { // horz
1595 x
= a
.x() + a
.width() / 4;
1598 if (dir
== 0 || dir
== 2) { // vert
1599 y
= a
.y() + a
.height() / 4;
1605 if (dir
== 0 || dir
== 1) // horz
1607 if (dir
== 0 || dir
== 2) // vert
1610 if (!_max_horz
&& !_max_vert
)
1611 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1613 changeState(); // change the state hints on the client
1615 frame
->frameGravity(x
, y
); // figure out where the client should be going
1616 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1619 void Client::fullscreen(bool fs
, bool savearea
)
1621 static FunctionFlags saved_func
;
1622 static DecorationFlags saved_decor
;
1624 if (!(_functions
& Func_Fullscreen
) || // can't
1625 _fullscreen
== fs
) return; // already done
1628 changeState(); // change the state hints on the client
1630 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1633 // save the functions and remove them
1634 saved_func
= _functions
;
1635 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1636 // save the decorations and remove them
1637 saved_decor
= _decorations
;
1641 dimensions
[0] = _area
.x();
1642 dimensions
[1] = _area
.y();
1643 dimensions
[2] = _area
.width();
1644 dimensions
[3] = _area
.height();
1645 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1646 otk::Property::atoms
.cardinal
,
1647 (long unsigned*)dimensions
, 4);
1649 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1652 w
= info
->size().width();
1653 h
= info
->size().height();
1655 _functions
= saved_func
;
1656 _decorations
= saved_decor
;
1659 long unsigned n
= 4;
1661 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1662 otk::Property::atoms
.cardinal
, &n
,
1663 (long unsigned**) &dimensions
)) {
1672 // pick some fallbacks...
1673 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1674 x
= a
.x() + a
.width() / 4;
1675 y
= a
.y() + a
.height() / 4;
1681 changeAllowedActions(); // based on the new _functions
1683 // when fullscreening, don't obey things like increments, fill the screen
1684 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1686 // raise (back) into our stacking layer
1687 openbox
->screen(_screen
)->raiseWindow(this);
1689 // try focus us when we go into fullscreen mode
1693 void Client::iconify(bool iconic
, bool curdesk
)
1695 if (_iconic
== iconic
) return; // nothing to do
1698 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1704 _wmstate
= IconicState
;
1706 // we unmap the client itself so that we can get MapRequest events, and
1707 // because the ICCCM tells us to!
1708 XUnmapWindow(**otk::display
, _window
);
1711 setDesktop(openbox
->screen(_screen
)->desktop());
1712 _wmstate
= NormalState
;
1713 XMapWindow(**otk::display
, _window
);
1717 openbox
->screen(_screen
)->updateStruts();
1720 void Client::disableDecorations(DecorationFlags flags
)
1722 _disabled_decorations
= flags
;
1723 setupDecorAndFunctions();
1726 void Client::installColormap(bool install
) const
1728 XWindowAttributes wa
;
1729 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1731 XInstallColormap(**otk::display
, wa
.colormap
);
1733 XUninstallColormap(**otk::display
, wa
.colormap
);
1737 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1739 List::const_iterator it
, end
= node
->_transients
.end();
1742 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1743 if (*it
== skip
) continue; // circular?
1744 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1745 if ((*it
)->_modal
) return *it
; // got one
1750 Client
*Client::findModalChild()
1752 return searchModalTree(this, this);
1756 bool Client::focus()
1758 // if we have a modal child, then focus it, not us
1759 Client
*c
= findModalChild();
1760 if (c
) return c
->focus();
1762 // won't try focus if the client doesn't want it, or if the window isn't
1763 // visible on the screen
1764 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1766 // do a check to see if the window has already been unmapped or destroyed
1767 // do this intelligently while watching out for unmaps we've generated
1768 // (ignore_unmaps > 0)
1770 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1771 XPutBackEvent(**otk::display
, &ev
);
1774 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1775 if (ignore_unmaps
) {
1776 unmapHandler(ev
.xunmap
);
1778 XPutBackEvent(**otk::display
, &ev
);
1784 XSetInputFocus(**otk::display
, _window
,
1785 RevertToNone
, CurrentTime
);
1787 if (_focus_notify
) {
1789 ce
.xclient
.type
= ClientMessage
;
1790 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1791 ce
.xclient
.display
= **otk::display
;
1792 ce
.xclient
.window
= _window
;
1793 ce
.xclient
.format
= 32;
1794 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1795 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1796 ce
.xclient
.data
.l
[2] = 0l;
1797 ce
.xclient
.data
.l
[3] = 0l;
1798 ce
.xclient
.data
.l
[4] = 0l;
1799 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1802 XSync(**otk::display
, False
);
1807 void Client::unfocus() const
1809 assert(openbox
->focusedClient() == this);
1810 openbox
->setFocusedClient(0);
1814 void Client::focusHandler(const XFocusChangeEvent
&e
)
1817 // printf("FocusIn for 0x%lx\n", e.window);
1820 otk::EventHandler::focusHandler(e
);
1823 frame
->adjustFocus();
1825 calcLayer(); // focus state can affect the stacking layer
1827 openbox
->setFocusedClient(this);
1831 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1834 // printf("FocusOut for 0x%lx\n", e.window);
1837 otk::EventHandler::unfocusHandler(e
);
1840 frame
->adjustFocus();
1842 calcLayer(); // focus state can affect the stacking layer
1844 if (openbox
->focusedClient() == this)
1845 openbox
->setFocusedClient(0);
1849 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1852 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1855 otk::EventHandler::configureRequestHandler(ec
);
1858 XConfigureRequestEvent e
= ec
;
1860 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1862 // XXX if this causes bad things.. we can compress config req's with the
1864 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1865 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1866 e
.x
= ev
.xconfigurerequest
.x
;
1867 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1868 e
.y
= ev
.xconfigurerequest
.y
;
1869 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1870 e
.width
= ev
.xconfigurerequest
.width
;
1871 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1872 e
.height
= ev
.xconfigurerequest
.height
;
1873 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1874 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1875 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1876 e
.detail
= ev
.xconfigurerequest
.detail
;
1879 // if we are iconic (or shaded (fvwm does this)) ignore the event
1880 if (_iconic
|| _shaded
) return;
1882 if (e
.value_mask
& CWBorderWidth
)
1883 _border_width
= e
.border_width
;
1885 // resize, then move, as specified in the EWMH section 7.7
1886 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1887 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1888 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1892 case NorthEastGravity
:
1896 case SouthWestGravity
:
1898 corner
= BottomLeft
;
1900 case SouthEastGravity
:
1901 corner
= BottomRight
;
1903 default: // NorthWest, Static, etc
1907 // if moving AND resizing ...
1908 if (e
.value_mask
& (CWX
| CWY
)) {
1909 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1910 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1911 internal_resize(corner
, w
, h
, false, x
, y
);
1912 } else // if JUST resizing...
1913 internal_resize(corner
, w
, h
, false);
1914 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1915 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1916 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1917 internal_move(x
, y
);
1920 if (e
.value_mask
& CWStackMode
) {
1924 openbox
->screen(_screen
)->lowerWindow(this);
1930 openbox
->screen(_screen
)->raiseWindow(this);
1937 void Client::unmapHandler(const XUnmapEvent
&e
)
1939 if (ignore_unmaps
) {
1941 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1948 printf("UnmapNotify for 0x%lx\n", e
.window
);
1951 otk::EventHandler::unmapHandler(e
);
1953 // this deletes us etc
1954 openbox
->screen(_screen
)->unmanageWindow(this);
1958 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1961 printf("DestroyNotify for 0x%lx\n", e
.window
);
1964 otk::EventHandler::destroyHandler(e
);
1966 // this deletes us etc
1967 openbox
->screen(_screen
)->unmanageWindow(this);
1971 void Client::reparentHandler(const XReparentEvent
&e
)
1973 // this is when the client is first taken captive in the frame
1974 if (e
.parent
== frame
->plate()) return;
1977 printf("ReparentNotify for 0x%lx\n", e
.window
);
1980 otk::EventHandler::reparentHandler(e
);
1983 This event is quite rare and is usually handled in unmapHandler.
1984 However, if the window is unmapped when the reparent event occurs,
1985 the window manager never sees it because an unmap event is not sent
1986 to an already unmapped window.
1989 // we don't want the reparent event, put it back on the stack for the X
1990 // server to deal with after we unmanage the window
1993 XPutBackEvent(**otk::display
, &ev
);
1995 // this deletes us etc
1996 openbox
->screen(_screen
)->unmanageWindow(this);
1999 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
2002 printf("MapRequest for already managed 0x%lx\n", e
.window
);
2005 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
2007 // move to the current desktop (uniconify)
2009 // XXX: should we focus/raise the window? (basically a net_wm_active_window)