1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
11 #include "otk/display.hh"
12 #include "otk/property.hh"
16 #include <X11/Xutil.h>
21 #define _(str) gettext(str)
26 OBClient::OBClient(int screen
, Window window
)
27 : otk::OtkEventHandler(),
28 OBWidget(OBWidget::Type_Client
),
29 frame(0), _screen(screen
), _window(window
)
36 // update EVERYTHING the first time!!
38 // the state is kinda assumed to be normal. is this right? XXX
39 _wmstate
= NormalState
; _iconic
= false;
40 // no default decors or functions, each has to be enabled
41 _decorations
= _functions
= 0;
44 // not a transient by default of course
54 setupDecorAndFunctions();
73 const otk::OBProperty
*property
= Openbox::instance
->property();
75 // clean up parents reference to this
77 _transient_for
->_transients
.remove(this); // remove from old parent
79 if (Openbox::instance
->state() != Openbox::State_Exiting
) {
80 // these values should not be persisted across a window unmapping/mapping
81 property
->erase(_window
, otk::OBProperty::net_wm_desktop
);
82 property
->erase(_window
, otk::OBProperty::net_wm_state
);
87 void OBClient::getDesktop()
89 const otk::OBProperty
*property
= Openbox::instance
->property();
91 // defaults to the current desktop
92 _desktop
= 0; // XXX: change this to the current desktop!
94 property
->get(_window
, otk::OBProperty::net_wm_desktop
,
95 otk::OBProperty::Atom_Cardinal
,
100 void OBClient::getType()
102 const otk::OBProperty
*property
= Openbox::instance
->property();
104 _type
= (WindowType
) -1;
107 unsigned long num
= (unsigned) -1;
108 if (property
->get(_window
, otk::OBProperty::net_wm_window_type
,
109 otk::OBProperty::Atom_Atom
,
111 // use the first value that we know about in the array
112 for (unsigned long i
= 0; i
< num
; ++i
) {
114 property
->atom(otk::OBProperty::net_wm_window_type_desktop
))
115 _type
= Type_Desktop
;
117 property
->atom(otk::OBProperty::net_wm_window_type_dock
))
120 property
->atom(otk::OBProperty::net_wm_window_type_toolbar
))
121 _type
= Type_Toolbar
;
123 property
->atom(otk::OBProperty::net_wm_window_type_menu
))
126 property
->atom(otk::OBProperty::net_wm_window_type_utility
))
127 _type
= Type_Utility
;
129 property
->atom(otk::OBProperty::net_wm_window_type_splash
))
132 property
->atom(otk::OBProperty::net_wm_window_type_dialog
))
135 property
->atom(otk::OBProperty::net_wm_window_type_normal
))
137 // else if (val[i] ==
138 // property->atom(otk::OBProperty::kde_net_wm_window_type_override))
139 // mwm_decorations = 0; // prevent this window from getting any decor
140 // XXX: make this work again
145 if (_type
== (WindowType
) -1) {
147 * the window type hint was not set, which means we either classify ourself
148 * as a normal window or a dialog, depending on if we are a transient.
158 void OBClient::setupDecorAndFunctions()
160 // start with everything
161 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
162 Decor_Iconify
| Decor_Maximize
;
163 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
167 // normal windows retain all of the possible decorations and
171 // dialogs cannot be maximized
172 _decorations
&= ~Decor_Maximize
;
173 _functions
&= ~Func_Maximize
;
179 // these windows get less functionality
180 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
181 _functions
&= ~(Func_Iconify
| Func_Resize
);
187 // none of these windows are manipulated by the window manager
193 // Mwm Hints are applied subtractively to what has already been chosen for
194 // decor and functionality
195 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
196 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
197 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
198 _decorations
&= ~Decor_Border
;
199 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
200 _decorations
&= ~Decor_Handle
;
201 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
202 _decorations
&= ~Decor_Titlebar
;
203 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
204 _decorations
&= ~Decor_Iconify
;
205 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
206 _decorations
&= ~Decor_Maximize
;
210 if (_mwmhints
.flags
& MwmFlag_Functions
) {
211 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
212 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
213 _functions
&= ~Func_Resize
;
214 if (! (_mwmhints
.functions
& MwmFunc_Move
))
215 _functions
&= ~Func_Move
;
216 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
217 _functions
&= ~Func_Iconify
;
218 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
219 _functions
&= ~Func_Maximize
;
220 // dont let mwm hints kill the close button
221 //if (! (_mwmhints.functions & MwmFunc_Close))
222 // _functions &= ~Func_Close;
226 // XXX: changeAllowedActions();
230 void OBClient::getMwmHints()
232 const otk::OBProperty
*property
= Openbox::instance
->property();
234 unsigned long num
= MwmHints::elements
;
235 unsigned long *hints
;
237 _mwmhints
.flags
= 0; // default to none
239 if (!property
->get(_window
, otk::OBProperty::motif_wm_hints
,
240 otk::OBProperty::motif_wm_hints
, &num
,
241 (unsigned long **)&hints
))
244 if (num
>= MwmHints::elements
) {
245 // retrieved the hints
246 _mwmhints
.flags
= hints
[0];
247 _mwmhints
.functions
= hints
[1];
248 _mwmhints
.decorations
= hints
[2];
255 void OBClient::getArea()
257 XWindowAttributes wattrib
;
260 ret
= XGetWindowAttributes(otk::OBDisplay::display
, _window
, &wattrib
);
261 assert(ret
!= BadWindow
);
263 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
264 _border_width
= wattrib
.border_width
;
268 void OBClient::getState()
270 const otk::OBProperty
*property
= Openbox::instance
->property();
272 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
273 _skip_taskbar
= _skip_pager
= false;
275 unsigned long *state
;
276 unsigned long num
= (unsigned) -1;
278 if (property
->get(_window
, otk::OBProperty::net_wm_state
,
279 otk::OBProperty::Atom_Atom
, &num
, &state
)) {
280 for (unsigned long i
= 0; i
< num
; ++i
) {
281 if (state
[i
] == property
->atom(otk::OBProperty::net_wm_state_modal
))
284 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
286 _wmstate
= IconicState
;
287 } else if (state
[i
] ==
288 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
289 _skip_taskbar
= true;
291 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
294 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
297 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
300 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
303 property
->atom(otk::OBProperty::net_wm_state_above
))
306 property
->atom(otk::OBProperty::net_wm_state_below
))
315 void OBClient::getShaped()
319 if (otk::OBDisplay::shape()) {
324 XShapeSelectInput(otk::OBDisplay::display
, _window
, ShapeNotifyMask
);
326 XShapeQueryExtents(otk::OBDisplay::display
, _window
, &s
, &foo
,
327 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
334 void OBClient::calcLayer() {
337 if (_iconic
) l
= Layer_Icon
;
338 else if (_fullscreen
) l
= Layer_Fullscreen
;
339 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
340 else if (_type
== Type_Dock
) {
341 if (!_below
) l
= Layer_Top
;
342 else l
= Layer_Normal
;
344 else if (_above
) l
= Layer_Above
;
345 else if (_below
) l
= Layer_Below
;
346 else l
= Layer_Normal
;
352 if we don't have a frame, then we aren't mapped yet (and this would
355 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
361 void OBClient::updateProtocols()
363 const otk::OBProperty
*property
= Openbox::instance
->property();
368 _focus_notify
= false;
369 _decorations
&= ~Decor_Close
;
370 _functions
&= ~Func_Close
;
372 if (XGetWMProtocols(otk::OBDisplay::display
, _window
, &proto
, &num_return
)) {
373 for (int i
= 0; i
< num_return
; ++i
) {
374 if (proto
[i
] == property
->atom(otk::OBProperty::wm_delete_window
)) {
375 _decorations
|= Decor_Close
;
376 _functions
|= Func_Close
;
378 frame
->adjustSize(); // update the decorations
379 } else if (proto
[i
] == property
->atom(otk::OBProperty::wm_take_focus
))
380 // if this protocol is requested, then the window will be notified
381 // by the window manager whenever it receives focus
382 _focus_notify
= true;
389 void OBClient::updateNormalHints()
393 int oldgravity
= _gravity
;
396 _gravity
= NorthWestGravity
;
397 _size_inc
.setPoint(1, 1);
398 _base_size
.setPoint(0, 0);
399 _min_size
.setPoint(0, 0);
400 _max_size
.setPoint(INT_MAX
, INT_MAX
);
402 // XXX: might want to cancel any interactive resizing of the window at this
405 // get the hints from the window
406 if (XGetWMNormalHints(otk::OBDisplay::display
, _window
, &size
, &ret
)) {
407 _positioned
= (size
.flags
& (PPosition
|USPosition
));
409 if (size
.flags
& PWinGravity
)
410 _gravity
= size
.win_gravity
;
412 if (size
.flags
& PMinSize
)
413 _min_size
.setPoint(size
.min_width
, size
.min_height
);
415 if (size
.flags
& PMaxSize
)
416 _max_size
.setPoint(size
.max_width
, size
.max_height
);
418 if (size
.flags
& PBaseSize
)
419 _base_size
.setPoint(size
.base_width
, size
.base_height
);
421 if (size
.flags
& PResizeInc
)
422 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
425 // if the client has a frame, i.e. has already been mapped and is
426 // changing its gravity
427 if (frame
&& _gravity
!= oldgravity
) {
428 // move our idea of the client's position based on its new gravity
430 frame
->frameGravity(x
, y
);
436 void OBClient::updateWMHints()
440 // assume a window takes input if it doesnt specify
444 if ((hints
= XGetWMHints(otk::OBDisplay::display
, _window
)) != NULL
) {
445 if (hints
->flags
& InputHint
)
446 _can_focus
= hints
->input
;
448 if (hints
->flags
& XUrgencyHint
)
451 if (hints
->flags
& WindowGroupHint
) {
452 if (hints
->window_group
!= _group
) {
453 // XXX: remove from the old group if there was one
454 _group
= hints
->window_group
;
455 // XXX: do stuff with the group
465 void OBClient::updateTitle()
467 const otk::OBProperty
*property
= Openbox::instance
->property();
472 if (! property
->get(_window
, otk::OBProperty::net_wm_name
,
473 otk::OBProperty::utf8
, &_title
)) {
475 property
->get(_window
, otk::OBProperty::wm_name
,
476 otk::OBProperty::ascii
, &_title
);
480 _title
= _("Unnamed Window");
483 frame
->setTitle(_title
);
487 void OBClient::updateIconTitle()
489 const otk::OBProperty
*property
= Openbox::instance
->property();
494 if (! property
->get(_window
, otk::OBProperty::net_wm_icon_name
,
495 otk::OBProperty::utf8
, &_icon_title
)) {
497 property
->get(_window
, otk::OBProperty::wm_icon_name
,
498 otk::OBProperty::ascii
, &_icon_title
);
502 _icon_title
= _("Unnamed Window");
506 void OBClient::updateClass()
508 const otk::OBProperty
*property
= Openbox::instance
->property();
511 _app_name
= _app_class
= _role
= "";
513 otk::OBProperty::StringVect v
;
514 unsigned long num
= 2;
516 if (property
->get(_window
, otk::OBProperty::wm_class
,
517 otk::OBProperty::ascii
, &num
, &v
)) {
518 if (num
> 0) _app_name
= v
[0];
519 if (num
> 1) _app_class
= v
[1];
524 if (property
->get(_window
, otk::OBProperty::wm_window_role
,
525 otk::OBProperty::ascii
, &num
, &v
)) {
526 if (num
> 0) _role
= v
[0];
531 void OBClient::updateStrut()
533 unsigned long num
= 4;
535 if (!Openbox::instance
->property()->get(_window
,
536 otk::OBProperty::net_wm_strut
,
537 otk::OBProperty::Atom_Cardinal
,
542 _strut
.left
= data
[0];
543 _strut
.right
= data
[1];
544 _strut
.top
= data
[2];
545 _strut
.bottom
= data
[3];
547 Openbox::instance
->screen(_screen
)->updateStrut();
554 void OBClient::updateTransientFor()
559 if (XGetTransientForHint(otk::OBDisplay::display
, _window
, &t
) &&
560 t
!= _window
) { // cant be transient to itself!
561 c
= Openbox::instance
->findClient(t
);
562 assert(c
!= this); // if this happens then we need to check for it
564 if (!c
/*XXX: && _group*/) {
565 // not transient to a client, see if it is transient for a group
566 if (//t == _group->leader() ||
568 t
== otk::OBDisplay::screenInfo(_screen
)->rootWindow()) {
569 // window is a transient for its group!
570 // XXX: for now this is treated as non-transient.
571 // this needs to be fixed!
576 // if anything has changed...
577 if (c
!= _transient_for
) {
579 _transient_for
->_transients
.remove(this); // remove from old parent
582 _transient_for
->_transients
.push_back(this); // add to new parent
584 // XXX: change decor status?
589 void OBClient::propertyHandler(const XPropertyEvent
&e
)
591 otk::OtkEventHandler::propertyHandler(e
);
593 const otk::OBProperty
*property
= Openbox::instance
->property();
595 // compress changes to a single property into a single change
597 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
598 // XXX: it would be nice to compress ALL changes to a property, not just
599 // changes in a row without other props between.
600 if (ce
.xproperty
.atom
!= e
.atom
) {
601 XPutBackEvent(otk::OBDisplay::display
, &ce
);
606 if (e
.atom
== XA_WM_NORMAL_HINTS
)
608 else if (e
.atom
== XA_WM_HINTS
)
610 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
611 updateTransientFor();
613 calcLayer(); // type may have changed, so update the layer
614 setupDecorAndFunctions();
615 frame
->adjustSize(); // this updates the frame for any new decor settings
617 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_name
) ||
618 e
.atom
== property
->atom(otk::OBProperty::wm_name
))
620 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_icon_name
) ||
621 e
.atom
== property
->atom(otk::OBProperty::wm_icon_name
))
623 else if (e
.atom
== property
->atom(otk::OBProperty::wm_class
))
625 else if (e
.atom
== property
->atom(otk::OBProperty::wm_protocols
))
627 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_strut
))
632 void OBClient::setWMState(long state
)
634 if (state
== _wmstate
) return; // no change
639 // XXX: cause it to iconify
642 // XXX: cause it to uniconify
648 void OBClient::setDesktop(long target
)
650 printf("Setting desktop %ld\n", target
);
651 assert(target
>= 0 || target
== (signed)0xffffffff);
652 //assert(target == 0xffffffff || target < MAX);
654 // XXX: move the window to the new desktop (and set root property)
659 void OBClient::setState(StateAction action
, long data1
, long data2
)
661 const otk::OBProperty
*property
= Openbox::instance
->property();
662 bool shadestate
= _shaded
;
664 if (!(action
== State_Add
|| action
== State_Remove
||
665 action
== State_Toggle
))
666 return; // an invalid action was passed to the client message, ignore it
668 for (int i
= 0; i
< 2; ++i
) {
669 Atom state
= i
== 0 ? data1
: data2
;
671 if (! state
) continue;
673 // if toggling, then pick whether we're adding or removing
674 if (action
== State_Toggle
) {
675 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
))
676 action
= _modal
? State_Remove
: State_Add
;
678 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
679 action
= _max_vert
? State_Remove
: State_Add
;
681 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
682 action
= _max_horz
? State_Remove
: State_Add
;
683 else if (state
== property
->atom(otk::OBProperty::net_wm_state_shaded
))
684 action
= _shaded
? State_Remove
: State_Add
;
686 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
687 action
= _skip_taskbar
? State_Remove
: State_Add
;
689 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
690 action
= _skip_pager
? State_Remove
: State_Add
;
692 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
693 action
= _fullscreen
? State_Remove
: State_Add
;
694 else if (state
== property
->atom(otk::OBProperty::net_wm_state_above
))
695 action
= _above
? State_Remove
: State_Add
;
696 else if (state
== property
->atom(otk::OBProperty::net_wm_state_below
))
697 action
= _below
? State_Remove
: State_Add
;
700 if (action
== State_Add
) {
701 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
702 if (_modal
) continue;
704 // XXX: give it focus if another window has focus that shouldnt now
706 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
707 if (_max_vert
) continue;
709 // XXX: resize the window etc
711 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
712 if (_max_horz
) continue;
714 // XXX: resize the window etc
716 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
717 if (_shaded
) continue;
718 // shade when we're all thru here
721 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
722 _skip_taskbar
= true;
724 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
727 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
728 if (_fullscreen
) continue;
731 property
->atom(otk::OBProperty::net_wm_state_above
)) {
732 if (_above
) continue;
735 property
->atom(otk::OBProperty::net_wm_state_below
)) {
736 if (_below
) continue;
740 } else { // action == State_Remove
741 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
742 if (!_modal
) continue;
745 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
746 if (!_max_vert
) continue;
748 // XXX: resize the window etc
750 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
751 if (!_max_horz
) continue;
753 // XXX: resize the window etc
755 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
756 if (!_shaded
) continue;
757 // unshade when we're all thru here
760 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
761 _skip_taskbar
= false;
763 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
766 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
767 if (!_fullscreen
) continue;
770 property
->atom(otk::OBProperty::net_wm_state_above
)) {
771 if (!_above
) continue;
774 property
->atom(otk::OBProperty::net_wm_state_below
)) {
775 if (!_below
) continue;
780 if (shadestate
!= _shaded
)
786 void OBClient::toggleClientBorder(bool addborder
)
788 // adjust our idea of where the client is, based on its border. When the
789 // border is removed, the client should now be considered to be in a
790 // different position.
791 // when re-adding the border to the client, the same operation needs to be
793 int x
= _area
.x(), y
= _area
.y();
795 case NorthWestGravity
:
797 case SouthWestGravity
:
799 case NorthEastGravity
:
801 case SouthEastGravity
:
802 if (addborder
) x
-= _border_width
* 2;
803 else x
+= _border_width
* 2;
807 case NorthWestGravity
:
809 case NorthEastGravity
:
811 case SouthWestGravity
:
813 case SouthEastGravity
:
814 if (addborder
) y
-= _border_width
* 2;
815 else y
+= _border_width
* 2;
818 // no change for StaticGravity etc.
824 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, _border_width
);
826 // move the client so it is back it the right spot _with_ its border!
827 XMoveWindow(otk::OBDisplay::display
, _window
, x
, y
);
829 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, 0);
833 void OBClient::clientMessageHandler(const XClientMessageEvent
&e
)
835 otk::OtkEventHandler::clientMessageHandler(e
);
837 if (e
.format
!= 32) return;
839 const otk::OBProperty
*property
= Openbox::instance
->property();
841 if (e
.message_type
== property
->atom(otk::OBProperty::wm_change_state
)) {
842 // compress changes into a single change
843 bool compress
= false;
845 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
846 // XXX: it would be nice to compress ALL messages of a type, not just
847 // messages in a row without other message types between.
848 if (ce
.xclient
.message_type
!= e
.message_type
) {
849 XPutBackEvent(otk::OBDisplay::display
, &ce
);
855 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
857 setWMState(e
.data
.l
[0]); // use the original event
858 } else if (e
.message_type
==
859 property
->atom(otk::OBProperty::net_wm_desktop
)) {
860 // compress changes into a single change
861 bool compress
= false;
863 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
864 // XXX: it would be nice to compress ALL messages of a type, not just
865 // messages in a row without other message types between.
866 if (ce
.xclient
.message_type
!= e
.message_type
) {
867 XPutBackEvent(otk::OBDisplay::display
, &ce
);
873 setDesktop(e
.data
.l
[0]); // use the found event
875 setDesktop(e
.data
.l
[0]); // use the original event
876 } else if (e
.message_type
== property
->atom(otk::OBProperty::net_wm_state
)) {
877 // can't compress these
878 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
879 } else if (e
.message_type
==
880 property
->atom(otk::OBProperty::net_close_window
)) {
882 } else if (e
.message_type
==
883 property
->atom(otk::OBProperty::net_active_window
)) {
885 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
892 void OBClient::shapeHandler(const XShapeEvent
&e
)
894 otk::OtkEventHandler::shapeHandler(e
);
897 frame
->adjustShape();
902 void OBClient::resize(Corner anchor
, int w
, int h
, int x
, int y
)
907 // for interactive resizing. have to move half an increment in each
909 w
+= _size_inc
.x() / 2;
910 h
+= _size_inc
.y() / 2;
912 // is the window resizable? if it is not, then don't check its sizes, the
913 // client can do what it wants and the user can't change it anyhow
914 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
915 // smaller than min size or bigger than max size?
916 if (w
< _min_size
.x()) w
= _min_size
.x();
917 else if (w
> _max_size
.x()) w
= _max_size
.x();
918 if (h
< _min_size
.y()) h
= _min_size
.y();
919 else if (h
> _max_size
.y()) h
= _max_size
.y();
922 // keep to the increments
926 // store the logical size
927 _logical_size
.setPoint(w
, h
);
935 if (x
== INT_MIN
|| y
== INT_MIN
) {
942 x
-= w
- _area
.width();
945 y
-= h
- _area
.height();
948 x
-= w
- _area
.width();
949 y
-= h
- _area
.height();
956 XResizeWindow(otk::OBDisplay::display
, _window
, w
, h
);
958 // resize the frame to match the request
964 void OBClient::move(int x
, int y
)
968 // move the frame to be in the requested position
969 frame
->adjustPosition();
973 void OBClient::close()
976 const otk::OBProperty
*property
= Openbox::instance
->property();
978 if (!(_functions
& Func_Close
)) return;
980 // XXX: itd be cool to do timeouts and shit here for killing the client's
982 // like... if the window is around after 5 seconds, then the close button
983 // turns a nice red, and if this function is called again, the client is
984 // explicitly killed.
986 ce
.xclient
.type
= ClientMessage
;
987 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
988 ce
.xclient
.display
= otk::OBDisplay::display
;
989 ce
.xclient
.window
= _window
;
990 ce
.xclient
.format
= 32;
991 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_delete_window
);
992 ce
.xclient
.data
.l
[1] = CurrentTime
;
993 ce
.xclient
.data
.l
[2] = 0l;
994 ce
.xclient
.data
.l
[3] = 0l;
995 ce
.xclient
.data
.l
[4] = 0l;
996 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1000 void OBClient::changeState()
1002 const otk::OBProperty
*property
= Openbox::instance
->property();
1004 unsigned long state
[2];
1005 state
[0] = _wmstate
;
1007 property
->set(_window
, otk::OBProperty::wm_state
, otk::OBProperty::wm_state
,
1013 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_modal
);
1015 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_shaded
);
1017 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_hidden
);
1020 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
);
1022 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_skip_pager
);
1024 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_fullscreen
);
1027 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
);
1030 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
);
1032 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_above
);
1034 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_below
);
1035 property
->set(_window
, otk::OBProperty::net_wm_state
,
1036 otk::OBProperty::Atom_Atom
, netstate
, num
);
1042 void OBClient::setStackLayer(int l
)
1045 _above
= _below
= false; // normal
1048 _below
= false; // above
1051 _below
= true; // below
1057 void OBClient::shade(bool shade
)
1059 if (shade
== _shaded
) return; // already done
1061 _wmstate
= shade
? IconicState
: NormalState
;
1064 frame
->adjustSize();
1068 bool OBClient::focus()
1070 if (!(_can_focus
|| _focus_notify
) || _focused
) return false;
1073 XSetInputFocus(otk::OBDisplay::display
, _window
, RevertToNone
, CurrentTime
);
1075 if (_focus_notify
) {
1077 const otk::OBProperty
*property
= Openbox::instance
->property();
1079 ce
.xclient
.type
= ClientMessage
;
1080 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
1081 ce
.xclient
.display
= otk::OBDisplay::display
;
1082 ce
.xclient
.window
= _window
;
1083 ce
.xclient
.format
= 32;
1084 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_take_focus
);
1085 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1086 ce
.xclient
.data
.l
[2] = 0l;
1087 ce
.xclient
.data
.l
[3] = 0l;
1088 ce
.xclient
.data
.l
[4] = 0l;
1089 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1096 void OBClient::unfocus()
1098 if (!_focused
) return;
1100 assert(Openbox::instance
->focusedClient() == this);
1101 Openbox::instance
->setFocusedClient(0);
1105 void OBClient::focusHandler(const XFocusChangeEvent
&e
)
1108 printf("FocusIn for 0x%lx\n", e
.window
);
1111 OtkEventHandler::focusHandler(e
);
1116 Openbox::instance
->setFocusedClient(this);
1120 void OBClient::unfocusHandler(const XFocusChangeEvent
&e
)
1123 printf("FocusOut for 0x%lx\n", e
.window
);
1126 OtkEventHandler::unfocusHandler(e
);
1131 if (Openbox::instance
->focusedClient() == this) {
1132 printf("UNFOCUSED!\n");
1133 Openbox::instance
->setFocusedClient(this);
1138 void OBClient::configureRequestHandler(const XConfigureRequestEvent
&e
)
1141 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1144 OtkEventHandler::configureRequestHandler(e
);
1146 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1148 if (e
.value_mask
& CWBorderWidth
)
1149 _border_width
= e
.border_width
;
1151 // resize, then move, as specified in the EWMH section 7.7
1152 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1153 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1154 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1158 case NorthEastGravity
:
1162 case SouthWestGravity
:
1164 corner
= BottomLeft
;
1166 case SouthEastGravity
:
1167 corner
= BottomRight
;
1169 default: // NorthWest, Static, etc
1173 // if moving AND resizing ...
1174 if (e
.value_mask
& (CWX
| CWY
)) {
1175 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1176 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1177 resize(corner
, w
, h
, x
, y
);
1178 } else // if JUST resizing...
1179 resize(corner
, w
, h
);
1180 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1181 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1182 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1186 if (e
.value_mask
& CWStackMode
) {
1190 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1196 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1203 void OBClient::unmapHandler(const XUnmapEvent
&e
)
1206 printf("UnmapNotify for 0x%lx\n", e
.window
);
1209 if (ignore_unmaps
) {
1214 OtkEventHandler::unmapHandler(e
);
1216 // this deletes us etc
1217 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1221 void OBClient::destroyHandler(const XDestroyWindowEvent
&e
)
1224 printf("DestroyNotify for 0x%lx\n", e
.window
);
1227 OtkEventHandler::destroyHandler(e
);
1229 // this deletes us etc
1230 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1234 void OBClient::reparentHandler(const XReparentEvent
&e
)
1236 // this is when the client is first taken captive in the frame
1237 if (e
.parent
== frame
->plate()) return;
1240 printf("ReparentNotify for 0x%lx\n", e
.window
);
1243 OtkEventHandler::reparentHandler(e
);
1246 This event is quite rare and is usually handled in unmapHandler.
1247 However, if the window is unmapped when the reparent event occurs,
1248 the window manager never sees it because an unmap event is not sent
1249 to an already unmapped window.
1252 // this deletes us etc
1253 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1257 void OBClient::mapRequestHandler(const XMapRequestEvent
&e
)
1259 printf("\nMAP REQUEST\n\n");
1261 otk::OtkEventHandler::mapRequestHandler(e
);
1265 // XXX: uniconify the window