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 if (Openbox::instance
->state() != Openbox::State_Exiting
) {
76 // these values should not be persisted across a window unmapping/mapping
77 property
->erase(_window
, otk::OBProperty::net_wm_desktop
);
78 property
->erase(_window
, otk::OBProperty::net_wm_state
);
83 void OBClient::getDesktop()
85 const otk::OBProperty
*property
= Openbox::instance
->property();
87 // defaults to the current desktop
88 _desktop
= 0; // XXX: change this to the current desktop!
90 property
->get(_window
, otk::OBProperty::net_wm_desktop
,
91 otk::OBProperty::Atom_Cardinal
,
96 void OBClient::getType()
98 const otk::OBProperty
*property
= Openbox::instance
->property();
100 _type
= (WindowType
) -1;
103 unsigned long num
= (unsigned) -1;
104 if (property
->get(_window
, otk::OBProperty::net_wm_window_type
,
105 otk::OBProperty::Atom_Atom
,
107 // use the first value that we know about in the array
108 for (unsigned long i
= 0; i
< num
; ++i
) {
110 property
->atom(otk::OBProperty::net_wm_window_type_desktop
))
111 _type
= Type_Desktop
;
113 property
->atom(otk::OBProperty::net_wm_window_type_dock
))
116 property
->atom(otk::OBProperty::net_wm_window_type_toolbar
))
117 _type
= Type_Toolbar
;
119 property
->atom(otk::OBProperty::net_wm_window_type_menu
))
122 property
->atom(otk::OBProperty::net_wm_window_type_utility
))
123 _type
= Type_Utility
;
125 property
->atom(otk::OBProperty::net_wm_window_type_splash
))
128 property
->atom(otk::OBProperty::net_wm_window_type_dialog
))
131 property
->atom(otk::OBProperty::net_wm_window_type_normal
))
133 // else if (val[i] ==
134 // property->atom(otk::OBProperty::kde_net_wm_window_type_override))
135 // mwm_decorations = 0; // prevent this window from getting any decor
136 // XXX: make this work again
141 if (_type
== (WindowType
) -1) {
143 * the window type hint was not set, which means we either classify ourself
144 * as a normal window or a dialog, depending on if we are a transient.
154 void OBClient::setupDecorAndFunctions()
156 // start with everything
157 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
158 Decor_Iconify
| Decor_Maximize
;
159 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
163 // normal windows retain all of the possible decorations and
167 // dialogs cannot be maximized
168 _decorations
&= ~Decor_Maximize
;
169 _functions
&= ~Func_Maximize
;
175 // these windows get less functionality
176 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
177 _functions
&= ~(Func_Iconify
| Func_Resize
);
183 // none of these windows are manipulated by the window manager
189 // Mwm Hints are applied subtractively to what has already been chosen for
190 // decor and functionality
191 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
192 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
193 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
194 _decorations
&= ~Decor_Border
;
195 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
196 _decorations
&= ~Decor_Handle
;
197 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
198 _decorations
&= ~Decor_Titlebar
;
199 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
200 _decorations
&= ~Decor_Iconify
;
201 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
202 _decorations
&= ~Decor_Maximize
;
206 if (_mwmhints
.flags
& MwmFlag_Functions
) {
207 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
208 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
209 _functions
&= ~Func_Resize
;
210 if (! (_mwmhints
.functions
& MwmFunc_Move
))
211 _functions
&= ~Func_Move
;
212 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
213 _functions
&= ~Func_Iconify
;
214 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
215 _functions
&= ~Func_Maximize
;
216 // dont let mwm hints kill the close button
217 //if (! (_mwmhints.functions & MwmFunc_Close))
218 // _functions &= ~Func_Close;
222 // XXX: changeAllowedActions();
226 void OBClient::getMwmHints()
228 const otk::OBProperty
*property
= Openbox::instance
->property();
230 unsigned long num
= MwmHints::elements
;
231 unsigned long *hints
;
233 _mwmhints
.flags
= 0; // default to none
235 if (!property
->get(_window
, otk::OBProperty::motif_wm_hints
,
236 otk::OBProperty::motif_wm_hints
, &num
,
237 (unsigned long **)&hints
))
240 if (num
>= MwmHints::elements
) {
241 // retrieved the hints
242 _mwmhints
.flags
= hints
[0];
243 _mwmhints
.functions
= hints
[1];
244 _mwmhints
.decorations
= hints
[2];
251 void OBClient::getArea()
253 XWindowAttributes wattrib
;
256 ret
= XGetWindowAttributes(otk::OBDisplay::display
, _window
, &wattrib
);
257 assert(ret
!= BadWindow
);
259 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
260 _border_width
= wattrib
.border_width
;
264 void OBClient::getState()
266 const otk::OBProperty
*property
= Openbox::instance
->property();
268 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
269 _skip_taskbar
= _skip_pager
= false;
271 unsigned long *state
;
272 unsigned long num
= (unsigned) -1;
274 if (property
->get(_window
, otk::OBProperty::net_wm_state
,
275 otk::OBProperty::Atom_Atom
, &num
, &state
)) {
276 for (unsigned long i
= 0; i
< num
; ++i
) {
277 if (state
[i
] == property
->atom(otk::OBProperty::net_wm_state_modal
))
280 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
282 _wmstate
= IconicState
;
283 } else if (state
[i
] ==
284 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
285 _skip_taskbar
= true;
287 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
290 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
293 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
296 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
299 property
->atom(otk::OBProperty::net_wm_state_above
))
302 property
->atom(otk::OBProperty::net_wm_state_below
))
311 void OBClient::getShaped()
315 if (otk::OBDisplay::shape()) {
320 XShapeSelectInput(otk::OBDisplay::display
, _window
, ShapeNotifyMask
);
322 XShapeQueryExtents(otk::OBDisplay::display
, _window
, &s
, &foo
,
323 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
330 void OBClient::calcLayer() {
333 if (_iconic
) l
= Layer_Icon
;
334 else if (_fullscreen
) l
= Layer_Fullscreen
;
335 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
336 else if (_type
== Type_Dock
) {
337 if (!_below
) l
= Layer_Top
;
338 else l
= Layer_Normal
;
340 else if (_above
) l
= Layer_Above
;
341 else if (_below
) l
= Layer_Below
;
342 else l
= Layer_Normal
;
348 if we don't have a frame, then we aren't mapped yet (and this would
351 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
357 void OBClient::updateProtocols()
359 const otk::OBProperty
*property
= Openbox::instance
->property();
364 _focus_notify
= false;
365 _decorations
&= ~Decor_Close
;
366 _functions
&= ~Func_Close
;
368 if (XGetWMProtocols(otk::OBDisplay::display
, _window
, &proto
, &num_return
)) {
369 for (int i
= 0; i
< num_return
; ++i
) {
370 if (proto
[i
] == property
->atom(otk::OBProperty::wm_delete_window
)) {
371 _decorations
|= Decor_Close
;
372 _functions
|= Func_Close
;
374 frame
->adjustSize(); // update the decorations
375 } else if (proto
[i
] == property
->atom(otk::OBProperty::wm_take_focus
))
376 // if this protocol is requested, then the window will be notified
377 // by the window manager whenever it receives focus
378 _focus_notify
= true;
385 void OBClient::updateNormalHints()
389 int oldgravity
= _gravity
;
392 _gravity
= NorthWestGravity
;
393 _size_inc
.setPoint(1, 1);
394 _base_size
.setPoint(0, 0);
395 _min_size
.setPoint(0, 0);
396 _max_size
.setPoint(INT_MAX
, INT_MAX
);
398 // XXX: might want to cancel any interactive resizing of the window at this
401 // get the hints from the window
402 if (XGetWMNormalHints(otk::OBDisplay::display
, _window
, &size
, &ret
)) {
403 _positioned
= (size
.flags
& (PPosition
|USPosition
));
405 if (size
.flags
& PWinGravity
)
406 _gravity
= size
.win_gravity
;
408 if (size
.flags
& PMinSize
)
409 _min_size
.setPoint(size
.min_width
, size
.min_height
);
411 if (size
.flags
& PMaxSize
)
412 _max_size
.setPoint(size
.max_width
, size
.max_height
);
414 if (size
.flags
& PBaseSize
)
415 _base_size
.setPoint(size
.base_width
, size
.base_height
);
417 if (size
.flags
& PResizeInc
)
418 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
421 // if the client has a frame, i.e. has already been mapped and is
422 // changing its gravity
423 if (frame
&& _gravity
!= oldgravity
) {
424 // move our idea of the client's position based on its new gravity
426 frame
->frameGravity(x
, y
);
432 void OBClient::updateWMHints()
436 // assume a window takes input if it doesnt specify
440 if ((hints
= XGetWMHints(otk::OBDisplay::display
, _window
)) != NULL
) {
441 if (hints
->flags
& InputHint
)
442 _can_focus
= hints
->input
;
444 if (hints
->flags
& XUrgencyHint
)
447 if (hints
->flags
& WindowGroupHint
) {
448 if (hints
->window_group
!= _group
) {
449 // XXX: remove from the old group if there was one
450 _group
= hints
->window_group
;
451 // XXX: do stuff with the group
461 void OBClient::updateTitle()
463 const otk::OBProperty
*property
= Openbox::instance
->property();
468 if (! property
->get(_window
, otk::OBProperty::net_wm_name
,
469 otk::OBProperty::utf8
, &_title
)) {
471 property
->get(_window
, otk::OBProperty::wm_name
,
472 otk::OBProperty::ascii
, &_title
);
476 _title
= _("Unnamed Window");
479 frame
->setTitle(_title
);
483 void OBClient::updateIconTitle()
485 const otk::OBProperty
*property
= Openbox::instance
->property();
490 if (! property
->get(_window
, otk::OBProperty::net_wm_icon_name
,
491 otk::OBProperty::utf8
, &_icon_title
)) {
493 property
->get(_window
, otk::OBProperty::wm_icon_name
,
494 otk::OBProperty::ascii
, &_icon_title
);
498 _icon_title
= _("Unnamed Window");
502 void OBClient::updateClass()
504 const otk::OBProperty
*property
= Openbox::instance
->property();
507 _app_name
= _app_class
= _role
= "";
509 otk::OBProperty::StringVect v
;
510 unsigned long num
= 2;
512 if (property
->get(_window
, otk::OBProperty::wm_class
,
513 otk::OBProperty::ascii
, &num
, &v
)) {
514 if (num
> 0) _app_name
= v
[0];
515 if (num
> 1) _app_class
= v
[1];
520 if (property
->get(_window
, otk::OBProperty::wm_window_role
,
521 otk::OBProperty::ascii
, &num
, &v
)) {
522 if (num
> 0) _role
= v
[0];
527 void OBClient::updateStrut()
529 unsigned long num
= 4;
531 if (!Openbox::instance
->property()->get(_window
,
532 otk::OBProperty::net_wm_strut
,
533 otk::OBProperty::Atom_Cardinal
,
538 _strut
.left
= data
[0];
539 _strut
.right
= data
[1];
540 _strut
.top
= data
[2];
541 _strut
.bottom
= data
[3];
543 Openbox::instance
->screen(_screen
)->updateStrut();
550 void OBClient::updateTransientFor()
555 if (XGetTransientForHint(otk::OBDisplay::display
, _window
, &t
) &&
556 t
!= _window
) { // cant be transient to itself!
557 c
= Openbox::instance
->findClient(t
);
558 assert(c
!= this); // if this happens then we need to check for it
560 if (!c
/*XXX: && _group*/) {
561 // not transient to a client, see if it is transient for a group
562 if (//t == _group->leader() ||
564 t
== otk::OBDisplay::screenInfo(_screen
)->rootWindow()) {
565 // window is a transient for its group!
566 // XXX: for now this is treated as non-transient.
567 // this needs to be fixed!
572 // if anything has changed...
573 if (c
!= _transient_for
) {
575 _transient_for
->_transients
.remove(this); // remove from old parent
578 _transient_for
->_transients
.push_back(this); // add to new parent
580 // XXX: change decor status?
585 void OBClient::propertyHandler(const XPropertyEvent
&e
)
587 otk::OtkEventHandler::propertyHandler(e
);
589 const otk::OBProperty
*property
= Openbox::instance
->property();
591 // compress changes to a single property into a single change
593 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
594 // XXX: it would be nice to compress ALL changes to a property, not just
595 // changes in a row without other props between.
596 if (ce
.xproperty
.atom
!= e
.atom
) {
597 XPutBackEvent(otk::OBDisplay::display
, &ce
);
602 if (e
.atom
== XA_WM_NORMAL_HINTS
)
604 else if (e
.atom
== XA_WM_HINTS
)
606 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
607 updateTransientFor();
609 calcLayer(); // type may have changed, so update the layer
610 setupDecorAndFunctions();
611 frame
->adjustSize(); // this updates the frame for any new decor settings
613 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_name
) ||
614 e
.atom
== property
->atom(otk::OBProperty::wm_name
))
616 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_icon_name
) ||
617 e
.atom
== property
->atom(otk::OBProperty::wm_icon_name
))
619 else if (e
.atom
== property
->atom(otk::OBProperty::wm_class
))
621 else if (e
.atom
== property
->atom(otk::OBProperty::wm_protocols
))
623 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_strut
))
628 void OBClient::setWMState(long state
)
630 if (state
== _wmstate
) return; // no change
635 // XXX: cause it to iconify
638 // XXX: cause it to uniconify
644 void OBClient::setDesktop(long target
)
646 printf("Setting desktop %ld\n", target
);
647 assert(target
>= 0 || target
== (signed)0xffffffff);
648 //assert(target == 0xffffffff || target < MAX);
650 // XXX: move the window to the new desktop (and set root property)
655 void OBClient::setState(StateAction action
, long data1
, long data2
)
657 const otk::OBProperty
*property
= Openbox::instance
->property();
658 bool shadestate
= _shaded
;
660 if (!(action
== State_Add
|| action
== State_Remove
||
661 action
== State_Toggle
))
662 return; // an invalid action was passed to the client message, ignore it
664 for (int i
= 0; i
< 2; ++i
) {
665 Atom state
= i
== 0 ? data1
: data2
;
667 if (! state
) continue;
669 // if toggling, then pick whether we're adding or removing
670 if (action
== State_Toggle
) {
671 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
))
672 action
= _modal
? State_Remove
: State_Add
;
674 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
675 action
= _max_vert
? State_Remove
: State_Add
;
677 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
678 action
= _max_horz
? State_Remove
: State_Add
;
679 else if (state
== property
->atom(otk::OBProperty::net_wm_state_shaded
))
680 action
= _shaded
? State_Remove
: State_Add
;
682 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
683 action
= _skip_taskbar
? State_Remove
: State_Add
;
685 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
686 action
= _skip_pager
? State_Remove
: State_Add
;
688 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
689 action
= _fullscreen
? State_Remove
: State_Add
;
690 else if (state
== property
->atom(otk::OBProperty::net_wm_state_above
))
691 action
= _above
? State_Remove
: State_Add
;
692 else if (state
== property
->atom(otk::OBProperty::net_wm_state_below
))
693 action
= _below
? State_Remove
: State_Add
;
696 if (action
== State_Add
) {
697 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
698 if (_modal
) continue;
700 // XXX: give it focus if another window has focus that shouldnt now
702 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
703 if (_max_vert
) continue;
705 // XXX: resize the window etc
707 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
708 if (_max_horz
) continue;
710 // XXX: resize the window etc
712 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
713 if (_shaded
) continue;
714 // shade when we're all thru here
717 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
718 _skip_taskbar
= true;
720 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
723 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
724 if (_fullscreen
) continue;
727 property
->atom(otk::OBProperty::net_wm_state_above
)) {
728 if (_above
) continue;
731 property
->atom(otk::OBProperty::net_wm_state_below
)) {
732 if (_below
) continue;
736 } else { // action == State_Remove
737 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
738 if (!_modal
) continue;
741 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
742 if (!_max_vert
) continue;
744 // XXX: resize the window etc
746 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
747 if (!_max_horz
) continue;
749 // XXX: resize the window etc
751 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
752 if (!_shaded
) continue;
753 // unshade when we're all thru here
756 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
757 _skip_taskbar
= false;
759 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
762 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
763 if (!_fullscreen
) continue;
766 property
->atom(otk::OBProperty::net_wm_state_above
)) {
767 if (!_above
) continue;
770 property
->atom(otk::OBProperty::net_wm_state_below
)) {
771 if (!_below
) continue;
776 if (shadestate
!= _shaded
)
782 void OBClient::toggleClientBorder(bool addborder
)
784 // adjust our idea of where the client is, based on its border. When the
785 // border is removed, the client should now be considered to be in a
786 // different position.
787 // when re-adding the border to the client, the same operation needs to be
789 int x
= _area
.x(), y
= _area
.y();
791 case NorthWestGravity
:
793 case SouthWestGravity
:
795 case NorthEastGravity
:
797 case SouthEastGravity
:
798 if (addborder
) x
-= _border_width
* 2;
799 else x
+= _border_width
* 2;
803 case NorthWestGravity
:
805 case NorthEastGravity
:
807 case SouthWestGravity
:
809 case SouthEastGravity
:
810 if (addborder
) y
-= _border_width
* 2;
811 else y
+= _border_width
* 2;
814 // no change for StaticGravity etc.
820 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, _border_width
);
822 // move the client so it is back it the right spot _with_ its border!
823 XMoveWindow(otk::OBDisplay::display
, _window
, x
, y
);
825 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, 0);
829 void OBClient::clientMessageHandler(const XClientMessageEvent
&e
)
831 otk::OtkEventHandler::clientMessageHandler(e
);
833 if (e
.format
!= 32) return;
835 const otk::OBProperty
*property
= Openbox::instance
->property();
837 if (e
.message_type
== property
->atom(otk::OBProperty::wm_change_state
)) {
838 // compress changes into a single change
839 bool compress
= false;
841 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
842 // XXX: it would be nice to compress ALL messages of a type, not just
843 // messages in a row without other message types between.
844 if (ce
.xclient
.message_type
!= e
.message_type
) {
845 XPutBackEvent(otk::OBDisplay::display
, &ce
);
851 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
853 setWMState(e
.data
.l
[0]); // use the original event
854 } else if (e
.message_type
==
855 property
->atom(otk::OBProperty::net_wm_desktop
)) {
856 // compress changes into a single change
857 bool compress
= false;
859 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
860 // XXX: it would be nice to compress ALL messages of a type, not just
861 // messages in a row without other message types between.
862 if (ce
.xclient
.message_type
!= e
.message_type
) {
863 XPutBackEvent(otk::OBDisplay::display
, &ce
);
869 setDesktop(e
.data
.l
[0]); // use the found event
871 setDesktop(e
.data
.l
[0]); // use the original event
872 } else if (e
.message_type
== property
->atom(otk::OBProperty::net_wm_state
)) {
873 // can't compress these
874 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
875 } else if (e
.message_type
==
876 property
->atom(otk::OBProperty::net_close_window
)) {
878 } else if (e
.message_type
==
879 property
->atom(otk::OBProperty::net_active_window
)) {
881 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
888 void OBClient::shapeHandler(const XShapeEvent
&e
)
890 otk::OtkEventHandler::shapeHandler(e
);
893 frame
->adjustShape();
898 void OBClient::resize(Corner anchor
, int w
, int h
, int x
, int y
)
903 // for interactive resizing. have to move half an increment in each
905 w
+= _size_inc
.x() / 2;
906 h
+= _size_inc
.y() / 2;
908 // is the window resizable? if it is not, then don't check its sizes, the
909 // client can do what it wants and the user can't change it anyhow
910 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
911 // smaller than min size or bigger than max size?
912 if (w
< _min_size
.x()) w
= _min_size
.x();
913 else if (w
> _max_size
.x()) w
= _max_size
.x();
914 if (h
< _min_size
.y()) h
= _min_size
.y();
915 else if (h
> _max_size
.y()) h
= _max_size
.y();
918 // keep to the increments
922 // store the logical size
923 _logical_size
.setPoint(w
, h
);
931 if (x
== INT_MIN
|| y
== INT_MIN
) {
938 x
-= w
- _area
.width();
941 y
-= h
- _area
.height();
944 x
-= w
- _area
.width();
945 y
-= h
- _area
.height();
952 XResizeWindow(otk::OBDisplay::display
, _window
, w
, h
);
954 // resize the frame to match the request
960 void OBClient::move(int x
, int y
)
964 // move the frame to be in the requested position
965 frame
->adjustPosition();
969 void OBClient::close()
972 const otk::OBProperty
*property
= Openbox::instance
->property();
974 if (!(_functions
& Func_Close
)) return;
976 // XXX: itd be cool to do timeouts and shit here for killing the client's
978 // like... if the window is around after 5 seconds, then the close button
979 // turns a nice red, and if this function is called again, the client is
980 // explicitly killed.
982 ce
.xclient
.type
= ClientMessage
;
983 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
984 ce
.xclient
.display
= otk::OBDisplay::display
;
985 ce
.xclient
.window
= _window
;
986 ce
.xclient
.format
= 32;
987 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_delete_window
);
988 ce
.xclient
.data
.l
[1] = CurrentTime
;
989 ce
.xclient
.data
.l
[2] = 0l;
990 ce
.xclient
.data
.l
[3] = 0l;
991 ce
.xclient
.data
.l
[4] = 0l;
992 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
996 void OBClient::changeState()
998 const otk::OBProperty
*property
= Openbox::instance
->property();
1000 unsigned long state
[2];
1001 state
[0] = _wmstate
;
1003 property
->set(_window
, otk::OBProperty::wm_state
, otk::OBProperty::wm_state
,
1009 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_modal
);
1011 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_shaded
);
1013 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_hidden
);
1016 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
);
1018 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_skip_pager
);
1020 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_fullscreen
);
1023 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
);
1026 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
);
1028 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_above
);
1030 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_below
);
1031 property
->set(_window
, otk::OBProperty::net_wm_state
,
1032 otk::OBProperty::Atom_Atom
, netstate
, num
);
1038 void OBClient::setStackLayer(int l
)
1041 _above
= _below
= false; // normal
1044 _below
= false; // above
1047 _below
= true; // below
1053 void OBClient::shade(bool shade
)
1055 if (shade
== _shaded
) return; // already done
1057 _wmstate
= shade
? IconicState
: NormalState
;
1060 frame
->adjustSize();
1064 bool OBClient::focus()
1066 if (!(_can_focus
|| _focus_notify
) || _focused
) return false;
1069 XSetInputFocus(otk::OBDisplay::display
, _window
, RevertToNone
, CurrentTime
);
1071 if (_focus_notify
) {
1073 const otk::OBProperty
*property
= Openbox::instance
->property();
1075 ce
.xclient
.type
= ClientMessage
;
1076 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
1077 ce
.xclient
.display
= otk::OBDisplay::display
;
1078 ce
.xclient
.window
= _window
;
1079 ce
.xclient
.format
= 32;
1080 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_take_focus
);
1081 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1082 ce
.xclient
.data
.l
[2] = 0l;
1083 ce
.xclient
.data
.l
[3] = 0l;
1084 ce
.xclient
.data
.l
[4] = 0l;
1085 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1092 void OBClient::unfocus()
1094 if (!_focused
) return;
1096 assert(Openbox::instance
->focusedClient() == this);
1097 Openbox::instance
->setFocusedClient(0);
1101 void OBClient::focusHandler(const XFocusChangeEvent
&e
)
1104 printf("FocusIn for 0x%lx\n", e
.window
);
1107 OtkEventHandler::focusHandler(e
);
1112 Openbox::instance
->setFocusedClient(this);
1116 void OBClient::unfocusHandler(const XFocusChangeEvent
&e
)
1119 printf("FocusOut for 0x%lx\n", e
.window
);
1122 OtkEventHandler::unfocusHandler(e
);
1127 if (Openbox::instance
->focusedClient() == this) {
1128 printf("UNFOCUSED!\n");
1129 Openbox::instance
->setFocusedClient(this);
1134 void OBClient::configureRequestHandler(const XConfigureRequestEvent
&e
)
1137 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1140 OtkEventHandler::configureRequestHandler(e
);
1142 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1144 if (e
.value_mask
& CWBorderWidth
)
1145 _border_width
= e
.border_width
;
1147 // resize, then move, as specified in the EWMH section 7.7
1148 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1149 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1150 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1154 case NorthEastGravity
:
1158 case SouthWestGravity
:
1160 corner
= BottomLeft
;
1162 case SouthEastGravity
:
1163 corner
= BottomRight
;
1165 default: // NorthWest, Static, etc
1169 // if moving AND resizing ...
1170 if (e
.value_mask
& (CWX
| CWY
)) {
1171 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1172 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1173 resize(corner
, w
, h
, x
, y
);
1174 } else // if JUST resizing...
1175 resize(corner
, w
, h
);
1176 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1177 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1178 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1182 if (e
.value_mask
& CWStackMode
) {
1186 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1192 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1199 void OBClient::unmapHandler(const XUnmapEvent
&e
)
1202 printf("UnmapNotify for 0x%lx\n", e
.window
);
1205 if (ignore_unmaps
) {
1210 OtkEventHandler::unmapHandler(e
);
1212 // this deletes us etc
1213 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1217 void OBClient::destroyHandler(const XDestroyWindowEvent
&e
)
1220 printf("DestroyNotify for 0x%lx\n", e
.window
);
1223 OtkEventHandler::destroyHandler(e
);
1225 // this deletes us etc
1226 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1230 void OBClient::reparentHandler(const XReparentEvent
&e
)
1232 // this is when the client is first taken captive in the frame
1233 if (e
.parent
== frame
->plate()) return;
1236 printf("ReparentNotify for 0x%lx\n", e
.window
);
1239 OtkEventHandler::reparentHandler(e
);
1242 This event is quite rare and is usually handled in unmapHandler.
1243 However, if the window is unmapped when the reparent event occurs,
1244 the window manager never sees it because an unmap event is not sent
1245 to an already unmapped window.
1248 // this deletes us etc
1249 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1253 void OBClient::mapRequestHandler(const XMapRequestEvent
&e
)
1255 printf("\nMAP REQUEST\n\n");
1257 otk::OtkEventHandler::mapRequestHandler(e
);
1261 // XXX: uniconify the window