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;
170 unsigned long num
= (unsigned) -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
;
375 unsigned long num
= (unsigned) -1;
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()
720 unsigned long num
= (unsigned) -1;
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(StateAction 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
== State_Add
|| action
== State_Remove
||
892 action
== State_Toggle
))
893 return; // an invalid action was passed to the client message, ignore it
895 for (int i
= 0; i
< 2; ++i
) {
896 Atom state
= i
== 0 ? data1
: data2
;
898 if (! state
) continue;
900 // if toggling, then pick whether we're adding or removing
901 if (action
== State_Toggle
) {
902 if (state
== otk::Property::atoms
.net_wm_state_modal
)
903 action
= _modal
? State_Remove
: State_Add
;
904 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
905 action
= _max_vert
? State_Remove
: State_Add
;
906 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
907 action
= _max_horz
? State_Remove
: State_Add
;
908 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
909 action
= _shaded
? State_Remove
: State_Add
;
910 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
911 action
= _skip_taskbar
? State_Remove
: State_Add
;
912 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
913 action
= _skip_pager
? State_Remove
: State_Add
;
914 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
915 action
= _fullscreen
? State_Remove
: State_Add
;
916 else if (state
== otk::Property::atoms
.net_wm_state_above
)
917 action
= _above
? State_Remove
: State_Add
;
918 else if (state
== otk::Property::atoms
.net_wm_state_below
)
919 action
= _below
? State_Remove
: State_Add
;
922 if (action
== State_Add
) {
923 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
924 if (_modal
) continue;
926 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
928 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
929 if (_max_horz
) continue;
931 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
933 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
934 _skip_taskbar
= true;
935 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
937 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
939 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
940 if (_above
) continue;
942 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
943 if (_below
) continue;
947 } else { // action == State_Remove
948 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
949 if (!_modal
) continue;
951 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
953 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
955 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
957 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
958 _skip_taskbar
= false;
959 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
961 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
963 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
964 if (!_above
) continue;
966 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
967 if (!_below
) continue;
972 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
973 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
974 if (maxh
== maxv
) { // both going the same way
975 maximize(maxh
, 0, true);
977 maximize(maxh
, 1, true);
978 maximize(maxv
, 2, true);
980 } else { // toggling one
981 if (maxh
!= _max_horz
)
982 maximize(maxh
, 1, true);
984 maximize(maxv
, 2, true);
987 // change fullscreen state before shading, as it will affect if the window
989 if (fsstate
!= _fullscreen
)
990 fullscreen(fsstate
, true);
991 if (shadestate
!= _shaded
)
994 changeState(); // change the hint to relect these changes
997 void Client::toggleClientBorder(bool addborder
)
999 // adjust our idea of where the client is, based on its border. When the
1000 // border is removed, the client should now be considered to be in a
1001 // different position.
1002 // when re-adding the border to the client, the same operation needs to be
1004 int oldx
= _area
.x(), oldy
= _area
.y();
1005 int x
= oldx
, y
= oldy
;
1008 case NorthWestGravity
:
1010 case SouthWestGravity
:
1012 case NorthEastGravity
:
1014 case SouthEastGravity
:
1015 if (addborder
) x
-= _border_width
* 2;
1016 else x
+= _border_width
* 2;
1023 if (addborder
) x
-= _border_width
;
1024 else x
+= _border_width
;
1029 case NorthWestGravity
:
1031 case NorthEastGravity
:
1033 case SouthWestGravity
:
1035 case SouthEastGravity
:
1036 if (addborder
) y
-= _border_width
* 2;
1037 else y
+= _border_width
* 2;
1044 if (addborder
) y
-= _border_width
;
1045 else y
+= _border_width
;
1048 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1051 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1053 // move the client so it is back it the right spot _with_ its border!
1054 if (x
!= oldx
|| y
!= oldy
)
1055 XMoveWindow(**otk::display
, _window
, x
, y
);
1057 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1060 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1062 otk::EventHandler::clientMessageHandler(e
);
1064 // validate cuz we query stuff off the client here
1065 if (!validate()) return;
1067 if (e
.format
!= 32) return;
1069 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1070 // compress changes into a single change
1071 bool compress
= false;
1073 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1074 // XXX: it would be nice to compress ALL messages of a type, not just
1075 // messages in a row without other message types between.
1076 if (ce
.xclient
.message_type
!= e
.message_type
) {
1077 XPutBackEvent(**otk::display
, &ce
);
1083 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1085 setWMState(e
.data
.l
[0]); // use the original event
1086 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1087 // compress changes into a single change
1088 bool compress
= false;
1090 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1091 // XXX: it would be nice to compress ALL messages of a type, not just
1092 // messages in a row without other message types between.
1093 if (ce
.xclient
.message_type
!= e
.message_type
) {
1094 XPutBackEvent(**otk::display
, &ce
);
1100 setDesktop(e
.data
.l
[0]); // use the found event
1102 setDesktop(e
.data
.l
[0]); // use the original event
1103 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1104 // can't compress these
1106 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1107 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1108 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1109 e
.data
.l
[1], e
.data
.l
[2], _window
);
1111 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1112 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1114 printf("net_close_window for 0x%lx\n", _window
);
1117 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1119 printf("net_active_window for 0x%lx\n", _window
);
1121 if (openbox
->screen(_screen
)->showingDesktop())
1122 openbox
->screen(_screen
)->showDesktop(false);
1125 else if (!frame
->visible()) // if its not visible for other reasons, then
1126 return; // don't mess with it
1130 openbox
->screen(_screen
)->raiseWindow(this);
1131 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1132 if (openbox
->screen(_screen
)->showingDesktop())
1133 openbox
->screen(_screen
)->showDesktop(false);
1136 else if (!frame
->visible()) // if its not visible for other reasons, then
1137 return; // don't mess with it
1138 if (e
.data
.l
[0] && _shaded
)
1142 openbox
->screen(_screen
)->raiseWindow(this);
1147 void Client::shapeHandler(const XShapeEvent
&e
)
1149 otk::EventHandler::shapeHandler(e
);
1151 if (e
.kind
== ShapeBounding
) {
1153 frame
->adjustShape();
1158 void Client::resize(Corner anchor
, int w
, int h
)
1160 if (!(_functions
& Func_Resize
)) return;
1161 internal_resize(anchor
, w
, h
);
1164 void Client::internal_resize(Corner anchor
, int w
, int h
,
1165 bool user
, int x
, int y
)
1167 w
-= _base_size
.width();
1168 h
-= _base_size
.height();
1171 // for interactive resizing. have to move half an increment in each
1173 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1174 int mh
= h
% _size_inc
.height();
1175 int aw
= _size_inc
.width() / 2; // amount to add
1176 int ah
= _size_inc
.height() / 2;
1177 // don't let us move into a new size increment
1178 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1179 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1183 // if this is a user-requested resize, then check against min/max sizes
1184 // and aspect ratios
1186 // smaller than min size or bigger than max size?
1187 if (w
> _max_size
.width()) w
= _max_size
.width();
1188 if (w
< _min_size
.width()) w
= _min_size
.width();
1189 if (h
> _max_size
.height()) h
= _max_size
.height();
1190 if (h
< _min_size
.height()) h
= _min_size
.height();
1192 // adjust the height ot match the width for the aspect ratios
1194 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1196 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1199 // keep to the increments
1200 w
/= _size_inc
.width();
1201 h
/= _size_inc
.height();
1203 // you cannot resize to nothing
1207 // store the logical size
1208 _logical_size
= otk::Size(w
, h
);
1210 w
*= _size_inc
.width();
1211 h
*= _size_inc
.height();
1213 w
+= _base_size
.width();
1214 h
+= _base_size
.height();
1216 if (x
== INT_MIN
|| y
== INT_MIN
) {
1223 x
-= w
- _area
.width();
1226 y
-= h
- _area
.height();
1229 x
-= w
- _area
.width();
1230 y
-= h
- _area
.height();
1235 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1237 XResizeWindow(**otk::display
, _window
, w
, h
);
1239 // resize the frame to match the request
1240 frame
->adjustSize();
1241 internal_move(x
, y
);
1244 const Icon
*Client::icon(const otk::Size
&s
) const
1246 unsigned long req
= s
.width() * s
.height();
1247 // si is the smallest image >= req
1248 // li is the largest image < req
1249 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1251 assert(_nicons
> 0); // there should always be a default..
1252 for (int i
= 0; i
< _nicons
; ++i
) {
1253 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1254 if (size
< smallest
&& size
>= req
) {
1258 if (size
> largest
&& size
<= req
) {
1263 if (largest
== 0) // didnt find one smaller than the requested size
1268 void Client::move(int x
, int y
)
1270 if (!(_functions
& Func_Move
)) return;
1271 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1273 internal_move(x
, y
);
1276 void Client::internal_move(int x
, int y
)
1278 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1280 // move the frame to be in the requested position
1281 if (frame
) { // this can be called while mapping, before frame exists
1282 frame
->adjustPosition();
1284 // send synthetic configure notify (we don't need to if we aren't mapped
1287 event
.type
= ConfigureNotify
;
1288 event
.xconfigure
.display
= **otk::display
;
1289 event
.xconfigure
.event
= _window
;
1290 event
.xconfigure
.window
= _window
;
1292 // root window coords with border in mind
1293 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1294 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1296 event
.xconfigure
.width
= _area
.width();
1297 event
.xconfigure
.height
= _area
.height();
1298 event
.xconfigure
.border_width
= _border_width
;
1299 event
.xconfigure
.above
= frame
->plate();
1300 event
.xconfigure
.override_redirect
= False
;
1301 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1302 StructureNotifyMask
, &event
);
1304 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1305 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1306 event
.xconfigure
.height
, event
.xconfigure
.window
);
1311 void Client::close()
1315 if (!(_functions
& Func_Close
)) return;
1317 // XXX: itd be cool to do timeouts and shit here for killing the client's
1319 // like... if the window is around after 5 seconds, then the close button
1320 // turns a nice red, and if this function is called again, the client is
1321 // explicitly killed.
1323 ce
.xclient
.type
= ClientMessage
;
1324 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1325 ce
.xclient
.display
= **otk::display
;
1326 ce
.xclient
.window
= _window
;
1327 ce
.xclient
.format
= 32;
1328 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1329 ce
.xclient
.data
.l
[1] = CurrentTime
;
1330 ce
.xclient
.data
.l
[2] = 0l;
1331 ce
.xclient
.data
.l
[3] = 0l;
1332 ce
.xclient
.data
.l
[4] = 0l;
1333 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1336 void Client::changeState()
1338 unsigned long state
[2];
1339 state
[0] = _wmstate
;
1341 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1342 otk::Property::atoms
.wm_state
, state
, 2);
1347 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1349 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1351 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1353 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1355 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1357 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1359 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1361 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1363 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1365 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1366 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1367 otk::Property::atoms
.atom
, netstate
, num
);
1372 frame
->adjustState();
1375 void Client::changeAllowedActions(void)
1380 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1382 if (_functions
& Func_Shade
)
1383 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1384 if (_functions
& Func_Close
)
1385 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1386 if (_functions
& Func_Move
)
1387 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1388 if (_functions
& Func_Iconify
)
1389 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1390 if (_functions
& Func_Resize
)
1391 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1392 if (_functions
& Func_Fullscreen
)
1393 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1394 if (_functions
& Func_Maximize
) {
1395 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1396 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1399 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1400 otk::Property::atoms
.atom
, actions
, num
);
1402 // make sure the window isn't breaking any rules now
1404 if (!(_functions
& Func_Shade
) && _shaded
)
1405 if (frame
) shade(false);
1406 else _shaded
= false;
1407 if (!(_functions
& Func_Iconify
) && _iconic
)
1408 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1409 else _iconic
= false;
1410 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1411 if (frame
) fullscreen(false);
1412 else _fullscreen
= false;
1413 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1414 if (frame
) maximize(false, 0);
1415 else _max_vert
= _max_horz
= false;
1418 void Client::remaximize()
1421 if (_max_horz
&& _max_vert
)
1428 return; // not maximized
1429 _max_horz
= _max_vert
= false;
1430 maximize(true, dir
, false);
1433 void Client::applyStartupState()
1435 // these are in a carefully crafted order..
1442 _fullscreen
= false;
1443 fullscreen(true, false);
1452 if (_max_vert
&& _max_horz
) {
1453 _max_vert
= _max_horz
= false;
1454 maximize(true, 0, false);
1455 } else if (_max_vert
) {
1457 maximize(true, 2, false);
1458 } else if (_max_horz
) {
1460 maximize(true, 1, false);
1463 if (_skip_taskbar
); // nothing to do for this
1464 if (_skip_pager
); // nothing to do for this
1465 if (_modal
); // nothing to do for this
1466 if (_above
); // nothing to do for this
1467 if (_below
); // nothing to do for this
1470 void Client::fireUrgent()
1472 // call the python UrgentWindow callbacks
1473 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1474 openbox
->bindings()->fireEvent(&data
);
1477 void Client::shade(bool shade
)
1479 if (!(_functions
& Func_Shade
) || // can't
1480 _shaded
== shade
) return; // already done
1482 // when we're iconic, don't change the wmstate
1484 _wmstate
= shade
? IconicState
: NormalState
;
1487 frame
->adjustSize();
1490 void Client::maximize(bool max
, int dir
, bool savearea
)
1492 assert(dir
== 0 || dir
== 1 || dir
== 2);
1493 if (!(_functions
& Func_Maximize
)) return; // can't
1495 // check if already done
1497 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1498 if (dir
== 1 && _max_horz
) return;
1499 if (dir
== 2 && _max_vert
) return;
1501 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1502 if (dir
== 1 && !_max_horz
) return;
1503 if (dir
== 2 && !_max_vert
) return;
1506 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1507 int x
= frame
->area().x(), y
= frame
->area().y(),
1508 w
= _area
.width(), h
= _area
.height();
1514 unsigned long n
= 4;
1521 // get the property off the window and use it for the dimentions we are
1523 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1524 otk::Property::atoms
.cardinal
, &n
,
1525 (long unsigned**) &readdim
)) {
1528 dimensions
[0] = readdim
[0];
1529 dimensions
[2] = readdim
[2];
1532 dimensions
[1] = readdim
[1];
1533 dimensions
[3] = readdim
[3];
1539 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1540 otk::Property::atoms
.cardinal
,
1541 (long unsigned*)dimensions
, 4);
1543 if (dir
== 0 || dir
== 1) { // horz
1547 if (dir
== 0 || dir
== 2) { // vert
1549 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1553 long unsigned n
= 4;
1555 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1556 otk::Property::atoms
.cardinal
, &n
,
1557 (long unsigned**) &dimensions
)) {
1559 if (dir
== 0 || dir
== 1) { // horz
1560 x
= (signed int)dimensions
[0];
1561 w
= (signed int)dimensions
[2];
1563 if (dir
== 0 || dir
== 2) { // vert
1564 y
= (signed int)dimensions
[1];
1565 h
= (signed int)dimensions
[3];
1570 // pick some fallbacks...
1571 if (dir
== 0 || dir
== 1) { // horz
1572 x
= a
.x() + a
.width() / 4;
1575 if (dir
== 0 || dir
== 2) { // vert
1576 y
= a
.y() + a
.height() / 4;
1582 if (dir
== 0 || dir
== 1) // horz
1584 if (dir
== 0 || dir
== 2) // vert
1587 if (!_max_horz
&& !_max_vert
)
1588 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1590 changeState(); // change the state hints on the client
1592 frame
->frameGravity(x
, y
); // figure out where the client should be going
1593 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1596 void Client::fullscreen(bool fs
, bool savearea
)
1598 static FunctionFlags saved_func
;
1599 static DecorationFlags saved_decor
;
1601 if (!(_functions
& Func_Fullscreen
) || // can't
1602 _fullscreen
== fs
) return; // already done
1605 changeState(); // change the state hints on the client
1607 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1610 // save the functions and remove them
1611 saved_func
= _functions
;
1612 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1613 // save the decorations and remove them
1614 saved_decor
= _decorations
;
1618 dimensions
[0] = _area
.x();
1619 dimensions
[1] = _area
.y();
1620 dimensions
[2] = _area
.width();
1621 dimensions
[3] = _area
.height();
1622 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1623 otk::Property::atoms
.cardinal
,
1624 (long unsigned*)dimensions
, 4);
1626 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1629 w
= info
->size().width();
1630 h
= info
->size().height();
1632 _functions
= saved_func
;
1633 _decorations
= saved_decor
;
1636 long unsigned n
= 4;
1638 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1639 otk::Property::atoms
.cardinal
, &n
,
1640 (long unsigned**) &dimensions
)) {
1649 // pick some fallbacks...
1650 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1651 x
= a
.x() + a
.width() / 4;
1652 y
= a
.y() + a
.height() / 4;
1658 changeAllowedActions(); // based on the new _functions
1660 // when fullscreening, don't obey things like increments, fill the screen
1661 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1663 // raise (back) into our stacking layer
1664 openbox
->screen(_screen
)->raiseWindow(this);
1666 // try focus us when we go into fullscreen mode
1670 void Client::iconify(bool iconic
, bool curdesk
)
1672 if (_iconic
== iconic
) return; // nothing to do
1675 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1681 _wmstate
= IconicState
;
1683 // we unmap the client itself so that we can get MapRequest events, and
1684 // because the ICCCM tells us to!
1685 XUnmapWindow(**otk::display
, _window
);
1688 setDesktop(openbox
->screen(_screen
)->desktop());
1689 _wmstate
= NormalState
;
1690 XMapWindow(**otk::display
, _window
);
1694 openbox
->screen(_screen
)->updateStruts();
1697 void Client::disableDecorations(DecorationFlags flags
)
1699 _disabled_decorations
= flags
;
1700 setupDecorAndFunctions();
1703 void Client::installColormap(bool install
) const
1705 XWindowAttributes wa
;
1706 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1708 XInstallColormap(**otk::display
, wa
.colormap
);
1710 XUninstallColormap(**otk::display
, wa
.colormap
);
1714 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1716 List::const_iterator it
, end
= node
->_transients
.end();
1719 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1720 if (*it
== skip
) continue; // circular?
1721 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1722 if ((*it
)->_modal
) return *it
; // got one
1727 Client
*Client::findModalChild()
1729 return searchModalTree(this, this);
1733 bool Client::focus()
1735 // if we have a modal child, then focus it, not us
1736 Client
*c
= findModalChild();
1737 if (c
) return c
->focus();
1739 // won't try focus if the client doesn't want it, or if the window isn't
1740 // visible on the screen
1741 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1743 // do a check to see if the window has already been unmapped or destroyed
1744 // do this intelligently while watching out for unmaps we've generated
1745 // (ignore_unmaps > 0)
1747 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1748 XPutBackEvent(**otk::display
, &ev
);
1751 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1752 if (ignore_unmaps
) {
1753 unmapHandler(ev
.xunmap
);
1755 XPutBackEvent(**otk::display
, &ev
);
1761 XSetInputFocus(**otk::display
, _window
,
1762 RevertToNone
, CurrentTime
);
1764 if (_focus_notify
) {
1766 ce
.xclient
.type
= ClientMessage
;
1767 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1768 ce
.xclient
.display
= **otk::display
;
1769 ce
.xclient
.window
= _window
;
1770 ce
.xclient
.format
= 32;
1771 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1772 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1773 ce
.xclient
.data
.l
[2] = 0l;
1774 ce
.xclient
.data
.l
[3] = 0l;
1775 ce
.xclient
.data
.l
[4] = 0l;
1776 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1779 XSync(**otk::display
, False
);
1784 void Client::unfocus() const
1786 assert(openbox
->focusedClient() == this);
1787 openbox
->setFocusedClient(0);
1791 void Client::focusHandler(const XFocusChangeEvent
&e
)
1794 // printf("FocusIn for 0x%lx\n", e.window);
1797 otk::EventHandler::focusHandler(e
);
1800 frame
->adjustFocus();
1802 calcLayer(); // focus state can affect the stacking layer
1804 openbox
->setFocusedClient(this);
1808 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1811 // printf("FocusOut for 0x%lx\n", e.window);
1814 otk::EventHandler::unfocusHandler(e
);
1817 frame
->adjustFocus();
1819 calcLayer(); // focus state can affect the stacking layer
1821 if (openbox
->focusedClient() == this)
1822 openbox
->setFocusedClient(0);
1826 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1829 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1832 otk::EventHandler::configureRequestHandler(ec
);
1835 XConfigureRequestEvent e
= ec
;
1837 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1839 // XXX if this causes bad things.. we can compress config req's with the
1841 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1842 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1843 e
.x
= ev
.xconfigurerequest
.x
;
1844 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1845 e
.y
= ev
.xconfigurerequest
.y
;
1846 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1847 e
.width
= ev
.xconfigurerequest
.width
;
1848 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1849 e
.height
= ev
.xconfigurerequest
.height
;
1850 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1851 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1852 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1853 e
.detail
= ev
.xconfigurerequest
.detail
;
1856 // if we are iconic (or shaded (fvwm does this)) ignore the event
1857 if (_iconic
|| _shaded
) return;
1859 if (e
.value_mask
& CWBorderWidth
)
1860 _border_width
= e
.border_width
;
1862 // resize, then move, as specified in the EWMH section 7.7
1863 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1864 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1865 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1869 case NorthEastGravity
:
1873 case SouthWestGravity
:
1875 corner
= BottomLeft
;
1877 case SouthEastGravity
:
1878 corner
= BottomRight
;
1880 default: // NorthWest, Static, etc
1884 // if moving AND resizing ...
1885 if (e
.value_mask
& (CWX
| CWY
)) {
1886 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1887 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1888 internal_resize(corner
, w
, h
, false, x
, y
);
1889 } else // if JUST resizing...
1890 internal_resize(corner
, w
, h
, false);
1891 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1892 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1893 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1894 internal_move(x
, y
);
1897 if (e
.value_mask
& CWStackMode
) {
1901 openbox
->screen(_screen
)->lowerWindow(this);
1907 openbox
->screen(_screen
)->raiseWindow(this);
1914 void Client::unmapHandler(const XUnmapEvent
&e
)
1916 if (ignore_unmaps
) {
1918 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1925 printf("UnmapNotify for 0x%lx\n", e
.window
);
1928 otk::EventHandler::unmapHandler(e
);
1930 // this deletes us etc
1931 openbox
->screen(_screen
)->unmanageWindow(this);
1935 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1938 printf("DestroyNotify for 0x%lx\n", e
.window
);
1941 otk::EventHandler::destroyHandler(e
);
1943 // this deletes us etc
1944 openbox
->screen(_screen
)->unmanageWindow(this);
1948 void Client::reparentHandler(const XReparentEvent
&e
)
1950 // this is when the client is first taken captive in the frame
1951 if (e
.parent
== frame
->plate()) return;
1954 printf("ReparentNotify for 0x%lx\n", e
.window
);
1957 otk::EventHandler::reparentHandler(e
);
1960 This event is quite rare and is usually handled in unmapHandler.
1961 However, if the window is unmapped when the reparent event occurs,
1962 the window manager never sees it because an unmap event is not sent
1963 to an already unmapped window.
1966 // we don't want the reparent event, put it back on the stack for the X
1967 // server to deal with after we unmanage the window
1970 XPutBackEvent(**otk::display
, &ev
);
1972 // this deletes us etc
1973 openbox
->screen(_screen
)->unmanageWindow(this);
1976 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1979 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1982 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1984 // move to the current desktop (uniconify)
1986 // XXX: should we focus/raise the window? (basically a net_wm_active_window)