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(Atom action
, long data1
, long data2
)
886 bool shadestate
= _shaded
;
887 bool fsstate
= _fullscreen
;
888 bool maxh
= _max_horz
;
889 bool maxv
= _max_vert
;
891 if (!(action
== otk::Property::atoms
.net_wm_state_add
||
892 action
== otk::Property::atoms
.net_wm_state_remove
||
893 action
== otk::Property::atoms
.net_wm_state_toggle
))
894 return; // an invalid action was passed to the client message, ignore it
896 for (int i
= 0; i
< 2; ++i
) {
897 Atom state
= i
== 0 ? data1
: data2
;
899 if (! state
) continue;
901 // if toggling, then pick whether we're adding or removing
902 if (action
== otk::Property::atoms
.net_wm_state_toggle
) {
903 if (state
== otk::Property::atoms
.net_wm_state_modal
)
904 action
= _modal
? otk::Property::atoms
.net_wm_state_remove
:
905 otk::Property::atoms
.net_wm_state_add
;
906 else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
)
907 action
= _max_vert
? otk::Property::atoms
.net_wm_state_remove
:
908 otk::Property::atoms
.net_wm_state_add
;
909 else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
)
910 action
= _max_horz
? otk::Property::atoms
.net_wm_state_remove
:
911 otk::Property::atoms
.net_wm_state_add
;
912 else if (state
== otk::Property::atoms
.net_wm_state_shaded
)
913 action
= _shaded
? otk::Property::atoms
.net_wm_state_remove
:
914 otk::Property::atoms
.net_wm_state_add
;
915 else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
)
916 action
= _skip_taskbar
? otk::Property::atoms
.net_wm_state_remove
:
917 otk::Property::atoms
.net_wm_state_add
;
918 else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
)
919 action
= _skip_pager
? otk::Property::atoms
.net_wm_state_remove
:
920 otk::Property::atoms
.net_wm_state_add
;
921 else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
)
922 action
= _fullscreen
? otk::Property::atoms
.net_wm_state_remove
:
923 otk::Property::atoms
.net_wm_state_add
;
924 else if (state
== otk::Property::atoms
.net_wm_state_above
)
925 action
= _above
? otk::Property::atoms
.net_wm_state_remove
:
926 otk::Property::atoms
.net_wm_state_add
;
927 else if (state
== otk::Property::atoms
.net_wm_state_below
)
928 action
= _below
? otk::Property::atoms
.net_wm_state_remove
:
929 otk::Property::atoms
.net_wm_state_add
;
932 if (action
== otk::Property::atoms
.net_wm_state_add
) {
933 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
934 if (_modal
) continue;
936 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
938 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
939 if (_max_horz
) continue;
941 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
943 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
944 _skip_taskbar
= true;
945 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
947 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
949 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
950 if (_above
) continue;
952 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
953 if (_below
) continue;
957 } else { // action == otk::Property::atoms.net_wm_state_remove
958 if (state
== otk::Property::atoms
.net_wm_state_modal
) {
959 if (!_modal
) continue;
961 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_vert
) {
963 } else if (state
== otk::Property::atoms
.net_wm_state_maximized_horz
) {
965 } else if (state
== otk::Property::atoms
.net_wm_state_shaded
) {
967 } else if (state
== otk::Property::atoms
.net_wm_state_skip_taskbar
) {
968 _skip_taskbar
= false;
969 } else if (state
== otk::Property::atoms
.net_wm_state_skip_pager
) {
971 } else if (state
== otk::Property::atoms
.net_wm_state_fullscreen
) {
973 } else if (state
== otk::Property::atoms
.net_wm_state_above
) {
974 if (!_above
) continue;
976 } else if (state
== otk::Property::atoms
.net_wm_state_below
) {
977 if (!_below
) continue;
982 if (maxh
!= _max_horz
|| maxv
!= _max_vert
) {
983 if (maxh
!= _max_horz
&& maxv
!= _max_vert
) { // toggling both
984 if (maxh
== maxv
) { // both going the same way
985 maximize(maxh
, 0, true);
987 maximize(maxh
, 1, true);
988 maximize(maxv
, 2, true);
990 } else { // toggling one
991 if (maxh
!= _max_horz
)
992 maximize(maxh
, 1, true);
994 maximize(maxv
, 2, true);
997 // change fullscreen state before shading, as it will affect if the window
999 if (fsstate
!= _fullscreen
)
1000 fullscreen(fsstate
, true);
1001 if (shadestate
!= _shaded
)
1004 changeState(); // change the hint to relect these changes
1007 void Client::toggleClientBorder(bool addborder
)
1009 // adjust our idea of where the client is, based on its border. When the
1010 // border is removed, the client should now be considered to be in a
1011 // different position.
1012 // when re-adding the border to the client, the same operation needs to be
1014 int oldx
= _area
.x(), oldy
= _area
.y();
1015 int x
= oldx
, y
= oldy
;
1018 case NorthWestGravity
:
1020 case SouthWestGravity
:
1022 case NorthEastGravity
:
1024 case SouthEastGravity
:
1025 if (addborder
) x
-= _border_width
* 2;
1026 else x
+= _border_width
* 2;
1033 if (addborder
) x
-= _border_width
;
1034 else x
+= _border_width
;
1039 case NorthWestGravity
:
1041 case NorthEastGravity
:
1043 case SouthWestGravity
:
1045 case SouthEastGravity
:
1046 if (addborder
) y
-= _border_width
* 2;
1047 else y
+= _border_width
* 2;
1054 if (addborder
) y
-= _border_width
;
1055 else y
+= _border_width
;
1058 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1061 XSetWindowBorderWidth(**otk::display
, _window
, _border_width
);
1063 // move the client so it is back it the right spot _with_ its border!
1064 if (x
!= oldx
|| y
!= oldy
)
1065 XMoveWindow(**otk::display
, _window
, x
, y
);
1067 XSetWindowBorderWidth(**otk::display
, _window
, 0);
1070 void Client::clientMessageHandler(const XClientMessageEvent
&e
)
1072 otk::EventHandler::clientMessageHandler(e
);
1074 // validate cuz we query stuff off the client here
1075 if (!validate()) return;
1077 if (e
.format
!= 32) return;
1079 if (e
.message_type
== otk::Property::atoms
.wm_change_state
) {
1080 // compress changes into a single change
1081 bool compress
= false;
1083 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1084 // XXX: it would be nice to compress ALL messages of a type, not just
1085 // messages in a row without other message types between.
1086 if (ce
.xclient
.message_type
!= e
.message_type
) {
1087 XPutBackEvent(**otk::display
, &ce
);
1093 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
1095 setWMState(e
.data
.l
[0]); // use the original event
1096 } else if (e
.message_type
== otk::Property::atoms
.net_wm_desktop
) {
1097 // compress changes into a single change
1098 bool compress
= false;
1100 while (XCheckTypedEvent(**otk::display
, e
.type
, &ce
)) {
1101 // XXX: it would be nice to compress ALL messages of a type, not just
1102 // messages in a row without other message types between.
1103 if (ce
.xclient
.message_type
!= e
.message_type
) {
1104 XPutBackEvent(**otk::display
, &ce
);
1110 setDesktop(e
.data
.l
[0]); // use the found event
1112 setDesktop(e
.data
.l
[0]); // use the original event
1113 } else if (e
.message_type
== otk::Property::atoms
.net_wm_state
) {
1114 // can't compress these
1116 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1117 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
1118 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
1119 e
.data
.l
[1], e
.data
.l
[2], _window
);
1121 setState(e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
1122 } else if (e
.message_type
== otk::Property::atoms
.net_close_window
) {
1124 printf("net_close_window for 0x%lx\n", _window
);
1127 } else if (e
.message_type
== otk::Property::atoms
.net_active_window
) {
1129 printf("net_active_window for 0x%lx\n", _window
);
1131 if (openbox
->screen(_screen
)->showingDesktop())
1132 openbox
->screen(_screen
)->showDesktop(false);
1135 else if (!frame
->visible()) // if its not visible for other reasons, then
1136 return; // don't mess with it
1140 openbox
->screen(_screen
)->raiseWindow(this);
1141 } else if (e
.message_type
== otk::Property::atoms
.openbox_active_window
) {
1142 if (openbox
->screen(_screen
)->showingDesktop())
1143 openbox
->screen(_screen
)->showDesktop(false);
1146 else if (!frame
->visible()) // if its not visible for other reasons, then
1147 return; // don't mess with it
1148 if (e
.data
.l
[0] && _shaded
)
1152 openbox
->screen(_screen
)->raiseWindow(this);
1153 } else if (e
.message_type
== otk::Property::atoms
.openbox_restack_window
) {
1155 printf("openbox_restack_window for 0x%lx\n", _window
);
1157 if (e
.data
.l
[0] == 0)
1158 openbox
->screen(_screen
)->raiseWindow(this);
1159 else if (e
.data
.l
[0] == 1)
1160 openbox
->screen(_screen
)->lowerWindow(this);
1165 void Client::shapeHandler(const XShapeEvent
&e
)
1167 otk::EventHandler::shapeHandler(e
);
1169 if (e
.kind
== ShapeBounding
) {
1171 frame
->adjustShape();
1176 void Client::resize(Corner anchor
, int w
, int h
)
1178 if (!(_functions
& Func_Resize
)) return;
1179 internal_resize(anchor
, w
, h
);
1182 void Client::internal_resize(Corner anchor
, int w
, int h
,
1183 bool user
, int x
, int y
)
1185 w
-= _base_size
.width();
1186 h
-= _base_size
.height();
1189 // for interactive resizing. have to move half an increment in each
1191 int mw
= w
% _size_inc
.width(); // how far we are towards the next size inc
1192 int mh
= h
% _size_inc
.height();
1193 int aw
= _size_inc
.width() / 2; // amount to add
1194 int ah
= _size_inc
.height() / 2;
1195 // don't let us move into a new size increment
1196 if (mw
+ aw
>= _size_inc
.width()) aw
= _size_inc
.width() - mw
- 1;
1197 if (mh
+ ah
>= _size_inc
.height()) ah
= _size_inc
.height() - mh
- 1;
1201 // if this is a user-requested resize, then check against min/max sizes
1202 // and aspect ratios
1204 // smaller than min size or bigger than max size?
1205 if (w
> _max_size
.width()) w
= _max_size
.width();
1206 if (w
< _min_size
.width()) w
= _min_size
.width();
1207 if (h
> _max_size
.height()) h
= _max_size
.height();
1208 if (h
< _min_size
.height()) h
= _min_size
.height();
1210 // adjust the height ot match the width for the aspect ratios
1212 if (h
* _min_ratio
> w
) h
= static_cast<int>(w
/ _min_ratio
);
1214 if (h
* _max_ratio
< w
) h
= static_cast<int>(w
/ _max_ratio
);
1217 // keep to the increments
1218 w
/= _size_inc
.width();
1219 h
/= _size_inc
.height();
1221 // you cannot resize to nothing
1225 // store the logical size
1226 _logical_size
= otk::Size(w
, h
);
1228 w
*= _size_inc
.width();
1229 h
*= _size_inc
.height();
1231 w
+= _base_size
.width();
1232 h
+= _base_size
.height();
1234 if (x
== INT_MIN
|| y
== INT_MIN
) {
1241 x
-= w
- _area
.width();
1244 y
-= h
- _area
.height();
1247 x
-= w
- _area
.width();
1248 y
-= h
- _area
.height();
1253 _area
= otk::Rect(_area
.position(), otk::Size(w
, h
));
1255 XResizeWindow(**otk::display
, _window
, w
, h
);
1257 // resize the frame to match the request
1258 frame
->adjustSize();
1259 internal_move(x
, y
);
1262 const Icon
*Client::icon(const otk::Size
&s
) const
1264 unsigned long req
= s
.width() * s
.height();
1265 // si is the smallest image >= req
1266 // li is the largest image < req
1267 unsigned long smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
1269 assert(_nicons
> 0); // there should always be a default..
1270 for (int i
= 0; i
< _nicons
; ++i
) {
1271 unsigned long size
= _icons
[i
].w
* _icons
[i
].h
;
1272 if (size
< smallest
&& size
>= req
) {
1276 if (size
> largest
&& size
<= req
) {
1281 if (largest
== 0) // didnt find one smaller than the requested size
1286 void Client::move(int x
, int y
)
1288 if (!(_functions
& Func_Move
)) return;
1289 frame
->frameGravity(x
, y
); // get the client's position based on x,y for the
1291 internal_move(x
, y
);
1294 void Client::internal_move(int x
, int y
)
1296 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
1298 // move the frame to be in the requested position
1299 if (frame
) { // this can be called while mapping, before frame exists
1300 frame
->adjustPosition();
1302 // send synthetic configure notify (we don't need to if we aren't mapped
1305 event
.type
= ConfigureNotify
;
1306 event
.xconfigure
.display
= **otk::display
;
1307 event
.xconfigure
.event
= _window
;
1308 event
.xconfigure
.window
= _window
;
1310 // root window coords with border in mind
1311 event
.xconfigure
.x
= x
- _border_width
+ frame
->size().left
;
1312 event
.xconfigure
.y
= y
- _border_width
+ frame
->size().top
;
1314 event
.xconfigure
.width
= _area
.width();
1315 event
.xconfigure
.height
= _area
.height();
1316 event
.xconfigure
.border_width
= _border_width
;
1317 event
.xconfigure
.above
= frame
->plate();
1318 event
.xconfigure
.override_redirect
= False
;
1319 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1320 StructureNotifyMask
, &event
);
1322 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1323 event
.xconfigure
.x
, event
.xconfigure
.y
, event
.xconfigure
.width
,
1324 event
.xconfigure
.height
, event
.xconfigure
.window
);
1329 void Client::close()
1333 if (!(_functions
& Func_Close
)) return;
1335 // XXX: itd be cool to do timeouts and shit here for killing the client's
1337 // like... if the window is around after 5 seconds, then the close button
1338 // turns a nice red, and if this function is called again, the client is
1339 // explicitly killed.
1341 ce
.xclient
.type
= ClientMessage
;
1342 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1343 ce
.xclient
.display
= **otk::display
;
1344 ce
.xclient
.window
= _window
;
1345 ce
.xclient
.format
= 32;
1346 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_delete_window
;
1347 ce
.xclient
.data
.l
[1] = CurrentTime
;
1348 ce
.xclient
.data
.l
[2] = 0l;
1349 ce
.xclient
.data
.l
[3] = 0l;
1350 ce
.xclient
.data
.l
[4] = 0l;
1351 XSendEvent(**otk::display
, _window
, false, NoEventMask
, &ce
);
1354 void Client::changeState()
1356 unsigned long state
[2];
1357 state
[0] = _wmstate
;
1359 otk::Property::set(_window
, otk::Property::atoms
.wm_state
,
1360 otk::Property::atoms
.wm_state
, state
, 2);
1365 netstate
[num
++] = otk::Property::atoms
.net_wm_state_modal
;
1367 netstate
[num
++] = otk::Property::atoms
.net_wm_state_shaded
;
1369 netstate
[num
++] = otk::Property::atoms
.net_wm_state_hidden
;
1371 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_taskbar
;
1373 netstate
[num
++] = otk::Property::atoms
.net_wm_state_skip_pager
;
1375 netstate
[num
++] = otk::Property::atoms
.net_wm_state_fullscreen
;
1377 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_vert
;
1379 netstate
[num
++] = otk::Property::atoms
.net_wm_state_maximized_horz
;
1381 netstate
[num
++] = otk::Property::atoms
.net_wm_state_above
;
1383 netstate
[num
++] = otk::Property::atoms
.net_wm_state_below
;
1384 otk::Property::set(_window
, otk::Property::atoms
.net_wm_state
,
1385 otk::Property::atoms
.atom
, netstate
, num
);
1390 frame
->adjustState();
1393 void Client::changeAllowedActions(void)
1398 actions
[num
++] = otk::Property::atoms
.net_wm_action_change_desktop
;
1400 if (_functions
& Func_Shade
)
1401 actions
[num
++] = otk::Property::atoms
.net_wm_action_shade
;
1402 if (_functions
& Func_Close
)
1403 actions
[num
++] = otk::Property::atoms
.net_wm_action_close
;
1404 if (_functions
& Func_Move
)
1405 actions
[num
++] = otk::Property::atoms
.net_wm_action_move
;
1406 if (_functions
& Func_Iconify
)
1407 actions
[num
++] = otk::Property::atoms
.net_wm_action_minimize
;
1408 if (_functions
& Func_Resize
)
1409 actions
[num
++] = otk::Property::atoms
.net_wm_action_resize
;
1410 if (_functions
& Func_Fullscreen
)
1411 actions
[num
++] = otk::Property::atoms
.net_wm_action_fullscreen
;
1412 if (_functions
& Func_Maximize
) {
1413 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_horz
;
1414 actions
[num
++] = otk::Property::atoms
.net_wm_action_maximize_vert
;
1417 otk::Property::set(_window
, otk::Property::atoms
.net_wm_allowed_actions
,
1418 otk::Property::atoms
.atom
, actions
, num
);
1420 // make sure the window isn't breaking any rules now
1422 if (!(_functions
& Func_Shade
) && _shaded
)
1423 if (frame
) shade(false);
1424 else _shaded
= false;
1425 if (!(_functions
& Func_Iconify
) && _iconic
)
1426 if (frame
) setDesktop(openbox
->screen(_screen
)->desktop());
1427 else _iconic
= false;
1428 if (!(_functions
& Func_Fullscreen
) && _fullscreen
)
1429 if (frame
) fullscreen(false);
1430 else _fullscreen
= false;
1431 if (!(_functions
& Func_Maximize
) && (_max_horz
|| _max_vert
))
1432 if (frame
) maximize(false, 0);
1433 else _max_vert
= _max_horz
= false;
1436 void Client::remaximize()
1439 if (_max_horz
&& _max_vert
)
1446 return; // not maximized
1447 _max_horz
= _max_vert
= false;
1448 maximize(true, dir
, false);
1451 void Client::applyStartupState()
1453 // these are in a carefully crafted order..
1460 _fullscreen
= false;
1461 fullscreen(true, false);
1470 if (_max_vert
&& _max_horz
) {
1471 _max_vert
= _max_horz
= false;
1472 maximize(true, 0, false);
1473 } else if (_max_vert
) {
1475 maximize(true, 2, false);
1476 } else if (_max_horz
) {
1478 maximize(true, 1, false);
1481 if (_skip_taskbar
); // nothing to do for this
1482 if (_skip_pager
); // nothing to do for this
1483 if (_modal
); // nothing to do for this
1484 if (_above
); // nothing to do for this
1485 if (_below
); // nothing to do for this
1488 void Client::fireUrgent()
1490 // call the python UrgentWindow callbacks
1491 EventData
data(_screen
, this, EventAction::UrgentWindow
, 0);
1492 openbox
->bindings()->fireEvent(&data
);
1495 void Client::shade(bool shade
)
1497 if (!(_functions
& Func_Shade
) || // can't
1498 _shaded
== shade
) return; // already done
1500 // when we're iconic, don't change the wmstate
1502 _wmstate
= shade
? IconicState
: NormalState
;
1505 frame
->adjustSize();
1508 void Client::maximize(bool max
, int dir
, bool savearea
)
1510 assert(dir
== 0 || dir
== 1 || dir
== 2);
1511 if (!(_functions
& Func_Maximize
)) return; // can't
1513 // check if already done
1515 if (dir
== 0 && _max_horz
&& _max_vert
) return;
1516 if (dir
== 1 && _max_horz
) return;
1517 if (dir
== 2 && _max_vert
) return;
1519 if (dir
== 0 && !_max_horz
&& !_max_vert
) return;
1520 if (dir
== 1 && !_max_horz
) return;
1521 if (dir
== 2 && !_max_vert
) return;
1524 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1525 int x
= frame
->area().x(), y
= frame
->area().y(),
1526 w
= _area
.width(), h
= _area
.height();
1532 unsigned long n
= 4;
1539 // get the property off the window and use it for the dimentions we are
1541 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1542 otk::Property::atoms
.cardinal
, &n
,
1543 (long unsigned**) &readdim
)) {
1546 dimensions
[0] = readdim
[0];
1547 dimensions
[2] = readdim
[2];
1550 dimensions
[1] = readdim
[1];
1551 dimensions
[3] = readdim
[3];
1557 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1558 otk::Property::atoms
.cardinal
,
1559 (long unsigned*)dimensions
, 4);
1561 if (dir
== 0 || dir
== 1) { // horz
1565 if (dir
== 0 || dir
== 2) { // vert
1567 h
= a
.height() - frame
->size().top
- frame
->size().bottom
;
1571 long unsigned n
= 4;
1573 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1574 otk::Property::atoms
.cardinal
, &n
,
1575 (long unsigned**) &dimensions
)) {
1577 if (dir
== 0 || dir
== 1) { // horz
1578 x
= (signed int)dimensions
[0];
1579 w
= (signed int)dimensions
[2];
1581 if (dir
== 0 || dir
== 2) { // vert
1582 y
= (signed int)dimensions
[1];
1583 h
= (signed int)dimensions
[3];
1588 // pick some fallbacks...
1589 if (dir
== 0 || dir
== 1) { // horz
1590 x
= a
.x() + a
.width() / 4;
1593 if (dir
== 0 || dir
== 2) { // vert
1594 y
= a
.y() + a
.height() / 4;
1600 if (dir
== 0 || dir
== 1) // horz
1602 if (dir
== 0 || dir
== 2) // vert
1605 if (!_max_horz
&& !_max_vert
)
1606 otk::Property::erase(_window
, otk::Property::atoms
.openbox_premax
);
1608 changeState(); // change the state hints on the client
1610 frame
->frameGravity(x
, y
); // figure out where the client should be going
1611 internal_resize(TopLeft
, w
, h
, true, x
, y
);
1614 void Client::fullscreen(bool fs
, bool savearea
)
1616 static FunctionFlags saved_func
;
1617 static DecorationFlags saved_decor
;
1619 if (!(_functions
& Func_Fullscreen
) || // can't
1620 _fullscreen
== fs
) return; // already done
1623 changeState(); // change the state hints on the client
1625 int x
= _area
.x(), y
= _area
.y(), w
= _area
.width(), h
= _area
.height();
1628 // save the functions and remove them
1629 saved_func
= _functions
;
1630 _functions
= _functions
& (Func_Close
| Func_Fullscreen
| Func_Iconify
);
1631 // save the decorations and remove them
1632 saved_decor
= _decorations
;
1636 dimensions
[0] = _area
.x();
1637 dimensions
[1] = _area
.y();
1638 dimensions
[2] = _area
.width();
1639 dimensions
[3] = _area
.height();
1640 otk::Property::set(_window
, otk::Property::atoms
.openbox_premax
,
1641 otk::Property::atoms
.cardinal
,
1642 (long unsigned*)dimensions
, 4);
1644 const otk::ScreenInfo
*info
= otk::display
->screenInfo(_screen
);
1647 w
= info
->size().width();
1648 h
= info
->size().height();
1650 _functions
= saved_func
;
1651 _decorations
= saved_decor
;
1654 long unsigned n
= 4;
1656 if (otk::Property::get(_window
, otk::Property::atoms
.openbox_premax
,
1657 otk::Property::atoms
.cardinal
, &n
,
1658 (long unsigned**) &dimensions
)) {
1667 // pick some fallbacks...
1668 const otk::Rect
&a
= openbox
->screen(_screen
)->area(_desktop
);
1669 x
= a
.x() + a
.width() / 4;
1670 y
= a
.y() + a
.height() / 4;
1676 changeAllowedActions(); // based on the new _functions
1678 // when fullscreening, don't obey things like increments, fill the screen
1679 internal_resize(TopLeft
, w
, h
, !fs
, x
, y
);
1681 // raise (back) into our stacking layer
1682 openbox
->screen(_screen
)->raiseWindow(this);
1684 // try focus us when we go into fullscreen mode
1688 void Client::iconify(bool iconic
, bool curdesk
)
1690 if (_iconic
== iconic
) return; // nothing to do
1693 printf("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"), _window
);
1699 _wmstate
= IconicState
;
1701 // we unmap the client itself so that we can get MapRequest events, and
1702 // because the ICCCM tells us to!
1703 XUnmapWindow(**otk::display
, _window
);
1706 setDesktop(openbox
->screen(_screen
)->desktop());
1707 _wmstate
= NormalState
;
1708 XMapWindow(**otk::display
, _window
);
1712 openbox
->screen(_screen
)->updateStruts();
1715 void Client::disableDecorations(DecorationFlags flags
)
1717 _disabled_decorations
= flags
;
1718 setupDecorAndFunctions();
1721 void Client::installColormap(bool install
) const
1723 XWindowAttributes wa
;
1724 if (XGetWindowAttributes(**otk::display
, _window
, &wa
)) {
1726 XInstallColormap(**otk::display
, wa
.colormap
);
1728 XUninstallColormap(**otk::display
, wa
.colormap
);
1732 Client
*Client::searchModalTree(Client
*node
, Client
*skip
)
1734 List::const_iterator it
, end
= node
->_transients
.end();
1737 for (it
= node
->_transients
.begin(); it
!= end
; ++it
) {
1738 if (*it
== skip
) continue; // circular?
1739 if ((ret
= searchModalTree(*it
, skip
))) return ret
; // got one
1740 if ((*it
)->_modal
) return *it
; // got one
1745 Client
*Client::findModalChild()
1747 return searchModalTree(this, this);
1751 bool Client::focus()
1753 // if we have a modal child, then focus it, not us
1754 Client
*c
= findModalChild();
1755 if (c
) return c
->focus();
1757 // won't try focus if the client doesn't want it, or if the window isn't
1758 // visible on the screen
1759 if (!(frame
->visible() && (_can_focus
|| _focus_notify
))) return false;
1761 // do a check to see if the window has already been unmapped or destroyed
1762 // do this intelligently while watching out for unmaps we've generated
1763 // (ignore_unmaps > 0)
1765 if (XCheckTypedWindowEvent(**otk::display
, _window
, DestroyNotify
, &ev
)) {
1766 XPutBackEvent(**otk::display
, &ev
);
1769 while (XCheckTypedWindowEvent(**otk::display
, _window
, UnmapNotify
, &ev
)) {
1770 if (ignore_unmaps
) {
1771 unmapHandler(ev
.xunmap
);
1773 XPutBackEvent(**otk::display
, &ev
);
1779 XSetInputFocus(**otk::display
, _window
,
1780 RevertToNone
, CurrentTime
);
1782 if (_focus_notify
) {
1784 ce
.xclient
.type
= ClientMessage
;
1785 ce
.xclient
.message_type
= otk::Property::atoms
.wm_protocols
;
1786 ce
.xclient
.display
= **otk::display
;
1787 ce
.xclient
.window
= _window
;
1788 ce
.xclient
.format
= 32;
1789 ce
.xclient
.data
.l
[0] = otk::Property::atoms
.wm_take_focus
;
1790 ce
.xclient
.data
.l
[1] = openbox
->lastTime();
1791 ce
.xclient
.data
.l
[2] = 0l;
1792 ce
.xclient
.data
.l
[3] = 0l;
1793 ce
.xclient
.data
.l
[4] = 0l;
1794 XSendEvent(**otk::display
, _window
, False
, NoEventMask
, &ce
);
1797 XSync(**otk::display
, False
);
1802 void Client::unfocus() const
1804 assert(openbox
->focusedClient() == this);
1805 openbox
->setFocusedClient(0);
1809 void Client::focusHandler(const XFocusChangeEvent
&e
)
1812 // printf("FocusIn for 0x%lx\n", e.window);
1815 otk::EventHandler::focusHandler(e
);
1818 frame
->adjustFocus();
1820 calcLayer(); // focus state can affect the stacking layer
1822 openbox
->setFocusedClient(this);
1826 void Client::unfocusHandler(const XFocusChangeEvent
&e
)
1829 // printf("FocusOut for 0x%lx\n", e.window);
1832 otk::EventHandler::unfocusHandler(e
);
1835 frame
->adjustFocus();
1837 calcLayer(); // focus state can affect the stacking layer
1839 if (openbox
->focusedClient() == this)
1840 openbox
->setFocusedClient(0);
1844 void Client::configureRequestHandler(const XConfigureRequestEvent
&ec
)
1847 printf("ConfigureRequest for 0x%lx\n", ec
.window
);
1850 otk::EventHandler::configureRequestHandler(ec
);
1853 XConfigureRequestEvent e
= ec
;
1855 while (XCheckTypedWindowEvent(**otk::display
, window(), ConfigureRequest
,
1857 // XXX if this causes bad things.. we can compress config req's with the
1859 e
.value_mask
|= ev
.xconfigurerequest
.value_mask
;
1860 if (ev
.xconfigurerequest
.value_mask
& CWX
)
1861 e
.x
= ev
.xconfigurerequest
.x
;
1862 if (ev
.xconfigurerequest
.value_mask
& CWY
)
1863 e
.y
= ev
.xconfigurerequest
.y
;
1864 if (ev
.xconfigurerequest
.value_mask
& CWWidth
)
1865 e
.width
= ev
.xconfigurerequest
.width
;
1866 if (ev
.xconfigurerequest
.value_mask
& CWHeight
)
1867 e
.height
= ev
.xconfigurerequest
.height
;
1868 if (ev
.xconfigurerequest
.value_mask
& CWBorderWidth
)
1869 e
.border_width
= ev
.xconfigurerequest
.border_width
;
1870 if (ev
.xconfigurerequest
.value_mask
& CWStackMode
)
1871 e
.detail
= ev
.xconfigurerequest
.detail
;
1874 // if we are iconic (or shaded (fvwm does this)) ignore the event
1875 if (_iconic
|| _shaded
) return;
1877 if (e
.value_mask
& CWBorderWidth
)
1878 _border_width
= e
.border_width
;
1880 // resize, then move, as specified in the EWMH section 7.7
1881 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1882 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1883 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1887 case NorthEastGravity
:
1891 case SouthWestGravity
:
1893 corner
= BottomLeft
;
1895 case SouthEastGravity
:
1896 corner
= BottomRight
;
1898 default: // NorthWest, Static, etc
1902 // if moving AND resizing ...
1903 if (e
.value_mask
& (CWX
| CWY
)) {
1904 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1905 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1906 internal_resize(corner
, w
, h
, false, x
, y
);
1907 } else // if JUST resizing...
1908 internal_resize(corner
, w
, h
, false);
1909 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1910 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1911 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1912 internal_move(x
, y
);
1915 if (e
.value_mask
& CWStackMode
) {
1919 openbox
->screen(_screen
)->lowerWindow(this);
1925 openbox
->screen(_screen
)->raiseWindow(this);
1932 void Client::unmapHandler(const XUnmapEvent
&e
)
1934 if (ignore_unmaps
) {
1936 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1943 printf("UnmapNotify for 0x%lx\n", e
.window
);
1946 otk::EventHandler::unmapHandler(e
);
1948 // this deletes us etc
1949 openbox
->screen(_screen
)->unmanageWindow(this);
1953 void Client::destroyHandler(const XDestroyWindowEvent
&e
)
1956 printf("DestroyNotify for 0x%lx\n", e
.window
);
1959 otk::EventHandler::destroyHandler(e
);
1961 // this deletes us etc
1962 openbox
->screen(_screen
)->unmanageWindow(this);
1966 void Client::reparentHandler(const XReparentEvent
&e
)
1968 // this is when the client is first taken captive in the frame
1969 if (e
.parent
== frame
->plate()) return;
1972 printf("ReparentNotify for 0x%lx\n", e
.window
);
1975 otk::EventHandler::reparentHandler(e
);
1978 This event is quite rare and is usually handled in unmapHandler.
1979 However, if the window is unmapped when the reparent event occurs,
1980 the window manager never sees it because an unmap event is not sent
1981 to an already unmapped window.
1984 // we don't want the reparent event, put it back on the stack for the X
1985 // server to deal with after we unmanage the window
1988 XPutBackEvent(**otk::display
, &ev
);
1990 // this deletes us etc
1991 openbox
->screen(_screen
)->unmanageWindow(this);
1994 void Client::mapRequestHandler(const XMapRequestEvent
&e
)
1997 printf("MapRequest for already managed 0x%lx\n", e
.window
);
2000 assert(_iconic
); // we shouldn't be able to get this unless we're iconic
2002 // move to the current desktop (uniconify)
2004 // XXX: should we focus/raise the window? (basically a net_wm_active_window)