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
= Openbox::instance
->screen(_screen
)->desktop();
94 property
->get(_window
, otk::OBProperty::net_wm_desktop
,
95 otk::OBProperty::Atom_Cardinal
,
96 (long unsigned*)&_desktop
);
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
141 if (_type
!= (WindowType
) -1)
142 break; // grab the first known type
147 if (_type
== (WindowType
) -1) {
149 * the window type hint was not set, which means we either classify ourself
150 * as a normal window or a dialog, depending on if we are a transient.
160 void OBClient::setupDecorAndFunctions()
162 // start with everything
163 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
164 Decor_Iconify
| Decor_Maximize
;
165 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
169 // normal windows retain all of the possible decorations and
173 // dialogs cannot be maximized
174 _decorations
&= ~Decor_Maximize
;
175 _functions
&= ~Func_Maximize
;
181 // these windows get less functionality
182 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
183 _functions
&= ~(Func_Iconify
| Func_Resize
);
189 // none of these windows are manipulated by the window manager
195 // Mwm Hints are applied subtractively to what has already been chosen for
196 // decor and functionality
197 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
198 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
199 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
200 _decorations
&= ~Decor_Border
;
201 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
202 _decorations
&= ~Decor_Handle
;
203 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
204 _decorations
&= ~Decor_Titlebar
;
205 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
206 _decorations
&= ~Decor_Iconify
;
207 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
208 _decorations
&= ~Decor_Maximize
;
212 if (_mwmhints
.flags
& MwmFlag_Functions
) {
213 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
214 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
215 _functions
&= ~Func_Resize
;
216 if (! (_mwmhints
.functions
& MwmFunc_Move
))
217 _functions
&= ~Func_Move
;
218 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
219 _functions
&= ~Func_Iconify
;
220 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
221 _functions
&= ~Func_Maximize
;
222 // dont let mwm hints kill the close button
223 //if (! (_mwmhints.functions & MwmFunc_Close))
224 // _functions &= ~Func_Close;
228 // XXX: changeAllowedActions();
232 void OBClient::getMwmHints()
234 const otk::OBProperty
*property
= Openbox::instance
->property();
236 unsigned long num
= MwmHints::elements
;
237 unsigned long *hints
;
239 _mwmhints
.flags
= 0; // default to none
241 if (!property
->get(_window
, otk::OBProperty::motif_wm_hints
,
242 otk::OBProperty::motif_wm_hints
, &num
,
243 (unsigned long **)&hints
))
246 if (num
>= MwmHints::elements
) {
247 // retrieved the hints
248 _mwmhints
.flags
= hints
[0];
249 _mwmhints
.functions
= hints
[1];
250 _mwmhints
.decorations
= hints
[2];
257 void OBClient::getArea()
259 XWindowAttributes wattrib
;
262 ret
= XGetWindowAttributes(otk::OBDisplay::display
, _window
, &wattrib
);
263 assert(ret
!= BadWindow
);
265 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
266 _border_width
= wattrib
.border_width
;
270 void OBClient::getState()
272 const otk::OBProperty
*property
= Openbox::instance
->property();
274 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
275 _skip_taskbar
= _skip_pager
= false;
277 unsigned long *state
;
278 unsigned long num
= (unsigned) -1;
280 if (property
->get(_window
, otk::OBProperty::net_wm_state
,
281 otk::OBProperty::Atom_Atom
, &num
, &state
)) {
282 for (unsigned long i
= 0; i
< num
; ++i
) {
283 if (state
[i
] == property
->atom(otk::OBProperty::net_wm_state_modal
))
286 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
288 _wmstate
= IconicState
;
289 } else if (state
[i
] ==
290 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
291 _skip_taskbar
= true;
293 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
296 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
299 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
302 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
305 property
->atom(otk::OBProperty::net_wm_state_above
))
308 property
->atom(otk::OBProperty::net_wm_state_below
))
317 void OBClient::getShaped()
321 if (otk::OBDisplay::shape()) {
326 XShapeSelectInput(otk::OBDisplay::display
, _window
, ShapeNotifyMask
);
328 XShapeQueryExtents(otk::OBDisplay::display
, _window
, &s
, &foo
,
329 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
336 void OBClient::calcLayer() {
339 if (_iconic
) l
= Layer_Icon
;
340 else if (_fullscreen
) l
= Layer_Fullscreen
;
341 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
342 else if (_type
== Type_Dock
) {
343 if (!_below
) l
= Layer_Top
;
344 else l
= Layer_Normal
;
346 else if (_above
) l
= Layer_Above
;
347 else if (_below
) l
= Layer_Below
;
348 else l
= Layer_Normal
;
354 if we don't have a frame, then we aren't mapped yet (and this would
357 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
363 void OBClient::updateProtocols()
365 const otk::OBProperty
*property
= Openbox::instance
->property();
370 _focus_notify
= false;
371 _decorations
&= ~Decor_Close
;
372 _functions
&= ~Func_Close
;
374 if (XGetWMProtocols(otk::OBDisplay::display
, _window
, &proto
, &num_return
)) {
375 for (int i
= 0; i
< num_return
; ++i
) {
376 if (proto
[i
] == property
->atom(otk::OBProperty::wm_delete_window
)) {
377 _decorations
|= Decor_Close
;
378 _functions
|= Func_Close
;
380 frame
->adjustSize(); // update the decorations
381 } else if (proto
[i
] == property
->atom(otk::OBProperty::wm_take_focus
))
382 // if this protocol is requested, then the window will be notified
383 // by the window manager whenever it receives focus
384 _focus_notify
= true;
391 void OBClient::updateNormalHints()
395 int oldgravity
= _gravity
;
398 _gravity
= NorthWestGravity
;
399 _size_inc
.setPoint(1, 1);
400 _base_size
.setPoint(0, 0);
401 _min_size
.setPoint(0, 0);
402 _max_size
.setPoint(INT_MAX
, INT_MAX
);
404 // XXX: might want to cancel any interactive resizing of the window at this
407 // get the hints from the window
408 if (XGetWMNormalHints(otk::OBDisplay::display
, _window
, &size
, &ret
)) {
409 _positioned
= (size
.flags
& (PPosition
|USPosition
));
411 if (size
.flags
& PWinGravity
)
412 _gravity
= size
.win_gravity
;
414 if (size
.flags
& PMinSize
)
415 _min_size
.setPoint(size
.min_width
, size
.min_height
);
417 if (size
.flags
& PMaxSize
)
418 _max_size
.setPoint(size
.max_width
, size
.max_height
);
420 if (size
.flags
& PBaseSize
)
421 _base_size
.setPoint(size
.base_width
, size
.base_height
);
423 if (size
.flags
& PResizeInc
)
424 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
427 // if the client has a frame, i.e. has already been mapped and is
428 // changing its gravity
429 if (frame
&& _gravity
!= oldgravity
) {
430 // move our idea of the client's position based on its new gravity
432 frame
->frameGravity(x
, y
);
438 void OBClient::updateWMHints()
442 // assume a window takes input if it doesnt specify
446 if ((hints
= XGetWMHints(otk::OBDisplay::display
, _window
)) != NULL
) {
447 if (hints
->flags
& InputHint
)
448 _can_focus
= hints
->input
;
450 if (hints
->flags
& XUrgencyHint
)
453 if (hints
->flags
& WindowGroupHint
) {
454 if (hints
->window_group
!= _group
) {
455 // XXX: remove from the old group if there was one
456 _group
= hints
->window_group
;
457 // XXX: do stuff with the group
467 void OBClient::updateTitle()
469 const otk::OBProperty
*property
= Openbox::instance
->property();
474 if (! property
->get(_window
, otk::OBProperty::net_wm_name
,
475 otk::OBProperty::utf8
, &_title
)) {
477 property
->get(_window
, otk::OBProperty::wm_name
,
478 otk::OBProperty::ascii
, &_title
);
482 _title
= _("Unnamed Window");
485 frame
->setTitle(_title
);
489 void OBClient::updateIconTitle()
491 const otk::OBProperty
*property
= Openbox::instance
->property();
496 if (! property
->get(_window
, otk::OBProperty::net_wm_icon_name
,
497 otk::OBProperty::utf8
, &_icon_title
)) {
499 property
->get(_window
, otk::OBProperty::wm_icon_name
,
500 otk::OBProperty::ascii
, &_icon_title
);
504 _icon_title
= _("Unnamed Window");
508 void OBClient::updateClass()
510 const otk::OBProperty
*property
= Openbox::instance
->property();
513 _app_name
= _app_class
= _role
= "";
515 otk::OBProperty::StringVect v
;
516 unsigned long num
= 2;
518 if (property
->get(_window
, otk::OBProperty::wm_class
,
519 otk::OBProperty::ascii
, &num
, &v
)) {
520 if (num
> 0) _app_name
= v
[0];
521 if (num
> 1) _app_class
= v
[1];
526 if (property
->get(_window
, otk::OBProperty::wm_window_role
,
527 otk::OBProperty::ascii
, &num
, &v
)) {
528 if (num
> 0) _role
= v
[0];
533 void OBClient::updateStrut()
535 unsigned long num
= 4;
537 if (!Openbox::instance
->property()->get(_window
,
538 otk::OBProperty::net_wm_strut
,
539 otk::OBProperty::Atom_Cardinal
,
544 _strut
.left
= data
[0];
545 _strut
.right
= data
[1];
546 _strut
.top
= data
[2];
547 _strut
.bottom
= data
[3];
549 Openbox::instance
->screen(_screen
)->updateStrut();
556 void OBClient::updateTransientFor()
561 if (XGetTransientForHint(otk::OBDisplay::display
, _window
, &t
) &&
562 t
!= _window
) { // cant be transient to itself!
563 c
= Openbox::instance
->findClient(t
);
564 assert(c
!= this); // if this happens then we need to check for it
566 if (!c
/*XXX: && _group*/) {
567 // not transient to a client, see if it is transient for a group
568 if (//t == _group->leader() ||
570 t
== otk::OBDisplay::screenInfo(_screen
)->rootWindow()) {
571 // window is a transient for its group!
572 // XXX: for now this is treated as non-transient.
573 // this needs to be fixed!
578 // if anything has changed...
579 if (c
!= _transient_for
) {
581 _transient_for
->_transients
.remove(this); // remove from old parent
584 _transient_for
->_transients
.push_back(this); // add to new parent
586 // XXX: change decor status?
591 void OBClient::propertyHandler(const XPropertyEvent
&e
)
593 otk::OtkEventHandler::propertyHandler(e
);
595 const otk::OBProperty
*property
= Openbox::instance
->property();
597 // compress changes to a single property into a single change
599 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
600 // XXX: it would be nice to compress ALL changes to a property, not just
601 // changes in a row without other props between.
602 if (ce
.xproperty
.atom
!= e
.atom
) {
603 XPutBackEvent(otk::OBDisplay::display
, &ce
);
608 if (e
.atom
== XA_WM_NORMAL_HINTS
)
610 else if (e
.atom
== XA_WM_HINTS
)
612 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
613 updateTransientFor();
615 calcLayer(); // type may have changed, so update the layer
616 setupDecorAndFunctions();
617 frame
->adjustSize(); // this updates the frame for any new decor settings
619 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_name
) ||
620 e
.atom
== property
->atom(otk::OBProperty::wm_name
))
622 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_icon_name
) ||
623 e
.atom
== property
->atom(otk::OBProperty::wm_icon_name
))
625 else if (e
.atom
== property
->atom(otk::OBProperty::wm_class
))
627 else if (e
.atom
== property
->atom(otk::OBProperty::wm_protocols
))
629 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_strut
))
634 void OBClient::setWMState(long state
)
636 if (state
== _wmstate
) return; // no change
641 // XXX: cause it to iconify
644 // XXX: cause it to uniconify
650 void OBClient::setDesktop(long target
)
652 printf("Setting desktop %ld\n", target
);
653 assert(target
>= 0 || target
== (signed)0xffffffff);
654 //assert(target == 0xffffffff || target < MAX);
656 // XXX: move the window to the new desktop (and set root property)
661 void OBClient::setState(StateAction action
, long data1
, long data2
)
663 const otk::OBProperty
*property
= Openbox::instance
->property();
664 bool shadestate
= _shaded
;
666 if (!(action
== State_Add
|| action
== State_Remove
||
667 action
== State_Toggle
))
668 return; // an invalid action was passed to the client message, ignore it
670 for (int i
= 0; i
< 2; ++i
) {
671 Atom state
= i
== 0 ? data1
: data2
;
673 if (! state
) continue;
675 // if toggling, then pick whether we're adding or removing
676 if (action
== State_Toggle
) {
677 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
))
678 action
= _modal
? State_Remove
: State_Add
;
680 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
681 action
= _max_vert
? State_Remove
: State_Add
;
683 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
684 action
= _max_horz
? State_Remove
: State_Add
;
685 else if (state
== property
->atom(otk::OBProperty::net_wm_state_shaded
))
686 action
= _shaded
? State_Remove
: State_Add
;
688 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
689 action
= _skip_taskbar
? State_Remove
: State_Add
;
691 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
692 action
= _skip_pager
? State_Remove
: State_Add
;
694 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
695 action
= _fullscreen
? State_Remove
: State_Add
;
696 else if (state
== property
->atom(otk::OBProperty::net_wm_state_above
))
697 action
= _above
? State_Remove
: State_Add
;
698 else if (state
== property
->atom(otk::OBProperty::net_wm_state_below
))
699 action
= _below
? State_Remove
: State_Add
;
702 if (action
== State_Add
) {
703 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
704 if (_modal
) continue;
706 // XXX: give it focus if another window has focus that shouldnt now
708 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
709 if (_max_vert
) continue;
711 // XXX: resize the window etc
713 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
714 if (_max_horz
) continue;
716 // XXX: resize the window etc
718 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
719 if (_shaded
) continue;
720 // shade when we're all thru here
723 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
724 _skip_taskbar
= true;
726 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
729 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
730 if (_fullscreen
) continue;
733 property
->atom(otk::OBProperty::net_wm_state_above
)) {
734 if (_above
) continue;
737 property
->atom(otk::OBProperty::net_wm_state_below
)) {
738 if (_below
) continue;
742 } else { // action == State_Remove
743 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
744 if (!_modal
) continue;
747 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
748 if (!_max_vert
) continue;
750 // XXX: resize the window etc
752 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
753 if (!_max_horz
) continue;
755 // XXX: resize the window etc
757 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
758 if (!_shaded
) continue;
759 // unshade when we're all thru here
762 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
763 _skip_taskbar
= false;
765 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
768 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
769 if (!_fullscreen
) continue;
772 property
->atom(otk::OBProperty::net_wm_state_above
)) {
773 if (!_above
) continue;
776 property
->atom(otk::OBProperty::net_wm_state_below
)) {
777 if (!_below
) continue;
782 if (shadestate
!= _shaded
)
788 void OBClient::toggleClientBorder(bool addborder
)
790 // adjust our idea of where the client is, based on its border. When the
791 // border is removed, the client should now be considered to be in a
792 // different position.
793 // when re-adding the border to the client, the same operation needs to be
795 int x
= _area
.x(), y
= _area
.y();
797 case NorthWestGravity
:
799 case SouthWestGravity
:
801 case NorthEastGravity
:
803 case SouthEastGravity
:
804 if (addborder
) x
-= _border_width
* 2;
805 else x
+= _border_width
* 2;
809 case NorthWestGravity
:
811 case NorthEastGravity
:
813 case SouthWestGravity
:
815 case SouthEastGravity
:
816 if (addborder
) y
-= _border_width
* 2;
817 else y
+= _border_width
* 2;
820 // no change for StaticGravity etc.
826 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, _border_width
);
828 // move the client so it is back it the right spot _with_ its border!
829 XMoveWindow(otk::OBDisplay::display
, _window
, x
, y
);
831 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, 0);
835 void OBClient::clientMessageHandler(const XClientMessageEvent
&e
)
837 otk::OtkEventHandler::clientMessageHandler(e
);
839 if (e
.format
!= 32) return;
841 const otk::OBProperty
*property
= Openbox::instance
->property();
843 if (e
.message_type
== property
->atom(otk::OBProperty::wm_change_state
)) {
844 // compress changes into a single change
845 bool compress
= false;
847 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
848 // XXX: it would be nice to compress ALL messages of a type, not just
849 // messages in a row without other message types between.
850 if (ce
.xclient
.message_type
!= e
.message_type
) {
851 XPutBackEvent(otk::OBDisplay::display
, &ce
);
857 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
859 setWMState(e
.data
.l
[0]); // use the original event
860 } else if (e
.message_type
==
861 property
->atom(otk::OBProperty::net_wm_desktop
)) {
862 // compress changes into a single change
863 bool compress
= false;
865 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
866 // XXX: it would be nice to compress ALL messages of a type, not just
867 // messages in a row without other message types between.
868 if (ce
.xclient
.message_type
!= e
.message_type
) {
869 XPutBackEvent(otk::OBDisplay::display
, &ce
);
875 setDesktop(e
.data
.l
[0]); // use the found event
877 setDesktop(e
.data
.l
[0]); // use the original event
878 } else if (e
.message_type
== property
->atom(otk::OBProperty::net_wm_state
)) {
879 // can't compress these
881 printf("net_wm_state for 0x%lx\n", _window
);
883 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
884 } else if (e
.message_type
==
885 property
->atom(otk::OBProperty::net_close_window
)) {
887 printf("net_close_window for 0x%lx\n", _window
);
890 } else if (e
.message_type
==
891 property
->atom(otk::OBProperty::net_active_window
)) {
893 printf("net_active_window for 0x%lx\n", _window
);
896 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
902 void OBClient::shapeHandler(const XShapeEvent
&e
)
904 otk::OtkEventHandler::shapeHandler(e
);
907 frame
->adjustShape();
912 void OBClient::resize(Corner anchor
, int w
, int h
, int x
, int y
)
917 // for interactive resizing. have to move half an increment in each
919 w
+= _size_inc
.x() / 2;
920 h
+= _size_inc
.y() / 2;
922 // is the window resizable? if it is not, then don't check its sizes, the
923 // client can do what it wants and the user can't change it anyhow
924 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
925 // smaller than min size or bigger than max size?
926 if (w
< _min_size
.x()) w
= _min_size
.x();
927 else if (w
> _max_size
.x()) w
= _max_size
.x();
928 if (h
< _min_size
.y()) h
= _min_size
.y();
929 else if (h
> _max_size
.y()) h
= _max_size
.y();
932 // keep to the increments
936 // store the logical size
937 _logical_size
.setPoint(w
, h
);
945 if (x
== INT_MIN
|| y
== INT_MIN
) {
952 x
-= w
- _area
.width();
955 y
-= h
- _area
.height();
958 x
-= w
- _area
.width();
959 y
-= h
- _area
.height();
966 XResizeWindow(otk::OBDisplay::display
, _window
, w
, h
);
968 // resize the frame to match the request
974 void OBClient::move(int x
, int y
)
978 // move the frame to be in the requested position
979 frame
->adjustPosition();
983 void OBClient::close()
986 const otk::OBProperty
*property
= Openbox::instance
->property();
988 if (!(_functions
& Func_Close
)) return;
990 // XXX: itd be cool to do timeouts and shit here for killing the client's
992 // like... if the window is around after 5 seconds, then the close button
993 // turns a nice red, and if this function is called again, the client is
994 // explicitly killed.
996 ce
.xclient
.type
= ClientMessage
;
997 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
998 ce
.xclient
.display
= otk::OBDisplay::display
;
999 ce
.xclient
.window
= _window
;
1000 ce
.xclient
.format
= 32;
1001 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_delete_window
);
1002 ce
.xclient
.data
.l
[1] = CurrentTime
;
1003 ce
.xclient
.data
.l
[2] = 0l;
1004 ce
.xclient
.data
.l
[3] = 0l;
1005 ce
.xclient
.data
.l
[4] = 0l;
1006 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1010 void OBClient::changeState()
1012 const otk::OBProperty
*property
= Openbox::instance
->property();
1014 unsigned long state
[2];
1015 state
[0] = _wmstate
;
1017 property
->set(_window
, otk::OBProperty::wm_state
, otk::OBProperty::wm_state
,
1023 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_modal
);
1025 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_shaded
);
1027 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_hidden
);
1030 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
);
1032 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_skip_pager
);
1034 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_fullscreen
);
1037 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
);
1040 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
);
1042 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_above
);
1044 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_below
);
1045 property
->set(_window
, otk::OBProperty::net_wm_state
,
1046 otk::OBProperty::Atom_Atom
, netstate
, num
);
1052 void OBClient::setStackLayer(int l
)
1055 _above
= _below
= false; // normal
1058 _below
= false; // above
1061 _below
= true; // below
1067 void OBClient::shade(bool shade
)
1069 if (shade
== _shaded
) return; // already done
1071 _wmstate
= shade
? IconicState
: NormalState
;
1074 frame
->adjustSize();
1078 bool OBClient::focus()
1080 if (!(_can_focus
|| _focus_notify
) || _focused
) return false;
1083 XSetInputFocus(otk::OBDisplay::display
, _window
, RevertToNone
, CurrentTime
);
1085 if (_focus_notify
) {
1087 const otk::OBProperty
*property
= Openbox::instance
->property();
1089 ce
.xclient
.type
= ClientMessage
;
1090 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
1091 ce
.xclient
.display
= otk::OBDisplay::display
;
1092 ce
.xclient
.window
= _window
;
1093 ce
.xclient
.format
= 32;
1094 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_take_focus
);
1095 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1096 ce
.xclient
.data
.l
[2] = 0l;
1097 ce
.xclient
.data
.l
[3] = 0l;
1098 ce
.xclient
.data
.l
[4] = 0l;
1099 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1106 void OBClient::unfocus()
1108 if (!_focused
) return;
1110 assert(Openbox::instance
->focusedClient() == this);
1111 Openbox::instance
->setFocusedClient(0);
1115 void OBClient::focusHandler(const XFocusChangeEvent
&e
)
1118 // printf("FocusIn for 0x%lx\n", e.window);
1121 OtkEventHandler::focusHandler(e
);
1126 Openbox::instance
->setFocusedClient(this);
1130 void OBClient::unfocusHandler(const XFocusChangeEvent
&e
)
1133 // printf("FocusOut for 0x%lx\n", e.window);
1136 OtkEventHandler::unfocusHandler(e
);
1141 if (Openbox::instance
->focusedClient() == this)
1142 Openbox::instance
->setFocusedClient(0);
1146 void OBClient::configureRequestHandler(const XConfigureRequestEvent
&e
)
1149 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1152 OtkEventHandler::configureRequestHandler(e
);
1154 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1156 if (e
.value_mask
& CWBorderWidth
)
1157 _border_width
= e
.border_width
;
1159 // resize, then move, as specified in the EWMH section 7.7
1160 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1161 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1162 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1166 case NorthEastGravity
:
1170 case SouthWestGravity
:
1172 corner
= BottomLeft
;
1174 case SouthEastGravity
:
1175 corner
= BottomRight
;
1177 default: // NorthWest, Static, etc
1181 // if moving AND resizing ...
1182 if (e
.value_mask
& (CWX
| CWY
)) {
1183 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1184 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1185 resize(corner
, w
, h
, x
, y
);
1186 } else // if JUST resizing...
1187 resize(corner
, w
, h
);
1188 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1189 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1190 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1194 if (e
.value_mask
& CWStackMode
) {
1198 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1204 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1211 void OBClient::unmapHandler(const XUnmapEvent
&e
)
1214 printf("UnmapNotify for 0x%lx\n", e
.window
);
1217 if (ignore_unmaps
) {
1222 OtkEventHandler::unmapHandler(e
);
1224 // this deletes us etc
1225 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1229 void OBClient::destroyHandler(const XDestroyWindowEvent
&e
)
1232 printf("DestroyNotify for 0x%lx\n", e
.window
);
1235 OtkEventHandler::destroyHandler(e
);
1237 // this deletes us etc
1238 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1242 void OBClient::reparentHandler(const XReparentEvent
&e
)
1244 // this is when the client is first taken captive in the frame
1245 if (e
.parent
== frame
->plate()) return;
1248 printf("ReparentNotify for 0x%lx\n", e
.window
);
1251 OtkEventHandler::reparentHandler(e
);
1254 This event is quite rare and is usually handled in unmapHandler.
1255 However, if the window is unmapped when the reparent event occurs,
1256 the window manager never sees it because an unmap event is not sent
1257 to an already unmapped window.
1260 // this deletes us etc
1261 Openbox::instance
->screen(_screen
)->unmanageWindow(this);