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
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
880 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
881 } else if (e
.message_type
==
882 property
->atom(otk::OBProperty::net_close_window
)) {
884 } else if (e
.message_type
==
885 property
->atom(otk::OBProperty::net_active_window
)) {
887 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
894 void OBClient::shapeHandler(const XShapeEvent
&e
)
896 otk::OtkEventHandler::shapeHandler(e
);
899 frame
->adjustShape();
904 void OBClient::resize(Corner anchor
, int w
, int h
, int x
, int y
)
909 // for interactive resizing. have to move half an increment in each
911 w
+= _size_inc
.x() / 2;
912 h
+= _size_inc
.y() / 2;
914 // is the window resizable? if it is not, then don't check its sizes, the
915 // client can do what it wants and the user can't change it anyhow
916 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
917 // smaller than min size or bigger than max size?
918 if (w
< _min_size
.x()) w
= _min_size
.x();
919 else if (w
> _max_size
.x()) w
= _max_size
.x();
920 if (h
< _min_size
.y()) h
= _min_size
.y();
921 else if (h
> _max_size
.y()) h
= _max_size
.y();
924 // keep to the increments
928 // store the logical size
929 _logical_size
.setPoint(w
, h
);
937 if (x
== INT_MIN
|| y
== INT_MIN
) {
944 x
-= w
- _area
.width();
947 y
-= h
- _area
.height();
950 x
-= w
- _area
.width();
951 y
-= h
- _area
.height();
958 XResizeWindow(otk::OBDisplay::display
, _window
, w
, h
);
960 // resize the frame to match the request
966 void OBClient::move(int x
, int y
)
970 // move the frame to be in the requested position
971 frame
->adjustPosition();
975 void OBClient::close()
978 const otk::OBProperty
*property
= Openbox::instance
->property();
980 if (!(_functions
& Func_Close
)) return;
982 // XXX: itd be cool to do timeouts and shit here for killing the client's
984 // like... if the window is around after 5 seconds, then the close button
985 // turns a nice red, and if this function is called again, the client is
986 // explicitly killed.
988 ce
.xclient
.type
= ClientMessage
;
989 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
990 ce
.xclient
.display
= otk::OBDisplay::display
;
991 ce
.xclient
.window
= _window
;
992 ce
.xclient
.format
= 32;
993 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_delete_window
);
994 ce
.xclient
.data
.l
[1] = CurrentTime
;
995 ce
.xclient
.data
.l
[2] = 0l;
996 ce
.xclient
.data
.l
[3] = 0l;
997 ce
.xclient
.data
.l
[4] = 0l;
998 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1002 void OBClient::changeState()
1004 const otk::OBProperty
*property
= Openbox::instance
->property();
1006 unsigned long state
[2];
1007 state
[0] = _wmstate
;
1009 property
->set(_window
, otk::OBProperty::wm_state
, otk::OBProperty::wm_state
,
1015 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_modal
);
1017 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_shaded
);
1019 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_hidden
);
1022 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
);
1024 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_skip_pager
);
1026 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_fullscreen
);
1029 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
);
1032 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
);
1034 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_above
);
1036 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_below
);
1037 property
->set(_window
, otk::OBProperty::net_wm_state
,
1038 otk::OBProperty::Atom_Atom
, netstate
, num
);
1044 void OBClient::setStackLayer(int l
)
1047 _above
= _below
= false; // normal
1050 _below
= false; // above
1053 _below
= true; // below
1059 void OBClient::shade(bool shade
)
1061 if (shade
== _shaded
) return; // already done
1063 _wmstate
= shade
? IconicState
: NormalState
;
1066 frame
->adjustSize();
1070 bool OBClient::focus()
1072 if (!(_can_focus
|| _focus_notify
) || _focused
) return false;
1075 XSetInputFocus(otk::OBDisplay::display
, _window
, RevertToNone
, CurrentTime
);
1077 if (_focus_notify
) {
1079 const otk::OBProperty
*property
= Openbox::instance
->property();
1081 ce
.xclient
.type
= ClientMessage
;
1082 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
1083 ce
.xclient
.display
= otk::OBDisplay::display
;
1084 ce
.xclient
.window
= _window
;
1085 ce
.xclient
.format
= 32;
1086 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_take_focus
);
1087 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1088 ce
.xclient
.data
.l
[2] = 0l;
1089 ce
.xclient
.data
.l
[3] = 0l;
1090 ce
.xclient
.data
.l
[4] = 0l;
1091 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1098 void OBClient::unfocus()
1100 if (!_focused
) return;
1102 assert(Openbox::instance
->focusedClient() == this);
1103 Openbox::instance
->setFocusedClient(0);
1107 void OBClient::focusHandler(const XFocusChangeEvent
&e
)
1110 printf("FocusIn for 0x%lx\n", e
.window
);
1113 OtkEventHandler::focusHandler(e
);
1118 Openbox::instance
->setFocusedClient(this);
1122 void OBClient::unfocusHandler(const XFocusChangeEvent
&e
)
1125 printf("FocusOut for 0x%lx\n", e
.window
);
1128 OtkEventHandler::unfocusHandler(e
);
1133 if (Openbox::instance
->focusedClient() == this) {
1134 printf("UNFOCUSED!\n");
1135 Openbox::instance
->setFocusedClient(this);
1140 void OBClient::configureRequestHandler(const XConfigureRequestEvent
&e
)
1143 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1146 OtkEventHandler::configureRequestHandler(e
);
1148 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1150 if (e
.value_mask
& CWBorderWidth
)
1151 _border_width
= e
.border_width
;
1153 // resize, then move, as specified in the EWMH section 7.7
1154 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1155 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1156 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1160 case NorthEastGravity
:
1164 case SouthWestGravity
:
1166 corner
= BottomLeft
;
1168 case SouthEastGravity
:
1169 corner
= BottomRight
;
1171 default: // NorthWest, Static, etc
1175 // if moving AND resizing ...
1176 if (e
.value_mask
& (CWX
| CWY
)) {
1177 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1178 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1179 resize(corner
, w
, h
, x
, y
);
1180 } else // if JUST resizing...
1181 resize(corner
, w
, h
);
1182 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1183 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1184 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1188 if (e
.value_mask
& CWStackMode
) {
1192 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1198 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1205 void OBClient::unmapHandler(const XUnmapEvent
&e
)
1208 printf("UnmapNotify for 0x%lx\n", e
.window
);
1211 if (ignore_unmaps
) {
1216 OtkEventHandler::unmapHandler(e
);
1218 // this deletes us etc
1219 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1223 void OBClient::destroyHandler(const XDestroyWindowEvent
&e
)
1226 printf("DestroyNotify for 0x%lx\n", e
.window
);
1229 OtkEventHandler::destroyHandler(e
);
1231 // this deletes us etc
1232 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1236 void OBClient::reparentHandler(const XReparentEvent
&e
)
1238 // this is when the client is first taken captive in the frame
1239 if (e
.parent
== frame
->plate()) return;
1242 printf("ReparentNotify for 0x%lx\n", e
.window
);
1245 OtkEventHandler::reparentHandler(e
);
1248 This event is quite rare and is usually handled in unmapHandler.
1249 However, if the window is unmapped when the reparent event occurs,
1250 the window manager never sees it because an unmap event is not sent
1251 to an already unmapped window.
1254 // this deletes us etc
1255 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1259 void OBClient::mapRequestHandler(const XMapRequestEvent
&e
)
1261 printf("\nMAP REQUEST\n\n");
1263 otk::OtkEventHandler::mapRequestHandler(e
);
1267 // XXX: uniconify the window