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
585 printf("Urgent Hint for 0x%lx: %s\n",
586 (long)_window
, _urgent
? "ON" : "OFF");
588 // fire the urgent callback if we're mapped, otherwise, wait until after
595 void Client::updateTitle()
600 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_name
,
601 otk::Property::utf8
, &_title
)) {
603 otk::Property::get(_window
, otk::Property::atoms
.wm_name
,
604 otk::Property::ascii
, &_title
);
608 _title
= _("Unnamed Window");
611 frame
->adjustTitle();
614 void Client::updateIconTitle()
619 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon_name
,
620 otk::Property::utf8
, &_icon_title
)) {
622 otk::Property::get(_window
, otk::Property::atoms
.wm_icon_name
,
623 otk::Property::ascii
, &_icon_title
);
627 _icon_title
= _("Unnamed Window");
630 void Client::updateClass()
633 _app_name
= _app_class
= _role
= "";
635 otk::Property::StringVect v
;
636 unsigned long num
= 2;
638 if (otk::Property::get(_window
, otk::Property::atoms
.wm_class
,
639 otk::Property::ascii
, &num
, &v
)) {
640 if (num
> 0) _app_name
= v
[0].c_str();
641 if (num
> 1) _app_class
= v
[1].c_str();
646 if (otk::Property::get(_window
, otk::Property::atoms
.wm_window_role
,
647 otk::Property::ascii
, &num
, &v
)) {
648 if (num
> 0) _role
= v
[0].c_str();
652 void Client::updateStrut()
654 unsigned long num
= 4;
656 if (!otk::Property::get(_window
, otk::Property::atoms
.net_wm_strut
,
657 otk::Property::atoms
.cardinal
, &num
, &data
))
661 _strut
.left
= data
[0];
662 _strut
.right
= data
[1];
663 _strut
.top
= data
[2];
664 _strut
.bottom
= data
[3];
666 // updating here is pointless while we're being mapped cuz we're not in
667 // the screen's client list yet
669 openbox
->screen(_screen
)->updateStruts();
675 void Client::updateTransientFor()
680 if (XGetTransientForHint(**otk::display
, _window
, &t
) &&
681 t
!= _window
) { // cant be transient to itself!
682 c
= openbox
->findClient(t
);
683 assert(c
!= this); // if this happens then we need to check for it
685 if (!c
/*XXX: && _group*/) {
686 // not transient to a client, see if it is transient for a group
687 if (//t == _group->leader() ||
689 t
== otk::display
->screenInfo(_screen
)->rootWindow()) {
690 // window is a transient for its group!
691 // XXX: for now this is treated as non-transient.
692 // this needs to be fixed!
697 // if anything has changed...
698 if (c
!= _transient_for
) {
700 _transient_for
->_transients
.remove(this); // remove from old parent
703 _transient_for
->_transients
.push_back(this); // add to new parent
707 void Client::updateIcons()
709 unsigned long num
= (unsigned) -1;
711 unsigned long w
, h
, i
= 0;
713 for (int j
= 0; j
< _nicons
; ++j
)
714 delete [] _icons
[j
].data
;
719 if (otk::Property::get(_window
, otk::Property::atoms
.net_wm_icon
,
720 otk::Property::atoms
.cardinal
, &num
, &data
)) {
721 // figure out how man valid icons are in here
722 while (num
- i
> 2) {
730 _icons
= new Icon
[_nicons
];
734 for (int j
= 0; j
< _nicons
; ++j
) {
735 w
= _icons
[j
].w
= data
[i
++];
736 h
= _icons
[j
].h
= data
[i
++];
737 _icons
[j
].data
= new unsigned long[w
* h
];
738 ::memcpy(_icons
[j
].data
, &data
[i
], w
* h
* sizeof(unsigned long));
748 _icons
= new Icon
[1];
754 assert(_nicons
> 0); // there should always be a default..
756 if (frame
) frame
->adjustIcon();
759 void Client::updateKwmIcon()
761 _kwm_icon
= _kwm_icon_mask
= None
;
763 unsigned long num
= 2;
765 if (otk::Property::get(_window
, otk::Property::atoms
.kwm_win_icon
,
766 otk::Property::atoms
.kwm_win_icon
, &num
, &data
)) {
769 _kwm_icon_mask
= data
[1];
775 void Client::propertyHandler(const XPropertyEvent
&e
)
777 otk::EventHandler::propertyHandler(e
);
779 // validate cuz we query stuff off the client here
780 if (!validate()) return;
782 // compress changes to a single property into a single change
784 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
785 // XXX: it would be nice to compress ALL changes to a property, not just
786 // changes in a row without other props between.
787 if (ce
.xproperty
.atom
!= e
.atom
) {
788 XPutBackEvent(**otk::display
, &ce
);
793 if (e
.atom
== XA_WM_NORMAL_HINTS
) {
795 setupDecorAndFunctions(); // normal hints can make a window non-resizable
796 } else if (e
.atom
== XA_WM_HINTS
)
798 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
799 updateTransientFor();
801 calcLayer(); // type may have changed, so update the layer
802 setupDecorAndFunctions();
804 else if (e
.atom
== otk::Property::atoms
.net_wm_name
||
805 e
.atom
== otk::Property::atoms
.wm_name
)
807 else if (e
.atom
== otk::Property::atoms
.net_wm_icon_name
||
808 e
.atom
== otk::Property::atoms
.wm_icon_name
)
810 else if (e
.atom
== otk::Property::atoms
.wm_class
)
812 else if (e
.atom
== otk::Property::atoms
.wm_protocols
) {
814 setupDecorAndFunctions();
816 else if (e
.atom
== otk::Property::atoms
.net_wm_strut
)
818 else if (e
.atom
== otk::Property::atoms
.net_wm_icon
)
820 else if (e
.atom
== otk::Property::atoms
.kwm_win_icon
)
824 void Client::setWMState(long state
)
826 if (state
== _wmstate
) return; // no change
838 void Client::setDesktop(unsigned int target
)
840 if (target
== _desktop
) return;
842 printf("Setting desktop %u\n", target
);
844 if (!(target
< openbox
->screen(_screen
)->numDesktops() ||
845 target
== 0xffffffff))
849 // set the desktop hint
850 otk::Property::set(_window
, otk::Property::atoms
.net_wm_desktop
,
851 otk::Property::atoms
.cardinal
, _desktop
);
852 frame
->adjustState(); // the frame can display the current desktop state
853 // 'move' the window to the new desktop
855 openbox
->screen(_screen
)->updateStruts();
858 void Client::showhide()
861 Screen
*s
= openbox
->screen(_screen
);
863 if (_iconic
) show
= false;
864 else if (!(_desktop
== s
->desktop() ||
865 _desktop
== 0xffffffff)) show
= false;
866 else if (normal() && s
->showingDesktop()) show
= false;
869 if (show
) frame
->show();
873 void Client::setState(StateAction action
, long data1
, long data2
)
875 bool shadestate
= _shaded
;
876 bool fsstate
= _fullscreen
;
877 bool maxh
= _max_horz
;
878 bool maxv
= _max_vert
;
880 if (!(action
== State_Add
|| action
== State_Remove
||
881 action
== State_Toggle
))
882 return; // an invalid action was passed to the client message, ignore it
884 for (int i
= 0; i
< 2; ++i
) {
885 Atom state
= i
== 0 ? data1
: data2
;
887 if (! state
) continue;
889 // if toggling, then pick whether we're adding or removing
890 if (action
== State_Toggle
) {
891 if (state
== otk::Property::atoms
.net_wm_state_modal
)
892 action
= _modal
? State_Remove
: State_Add
;
893 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
894 action
= _max_vert
? State_Remove
: State_Add
;
895 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
896 action
= _max_horz
? State_Remove
: State_Add
;
897 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
898 action
= _shaded
? State_Remove
: State_Add
;
899 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
900 action
= _skip_taskbar
? State_Remove
: State_Add
;
901 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
902 action
= _skip_pager
? State_Remove
: State_Add
;
903 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
904 action
= _fullscreen
? State_Remove
: State_Add
;
905 else if (state
== otk::Property::atoms
.net_wm_state_above
)
906 action
= _above
? State_Remove
: State_Add
;
907 else if (state
== otk::Property::atoms
.net_wm_state_below
)
908 action
= _below
? State_Remove
: State_Add
;
911 if (action
== State_Add
) {
912 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
913 if (_modal
) continue;
915 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
917 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
918 if (_max_horz
) continue;
920 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
922 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
923 _skip_taskbar
= true;
924 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
926 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
928 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
929 if (_above
) continue;
931 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
932 if (_below
) continue;
936 } else { // action == State_Remove
937 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
938 if (!_modal
) continue;
940 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
942 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
944 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
946 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
947 _skip_taskbar
= false;
948 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
950 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
952 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
953 if (!_above
) continue;
955 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
956 if (!_below
) continue;
961 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
962 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
963 if (maxh
== maxv
) { // both going the same way
964 maximize(maxh
, 0, true);
966 maximize(maxh
, 1, true);
967 maximize(maxv
, 2, true);
969 } else { // toggling one
970 if (maxh
!= _max_horz
)
971 maximize(maxh
, 1, true);
973 maximize(maxv
, 2, true);
976 // change fullscreen state before shading, as it will affect if the window
978 if (fsstate
!= _fullscreen
)
979 fullscreen(fsstate
, true);
980 if (shadestate
!= _shaded
)
983 changeState(); // change the hint to relect these changes
986 void Client::toggleClientBorder(bool addborder
)
988 // adjust our idea of where the client is, based on its border. When the
989 // border is removed, the client should now be considered to be in a
990 // different position.
991 // when re-adding the border to the client, the same operation needs to be
993 int oldx
= _area
.x(), oldy
= _area
.y();
994 int x
= oldx
, y
= oldy
;
997 case NorthWestGravity
:
999 case SouthWestGravity
:
1001 case NorthEastGravity
:
1003 case SouthEastGravity
:
1004 if (addborder
) x
-= _border_width
* 2;
1005 else x
+= _border_width
* 2;
1012 if (addborder
) x
-= _border_width
;
1013 else x
+= _border_width
;
1018 case NorthWestGravity
:
1020 case NorthEastGravity
:
1022 case SouthWestGravity
:
1024 case SouthEastGravity
:
1025 if (addborder
) y
-= _border_width
* 2;
1026 else y
+= _border_width
* 2;
1033 if (addborder
) y
-= _border_width
;
1034 else y
+= _border_width
;
1037 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1040 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1042 // move the client so it is back it the right spot _with_ its border!
1043 if (x
!= oldx
|| y
!= oldy
)
1044 XMoveWindow(**otk::display
, _window
, x
, y
);
1046 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1049 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1051 otk::EventHandler::clientMessageHandler(e
);
1053 // validate cuz we query stuff off the client here
1054 if (!validate()) return;
1056 if (e
.format
!= 32) return;
1058 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1059 // compress changes into a single change
1060 bool compress
= false;
1062 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1063 // XXX: it would be nice to compress ALL messages of a type, not just
1064 // messages in a row without other message types between.
1065 if (ce
.xclient
.message_type
!= e
.message_type
) {
1066 XPutBackEvent(**otk::display
, &ce
);
1072 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1074 setWMState(e
.data
.l
[0]); // use the original event
1075 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1076 // compress changes into a single change
1077 bool compress
= false;
1079 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1080 // XXX: it would be nice to compress ALL messages of a type, not just
1081 // messages in a row without other message types between.
1082 if (ce
.xclient
.message_type
!= e
.message_type
) {
1083 XPutBackEvent(**otk::display
, &ce
);
1089 setDesktop(e
.data
.l
[0]); // use the found event
1091 setDesktop(e
.data
.l
[0]); // use the original event
1092 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1093 // can't compress these
1095 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1096 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1097 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1098 e
.data
.l
[1], e
.data
.l
[2], _window
);
1100 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1101 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1103 printf("net_close_window for 0x%lx\n", _window
);
1106 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1108 printf("net_active_window for 0x%lx\n", _window
);
1110 if (openbox
->screen(_screen
)->showingDesktop())
1111 openbox
->screen(_screen
)->showDesktop(false);
1114 else if (!frame
->visible()) // if its not visible for other reasons, then
1115 return; // don't mess with it
1119 openbox
->screen(_screen
)->raiseWindow(this);
1120 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_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
1127 if (e
.data
.l
[0] && _shaded
)
1131 openbox
->screen(_screen
)->raiseWindow(this);
1136 void Client::shapeHandler(const XShapeEvent
&e
)
1138 otk::EventHandler::shapeHandler(e
);
1140 if (e
.kind
== ShapeBounding
) {
1142 frame
->adjustShape();
1147 void Client::resize(Corner anchor
, int w
, int h
)
1149 if (!(_functions
& Func_Resize
)) return;
1150 internal_resize(anchor
, w
, h
);
1153 void Client::internal_resize(Corner anchor
, int w
, int h
,
1154 bool user
, int x
, int y
)
1156 w
-= _base_size
.width();
1157 h
-= _base_size
.height();
1160 // for interactive resizing. have to move half an increment in each
1162 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1163 int mh
= h
% _size_inc
.height();
1164 int aw
= _size_inc
.width() / 2; // amount to add
1165 int ah
= _size_inc
.height() / 2;
1166 // don't let us move into a new size increment
1167 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1168 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1172 // if this is a user-requested resize, then check against min/max sizes
1173 // and aspect ratios
1175 // smaller than min size or bigger than max size?
1176 if (w
> _max_size
.width()) w
= _max_size
.width();
1177 if (w
< _min_size
.width()) w
= _min_size
.width();
1178 if (h
> _max_size
.height()) h
= _max_size
.height();
1179 if (h
< _min_size
.height()) h
= _min_size
.height();
1181 // adjust the height ot match the width for the aspect ratios
1183 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1185 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1188 // keep to the increments
1189 w
/= _size_inc
.width();
1190 h
/= _size_inc
.height();
1192 // you cannot resize to nothing
1196 // store the logical size
1197 _logical_size
= otk::Size(w
, h
);
1199 w
*= _size_inc
.width();
1200 h
*= _size_inc
.height();
1202 w
+= _base_size
.width();
1203 h
+= _base_size
.height();
1205 if (x
== INT_MIN
|| y
== INT_MIN
) {
1212 x
-= w
- _area
.width();
1215 y
-= h
- _area
.height();
1218 x
-= w
- _area
.width();
1219 y
-= h
- _area
.height();
1224 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1226 XResizeWindow(**otk::display
, _window
, w
, h
);
1228 // resize the frame to match the request
1229 frame
->adjustSize();
1230 internal_move(x
, y
);
1233 const Icon
*Client::icon(const otk::Size
&s
) const
1235 unsigned long req
= s
.width() * s
.height();
1236 // si is the smallest image >= req
1237 // li is the largest image < req
1238 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1240 assert(_nicons
> 0); // there should always be a default..
1241 for (int i
= 0; i
< _nicons
; ++i
) {
1242 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1243 if (size
< smallest
&& size
>= req
) {
1247 if (size
> largest
&& size
<= req
) {
1252 if (largest
== 0) // didnt find one smaller than the requested size
1257 void Client::move(int x
, int y
)
1259 if (!(_functions
& Func_Move
)) return;
1260 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1262 internal_move(x
, y
);
1265 void Client::internal_move(int x
, int y
)
1267 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1269 // move the frame to be in the requested position
1270 if (frame
) { // this can be called while mapping, before frame exists
1271 frame
->adjustPosition();
1273 // send synthetic configure notify (we don't need to if we aren't mapped
1276 event
.type
= ConfigureNotify
;
1277 event
.xconfigure
.display
= **otk::display
;
1278 event
.xconfigure
.event
= _window
;
1279 event
.xconfigure
.window
= _window
;
1281 // root window coords with border in mind
1282 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1283 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1285 event
.xconfigure
.width
= _area
.width();
1286 event
.xconfigure
.height
= _area
.height();
1287 event
.xconfigure
.border_width
= _border_width
;
1288 event
.xconfigure
.above
= frame
->plate();
1289 event
.xconfigure
.override_redirect
= False
;
1290 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1291 StructureNotifyMask
, &event
);
1293 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1294 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1295 event
.xconfigure
.height
, event
.xconfigure
.window
);
1300 void Client::close()
1304 if (!(_functions
& Func_Close
)) return;
1306 // XXX: itd be cool to do timeouts and shit here for killing the client's
1308 // like... if the window is around after 5 seconds, then the close button
1309 // turns a nice red, and if this function is called again, the client is
1310 // explicitly killed.
1312 ce
.xclient
.type
= ClientMessage
;
1313 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1314 ce
.xclient
.display
= **otk::display
;
1315 ce
.xclient
.window
= _window
;
1316 ce
.xclient
.format
= 32;
1317 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1318 ce
.xclient
.data
.l
[1] = CurrentTime
;
1319 ce
.xclient
.data
.l
[2] = 0l;
1320 ce
.xclient
.data
.l
[3] = 0l;
1321 ce
.xclient
.data
.l
[4] = 0l;
1322 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1325 void Client::changeState()
1327 unsigned long state
[2];
1328 state
[0] = _wmstate
;
1330 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1331 otk::Property::atoms
.wm_state
, state
, 2);
1336 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1338 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1340 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1342 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1344 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1346 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1348 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1350 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1352 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1354 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1355 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1356 otk::Property::atoms
.atom
, netstate
, num
);
1361 frame
->adjustState();
1364 void Client::changeAllowedActions(void)
1369 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1371 if (_functions
& Func_Shade
)
1372 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1373 if (_functions
& Func_Close
)
1374 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1375 if (_functions
& Func_Move
)
1376 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1377 if (_functions
& Func_Iconify
)
1378 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1379 if (_functions
& Func_Resize
)
1380 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1381 if (_functions
& Func_Fullscreen
)
1382 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1383 if (_functions
& Func_Maximize
) {
1384 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1385 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1388 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1389 otk::Property::atoms
.atom
, actions
, num
);
1391 // make sure the window isn't breaking any rules now
1393 if (!(_functions
& Func_Shade
) && _shaded
)
1394 if (frame
) shade(false);
1395 else _shaded
= false;
1396 if (!(_functions
& Func_Iconify
) && _iconic
)
1397 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1398 else _iconic
= false;
1399 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1400 if (frame
) fullscreen(false);
1401 else _fullscreen
= false;
1402 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1403 if (frame
) maximize(false, 0);
1404 else _max_vert
= _max_horz
= false;
1407 void Client::remaximize()
1410 if (_max_horz
&& _max_vert
)
1417 return; // not maximized
1418 _max_horz
= _max_vert
= false;
1419 maximize(true, dir
, false);
1422 void Client::applyStartupState()
1424 // these are in a carefully crafted order..
1431 _fullscreen
= false;
1432 fullscreen(true, false);
1441 if (_max_vert
&& _max_horz
) {
1442 _max_vert
= _max_horz
= false;
1443 maximize(true, 0, false);
1444 } else if (_max_vert
) {
1446 maximize(true, 2, false);
1447 } else if (_max_horz
) {
1449 maximize(true, 1, false);
1452 if (_skip_taskbar
); // nothing to do for this
1453 if (_skip_pager
); // nothing to do for this
1454 if (_modal
); // nothing to do for this
1455 if (_above
); // nothing to do for this
1456 if (_below
); // nothing to do for this
1459 void Client::fireUrgent()
1461 // call the python UrgentWindow callbacks
1462 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1463 openbox
->bindings()->fireEvent(&data
);
1466 void Client::shade(bool shade
)
1468 if (!(_functions
& Func_Shade
) || // can't
1469 _shaded
== shade
) return; // already done
1471 // when we're iconic, don't change the wmstate
1473 _wmstate
= shade
? IconicState
: NormalState
;
1476 frame
->adjustSize();
1479 void Client::maximize(bool max
, int dir
, bool savearea
)
1481 assert(dir
== 0 || dir
== 1 || dir
== 2);
1482 if (!(_functions
& Func_Maximize
)) return; // can't
1484 // check if already done
1486 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1487 if (dir
== 1 && _max_horz
) return;
1488 if (dir
== 2 && _max_vert
) return;
1490 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1491 if (dir
== 1 && !_max_horz
) return;
1492 if (dir
== 2 && !_max_vert
) return;
1495 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1496 int x
= frame
->area().x(), y
= frame
->area().y(),
1497 w
= _area
.width(), h
= _area
.height();
1503 unsigned long n
= 4;
1510 // get the property off the window and use it for the dimentions we are
1512 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1513 otk::Property::atoms
.cardinal
, &n
,
1514 (long unsigned**) &readdim
)) {
1517 dimensions
[0] = readdim
[0];
1518 dimensions
[2] = readdim
[2];
1521 dimensions
[1] = readdim
[1];
1522 dimensions
[3] = readdim
[3];
1528 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1529 otk::Property::atoms
.cardinal
,
1530 (long unsigned*)dimensions
, 4);
1532 if (dir
== 0 || dir
== 1) { // horz
1536 if (dir
== 0 || dir
== 2) { // vert
1538 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1542 long unsigned n
= 4;
1544 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1545 otk::Property::atoms
.cardinal
, &n
,
1546 (long unsigned**) &dimensions
)) {
1548 if (dir
== 0 || dir
== 1) { // horz
1549 x
= (signed int)dimensions
[0];
1550 w
= (signed int)dimensions
[2];
1552 if (dir
== 0 || dir
== 2) { // vert
1553 y
= (signed int)dimensions
[1];
1554 h
= (signed int)dimensions
[3];
1559 // pick some fallbacks...
1560 if (dir
== 0 || dir
== 1) { // horz
1561 x
= a
.x() + a
.width() / 4;
1564 if (dir
== 0 || dir
== 2) { // vert
1565 y
= a
.y() + a
.height() / 4;
1571 if (dir
== 0 || dir
== 1) // horz
1573 if (dir
== 0 || dir
== 2) // vert
1576 if (!_max_horz
&& !_max_vert
)
1577 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1579 changeState(); // change the state hints on the client
1581 frame
->frameGravity(x
, y
); // figure out where the client should be going
1582 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1585 void Client::fullscreen(bool fs
, bool savearea
)
1587 static FunctionFlags saved_func
;
1588 static DecorationFlags saved_decor
;
1590 if (!(_functions
& Func_Fullscreen
) || // can't
1591 _fullscreen
== fs
) return; // already done
1594 changeState(); // change the state hints on the client
1596 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1599 // save the functions and remove them
1600 saved_func
= _functions
;
1601 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1602 // save the decorations and remove them
1603 saved_decor
= _decorations
;
1607 dimensions
[0] = _area
.x();
1608 dimensions
[1] = _area
.y();
1609 dimensions
[2] = _area
.width();
1610 dimensions
[3] = _area
.height();
1611 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1612 otk::Property::atoms
.cardinal
,
1613 (long unsigned*)dimensions
, 4);
1615 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1618 w
= info
->size().width();
1619 h
= info
->size().height();
1621 _functions
= saved_func
;
1622 _decorations
= saved_decor
;
1625 long unsigned n
= 4;
1627 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1628 otk::Property::atoms
.cardinal
, &n
,
1629 (long unsigned**) &dimensions
)) {
1638 // pick some fallbacks...
1639 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1640 x
= a
.x() + a
.width() / 4;
1641 y
= a
.y() + a
.height() / 4;
1647 changeAllowedActions(); // based on the new _functions
1649 // when fullscreening, don't obey things like increments, fill the screen
1650 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1652 // raise (back) into our stacking layer
1653 openbox
->screen(_screen
)->raiseWindow(this);
1655 // try focus us when we go into fullscreen mode
1659 void Client::iconify(bool iconic
, bool curdesk
)
1661 if (_iconic
== iconic
) return; // nothing to do
1664 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1670 _wmstate
= IconicState
;
1672 // we unmap the client itself so that we can get MapRequest events, and
1673 // because the ICCCM tells us to!
1674 XUnmapWindow(**otk::display
, _window
);
1677 setDesktop(openbox
->screen(_screen
)->desktop());
1678 _wmstate
= NormalState
;
1679 XMapWindow(**otk::display
, _window
);
1683 openbox
->screen(_screen
)->updateStruts();
1686 void Client::disableDecorations(DecorationFlags flags
)
1688 _disabled_decorations
= flags
;
1689 setupDecorAndFunctions();
1692 void Client::installColormap(bool install
) const
1694 XWindowAttributes wa
;
1695 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1697 XInstallColormap(**otk::display
, wa
.colormap
);
1699 XUninstallColormap(**otk::display
, wa
.colormap
);
1703 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1705 List::const_iterator it
, end
= node
->_transients
.end();
1708 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1709 if (*it
== skip
) continue; // circular?
1710 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1711 if ((*it
)->_modal
) return *it
; // got one
1716 Client
*Client::findModalChild()
1718 return searchModalTree(this, this);
1722 bool Client::focus()
1724 // if we have a modal child, then focus it, not us
1725 Client
*c
= findModalChild();
1726 if (c
) return c
->focus();
1728 // won't try focus if the client doesn't want it, or if the window isn't
1729 // visible on the screen
1730 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1732 // do a check to see if the window has already been unmapped or destroyed
1733 // do this intelligently while watching out for unmaps we've generated
1734 // (ignore_unmaps > 0)
1736 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1737 XPutBackEvent(**otk::display
, &ev
);
1740 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1741 if (ignore_unmaps
) {
1742 unmapHandler(ev
.xunmap
);
1744 XPutBackEvent(**otk::display
, &ev
);
1750 XSetInputFocus(**otk::display
, _window
,
1751 RevertToNone
, CurrentTime
);
1753 if (_focus_notify
) {
1755 ce
.xclient
.type
= ClientMessage
;
1756 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1757 ce
.xclient
.display
= **otk::display
;
1758 ce
.xclient
.window
= _window
;
1759 ce
.xclient
.format
= 32;
1760 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1761 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1762 ce
.xclient
.data
.l
[2] = 0l;
1763 ce
.xclient
.data
.l
[3] = 0l;
1764 ce
.xclient
.data
.l
[4] = 0l;
1765 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1768 XSync(**otk::display
, False
);
1773 void Client::unfocus() const
1775 assert(openbox
->focusedClient() == this);
1776 openbox
->setFocusedClient(0);
1780 void Client::focusHandler(const XFocusChangeEvent
&e
)
1783 // printf("FocusIn for 0x%lx\n", e.window);
1786 otk::EventHandler::focusHandler(e
);
1789 frame
->adjustFocus();
1791 calcLayer(); // focus state can affect the stacking layer
1793 openbox
->setFocusedClient(this);
1797 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1800 // printf("FocusOut for 0x%lx\n", e.window);
1803 otk::EventHandler::unfocusHandler(e
);
1806 frame
->adjustFocus();
1808 calcLayer(); // focus state can affect the stacking layer
1810 if (openbox
->focusedClient() == this)
1811 openbox
->setFocusedClient(0);
1815 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1818 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1821 otk::EventHandler::configureRequestHandler(ec
);
1824 XConfigureRequestEvent e
= ec
;
1826 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1828 // XXX if this causes bad things.. we can compress config req's with the
1830 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1831 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1832 e
.x
= ev
.xconfigurerequest
.x
;
1833 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1834 e
.y
= ev
.xconfigurerequest
.y
;
1835 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1836 e
.width
= ev
.xconfigurerequest
.width
;
1837 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1838 e
.height
= ev
.xconfigurerequest
.height
;
1839 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1840 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1841 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1842 e
.detail
= ev
.xconfigurerequest
.detail
;
1845 // if we are iconic (or shaded (fvwm does this)) ignore the event
1846 if (_iconic
|| _shaded
) return;
1848 if (e
.value_mask
& CWBorderWidth
)
1849 _border_width
= e
.border_width
;
1851 // resize, then move, as specified in the EWMH section 7.7
1852 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1853 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1854 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1858 case NorthEastGravity
:
1862 case SouthWestGravity
:
1864 corner
= BottomLeft
;
1866 case SouthEastGravity
:
1867 corner
= BottomRight
;
1869 default: // NorthWest, Static, etc
1873 // if moving AND resizing ...
1874 if (e
.value_mask
& (CWX
| CWY
)) {
1875 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1876 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1877 internal_resize(corner
, w
, h
, false, x
, y
);
1878 } else // if JUST resizing...
1879 internal_resize(corner
, w
, h
, false);
1880 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1881 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1882 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1883 internal_move(x
, y
);
1886 if (e
.value_mask
& CWStackMode
) {
1890 openbox
->screen(_screen
)->lowerWindow(this);
1896 openbox
->screen(_screen
)->raiseWindow(this);
1903 void Client::unmapHandler(const XUnmapEvent
&e
)
1905 if (ignore_unmaps
) {
1907 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1914 printf("UnmapNotify for 0x%lx\n", e
.window
);
1917 otk::EventHandler::unmapHandler(e
);
1919 // this deletes us etc
1920 openbox
->screen(_screen
)->unmanageWindow(this);
1924 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1927 printf("DestroyNotify for 0x%lx\n", e
.window
);
1930 otk::EventHandler::destroyHandler(e
);
1932 // this deletes us etc
1933 openbox
->screen(_screen
)->unmanageWindow(this);
1937 void Client::reparentHandler(const XReparentEvent
&e
)
1939 // this is when the client is first taken captive in the frame
1940 if (e
.parent
== frame
->plate()) return;
1943 printf("ReparentNotify for 0x%lx\n", e
.window
);
1946 otk::EventHandler::reparentHandler(e
);
1949 This event is quite rare and is usually handled in unmapHandler.
1950 However, if the window is unmapped when the reparent event occurs,
1951 the window manager never sees it because an unmap event is not sent
1952 to an already unmapped window.
1955 // we don't want the reparent event, put it back on the stack for the X
1956 // server to deal with after we unmanage the window
1959 XPutBackEvent(**otk::display
, &ev
);
1961 // this deletes us etc
1962 openbox
->screen(_screen
)->unmanageWindow(this);
1965 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1968 printf("MapRequest for already managed 0x%lx\n", e
.window
);
1971 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
1973 // move to the current desktop (uniconify)
1975 // XXX: should we focus/raise the window? (basically a net_wm_active_window)