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();
155 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_desktop
,
156 otk::Property::atoms
.cardinal
,
157 (long unsigned*)&_desktop
)) {
159 // printf("Window requested desktop: %ld\n", _desktop);
165 void Client::getType()
167 _type
= (WindowType
) -1;
171 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_window_type
,
172 otk::Property::atoms
.atom
, &num
, &val
)) {
173 // use the first value that we know about in the array
174 for (unsigned long i
= 0; i
< num
; ++i
) {
175 if (val
[i
] == otk::Property::atoms
.net_wm_window_type_desktop
)
176 _type
= Type_Desktop
;
177 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dock
)
179 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_toolbar
)
180 _type
= Type_Toolbar
;
181 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_menu
)
183 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_utility
)
184 _type
= Type_Utility
;
185 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_splash
)
187 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_dialog
)
189 else if (val
[i
] == otk::Property::atoms
.net_wm_window_type_normal
)
191 else if (val
[i
] == otk::Property::atoms
.kde_net_wm_window_type_override
){
192 // prevent this window from getting any decor or functionality
193 _mwmhints
.flags
&= MwmFlag_Functions
| MwmFlag_Decorations
;
194 _mwmhints
.decorations
= 0;
195 _mwmhints
.functions
= 0;
197 if (_type
!= (WindowType
) -1)
198 break; // grab the first known type
203 if (_type
== (WindowType
) -1) {
205 * the window type hint was not set, which means we either classify ourself
206 * as a normal window or a dialog, depending on if we are a transient.
216 void Client::setupDecorAndFunctions()
218 // start with everything (cept fullscreen)
219 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
| Decor_Icon
|
220 Decor_AllDesktops
| Decor_Iconify
| Decor_Maximize
;
221 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
|
223 if (_delete_window
) {
224 _decorations
|= Decor_Close
;
225 _functions
|= Func_Close
;
228 if (!(_min_size
.width() < _max_size
.width() ||
229 _min_size
.height() < _max_size
.height())) {
230 _decorations
&= ~(Decor_Maximize
| Decor_Handle
);
231 _functions
&= ~(Func_Resize
| Func_Maximize
);
236 // normal windows retain all of the possible decorations and
237 // functionality, and are the only windows that you can fullscreen
238 _functions
|= Func_Fullscreen
;
242 // dialogs cannot be maximized
243 _decorations
&= ~Decor_Maximize
;
244 _functions
&= ~Func_Maximize
;
250 // these windows get less functionality
251 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
252 _functions
&= ~(Func_Iconify
| Func_Resize
);
258 // none of these windows are manipulated by the window manager
264 // Mwm Hints are applied subtractively to what has already been chosen for
265 // decor and functionality
266 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
267 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
268 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
269 _decorations
&= ~Decor_Border
;
270 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
271 _decorations
&= ~Decor_Handle
;
272 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
273 _decorations
&= ~Decor_Titlebar
;
274 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
275 _decorations
&= ~Decor_Iconify
;
276 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
277 _decorations
&= ~Decor_Maximize
;
281 if (_mwmhints
.flags
& MwmFlag_Functions
) {
282 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
283 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
284 _functions
&= ~Func_Resize
;
285 if (! (_mwmhints
.functions
& MwmFunc_Move
))
286 _functions
&= ~Func_Move
;
287 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
288 _functions
&= ~Func_Iconify
;
289 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
290 _functions
&= ~Func_Maximize
;
291 // dont let mwm hints kill the close button
292 //if (! (_mwmhints.functions & MwmFunc_Close))
293 // _functions &= ~Func_Close;
297 // can't maximize without moving/resizing
298 if (!((_functions
& Func_Move
) && (_functions
& Func_Resize
)))
299 _functions
&= ~Func_Maximize
;
301 // finally, user specified disabled decorations are applied to subtract
303 if (_disabled_decorations
& Decor_Titlebar
)
304 _decorations
&= ~Decor_Titlebar
;
305 if (_disabled_decorations
& Decor_Handle
)
306 _decorations
&= ~Decor_Handle
;
307 if (_disabled_decorations
& Decor_Border
)
308 _decorations
&= ~Decor_Border
;
309 if (_disabled_decorations
& Decor_Iconify
)
310 _decorations
&= ~Decor_Iconify
;
311 if (_disabled_decorations
& Decor_Maximize
)
312 _decorations
&= ~Decor_Maximize
;
313 if (_disabled_decorations
& Decor_AllDesktops
)
314 _decorations
&= ~Decor_AllDesktops
;
315 if (_disabled_decorations
& Decor_Close
)
316 _decorations
&= ~Decor_Close
;
318 // if we don't have a titlebar, then we cannot shade!
319 if (!(_decorations
& Decor_Titlebar
))
320 _functions
&= ~Func_Shade
;
322 changeAllowedActions();
325 frame
->adjustSize(); // change the decors on the frame
326 frame
->adjustPosition(); // with more/less decorations, we may need to be
328 remaximize(); // with new decor, the window's maximized size may change
333 void Client::getMwmHints()
335 unsigned long num
= MwmHints::elements
;
336 unsigned long *hints
;
338 _mwmhints
.flags
= 0; // default to none
340 if (!otk::Property::get(_window
, otk::Property::atoms
.motif_wm_hints
,
341 otk::Property::atoms
.motif_wm_hints
, &num
,
342 (unsigned long **)&hints
))
345 if (num
>= MwmHints::elements
) {
346 // retrieved the hints
347 _mwmhints
.flags
= hints
[0];
348 _mwmhints
.functions
= hints
[1];
349 _mwmhints
.decorations
= hints
[2];
356 void Client::getArea()
358 XWindowAttributes wattrib
;
361 ret
= XGetWindowAttributes(**otk::display
, _window
, &wattrib
);
362 assert(ret
!= BadWindow
);
364 _area
= otk::Rect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
365 _border_width
= wattrib
.border_width
;
369 void Client::getState()
371 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
372 _iconic
= _skip_taskbar
= _skip_pager
= false;
374 unsigned long *state
;
377 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_state
,
378 otk::Property::atoms
.atom
, &num
, &state
)) {
379 for (unsigned long i
= 0; i
< num
; ++i
) {
380 if (state
[i
] == otk::Property::atoms
.net_wm_state_modal
)
382 else if (state
[i
] == otk::Property::atoms
.net_wm_state_shaded
)
384 else if (state
[i
] == otk::Property::atoms
.net_wm_state_hidden
)
386 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_taskbar
)
387 _skip_taskbar
= true;
388 else if (state
[i
] == otk::Property::atoms
.net_wm_state_skip_pager
)
390 else if (state
[i
] == otk::Property::atoms
.net_wm_state_fullscreen
)
392 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_vert
)
394 else if (state
[i
] == otk::Property::atoms
.net_wm_state_maximized_horz
)
396 else if (state
[i
] == otk::Property::atoms
.net_wm_state_above
)
398 else if (state
[i
] == otk::Property::atoms
.net_wm_state_below
)
406 void Client::getShaped()
410 if (otk::display
->shape()) {
415 XShapeSelectInput(**otk::display
, _window
, ShapeNotifyMask
);
417 XShapeQueryExtents(**otk::display
, _window
, &s
, &foo
,
418 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
424 Client
*Client::searchFocusTree(Client
*node
, Client
*skip
)
426 List::const_iterator it
, end
= node
->_transients
.end();
429 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
430 if (*it
== skip
) continue; // circular?
431 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
432 if ((*it
)->_focused
) return *it
; // got one
437 void Client::calcLayer() {
441 // are we fullscreen, or do we have a fullscreen transient parent?
444 if (c
->_fullscreen
) {
448 c
= c
->_transient_for
;
450 if (!fs
&& _fullscreen
) {
451 // is one of our transients focused?
452 c
= searchFocusTree(this, this);
456 if (_iconic
) l
= Layer_Icon
;
457 else if (fs
) l
= Layer_Fullscreen
;
458 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
459 else if (_type
== Type_Dock
) {
460 if (!_below
) l
= Layer_Top
;
461 else l
= Layer_Normal
;
463 else if (_above
) l
= Layer_Above
;
464 else if (_below
) l
= Layer_Below
;
465 else l
= Layer_Normal
;
471 if we don't have a frame, then we aren't mapped yet (and this would
474 openbox
->screen(_screen
)->raiseWindow(this);
479 void Client::updateProtocols()
484 _focus_notify
= false;
485 _delete_window
= false;
487 if (XGetWMProtocols(**otk::display
, _window
, &proto
, &num_return
)) {
488 for (int i
= 0; i
< num_return
; ++i
) {
489 if (proto
[i
] == otk::Property::atoms
.wm_delete_window
) {
490 // this means we can request the window to close
491 _delete_window
= true;
492 } else if (proto
[i
] == otk::Property::atoms
.wm_take_focus
)
493 // if this protocol is requested, then the window will be notified
494 // by the window manager whenever it receives focus
495 _focus_notify
= true;
501 void Client::updateNormalHints()
505 int oldgravity
= _gravity
;
510 _size_inc
= otk::Size(1, 1);
511 _base_size
= otk::Size(0, 0);
512 _min_size
= otk::Size(0, 0);
513 _max_size
= otk::Size(INT_MAX
, INT_MAX
);
515 // get the hints from the window
516 if (XGetWMNormalHints(**otk::display
, _window
, &size
, &ret
)) {
517 _positioned
= (size
.flags
& (PPosition
|USPosition
));
519 if (size
.flags
& PWinGravity
) {
520 _gravity
= size
.win_gravity
;
522 // if the client has a frame, i.e. has already been mapped and is
523 // changing its gravity
524 if (frame
&& _gravity
!= oldgravity
) {
525 // move our idea of the client's position based on its new gravity
526 int x
= frame
->area().x(), y
= frame
->area().y();
527 frame
->frameGravity(x
, y
);
528 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
532 if (size
.flags
& PAspect
) {
533 if (size
.min_aspect
.y
) _min_ratio
= size
.min_aspect
.x
/size
.min_aspect
.y
;
534 if (size
.max_aspect
.y
) _max_ratio
= size
.max_aspect
.x
/size
.max_aspect
.y
;
537 if (size
.flags
& PMinSize
)
538 _min_size
= otk::Size(size
.min_width
, size
.min_height
);
540 if (size
.flags
& PMaxSize
)
541 _max_size
= otk::Size(size
.max_width
, size
.max_height
);
543 if (size
.flags
& PBaseSize
)
544 _base_size
= otk::Size(size
.base_width
, size
.base_height
);
546 if (size
.flags
& PResizeInc
)
547 _size_inc
= otk::Size(size
.width_inc
, size
.height_inc
);
551 void Client::updateWMHints(bool initstate
)
555 // assume a window takes input if it doesnt specify
559 if ((hints
= XGetWMHints(**otk::display
, _window
)) != NULL
) {
560 if (hints
->flags
& InputHint
)
561 _can_focus
= hints
->input
;
563 // only do this when initstate is true!
564 if (initstate
&& (hints
->flags
& StateHint
))
565 _iconic
= hints
->initial_state
== IconicState
;
567 if (hints
->flags
& XUrgencyHint
)
570 if (hints
->flags
& WindowGroupHint
) {
571 if (hints
->window_group
!= _group
) {
572 // XXX: remove from the old group if there was one
573 _group
= hints
->window_group
;
574 // XXX: do stuff with the group
579 if (hints
->flags
& IconPixmapHint
) {
580 updateKwmIcon(); // try get the kwm icon first, this is a fallback only
581 if (_pixmap_icon
== None
) {
582 _pixmap_icon
= hints
->icon_pixmap
;
583 if (hints
->flags
& IconMaskHint
)
584 _pixmap_icon_mask
= hints
->icon_mask
;
586 _pixmap_icon_mask
= None
;
596 printf("Urgent Hint for 0x%lx: %s\n",
597 (long)_window
, _urgent
? "ON" : "OFF");
599 // fire the urgent callback if we're mapped, otherwise, wait until after
606 void Client::updateTitle()
611 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
612 otk::Property::utf8
, &_title
)) {
614 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
615 otk::Property::ascii
, &_title
);
619 _title
= _("Unnamed Window");
622 frame
->adjustTitle();
625 void Client::updateIconTitle()
630 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
631 otk::Property::utf8
, &_icon_title
)) {
633 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
634 otk::Property::ascii
, &_icon_title
);
638 _icon_title
= _("Unnamed Window");
641 void Client::updateClass()
644 _app_name
= _app_class
= _role
= "";
646 otk::Property::StringVect v
;
647 unsigned long num
= 2;
649 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
650 otk::Property::ascii
, &num
, &v
)) {
651 if (num
> 0) _app_name
= v
[0].c_str();
652 if (num
> 1) _app_class
= v
[1].c_str();
657 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
658 otk::Property::ascii
, &num
, &v
)) {
659 if (num
> 0) _role
= v
[0].c_str();
663 void Client::updateStrut()
665 unsigned long num
= 4;
667 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
668 otk::Property::atoms
.cardinal
, &num
, &data
))
672 _strut
.left
= data
[0];
673 _strut
.right
= data
[1];
674 _strut
.top
= data
[2];
675 _strut
.bottom
= data
[3];
677 // updating here is pointless while we're being mapped cuz we're not in
678 // the screen's client list yet
680 openbox
->screen(_screen
)->updateStruts();
686 void Client::updateTransientFor()
691 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
692 t
!= _window
) { // cant be transient to itself!
693 c
= openbox
->findClient(t
);
694 assert(c
!= this); // if this happens then we need to check for it
696 if (!c
/*XXX: && _group*/) {
697 // not transient to a client, see if it is transient for a group
698 if (//t == _group->leader() ||
700 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
701 // window is a transient for its group!
702 // XXX: for now this is treated as non-transient.
703 // this needs to be fixed!
708 // if anything has changed...
709 if (c
!= _transient_for
) {
711 _transient_for
->_transients
.remove(this); // remove from old parent
714 _transient_for
->_transients
.push_back(this); // add to new parent
718 void Client::updateIcons()
722 unsigned long w
, h
, i
= 0;
724 for (int j
= 0; j
< _nicons
; ++j
)
725 delete [] _icons
[j
].data
;
730 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon
,
731 otk::Property::atoms
.cardinal
, &num
, &data
)) {
732 // figure out how man valid icons are in here
733 while (num
- i
> 2) {
741 _icons
= new Icon
[_nicons
];
745 for (int j
= 0; j
< _nicons
; ++j
) {
746 w
= _icons
[j
].w
= data
[i
++];
747 h
= _icons
[j
].h
= data
[i
++];
748 _icons
[j
].data
= new unsigned long[w
* h
];
749 ::memcpy(_icons
[j
].data
, &data
[i
], w
* h
* sizeof(unsigned long));
759 _icons
= new Icon
[1];
765 assert(_nicons
> 0); // there should always be a default..
767 if (frame
) frame
->adjustIcon();
770 void Client::updateKwmIcon()
772 _pixmap_icon
= _pixmap_icon_mask
= None
;
774 unsigned long num
= 2;
776 if (otk::Property::get(_window
, otk::Property::atoms
.kwm_win_icon
,
777 otk::Property::atoms
.kwm_win_icon
, &num
, &data
)) {
779 _pixmap_icon
= data
[0];
780 _pixmap_icon_mask
= data
[1];
786 void Client::propertyHandler(const XPropertyEvent
&e
)
788 otk::EventHandler::propertyHandler(e
);
790 // validate cuz we query stuff off the client here
791 if (!validate()) return;
793 // compress changes to a single property into a single change
795 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
796 // XXX: it would be nice to compress ALL changes to a property, not just
797 // changes in a row without other props between.
798 if (ce
.xproperty
.atom
!= e
.atom
) {
799 XPutBackEvent(**otk::display
, &ce
);
804 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
806 setupDecorAndFunctions(); // normal hints can make a window non-resizable
807 } else if (e
.atom
== XA_WM_HINTS
)
809 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
810 updateTransientFor();
812 calcLayer(); // type may have changed, so update the layer
813 setupDecorAndFunctions();
815 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
816 e
.atom
== otk::Property::atoms
.wm_name
)
818 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
819 e
.atom
== otk::Property::atoms
.wm_icon_name
)
821 else if (e
.atom
== otk::Property::atoms
.wm_class
)
823 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
825 setupDecorAndFunctions();
827 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
829 else if (e
.atom
== otk::Property::atoms
.net_wm_icon
)
831 else if (e
.atom
== otk::Property::atoms
.kwm_win_icon
)
835 void Client::setWMState(long state
)
837 if (state
== _wmstate
) return; // no change
849 void Client::setDesktop(unsigned int target
)
851 if (target
== _desktop
) return;
853 printf("Setting desktop %u\n", target
);
855 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
856 target
== 0xffffffff))
860 // set the desktop hint
861 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
862 otk::Property::atoms
.cardinal
, _desktop
);
863 frame
->adjustState(); // the frame can display the current desktop state
864 // 'move' the window to the new desktop
866 openbox
->screen(_screen
)->updateStruts();
869 void Client::showhide()
872 Screen
*s
= openbox
->screen(_screen
);
874 if (_iconic
) show
= false;
875 else if (!(_desktop
== s
->desktop() ||
876 _desktop
== 0xffffffff)) show
= false;
877 else if (normal() && s
->showingDesktop()) show
= false;
880 if (show
) frame
->show();
884 void Client::setState(Atom action
, long data1
, long data2
)
886 bool shadestate
= _shaded
;
887 bool fsstate
= _fullscreen
;
888 bool maxh
= _max_horz
;
889 bool maxv
= _max_vert
;
891 if (!(action
== otk::Property::atoms
.net_wm_state_add
||
892 action
== otk::Property::atoms
.net_wm_state_remove
||
893 action
== otk::Property::atoms
.net_wm_state_toggle
))
894 return; // an invalid action was passed to the client message, ignore it
896 for (int i
= 0; i
< 2; ++i
) {
897 Atom state
= i
== 0 ? data1
: data2
;
899 if (! state
) continue;
901 // if toggling, then pick whether we're adding or removing
902 if (action
== otk::Property::atoms
.net_wm_state_toggle
) {
903 if (state
== otk::Property::atoms
.net_wm_state_modal
)
904 action
= _modal
? otk::Property::atoms
.net_wm_state_remove
:
905 otk::Property::atoms
.net_wm_state_add
;
906 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
907 action
= _max_vert
? 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_horz
)
910 action
= _max_horz
? 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_shaded
)
913 action
= _shaded
? 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_skip_taskbar
)
916 action
= _skip_taskbar
? 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_pager
)
919 action
= _skip_pager
? 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_fullscreen
)
922 action
= _fullscreen
? 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_above
)
925 action
= _above
? 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_below
)
928 action
= _below
? otk::Property::atoms
.net_wm_state_remove
:
929 otk::Property::atoms
.net_wm_state_add
;
932 if (action
== otk::Property::atoms
.net_wm_state_add
) {
933 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
934 if (_modal
) continue;
936 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
938 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
939 if (_max_horz
) continue;
941 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
943 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
944 _skip_taskbar
= true;
945 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
947 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
949 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
950 if (_above
) continue;
952 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
953 if (_below
) continue;
957 } else { // action == otk::Property::atoms.net_wm_state_remove
958 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
959 if (!_modal
) continue;
961 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
963 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
965 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
967 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
968 _skip_taskbar
= false;
969 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
971 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
973 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
974 if (!_above
) continue;
976 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
977 if (!_below
) continue;
982 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
983 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
984 if (maxh
== maxv
) { // both going the same way
985 maximize(maxh
, 0, true);
987 maximize(maxh
, 1, true);
988 maximize(maxv
, 2, true);
990 } else { // toggling one
991 if (maxh
!= _max_horz
)
992 maximize(maxh
, 1, true);
994 maximize(maxv
, 2, true);
997 // change fullscreen state before shading, as it will affect if the window
999 if (fsstate
!= _fullscreen
)
1000 fullscreen(fsstate
, true);
1001 if (shadestate
!= _shaded
)
1004 changeState(); // change the hint to relect these changes
1007 void Client::toggleClientBorder(bool addborder
)
1009 // adjust our idea of where the client is, based on its border. When the
1010 // border is removed, the client should now be considered to be in a
1011 // different position.
1012 // when re-adding the border to the client, the same operation needs to be
1014 int oldx
= _area
.x(), oldy
= _area
.y();
1015 int x
= oldx
, y
= oldy
;
1018 case NorthWestGravity
:
1020 case SouthWestGravity
:
1022 case NorthEastGravity
:
1024 case SouthEastGravity
:
1025 if (addborder
) x
-= _border_width
* 2;
1026 else x
+= _border_width
* 2;
1033 if (addborder
) x
-= _border_width
;
1034 else x
+= _border_width
;
1039 case NorthWestGravity
:
1041 case NorthEastGravity
:
1043 case SouthWestGravity
:
1045 case SouthEastGravity
:
1046 if (addborder
) y
-= _border_width
* 2;
1047 else y
+= _border_width
* 2;
1054 if (addborder
) y
-= _border_width
;
1055 else y
+= _border_width
;
1058 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1061 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1063 // move the client so it is back it the right spot _with_ its border!
1064 if (x
!= oldx
|| y
!= oldy
)
1065 XMoveWindow(**otk::display
, _window
, x
, y
);
1067 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1070 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1072 otk::EventHandler::clientMessageHandler(e
);
1074 // validate cuz we query stuff off the client here
1075 if (!validate()) return;
1077 if (e
.format
!= 32) return;
1079 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1080 // compress changes into a single change
1081 bool compress
= false;
1083 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1084 // XXX: it would be nice to compress ALL messages of a type, not just
1085 // messages in a row without other message types between.
1086 if (ce
.xclient
.message_type
!= e
.message_type
) {
1087 XPutBackEvent(**otk::display
, &ce
);
1093 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1095 setWMState(e
.data
.l
[0]); // use the original event
1096 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1097 // compress changes into a single change
1098 bool compress
= false;
1100 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1101 // XXX: it would be nice to compress ALL messages of a type, not just
1102 // messages in a row without other message types between.
1103 if (ce
.xclient
.message_type
!= e
.message_type
) {
1104 XPutBackEvent(**otk::display
, &ce
);
1110 setDesktop(e
.data
.l
[0]); // use the found event
1112 setDesktop(e
.data
.l
[0]); // use the original event
1113 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1114 // can't compress these
1116 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1117 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1118 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1119 e
.data
.l
[1], e
.data
.l
[2], _window
);
1121 setState(e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1122 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1124 printf("net_close_window for 0x%lx\n", _window
);
1127 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1129 printf("net_active_window for 0x%lx\n", _window
);
1131 if (openbox
->screen(_screen
)->showingDesktop())
1132 openbox
->screen(_screen
)->showDesktop(false);
1135 else if (!frame
->visible()) // if its not visible for other reasons, then
1136 return; // don't mess with it
1140 openbox
->screen(_screen
)->raiseWindow(this);
1141 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1142 if (openbox
->screen(_screen
)->showingDesktop())
1143 openbox
->screen(_screen
)->showDesktop(false);
1146 else if (!frame
->visible()) // if its not visible for other reasons, then
1147 return; // don't mess with it
1148 if (e
.data
.l
[0] && _shaded
)
1152 openbox
->screen(_screen
)->raiseWindow(this);
1153 } else if (e
.message_type
== otk::Property::atoms
.openbox_restack_window
) {
1155 printf("openbox_restack_window for 0x%lx\n", _window
);
1157 if (e
.data
.l
[0] == 0)
1158 openbox
->screen(_screen
)->raiseWindow(this);
1159 else if (e
.data
.l
[0] == 1)
1160 openbox
->screen(_screen
)->lowerWindow(this);
1165 void Client::shapeHandler(const XShapeEvent
&e
)
1167 otk::EventHandler::shapeHandler(e
);
1169 if (e
.kind
== ShapeBounding
) {
1171 frame
->adjustShape();
1176 void Client::resize(Corner anchor
, int w
, int h
)
1178 if (!(_functions
& Func_Resize
)) return;
1179 internal_resize(anchor
, w
, h
);
1182 void Client::internal_resize(Corner anchor
, int w
, int h
,
1183 bool user
, int x
, int y
)
1185 w
-= _base_size
.width();
1186 h
-= _base_size
.height();
1189 // for interactive resizing. have to move half an increment in each
1191 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1192 int mh
= h
% _size_inc
.height();
1193 int aw
= _size_inc
.width() / 2; // amount to add
1194 int ah
= _size_inc
.height() / 2;
1195 // don't let us move into a new size increment
1196 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1197 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1201 // if this is a user-requested resize, then check against min/max sizes
1202 // and aspect ratios
1204 // smaller than min size or bigger than max size?
1205 if (w
> _max_size
.width()) w
= _max_size
.width();
1206 if (w
< _min_size
.width()) w
= _min_size
.width();
1207 if (h
> _max_size
.height()) h
= _max_size
.height();
1208 if (h
< _min_size
.height()) h
= _min_size
.height();
1210 // adjust the height ot match the width for the aspect ratios
1212 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1214 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1217 // keep to the increments
1218 w
/= _size_inc
.width();
1219 h
/= _size_inc
.height();
1221 // you cannot resize to nothing
1225 // store the logical size
1226 _logical_size
= otk::Size(w
, h
);
1228 w
*= _size_inc
.width();
1229 h
*= _size_inc
.height();
1231 w
+= _base_size
.width();
1232 h
+= _base_size
.height();
1234 if (x
== INT_MIN
|| y
== INT_MIN
) {
1241 x
-= w
- _area
.width();
1244 y
-= h
- _area
.height();
1247 x
-= w
- _area
.width();
1248 y
-= h
- _area
.height();
1253 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1255 XResizeWindow(**otk::display
, _window
, w
, h
);
1257 // resize the frame to match the request
1258 frame
->adjustSize();
1259 internal_move(x
, y
);
1262 const Icon
*Client::icon(const otk::Size
&s
) const
1264 unsigned long req
= s
.width() * s
.height();
1265 // si is the smallest image >= req
1266 // li is the largest image < req
1267 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1269 assert(_nicons
> 0); // there should always be a default..
1270 for (int i
= 0; i
< _nicons
; ++i
) {
1271 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1272 if (size
< smallest
&& size
>= req
) {
1276 if (size
> largest
&& size
<= req
) {
1281 if (largest
== 0) // didnt find one smaller than the requested size
1286 void Client::move(int x
, int y
, bool final
)
1288 if (!(_functions
& Func_Move
)) return;
1289 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1291 internal_move(x
, y
, final
);
1294 void Client::internal_move(int x
, int y
, bool final
)
1296 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1298 // move the frame to be in the requested position
1299 if (frame
) { // this can be called while mapping, before frame exists
1300 frame
->adjustPosition();
1302 // send synthetic configure notify (we don't need to if we aren't mapped
1306 event
.type
= ConfigureNotify
;
1307 event
.xconfigure
.display
= **otk::display
;
1308 event
.xconfigure
.event
= _window
;
1309 event
.xconfigure
.window
= _window
;
1311 // root window coords with border in mind
1312 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1313 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1315 event
.xconfigure
.width
= _area
.width();
1316 event
.xconfigure
.height
= _area
.height();
1317 event
.xconfigure
.border_width
= _border_width
;
1318 event
.xconfigure
.above
= frame
->plate();
1319 event
.xconfigure
.override_redirect
= False
;
1320 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1321 StructureNotifyMask
, &event
);
1323 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1324 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1325 event
.xconfigure
.height
, event
.xconfigure
.window
);
1331 void Client::close()
1335 if (!(_functions
& Func_Close
)) return;
1337 // XXX: itd be cool to do timeouts and shit here for killing the client's
1339 // like... if the window is around after 5 seconds, then the close button
1340 // turns a nice red, and if this function is called again, the client is
1341 // explicitly killed.
1343 ce
.xclient
.type
= ClientMessage
;
1344 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1345 ce
.xclient
.display
= **otk::display
;
1346 ce
.xclient
.window
= _window
;
1347 ce
.xclient
.format
= 32;
1348 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1349 ce
.xclient
.data
.l
[1] = CurrentTime
;
1350 ce
.xclient
.data
.l
[2] = 0l;
1351 ce
.xclient
.data
.l
[3] = 0l;
1352 ce
.xclient
.data
.l
[4] = 0l;
1353 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1356 void Client::changeState()
1358 unsigned long state
[2];
1359 state
[0] = _wmstate
;
1361 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1362 otk::Property::atoms
.wm_state
, state
, 2);
1367 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1369 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1371 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1373 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1375 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1377 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1379 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1381 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1383 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1385 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1386 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1387 otk::Property::atoms
.atom
, netstate
, num
);
1392 frame
->adjustState();
1395 void Client::changeAllowedActions(void)
1400 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1402 if (_functions
& Func_Shade
)
1403 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1404 if (_functions
& Func_Close
)
1405 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1406 if (_functions
& Func_Move
)
1407 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1408 if (_functions
& Func_Iconify
)
1409 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1410 if (_functions
& Func_Resize
)
1411 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1412 if (_functions
& Func_Fullscreen
)
1413 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1414 if (_functions
& Func_Maximize
) {
1415 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1416 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1419 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1420 otk::Property::atoms
.atom
, actions
, num
);
1422 // make sure the window isn't breaking any rules now
1424 if (!(_functions
& Func_Shade
) && _shaded
)
1425 if (frame
) shade(false);
1426 else _shaded
= false;
1427 if (!(_functions
& Func_Iconify
) && _iconic
)
1428 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1429 else _iconic
= false;
1430 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1431 if (frame
) fullscreen(false);
1432 else _fullscreen
= false;
1433 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1434 if (frame
) maximize(false, 0);
1435 else _max_vert
= _max_horz
= false;
1438 void Client::remaximize()
1441 if (_max_horz
&& _max_vert
)
1448 return; // not maximized
1449 _max_horz
= _max_vert
= false;
1450 maximize(true, dir
, false);
1453 void Client::applyStartupState()
1455 // these are in a carefully crafted order..
1462 _fullscreen
= false;
1463 fullscreen(true, false);
1472 if (_max_vert
&& _max_horz
) {
1473 _max_vert
= _max_horz
= false;
1474 maximize(true, 0, false);
1475 } else if (_max_vert
) {
1477 maximize(true, 2, false);
1478 } else if (_max_horz
) {
1480 maximize(true, 1, false);
1483 if (_skip_taskbar
); // nothing to do for this
1484 if (_skip_pager
); // nothing to do for this
1485 if (_modal
); // nothing to do for this
1486 if (_above
); // nothing to do for this
1487 if (_below
); // nothing to do for this
1490 void Client::fireUrgent()
1492 // call the python UrgentWindow callbacks
1493 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1494 openbox
->bindings()->fireEvent(&data
);
1497 void Client::shade(bool shade
)
1499 if (!(_functions
& Func_Shade
) || // can't
1500 _shaded
== shade
) return; // already done
1502 // when we're iconic, don't change the wmstate
1504 _wmstate
= shade
? IconicState
: NormalState
;
1507 frame
->adjustSize();
1510 void Client::maximize(bool max
, int dir
, bool savearea
)
1512 assert(dir
== 0 || dir
== 1 || dir
== 2);
1513 if (!(_functions
& Func_Maximize
)) return; // can't
1515 // check if already done
1517 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1518 if (dir
== 1 && _max_horz
) return;
1519 if (dir
== 2 && _max_vert
) return;
1521 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1522 if (dir
== 1 && !_max_horz
) return;
1523 if (dir
== 2 && !_max_vert
) return;
1526 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1527 int x
= frame
->area().x(), y
= frame
->area().y(),
1528 w
= _area
.width(), h
= _area
.height();
1534 unsigned long n
= 4;
1541 // get the property off the window and use it for the dimentions we are
1543 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1544 otk::Property::atoms
.cardinal
, &n
,
1545 (long unsigned**) &readdim
)) {
1548 dimensions
[0] = readdim
[0];
1549 dimensions
[2] = readdim
[2];
1552 dimensions
[1] = readdim
[1];
1553 dimensions
[3] = readdim
[3];
1559 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1560 otk::Property::atoms
.cardinal
,
1561 (long unsigned*)dimensions
, 4);
1563 if (dir
== 0 || dir
== 1) { // horz
1567 if (dir
== 0 || dir
== 2) { // vert
1569 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1573 long unsigned n
= 4;
1575 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1576 otk::Property::atoms
.cardinal
, &n
,
1577 (long unsigned**) &dimensions
)) {
1579 if (dir
== 0 || dir
== 1) { // horz
1580 x
= (signed int)dimensions
[0];
1581 w
= (signed int)dimensions
[2];
1583 if (dir
== 0 || dir
== 2) { // vert
1584 y
= (signed int)dimensions
[1];
1585 h
= (signed int)dimensions
[3];
1590 // pick some fallbacks...
1591 if (dir
== 0 || dir
== 1) { // horz
1592 x
= a
.x() + a
.width() / 4;
1595 if (dir
== 0 || dir
== 2) { // vert
1596 y
= a
.y() + a
.height() / 4;
1602 if (dir
== 0 || dir
== 1) // horz
1604 if (dir
== 0 || dir
== 2) // vert
1607 if (!_max_horz
&& !_max_vert
)
1608 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1610 changeState(); // change the state hints on the client
1612 frame
->frameGravity(x
, y
); // figure out where the client should be going
1613 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1616 void Client::fullscreen(bool fs
, bool savearea
)
1618 static FunctionFlags saved_func
;
1619 static DecorationFlags saved_decor
;
1621 if (!(_functions
& Func_Fullscreen
) || // can't
1622 _fullscreen
== fs
) return; // already done
1625 changeState(); // change the state hints on the client
1627 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1630 // save the functions and remove them
1631 saved_func
= _functions
;
1632 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1633 // save the decorations and remove them
1634 saved_decor
= _decorations
;
1638 dimensions
[0] = _area
.x();
1639 dimensions
[1] = _area
.y();
1640 dimensions
[2] = _area
.width();
1641 dimensions
[3] = _area
.height();
1642 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1643 otk::Property::atoms
.cardinal
,
1644 (long unsigned*)dimensions
, 4);
1646 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1649 w
= info
->size().width();
1650 h
= info
->size().height();
1652 _functions
= saved_func
;
1653 _decorations
= saved_decor
;
1656 long unsigned n
= 4;
1658 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1659 otk::Property::atoms
.cardinal
, &n
,
1660 (long unsigned**) &dimensions
)) {
1669 // pick some fallbacks...
1670 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1671 x
= a
.x() + a
.width() / 4;
1672 y
= a
.y() + a
.height() / 4;
1678 changeAllowedActions(); // based on the new _functions
1680 // when fullscreening, don't obey things like increments, fill the screen
1681 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1683 // raise (back) into our stacking layer
1684 openbox
->screen(_screen
)->raiseWindow(this);
1686 // try focus us when we go into fullscreen mode
1690 void Client::iconify(bool iconic
, bool curdesk
)
1692 if (_iconic
== iconic
) return; // nothing to do
1695 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1701 _wmstate
= IconicState
;
1703 // we unmap the client itself so that we can get MapRequest events, and
1704 // because the ICCCM tells us to!
1705 XUnmapWindow(**otk::display
, _window
);
1708 setDesktop(openbox
->screen(_screen
)->desktop());
1709 _wmstate
= NormalState
;
1710 XMapWindow(**otk::display
, _window
);
1714 openbox
->screen(_screen
)->updateStruts();
1717 void Client::disableDecorations(DecorationFlags flags
)
1719 _disabled_decorations
= flags
;
1720 setupDecorAndFunctions();
1723 void Client::installColormap(bool install
) const
1725 XWindowAttributes wa
;
1726 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1728 XInstallColormap(**otk::display
, wa
.colormap
);
1730 XUninstallColormap(**otk::display
, wa
.colormap
);
1734 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1736 List::const_iterator it
, end
= node
->_transients
.end();
1739 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1740 if (*it
== skip
) continue; // circular?
1741 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1742 if ((*it
)->_modal
) return *it
; // got one
1747 Client
*Client::findModalChild()
1749 return searchModalTree(this, this);
1753 bool Client::focus()
1755 // if we have a modal child, then focus it, not us
1756 Client
*c
= findModalChild();
1757 if (c
) return c
->focus();
1759 // won't try focus if the client doesn't want it, or if the window isn't
1760 // visible on the screen
1761 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1763 // do a check to see if the window has already been unmapped or destroyed
1764 // do this intelligently while watching out for unmaps we've generated
1765 // (ignore_unmaps > 0)
1767 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1768 XPutBackEvent(**otk::display
, &ev
);
1771 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1772 if (ignore_unmaps
) {
1773 unmapHandler(ev
.xunmap
);
1775 XPutBackEvent(**otk::display
, &ev
);
1781 XSetInputFocus(**otk::display
, _window
,
1782 RevertToNone
, CurrentTime
);
1784 if (_focus_notify
) {
1786 ce
.xclient
.type
= ClientMessage
;
1787 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1788 ce
.xclient
.display
= **otk::display
;
1789 ce
.xclient
.window
= _window
;
1790 ce
.xclient
.format
= 32;
1791 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1792 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1793 ce
.xclient
.data
.l
[2] = 0l;
1794 ce
.xclient
.data
.l
[3] = 0l;
1795 ce
.xclient
.data
.l
[4] = 0l;
1796 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1799 XSync(**otk::display
, False
);
1804 void Client::unfocus() const
1806 assert(openbox
->focusedClient() == this);
1807 openbox
->setFocusedClient(0);
1811 void Client::focusHandler(const XFocusChangeEvent
&e
)
1814 // printf("FocusIn for 0x%lx\n", e.window);
1817 otk::EventHandler::focusHandler(e
);
1820 frame
->adjustFocus();
1822 calcLayer(); // focus state can affect the stacking layer
1824 openbox
->setFocusedClient(this);
1828 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1831 // printf("FocusOut for 0x%lx\n", e.window);
1834 otk::EventHandler::unfocusHandler(e
);
1837 frame
->adjustFocus();
1839 calcLayer(); // focus state can affect the stacking layer
1841 if (openbox
->focusedClient() == this)
1842 openbox
->setFocusedClient(0);
1846 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1849 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1852 otk::EventHandler::configureRequestHandler(ec
);
1855 XConfigureRequestEvent e
= ec
;
1857 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1859 // XXX if this causes bad things.. we can compress config req's with the
1861 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1862 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1863 e
.x
= ev
.xconfigurerequest
.x
;
1864 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1865 e
.y
= ev
.xconfigurerequest
.y
;
1866 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1867 e
.width
= ev
.xconfigurerequest
.width
;
1868 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1869 e
.height
= ev
.xconfigurerequest
.height
;
1870 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1871 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1872 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1873 e
.detail
= ev
.xconfigurerequest
.detail
;
1876 // if we are iconic (or shaded (fvwm does this)) ignore the event
1877 if (_iconic
|| _shaded
) return;
1879 if (e
.value_mask
& CWBorderWidth
)
1880 _border_width
= e
.border_width
;
1882 // resize, then move, as specified in the EWMH section 7.7
1883 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1884 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1885 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1889 case NorthEastGravity
:
1893 case SouthWestGravity
:
1895 corner
= BottomLeft
;
1897 case SouthEastGravity
:
1898 corner
= BottomRight
;
1900 default: // NorthWest, Static, etc
1904 // if moving AND resizing ...
1905 if (e
.value_mask
& (CWX
| CWY
)) {
1906 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1907 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1908 internal_resize(corner
, w
, h
, false, x
, y
);
1909 } else // if JUST resizing...
1910 internal_resize(corner
, w
, h
, false);
1911 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1912 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1913 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1914 internal_move(x
, y
);
1917 if (e
.value_mask
& CWStackMode
) {
1921 openbox
->screen(_screen
)->lowerWindow(this);
1927 openbox
->screen(_screen
)->raiseWindow(this);
1934 void Client::unmapHandler(const XUnmapEvent
&e
)
1936 if (ignore_unmaps
) {
1938 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1945 printf("UnmapNotify for 0x%lx\n", e
.window
);
1948 otk::EventHandler::unmapHandler(e
);
1950 // this deletes us etc
1951 openbox
->screen(_screen
)->unmanageWindow(this);
1955 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1958 printf("DestroyNotify for 0x%lx\n", e
.window
);
1961 otk::EventHandler::destroyHandler(e
);
1963 // this deletes us etc
1964 openbox
->screen(_screen
)->unmanageWindow(this);
1968 void Client::reparentHandler(const XReparentEvent
&e
)
1970 // this is when the client is first taken captive in the frame
1971 if (e
.parent
== frame
->plate()) return;
1974 printf("ReparentNotify for 0x%lx\n", e
.window
);
1977 otk::EventHandler::reparentHandler(e
);
1980 This event is quite rare and is usually handled in unmapHandler.
1981 However, if the window is unmapped when the reparent event occurs,
1982 the window manager never sees it because an unmap event is not sent
1983 to an already unmapped window.
1986 // we don't want the reparent event, put it back on the stack for the X
1987 // server to deal with after we unmanage the window
1990 XPutBackEvent(**otk::display
, &ev
);
1992 // this deletes us etc
1993 openbox
->screen(_screen
)->unmanageWindow(this);
1996 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1999 printf("MapRequest for already managed 0x%lx\n", e
.window
);
2002 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
2004 // move to the current desktop (uniconify)
2006 // XXX: should we focus/raise the window? (basically a net_wm_active_window)