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 if (!property
->get(_window
, otk::OBProperty::net_wm_desktop
,
95 otk::OBProperty::Atom_Cardinal
,
96 (long unsigned*)&_desktop
)) {
97 // make sure the hint exists
98 Openbox::instance
->property()->set(_window
,
99 otk::OBProperty::net_wm_desktop
,
100 otk::OBProperty::Atom_Cardinal
,
106 void OBClient::getType()
108 const otk::OBProperty
*property
= Openbox::instance
->property();
110 _type
= (WindowType
) -1;
113 unsigned long num
= (unsigned) -1;
114 if (property
->get(_window
, otk::OBProperty::net_wm_window_type
,
115 otk::OBProperty::Atom_Atom
,
117 // use the first value that we know about in the array
118 for (unsigned long i
= 0; i
< num
; ++i
) {
120 property
->atom(otk::OBProperty::net_wm_window_type_desktop
))
121 _type
= Type_Desktop
;
123 property
->atom(otk::OBProperty::net_wm_window_type_dock
))
126 property
->atom(otk::OBProperty::net_wm_window_type_toolbar
))
127 _type
= Type_Toolbar
;
129 property
->atom(otk::OBProperty::net_wm_window_type_menu
))
132 property
->atom(otk::OBProperty::net_wm_window_type_utility
))
133 _type
= Type_Utility
;
135 property
->atom(otk::OBProperty::net_wm_window_type_splash
))
138 property
->atom(otk::OBProperty::net_wm_window_type_dialog
))
141 property
->atom(otk::OBProperty::net_wm_window_type_normal
))
143 // else if (val[i] ==
144 // property->atom(otk::OBProperty::kde_net_wm_window_type_override))
145 // mwm_decorations = 0; // prevent this window from getting any decor
146 // XXX: make this work again
147 if (_type
!= (WindowType
) -1)
148 break; // grab the first known type
153 if (_type
== (WindowType
) -1) {
155 * the window type hint was not set, which means we either classify ourself
156 * as a normal window or a dialog, depending on if we are a transient.
166 void OBClient::setupDecorAndFunctions()
168 // start with everything
169 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
170 Decor_Iconify
| Decor_Maximize
;
171 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
175 // normal windows retain all of the possible decorations and
179 // dialogs cannot be maximized
180 _decorations
&= ~Decor_Maximize
;
181 _functions
&= ~Func_Maximize
;
187 // these windows get less functionality
188 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
189 _functions
&= ~(Func_Iconify
| Func_Resize
);
195 // none of these windows are manipulated by the window manager
201 // Mwm Hints are applied subtractively to what has already been chosen for
202 // decor and functionality
203 if (_mwmhints
.flags
& MwmFlag_Decorations
) {
204 if (! (_mwmhints
.decorations
& MwmDecor_All
)) {
205 if (! (_mwmhints
.decorations
& MwmDecor_Border
))
206 _decorations
&= ~Decor_Border
;
207 if (! (_mwmhints
.decorations
& MwmDecor_Handle
))
208 _decorations
&= ~Decor_Handle
;
209 if (! (_mwmhints
.decorations
& MwmDecor_Title
))
210 _decorations
&= ~Decor_Titlebar
;
211 if (! (_mwmhints
.decorations
& MwmDecor_Iconify
))
212 _decorations
&= ~Decor_Iconify
;
213 if (! (_mwmhints
.decorations
& MwmDecor_Maximize
))
214 _decorations
&= ~Decor_Maximize
;
218 if (_mwmhints
.flags
& MwmFlag_Functions
) {
219 if (! (_mwmhints
.functions
& MwmFunc_All
)) {
220 if (! (_mwmhints
.functions
& MwmFunc_Resize
))
221 _functions
&= ~Func_Resize
;
222 if (! (_mwmhints
.functions
& MwmFunc_Move
))
223 _functions
&= ~Func_Move
;
224 if (! (_mwmhints
.functions
& MwmFunc_Iconify
))
225 _functions
&= ~Func_Iconify
;
226 if (! (_mwmhints
.functions
& MwmFunc_Maximize
))
227 _functions
&= ~Func_Maximize
;
228 // dont let mwm hints kill the close button
229 //if (! (_mwmhints.functions & MwmFunc_Close))
230 // _functions &= ~Func_Close;
234 // XXX: changeAllowedActions();
238 void OBClient::getMwmHints()
240 const otk::OBProperty
*property
= Openbox::instance
->property();
242 unsigned long num
= MwmHints::elements
;
243 unsigned long *hints
;
245 _mwmhints
.flags
= 0; // default to none
247 if (!property
->get(_window
, otk::OBProperty::motif_wm_hints
,
248 otk::OBProperty::motif_wm_hints
, &num
,
249 (unsigned long **)&hints
))
252 if (num
>= MwmHints::elements
) {
253 // retrieved the hints
254 _mwmhints
.flags
= hints
[0];
255 _mwmhints
.functions
= hints
[1];
256 _mwmhints
.decorations
= hints
[2];
263 void OBClient::getArea()
265 XWindowAttributes wattrib
;
268 ret
= XGetWindowAttributes(otk::OBDisplay::display
, _window
, &wattrib
);
269 assert(ret
!= BadWindow
);
271 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
272 _border_width
= wattrib
.border_width
;
276 void OBClient::getState()
278 const otk::OBProperty
*property
= Openbox::instance
->property();
280 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _above
= _below
=
281 _skip_taskbar
= _skip_pager
= false;
283 unsigned long *state
;
284 unsigned long num
= (unsigned) -1;
286 if (property
->get(_window
, otk::OBProperty::net_wm_state
,
287 otk::OBProperty::Atom_Atom
, &num
, &state
)) {
288 for (unsigned long i
= 0; i
< num
; ++i
) {
289 if (state
[i
] == property
->atom(otk::OBProperty::net_wm_state_modal
))
292 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
294 _wmstate
= IconicState
;
295 } else if (state
[i
] ==
296 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
297 _skip_taskbar
= true;
299 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
302 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
305 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
308 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
311 property
->atom(otk::OBProperty::net_wm_state_above
))
314 property
->atom(otk::OBProperty::net_wm_state_below
))
323 void OBClient::getShaped()
327 if (otk::OBDisplay::shape()) {
332 XShapeSelectInput(otk::OBDisplay::display
, _window
, ShapeNotifyMask
);
334 XShapeQueryExtents(otk::OBDisplay::display
, _window
, &s
, &foo
,
335 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
342 void OBClient::calcLayer() {
345 if (_iconic
) l
= Layer_Icon
;
346 else if (_fullscreen
) l
= Layer_Fullscreen
;
347 else if (_type
== Type_Desktop
) l
= Layer_Desktop
;
348 else if (_type
== Type_Dock
) {
349 if (!_below
) l
= Layer_Top
;
350 else l
= Layer_Normal
;
352 else if (_above
) l
= Layer_Above
;
353 else if (_below
) l
= Layer_Below
;
354 else l
= Layer_Normal
;
360 if we don't have a frame, then we aren't mapped yet (and this would
363 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
369 void OBClient::updateProtocols()
371 const otk::OBProperty
*property
= Openbox::instance
->property();
376 _focus_notify
= false;
377 _decorations
&= ~Decor_Close
;
378 _functions
&= ~Func_Close
;
380 if (XGetWMProtocols(otk::OBDisplay::display
, _window
, &proto
, &num_return
)) {
381 for (int i
= 0; i
< num_return
; ++i
) {
382 if (proto
[i
] == property
->atom(otk::OBProperty::wm_delete_window
)) {
383 _decorations
|= Decor_Close
;
384 _functions
|= Func_Close
;
386 frame
->adjustSize(); // update the decorations
387 } else if (proto
[i
] == property
->atom(otk::OBProperty::wm_take_focus
))
388 // if this protocol is requested, then the window will be notified
389 // by the window manager whenever it receives focus
390 _focus_notify
= true;
397 void OBClient::updateNormalHints()
401 int oldgravity
= _gravity
;
404 _gravity
= NorthWestGravity
;
405 _size_inc
.setPoint(1, 1);
406 _base_size
.setPoint(0, 0);
407 _min_size
.setPoint(0, 0);
408 _max_size
.setPoint(INT_MAX
, INT_MAX
);
410 // XXX: might want to cancel any interactive resizing of the window at this
413 // get the hints from the window
414 if (XGetWMNormalHints(otk::OBDisplay::display
, _window
, &size
, &ret
)) {
415 _positioned
= (size
.flags
& (PPosition
|USPosition
));
417 if (size
.flags
& PWinGravity
)
418 _gravity
= size
.win_gravity
;
420 if (size
.flags
& PMinSize
)
421 _min_size
.setPoint(size
.min_width
, size
.min_height
);
423 if (size
.flags
& PMaxSize
)
424 _max_size
.setPoint(size
.max_width
, size
.max_height
);
426 if (size
.flags
& PBaseSize
)
427 _base_size
.setPoint(size
.base_width
, size
.base_height
);
429 if (size
.flags
& PResizeInc
)
430 _size_inc
.setPoint(size
.width_inc
, size
.height_inc
);
433 // if the client has a frame, i.e. has already been mapped and is
434 // changing its gravity
435 if (frame
&& _gravity
!= oldgravity
) {
436 // move our idea of the client's position based on its new gravity
438 frame
->frameGravity(x
, y
);
444 void OBClient::updateWMHints()
448 // assume a window takes input if it doesnt specify
452 if ((hints
= XGetWMHints(otk::OBDisplay::display
, _window
)) != NULL
) {
453 if (hints
->flags
& InputHint
)
454 _can_focus
= hints
->input
;
456 if (hints
->flags
& XUrgencyHint
)
459 if (hints
->flags
& WindowGroupHint
) {
460 if (hints
->window_group
!= _group
) {
461 // XXX: remove from the old group if there was one
462 _group
= hints
->window_group
;
463 // XXX: do stuff with the group
473 void OBClient::updateTitle()
475 const otk::OBProperty
*property
= Openbox::instance
->property();
480 if (! property
->get(_window
, otk::OBProperty::net_wm_name
,
481 otk::OBProperty::utf8
, &_title
)) {
483 property
->get(_window
, otk::OBProperty::wm_name
,
484 otk::OBProperty::ascii
, &_title
);
488 _title
= _("Unnamed Window");
491 frame
->setTitle(_title
);
495 void OBClient::updateIconTitle()
497 const otk::OBProperty
*property
= Openbox::instance
->property();
502 if (! property
->get(_window
, otk::OBProperty::net_wm_icon_name
,
503 otk::OBProperty::utf8
, &_icon_title
)) {
505 property
->get(_window
, otk::OBProperty::wm_icon_name
,
506 otk::OBProperty::ascii
, &_icon_title
);
510 _icon_title
= _("Unnamed Window");
514 void OBClient::updateClass()
516 const otk::OBProperty
*property
= Openbox::instance
->property();
519 _app_name
= _app_class
= _role
= "";
521 otk::OBProperty::StringVect v
;
522 unsigned long num
= 2;
524 if (property
->get(_window
, otk::OBProperty::wm_class
,
525 otk::OBProperty::ascii
, &num
, &v
)) {
526 if (num
> 0) _app_name
= v
[0];
527 if (num
> 1) _app_class
= v
[1];
532 if (property
->get(_window
, otk::OBProperty::wm_window_role
,
533 otk::OBProperty::ascii
, &num
, &v
)) {
534 if (num
> 0) _role
= v
[0];
539 void OBClient::updateStrut()
541 unsigned long num
= 4;
543 if (!Openbox::instance
->property()->get(_window
,
544 otk::OBProperty::net_wm_strut
,
545 otk::OBProperty::Atom_Cardinal
,
550 _strut
.left
= data
[0];
551 _strut
.right
= data
[1];
552 _strut
.top
= data
[2];
553 _strut
.bottom
= data
[3];
555 Openbox::instance
->screen(_screen
)->updateStrut();
562 void OBClient::updateTransientFor()
567 if (XGetTransientForHint(otk::OBDisplay::display
, _window
, &t
) &&
568 t
!= _window
) { // cant be transient to itself!
569 c
= Openbox::instance
->findClient(t
);
570 assert(c
!= this); // if this happens then we need to check for it
572 if (!c
/*XXX: && _group*/) {
573 // not transient to a client, see if it is transient for a group
574 if (//t == _group->leader() ||
576 t
== otk::OBDisplay::screenInfo(_screen
)->rootWindow()) {
577 // window is a transient for its group!
578 // XXX: for now this is treated as non-transient.
579 // this needs to be fixed!
584 // if anything has changed...
585 if (c
!= _transient_for
) {
587 _transient_for
->_transients
.remove(this); // remove from old parent
590 _transient_for
->_transients
.push_back(this); // add to new parent
592 // XXX: change decor status?
597 void OBClient::propertyHandler(const XPropertyEvent
&e
)
599 otk::OtkEventHandler::propertyHandler(e
);
601 const otk::OBProperty
*property
= Openbox::instance
->property();
603 // compress changes to a single property into a single change
605 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
606 // XXX: it would be nice to compress ALL changes to a property, not just
607 // changes in a row without other props between.
608 if (ce
.xproperty
.atom
!= e
.atom
) {
609 XPutBackEvent(otk::OBDisplay::display
, &ce
);
614 if (e
.atom
== XA_WM_NORMAL_HINTS
)
616 else if (e
.atom
== XA_WM_HINTS
)
618 else if (e
.atom
== XA_WM_TRANSIENT_FOR
) {
619 updateTransientFor();
621 calcLayer(); // type may have changed, so update the layer
622 setupDecorAndFunctions();
623 frame
->adjustSize(); // this updates the frame for any new decor settings
625 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_name
) ||
626 e
.atom
== property
->atom(otk::OBProperty::wm_name
))
628 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_icon_name
) ||
629 e
.atom
== property
->atom(otk::OBProperty::wm_icon_name
))
631 else if (e
.atom
== property
->atom(otk::OBProperty::wm_class
))
633 else if (e
.atom
== property
->atom(otk::OBProperty::wm_protocols
))
635 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_strut
))
640 void OBClient::setWMState(long state
)
642 if (state
== _wmstate
) return; // no change
647 // XXX: cause it to iconify
650 // XXX: cause it to uniconify
656 void OBClient::setDesktop(long target
)
658 if (target
== _desktop
) return;
660 printf("Setting desktop %ld\n", target
);
662 if (!(target
>= 0 || target
== (signed)0xffffffff)) return;
666 Openbox::instance
->property()->set(_window
,
667 otk::OBProperty::net_wm_desktop
,
668 otk::OBProperty::Atom_Cardinal
,
671 // 'move' the window to the new desktop
672 if (_desktop
== Openbox::instance
->screen(_screen
)->desktop() ||
673 _desktop
== (signed)0xffffffff)
680 void OBClient::setState(StateAction action
, long data1
, long data2
)
682 const otk::OBProperty
*property
= Openbox::instance
->property();
683 bool shadestate
= _shaded
;
685 if (!(action
== State_Add
|| action
== State_Remove
||
686 action
== State_Toggle
))
687 return; // an invalid action was passed to the client message, ignore it
689 for (int i
= 0; i
< 2; ++i
) {
690 Atom state
= i
== 0 ? data1
: data2
;
692 if (! state
) continue;
694 // if toggling, then pick whether we're adding or removing
695 if (action
== State_Toggle
) {
696 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
))
697 action
= _modal
? State_Remove
: State_Add
;
699 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
700 action
= _max_vert
? State_Remove
: State_Add
;
702 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
703 action
= _max_horz
? State_Remove
: State_Add
;
704 else if (state
== property
->atom(otk::OBProperty::net_wm_state_shaded
))
705 action
= _shaded
? State_Remove
: State_Add
;
707 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
))
708 action
= _skip_taskbar
? State_Remove
: State_Add
;
710 property
->atom(otk::OBProperty::net_wm_state_skip_pager
))
711 action
= _skip_pager
? State_Remove
: State_Add
;
713 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
714 action
= _fullscreen
? State_Remove
: State_Add
;
715 else if (state
== property
->atom(otk::OBProperty::net_wm_state_above
))
716 action
= _above
? State_Remove
: State_Add
;
717 else if (state
== property
->atom(otk::OBProperty::net_wm_state_below
))
718 action
= _below
? State_Remove
: State_Add
;
721 if (action
== State_Add
) {
722 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
723 if (_modal
) continue;
725 // XXX: give it focus if another window has focus that shouldnt now
727 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
728 if (_max_vert
) continue;
730 // XXX: resize the window etc
732 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
733 if (_max_horz
) continue;
735 // XXX: resize the window etc
737 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
738 if (_shaded
) continue;
739 // shade when we're all thru here
742 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
743 _skip_taskbar
= true;
745 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
748 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
749 if (_fullscreen
) continue;
752 property
->atom(otk::OBProperty::net_wm_state_above
)) {
753 if (_above
) continue;
756 property
->atom(otk::OBProperty::net_wm_state_below
)) {
757 if (_below
) continue;
761 } else { // action == State_Remove
762 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
763 if (!_modal
) continue;
766 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
767 if (!_max_vert
) continue;
769 // XXX: resize the window etc
771 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
772 if (!_max_horz
) continue;
774 // XXX: resize the window etc
776 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
777 if (!_shaded
) continue;
778 // unshade when we're all thru here
781 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
)) {
782 _skip_taskbar
= false;
784 property
->atom(otk::OBProperty::net_wm_state_skip_pager
)) {
787 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
788 if (!_fullscreen
) continue;
791 property
->atom(otk::OBProperty::net_wm_state_above
)) {
792 if (!_above
) continue;
795 property
->atom(otk::OBProperty::net_wm_state_below
)) {
796 if (!_below
) continue;
801 if (shadestate
!= _shaded
)
807 void OBClient::toggleClientBorder(bool addborder
)
809 // adjust our idea of where the client is, based on its border. When the
810 // border is removed, the client should now be considered to be in a
811 // different position.
812 // when re-adding the border to the client, the same operation needs to be
814 int x
= _area
.x(), y
= _area
.y();
816 case NorthWestGravity
:
818 case SouthWestGravity
:
820 case NorthEastGravity
:
822 case SouthEastGravity
:
823 if (addborder
) x
-= _border_width
* 2;
824 else x
+= _border_width
* 2;
828 case NorthWestGravity
:
830 case NorthEastGravity
:
832 case SouthWestGravity
:
834 case SouthEastGravity
:
835 if (addborder
) y
-= _border_width
* 2;
836 else y
+= _border_width
* 2;
839 // no change for StaticGravity etc.
845 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, _border_width
);
847 // move the client so it is back it the right spot _with_ its border!
848 XMoveWindow(otk::OBDisplay::display
, _window
, x
, y
);
850 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
, 0);
854 void OBClient::clientMessageHandler(const XClientMessageEvent
&e
)
856 otk::OtkEventHandler::clientMessageHandler(e
);
858 if (e
.format
!= 32) return;
860 const otk::OBProperty
*property
= Openbox::instance
->property();
862 if (e
.message_type
== property
->atom(otk::OBProperty::wm_change_state
)) {
863 // compress changes into a single change
864 bool compress
= false;
866 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
867 // XXX: it would be nice to compress ALL messages of a type, not just
868 // messages in a row without other message types between.
869 if (ce
.xclient
.message_type
!= e
.message_type
) {
870 XPutBackEvent(otk::OBDisplay::display
, &ce
);
876 setWMState(ce
.xclient
.data
.l
[0]); // use the found event
878 setWMState(e
.data
.l
[0]); // use the original event
879 } else if (e
.message_type
==
880 property
->atom(otk::OBProperty::net_wm_desktop
)) {
881 // compress changes into a single change
882 bool compress
= false;
884 while (XCheckTypedEvent(otk::OBDisplay::display
, e
.type
, &ce
)) {
885 // XXX: it would be nice to compress ALL messages of a type, not just
886 // messages in a row without other message types between.
887 if (ce
.xclient
.message_type
!= e
.message_type
) {
888 XPutBackEvent(otk::OBDisplay::display
, &ce
);
894 setDesktop(e
.data
.l
[0]); // use the found event
896 setDesktop(e
.data
.l
[0]); // use the original event
897 } else if (e
.message_type
== property
->atom(otk::OBProperty::net_wm_state
)) {
898 // can't compress these
900 printf("net_wm_state %s %ld %ld for 0x%lx\n",
901 (e
.data
.l
[0] == 0 ? "Remove" : e
.data
.l
[0] == 1 ? "Add" :
902 e
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
903 e
.data
.l
[1], e
.data
.l
[2], _window
);
905 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
906 } else if (e
.message_type
==
907 property
->atom(otk::OBProperty::net_close_window
)) {
909 printf("net_close_window for 0x%lx\n", _window
);
912 } else if (e
.message_type
==
913 property
->atom(otk::OBProperty::net_active_window
)) {
915 printf("net_active_window for 0x%lx\n", _window
);
921 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
927 void OBClient::shapeHandler(const XShapeEvent
&e
)
929 otk::OtkEventHandler::shapeHandler(e
);
931 if (e
.kind
== ShapeBounding
) {
933 frame
->adjustShape();
939 void OBClient::resize(Corner anchor
, int w
, int h
, int x
, int y
)
944 // for interactive resizing. have to move half an increment in each
946 w
+= _size_inc
.x() / 2;
947 h
+= _size_inc
.y() / 2;
949 // is the window resizable? if it is not, then don't check its sizes, the
950 // client can do what it wants and the user can't change it anyhow
951 if (_min_size
.x() <= _max_size
.x() && _min_size
.y() <= _max_size
.y()) {
952 // smaller than min size or bigger than max size?
953 if (w
< _min_size
.x()) w
= _min_size
.x();
954 else if (w
> _max_size
.x()) w
= _max_size
.x();
955 if (h
< _min_size
.y()) h
= _min_size
.y();
956 else if (h
> _max_size
.y()) h
= _max_size
.y();
959 // keep to the increments
963 // store the logical size
964 _logical_size
.setPoint(w
, h
);
972 if (x
== INT_MIN
|| y
== INT_MIN
) {
979 x
-= w
- _area
.width();
982 y
-= h
- _area
.height();
985 x
-= w
- _area
.width();
986 y
-= h
- _area
.height();
993 XResizeWindow(otk::OBDisplay::display
, _window
, w
, h
);
995 // resize the frame to match the request
1001 void OBClient::move(int x
, int y
)
1005 // move the frame to be in the requested position
1006 if (frame
) { // this can be called while mapping, before frame exists
1007 frame
->adjustPosition();
1009 // send synthetic configure notify (we don't need to if we aren't mapped
1012 event
.type
= ConfigureNotify
;
1013 event
.xconfigure
.display
= otk::OBDisplay::display
;
1014 event
.xconfigure
.event
= _window
;
1015 event
.xconfigure
.window
= _window
;
1016 event
.xconfigure
.x
= x
;
1017 event
.xconfigure
.y
= y
;
1018 event
.xconfigure
.width
= _area
.width();
1019 event
.xconfigure
.height
= _area
.height();
1020 event
.xconfigure
.border_width
= _border_width
;
1021 event
.xconfigure
.above
= frame
->window();
1022 event
.xconfigure
.override_redirect
= False
;
1023 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
, False
,
1024 StructureNotifyMask
, &event
);
1029 void OBClient::close()
1032 const otk::OBProperty
*property
= Openbox::instance
->property();
1034 if (!(_functions
& Func_Close
)) return;
1036 // XXX: itd be cool to do timeouts and shit here for killing the client's
1038 // like... if the window is around after 5 seconds, then the close button
1039 // turns a nice red, and if this function is called again, the client is
1040 // explicitly killed.
1042 ce
.xclient
.type
= ClientMessage
;
1043 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
1044 ce
.xclient
.display
= otk::OBDisplay::display
;
1045 ce
.xclient
.window
= _window
;
1046 ce
.xclient
.format
= 32;
1047 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_delete_window
);
1048 ce
.xclient
.data
.l
[1] = CurrentTime
;
1049 ce
.xclient
.data
.l
[2] = 0l;
1050 ce
.xclient
.data
.l
[3] = 0l;
1051 ce
.xclient
.data
.l
[4] = 0l;
1052 XSendEvent(otk::OBDisplay::display
, _window
, false, NoEventMask
, &ce
);
1056 void OBClient::changeState()
1058 const otk::OBProperty
*property
= Openbox::instance
->property();
1060 unsigned long state
[2];
1061 state
[0] = _wmstate
;
1063 property
->set(_window
, otk::OBProperty::wm_state
, otk::OBProperty::wm_state
,
1069 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_modal
);
1071 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_shaded
);
1073 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_hidden
);
1076 property
->atom(otk::OBProperty::net_wm_state_skip_taskbar
);
1078 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_skip_pager
);
1080 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_fullscreen
);
1083 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
);
1086 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
);
1088 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_above
);
1090 netstate
[num
++] = property
->atom(otk::OBProperty::net_wm_state_below
);
1091 property
->set(_window
, otk::OBProperty::net_wm_state
,
1092 otk::OBProperty::Atom_Atom
, netstate
, num
);
1098 void OBClient::shade(bool shade
)
1100 if (shade
== _shaded
) return; // already done
1102 _wmstate
= shade
? IconicState
: NormalState
;
1105 frame
->adjustSize();
1109 bool OBClient::focus() const
1111 // won't try focus if the client doesn't want it, or if the window isn't
1112 // visible on the screen
1113 if (!(frame
->isVisible() && (_can_focus
|| _focus_notify
))) return false;
1115 if (_focused
) return true;
1118 XSetInputFocus(otk::OBDisplay::display
, _window
,
1119 RevertToNone
, CurrentTime
);
1121 if (_focus_notify
) {
1123 const otk::OBProperty
*property
= Openbox::instance
->property();
1125 ce
.xclient
.type
= ClientMessage
;
1126 ce
.xclient
.message_type
= property
->atom(otk::OBProperty::wm_protocols
);
1127 ce
.xclient
.display
= otk::OBDisplay::display
;
1128 ce
.xclient
.window
= _window
;
1129 ce
.xclient
.format
= 32;
1130 ce
.xclient
.data
.l
[0] = property
->atom(otk::OBProperty::wm_take_focus
);
1131 ce
.xclient
.data
.l
[1] = Openbox::instance
->lastTime();
1132 ce
.xclient
.data
.l
[2] = 0l;
1133 ce
.xclient
.data
.l
[3] = 0l;
1134 ce
.xclient
.data
.l
[4] = 0l;
1135 XSendEvent(otk::OBDisplay::display
, _window
, False
, NoEventMask
, &ce
);
1142 void OBClient::unfocus() const
1144 if (!_focused
) return;
1146 assert(Openbox::instance
->focusedClient() == this);
1147 Openbox::instance
->setFocusedClient(0);
1151 void OBClient::focusHandler(const XFocusChangeEvent
&e
)
1154 // printf("FocusIn for 0x%lx\n", e.window);
1157 OtkEventHandler::focusHandler(e
);
1162 Openbox::instance
->setFocusedClient(this);
1166 void OBClient::unfocusHandler(const XFocusChangeEvent
&e
)
1169 // printf("FocusOut for 0x%lx\n", e.window);
1172 OtkEventHandler::unfocusHandler(e
);
1177 if (Openbox::instance
->focusedClient() == this)
1178 Openbox::instance
->setFocusedClient(0);
1182 void OBClient::configureRequestHandler(const XConfigureRequestEvent
&e
)
1185 printf("ConfigureRequest for 0x%lx\n", e
.window
);
1188 OtkEventHandler::configureRequestHandler(e
);
1190 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1192 if (e
.value_mask
& CWBorderWidth
)
1193 _border_width
= e
.border_width
;
1195 // resize, then move, as specified in the EWMH section 7.7
1196 if (e
.value_mask
& (CWWidth
| CWHeight
)) {
1197 int w
= (e
.value_mask
& CWWidth
) ? e
.width
: _area
.width();
1198 int h
= (e
.value_mask
& CWHeight
) ? e
.height
: _area
.height();
1202 case NorthEastGravity
:
1206 case SouthWestGravity
:
1208 corner
= BottomLeft
;
1210 case SouthEastGravity
:
1211 corner
= BottomRight
;
1213 default: // NorthWest, Static, etc
1217 // if moving AND resizing ...
1218 if (e
.value_mask
& (CWX
| CWY
)) {
1219 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1220 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1221 resize(corner
, w
, h
, x
, y
);
1222 } else // if JUST resizing...
1223 resize(corner
, w
, h
);
1224 } else if (e
.value_mask
& (CWX
| CWY
)) { // if JUST moving...
1225 int x
= (e
.value_mask
& CWX
) ? e
.x
: _area
.x();
1226 int y
= (e
.value_mask
& CWY
) ? e
.y
: _area
.y();
1230 if (e
.value_mask
& CWStackMode
) {
1234 Openbox::instance
->screen(_screen
)->restack(false, this); // lower
1240 Openbox::instance
->screen(_screen
)->restack(true, this); // raise
1247 void OBClient::unmapHandler(const XUnmapEvent
&e
)
1249 if (ignore_unmaps
) {
1251 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e
.window
, e
.event
);
1258 printf("UnmapNotify for 0x%lx\n", e
.window
);
1261 OtkEventHandler::unmapHandler(e
);
1263 // this deletes us etc
1264 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1268 void OBClient::destroyHandler(const XDestroyWindowEvent
&e
)
1271 printf("DestroyNotify for 0x%lx\n", e
.window
);
1274 OtkEventHandler::destroyHandler(e
);
1276 // this deletes us etc
1277 Openbox::instance
->screen(_screen
)->unmanageWindow(this);
1281 void OBClient::reparentHandler(const XReparentEvent
&e
)
1283 // this is when the client is first taken captive in the frame
1284 if (e
.parent
== frame
->plate()) return;
1287 printf("ReparentNotify for 0x%lx\n", e
.window
);
1290 OtkEventHandler::reparentHandler(e
);
1293 This event is quite rare and is usually handled in unmapHandler.
1294 However, if the window is unmapped when the reparent event occurs,
1295 the window manager never sees it because an unmap event is not sent
1296 to an already unmapped window.
1299 // we don't want the reparent event, put it back on the stack for the X
1300 // server to deal with after we unmanage the window
1303 XPutBackEvent(otk::OBDisplay::display
, &ev
);
1305 // this deletes us etc
1306 Openbox::instance
->screen(_screen
)->unmanageWindow(this);