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();
612 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_name
) ||
613 e
.atom
== property
->atom(otk::OBProperty::wm_name
))
615 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_icon_name
) ||
616 e
.atom
== property
->atom(otk::OBProperty::wm_icon_name
))
618 else if (e
.atom
== property
->atom(otk::OBProperty::wm_class
))
620 else if (e
.atom
== property
->atom(otk::OBProperty::wm_protocols
))
622 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_strut
))
627 void OBClient::setWMState(long state
)
629 if (state
== _wmstate
) return; // no change
634 // XXX: cause it to iconify
637 // XXX: cause it to uniconify
643 void OBClient::setDesktop(long target
)
645 printf("Setting desktop %ld\n", target
);
646 assert(target
>= 0 || target
== (signed)0xffffffff);
647 //assert(target == 0xffffffff || target < MAX);
649 // XXX: move the window to the new desktop (and set root property)
654 void OBClient::setState(StateAction action
, long data1
, long data2
)
656 const otk::OBProperty
*property
= Openbox::instance
->property();
657 bool shadestate
= _shaded
;
659 if (!(action
== State_Add
|| action
== State_Remove
||
660 action
== State_Toggle
))
661 return; // an invalid action was passed to the client message, ignore it
663 for (int i
= 0; i
< 2; ++i
) {
664 Atom state
= i
== 0 ? data1
: data2
;
666 if (! state
) continue;
668 // if toggling, then pick whether we're adding or removing
669 if (action
== State_Toggle
) {
670 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
))
671 action
= _modal
? State_Remove
: State_Add
;
673 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
674 action
= _max_vert
? State_Remove
: State_Add
;
676 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
677 action
= _max_horz
? State_Remove
: State_Add
;
678 else if (state
== property
->atom(otk::OBProperty::net_wm_state_shaded
))
679 action
= _shaded
? State_Remove
: State_Add
;
681 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
682 action
= _skip_taskbar
? State_Remove
: State_Add
;
684 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
685 action
= _skip_pager
? State_Remove
: State_Add
;
687 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
688 action
= _fullscreen
? State_Remove
: State_Add
;
689 else if (state
== property
->atom(otk::OBProperty::net_wm_state_above
))
690 action
= _above
? State_Remove
: State_Add
;
691 else if (state
== property
->atom(otk::OBProperty::net_wm_state_below
))
692 action
= _below
? State_Remove
: State_Add
;
695 if (action
== State_Add
) {
696 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
697 if (_modal
) continue;
699 // XXX: give it focus if another window has focus that shouldnt now
701 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
702 if (_max_vert
) continue;
704 // XXX: resize the window etc
706 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
707 if (_max_horz
) continue;
709 // XXX: resize the window etc
711 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
712 if (_shaded
) continue;
713 // shade when we're all thru here
716 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
717 _skip_taskbar
= true;
719 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
722 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
723 if (_fullscreen
) continue;
726 property
->atom(otk::OBProperty::net_wm_state_above
)) {
727 if (_above
) continue;
730 property
->atom(otk::OBProperty::net_wm_state_below
)) {
731 if (_below
) continue;
735 } else { // action == State_Remove
736 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
737 if (!_modal
) continue;
740 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
741 if (!_max_vert
) continue;
743 // XXX: resize the window etc
745 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
746 if (!_max_horz
) continue;
748 // XXX: resize the window etc
750 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
751 if (!_shaded
) continue;
752 // unshade when we're all thru here
755 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
756 _skip_taskbar
= false;
758 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
761 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
762 if (!_fullscreen
) continue;
765 property
->atom(otk::OBProperty::net_wm_state_above
)) {
766 if (!_above
) continue;
769 property
->atom(otk::OBProperty::net_wm_state_below
)) {
770 if (!_below
) continue;
775 if (shadestate
!= _shaded
)
781 void OBClient::toggleClientBorder(bool addborder
)
783 // adjust our idea of where the client is, based on its border. When the
784 // border is removed, the client should now be considered to be in a
785 // different position.
786 // when re-adding the border to the client, the same operation needs to be
788 int x
= _area
.x(), y
= _area
.y();
790 case NorthWestGravity
:
792 case SouthWestGravity
:
794 case NorthEastGravity
:
796 case SouthEastGravity
:
797 if (addborder
) x
-= _border_width
* 2;
798 else x
+= _border_width
* 2;
802 case NorthWestGravity
:
804 case NorthEastGravity
:
806 case SouthWestGravity
:
808 case SouthEastGravity
:
809 if (addborder
) y
-= _border_width
* 2;
810 else y
+= _border_width
* 2;
813 // no change for StaticGravity etc.
819 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, _border_width
);
821 // move the client so it is back it the right spot _with_ its border!
822 XMoveWindow(otk::OBDisplay::display
, _window
, x
, y
);
824 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, 0);
828 void OBClient::clientMessageHandler(const XClientMessageEvent
&e
)
830 otk::OtkEventHandler::clientMessageHandler(e
);
832 if (e
.format
!= 32) return;
834 const otk::OBProperty
*property
= Openbox::instance
->property();
836 if (e
.message_type
== property
->atom(otk::OBProperty::wm_change_state
)) {
837 // compress changes into a single change
838 bool compress
= false;
840 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
841 // XXX: it would be nice to compress ALL messages of a type, not just
842 // messages in a row without other message types between.
843 if (ce
.xclient
.message_type
!= e
.message_type
) {
844 XPutBackEvent(otk::OBDisplay::display
, &ce
);
850 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
852 setWMState(e
.data
.l
[0]); // use the original event
853 } else if (e
.message_type
==
854 property
->atom(otk::OBProperty::net_wm_desktop
)) {
855 // compress changes into a single change
856 bool compress
= false;
858 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
859 // XXX: it would be nice to compress ALL messages of a type, not just
860 // messages in a row without other message types between.
861 if (ce
.xclient
.message_type
!= e
.message_type
) {
862 XPutBackEvent(otk::OBDisplay::display
, &ce
);
868 setDesktop(e
.data
.l
[0]); // use the found event
870 setDesktop(e
.data
.l
[0]); // use the original event
871 } else if (e
.message_type
== property
->atom(otk::OBProperty::net_wm_state
)) {
872 // can't compress these
873 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
874 } else if (e
.message_type
==
875 property
->atom(otk::OBProperty::net_close_window
)) {
877 } else if (e
.message_type
==
878 property
->atom(otk::OBProperty::net_active_window
)) {
880 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
887 void OBClient::shapeHandler(const XShapeEvent
&e
)
889 otk::OtkEventHandler::shapeHandler(e
);
892 frame
->adjustShape();
897 void OBClient::resize(Corner anchor
, int w
, int h
, int x
, int y
)
902 // for interactive resizing. have to move half an increment in each
904 w
+= _size_inc
.x() / 2;
905 h
+= _size_inc
.y() / 2;
907 // is the window resizable? if it is not, then don't check its sizes, the
908 // client can do what it wants and the user can't change it anyhow
909 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
910 // smaller than min size or bigger than max size?
911 if (w
< _min_size
.x()) w
= _min_size
.x();
912 else if (w
> _max_size
.x()) w
= _max_size
.x();
913 if (h
< _min_size
.y()) h
= _min_size
.y();
914 else if (h
> _max_size
.y()) h
= _max_size
.y();
917 // keep to the increments
921 // store the logical size
922 _logical_size
.setPoint(w
, h
);
930 if (x
== INT_MIN
|| y
== INT_MIN
) {
937 x
-= w
- _area
.width();
940 y
-= h
- _area
.height();
943 x
-= w
- _area
.width();
944 y
-= h
- _area
.height();
951 XResizeWindow(otk::OBDisplay::display
, _window
, w
, h
);
953 // resize the frame to match the request
959 void OBClient::move(int x
, int y
)
963 // move the frame to be in the requested position
964 frame
->adjustPosition();
968 void OBClient::close()
971 const otk::OBProperty
*property
= Openbox::instance
->property();
973 if (!(_functions
& Func_Close
)) return;
975 // XXX: itd be cool to do timeouts and shit here for killing the client's
977 // like... if the window is around after 5 seconds, then the close button
978 // turns a nice red, and if this function is called again, the client is
979 // explicitly killed.
981 ce
.xclient
.type
= ClientMessage
;
982 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
983 ce
.xclient
.display
= otk::OBDisplay::display
;
984 ce
.xclient
.window
= _window
;
985 ce
.xclient
.format
= 32;
986 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_delete_window
);
987 ce
.xclient
.data
.l
[1] = CurrentTime
;
988 ce
.xclient
.data
.l
[2] = 0l;
989 ce
.xclient
.data
.l
[3] = 0l;
990 ce
.xclient
.data
.l
[4] = 0l;
991 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
995 void OBClient::changeState()
997 const otk::OBProperty
*property
= Openbox::instance
->property();
999 unsigned long state
[2];
1000 state
[0] = _wmstate
;
1002 property
->set(_window
, otk::OBProperty::wm_state
, otk::OBProperty::wm_state
,
1008 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_modal
);
1010 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_shaded
);
1012 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_hidden
);
1015 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
);
1017 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_skip_pager
);
1019 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_fullscreen
);
1022 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
);
1025 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
);
1027 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_above
);
1029 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_below
);
1030 property
->set(_window
, otk::OBProperty::net_wm_state
,
1031 otk::OBProperty::Atom_Atom
, netstate
, num
);
1037 void OBClient::setStackLayer(int l
)
1040 _above
= _below
= false; // normal
1043 _below
= false; // above
1046 _below
= true; // below
1052 void OBClient::shade(bool shade
)
1054 if (shade
== _shaded
) return; // already done
1056 _wmstate
= shade
? IconicState
: NormalState
;
1059 frame
->adjustSize();
1063 bool OBClient::focus()
1065 if (!(_can_focus
|| _focus_notify
) || _focused
) return false;
1068 XSetInputFocus(otk::OBDisplay::display
, _window
, RevertToNone
, CurrentTime
);
1070 if (_focus_notify
) {
1072 const otk::OBProperty
*property
= Openbox::instance
->property();
1074 ce
.xclient
.type
= ClientMessage
;
1075 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
1076 ce
.xclient
.display
= otk::OBDisplay::display
;
1077 ce
.xclient
.window
= _window
;
1078 ce
.xclient
.format
= 32;
1079 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_take_focus
);
1080 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1081 ce
.xclient
.data
.l
[2] = 0l;
1082 ce
.xclient
.data
.l
[3] = 0l;
1083 ce
.xclient
.data
.l
[4] = 0l;
1084 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1091 void OBClient::unfocus()
1093 if (!_focused
) return;
1095 assert(Openbox::instance
->focusedClient() == this);
1096 Openbox::instance
->setFocusedClient(0);
1100 void OBClient::focusHandler(const XFocusChangeEvent
&e
)
1103 printf("FocusIn for 0x%lx\n", e
.window
);
1106 OtkEventHandler::focusHandler(e
);
1111 Openbox::instance
->setFocusedClient(this);
1115 void OBClient::unfocusHandler(const XFocusChangeEvent
&e
)
1118 printf("FocusOut for 0x%lx\n", e
.window
);
1121 OtkEventHandler::unfocusHandler(e
);
1126 if (Openbox::instance
->focusedClient() == this) {
1127 printf("UNFOCUSED!\n");
1128 Openbox::instance
->setFocusedClient(this);
1133 void OBClient::configureRequestHandler(const XConfigureRequestEvent
&e
)
1136 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1139 OtkEventHandler::configureRequestHandler(e
);
1141 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1143 if (e
.value_mask
& CWBorderWidth
)
1144 _border_width
= e
.border_width
;
1146 // resize, then move, as specified in the EWMH section 7.7
1147 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1148 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1149 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1153 case NorthEastGravity
:
1157 case SouthWestGravity
:
1159 corner
= BottomLeft
;
1161 case SouthEastGravity
:
1162 corner
= BottomRight
;
1164 default: // NorthWest, Static, etc
1168 // if moving AND resizing ...
1169 if (e
.value_mask
& (CWX
| CWY
)) {
1170 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1171 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1172 resize(corner
, w
, h
, x
, y
);
1173 } else // if JUST resizing...
1174 resize(corner
, w
, h
);
1175 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1176 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1177 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1181 if (e
.value_mask
& CWStackMode
) {
1185 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1191 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1198 void OBClient::unmapHandler(const XUnmapEvent
&e
)
1201 printf("UnmapNotify for 0x%lx\n", e
.window
);
1204 if (ignore_unmaps
) {
1209 OtkEventHandler::unmapHandler(e
);
1211 // this deletes us etc
1212 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1216 void OBClient::destroyHandler(const XDestroyWindowEvent
&e
)
1219 printf("DestroyNotify for 0x%lx\n", e
.window
);
1222 OtkEventHandler::destroyHandler(e
);
1224 // this deletes us etc
1225 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1229 void OBClient::reparentHandler(const XReparentEvent
&e
)
1231 // this is when the client is first taken captive in the frame
1232 if (e
.parent
== frame
->plate()) return;
1235 printf("ReparentNotify for 0x%lx\n", e
.window
);
1238 OtkEventHandler::reparentHandler(e
);
1241 This event is quite rare and is usually handled in unmapHandler.
1242 However, if the window is unmapped when the reparent event occurs,
1243 the window manager never sees it because an unmap event is not sent
1244 to an already unmapped window.
1247 // this deletes us etc
1248 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1252 void OBClient::mapRequestHandler(const XMapRequestEvent
&e
)
1254 printf("\nMAP REQUEST\n\n");
1256 otk::OtkEventHandler::mapRequestHandler(e
);
1260 // XXX: uniconify the window