1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
6 #include "otk/display.hh"
7 #include "otk/property.hh"
11 #include <X11/Xutil.h>
16 #define _(str) gettext(str)
21 OBClient::OBClient(Window window
)
26 // update EVERYTHING the first time!!
28 // the state is kinda assumed to be normal. is this right? XXX
29 _wmstate
= NormalState
;
30 // no default decors or functions, each has to be enabled
31 _decorations
= _functions
= 0;
37 // set the decorations and functions
40 // normal windows retain all of the possible decorations and
42 _decorations
= Decor_Titlebar
| Decor_Handle
| Decor_Border
|
43 Decor_Iconify
| Decor_Maximize
;
44 _functions
= Func_Resize
| Func_Move
| Func_Iconify
| Func_Maximize
;
47 // dialogs cannot be maximized
48 _decorations
&= ~Decor_Maximize
;
49 _functions
&= ~Func_Maximize
;
55 // these windows get less functionality
56 _decorations
&= ~(Decor_Iconify
| Decor_Handle
);
57 _functions
&= ~(Func_Iconify
| Func_Resize
);
63 // none of these windows are manipulated by the window manager
69 getMwmHints(); // this fucks (in good ways) with the decors and functions
76 // XXX: updateTransientFor();
81 printf("Mapped window: 0x%lx\n"
82 " title: \t%s\t icon title: \t%s\n"
83 " app name: \t%s\t\t class: \t%s\n"
84 " position: \t%d, %d\t\t size: \t%d, %d\n"
85 " desktop: \t%lu\t\t group: \t0x%lx\n"
86 " type: \t%d\t\t min size \t%d, %d\n"
87 " base size \t%d, %d\t\t max size \t%d, %d\n"
88 " size incr \t%d, %d\t\t gravity \t%d\n"
89 " wm state \t%ld\t\t can be focused:\t%s\n"
90 " notify focus: \t%s\t\t urgent: \t%s\n"
91 " shaped: \t%s\t\t modal: \t%s\n"
92 " shaded: \t%s\t\t iconic: \t%s\n"
93 " vert maximized:\t%s\t\t horz maximized:\t%s\n"
94 " fullscreen: \t%s\t\t floating: \t%s\n"
95 " requested pos: \t%s\n",
101 _area
.x(), _area
.y(),
102 _area
.width(), _area
.height(),
112 _can_focus
? "yes" : "no",
113 _focus_notify
? "yes" : "no",
114 _urgent
? "yes" : "no",
115 _shaped
? "yes" : "no",
116 _modal
? "yes" : "no",
117 _shaded
? "yes" : "no",
118 _iconic
? "yes" : "no",
119 _max_vert
? "yes" : "no",
120 _max_horz
? "yes" : "no",
121 _fullscreen
? "yes" : "no",
122 _floating
? "yes" : "no",
123 _positioned
? "yes" : "no");
128 OBClient::~OBClient()
130 const otk::OBProperty
*property
= Openbox::instance
->property();
132 // these values should not be persisted across a window unmapping/mapping
133 property
->erase(_window
, otk::OBProperty::net_wm_desktop
);
134 property
->erase(_window
, otk::OBProperty::net_wm_state
);
138 void OBClient::getDesktop()
140 const otk::OBProperty
*property
= Openbox::instance
->property();
142 // defaults to the current desktop
143 _desktop
= 0; // XXX: change this to the current desktop!
145 property
->get(_window
, otk::OBProperty::net_wm_desktop
,
146 otk::OBProperty::Atom_Cardinal
,
151 void OBClient::getType()
153 const otk::OBProperty
*property
= Openbox::instance
->property();
155 _type
= (WindowType
) -1;
158 unsigned long num
= (unsigned) -1;
159 if (property
->get(_window
, otk::OBProperty::net_wm_window_type
,
160 otk::OBProperty::Atom_Atom
,
162 // use the first value that we know about in the array
163 for (unsigned long i
= 0; i
< num
; ++i
) {
165 property
->atom(otk::OBProperty::net_wm_window_type_desktop
))
166 _type
= Type_Desktop
;
168 property
->atom(otk::OBProperty::net_wm_window_type_dock
))
171 property
->atom(otk::OBProperty::net_wm_window_type_toolbar
))
172 _type
= Type_Toolbar
;
174 property
->atom(otk::OBProperty::net_wm_window_type_menu
))
177 property
->atom(otk::OBProperty::net_wm_window_type_utility
))
178 _type
= Type_Utility
;
180 property
->atom(otk::OBProperty::net_wm_window_type_splash
))
183 property
->atom(otk::OBProperty::net_wm_window_type_dialog
))
186 property
->atom(otk::OBProperty::net_wm_window_type_normal
))
188 // else if (val[i] ==
189 // property->atom(otk::OBProperty::kde_net_wm_window_type_override))
190 // mwm_decorations = 0; // prevent this window from getting any decor
191 // XXX: make this work again
196 if (_type
== (WindowType
) -1) {
198 * the window type hint was not set, which means we either classify ourself
199 * as a normal window or a dialog, depending on if we are a transient.
201 // XXX: make this code work!
203 // _type = Type_Dialog;
210 void OBClient::getMwmHints()
212 const otk::OBProperty
*property
= Openbox::instance
->property();
217 num
= MwmHints::elements
;
218 if (!property
->get(_window
, otk::OBProperty::motif_wm_hints
,
219 otk::OBProperty::motif_wm_hints
, &num
,
220 (unsigned long **)&hints
))
223 if (num
< MwmHints::elements
) {
228 // retrieved the hints
229 // Mwm Hints are applied subtractively to what has already been chosen for
230 // decor and functionality
232 if (hints
->flags
& MwmFlag_Decorations
) {
233 if (! (hints
->decorations
& MwmDecor_All
)) {
234 if (! (hints
->decorations
& MwmDecor_Border
))
235 _decorations
&= ~Decor_Border
;
236 if (! (hints
->decorations
& MwmDecor_Handle
))
237 _decorations
&= ~Decor_Handle
;
238 if (! (hints
->decorations
& MwmDecor_Title
))
239 _decorations
&= ~Decor_Titlebar
;
240 if (! (hints
->decorations
& MwmDecor_Iconify
))
241 _decorations
&= ~Decor_Iconify
;
242 if (! (hints
->decorations
& MwmDecor_Maximize
))
243 _decorations
&= ~Decor_Maximize
;
247 if (hints
->flags
& MwmFlag_Functions
) {
248 if (! (hints
->functions
& MwmFunc_All
)) {
249 if (! (hints
->functions
& MwmFunc_Resize
))
250 _functions
&= ~Func_Resize
;
251 if (! (hints
->functions
& MwmFunc_Move
))
252 _functions
&= ~Func_Move
;
253 if (! (hints
->functions
& MwmFunc_Iconify
))
254 _functions
&= ~Func_Iconify
;
255 if (! (hints
->functions
& MwmFunc_Maximize
))
256 _functions
&= ~Func_Maximize
;
257 //if (! (hints->functions & MwmFunc_Close))
258 // _functions &= ~Func_Close;
265 void OBClient::getArea()
267 XWindowAttributes wattrib
;
268 assert(XGetWindowAttributes(otk::OBDisplay::display
, _window
, &wattrib
));
270 _area
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
271 _border_width
= wattrib
.border_width
;
275 void OBClient::getState()
277 const otk::OBProperty
*property
= Openbox::instance
->property();
279 _modal
= _shaded
= _max_horz
= _max_vert
= _fullscreen
= _floating
= false;
281 unsigned long *state
;
282 unsigned long num
= (unsigned) -1;
284 if (property
->get(_window
, otk::OBProperty::net_wm_state
,
285 otk::OBProperty::Atom_Atom
, &num
, &state
)) {
286 for (unsigned long i
= 0; i
< num
; ++i
) {
287 if (state
[i
] == property
->atom(otk::OBProperty::net_wm_state_modal
))
290 property
->atom(otk::OBProperty::net_wm_state_shaded
))
293 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
296 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
299 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
308 void OBClient::getShaped()
312 if (otk::OBDisplay::shape()) {
316 XShapeQueryExtents(otk::OBDisplay::display
, client
.window
, &_shaped
, &foo
,
317 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
, &ufoo
);
323 void OBClient::updateProtocols()
325 const otk::OBProperty
*property
= Openbox::instance
->property();
330 _focus_notify
= false;
332 if (XGetWMProtocols(otk::OBDisplay::display
, _window
, &proto
, &num_return
)) {
333 for (int i
= 0; i
< num_return
; ++i
) {
334 if (proto
[i
] == property
->atom(otk::OBProperty::wm_delete_window
)) {
335 _decorations
|= Decor_Close
;
336 _functions
|= Func_Close
;
337 // XXX: update the decor?
338 } else if (proto
[i
] == property
->atom(otk::OBProperty::wm_take_focus
))
339 // if this protocol is requested, then the window will be notified
340 // by the window manager whenever it receives focus
341 _focus_notify
= true;
348 void OBClient::updateNormalHints()
354 _gravity
= NorthWestGravity
;
356 _base_x
= _base_y
= 0;
358 _max_x
= _max_y
= INT_MAX
;
360 // XXX: might want to cancel any interactive resizing of the window at this
363 // get the hints from the window
364 if (XGetWMNormalHints(otk::OBDisplay::display
, _window
, &size
, &ret
)) {
365 _positioned
= (size
.flags
& (PPosition
|USPosition
));
367 if (size
.flags
& PWinGravity
)
368 _gravity
= size
.win_gravity
;
370 if (size
.flags
& PMinSize
) {
371 _min_x
= size
.min_width
;
372 _min_y
= size
.min_height
;
375 if (size
.flags
& PMaxSize
) {
376 _max_x
= size
.max_width
;
377 _max_y
= size
.max_height
;
380 if (size
.flags
& PBaseSize
) {
381 _base_x
= size
.base_width
;
382 _base_y
= size
.base_height
;
385 if (size
.flags
& PResizeInc
) {
386 _inc_x
= size
.width_inc
;
387 _inc_y
= size
.height_inc
;
393 void OBClient::updateWMHints()
397 // assume a window takes input if it doesnt specify
401 if ((hints
= XGetWMHints(otk::OBDisplay::display
, _window
)) != NULL
) {
402 if (hints
->flags
& InputHint
)
403 _can_focus
= hints
->input
;
405 if (hints
->flags
& XUrgencyHint
)
408 if (hints
->flags
& WindowGroupHint
) {
409 if (hints
->window_group
!= _group
) {
410 // XXX: remove from the old group if there was one
411 _group
= hints
->window_group
;
412 // XXX: do stuff with the group
422 void OBClient::updateTitle()
424 const otk::OBProperty
*property
= Openbox::instance
->property();
429 if (! property
->get(_window
, otk::OBProperty::net_wm_name
,
430 otk::OBProperty::utf8
, &_title
)) {
432 property
->get(_window
, otk::OBProperty::wm_name
,
433 otk::OBProperty::ascii
, &_title
);
437 _title
= _("Unnamed Window");
441 void OBClient::updateClass()
443 const otk::OBProperty
*property
= Openbox::instance
->property();
446 _app_name
= _app_class
= "";
448 otk::OBProperty::StringVect v
;
449 unsigned long num
= 2;
451 if (! property
->get(_window
, otk::OBProperty::wm_class
,
452 otk::OBProperty::ascii
, &num
, &v
))
455 if (num
> 0) _app_name
= v
[0];
456 if (num
> 1) _app_class
= v
[1];
460 void OBClient::update(const XPropertyEvent
&e
)
462 const otk::OBProperty
*property
= Openbox::instance
->property();
464 if (e
.atom
== XA_WM_NORMAL_HINTS
)
466 else if (e
.atom
== XA_WM_HINTS
)
468 else if (e
.atom
== property
->atom(otk::OBProperty::net_wm_name
) ||
469 e
.atom
== property
->atom(otk::OBProperty::wm_name
) ||
470 e
.atom
== property
->atom(otk::OBProperty::net_wm_icon_name
) ||
471 e
.atom
== property
->atom(otk::OBProperty::wm_icon_name
))
473 else if (e
.atom
== property
->atom(otk::OBProperty::wm_class
))
475 else if (e
.atom
== property
->atom(otk::OBProperty::wm_protocols
))
477 // XXX: transient for hint
482 void OBClient::setWMState(long state
)
484 if (state
== _wmstate
) return; // no change
488 // XXX: cause it to iconify
491 // XXX: cause it to uniconify
498 void OBClient::setDesktop(long target
)
501 //assert(target == 0xffffffff || target < MAX);
503 // XXX: move the window to the new desktop
508 void OBClient::setState(StateAction action
, long data1
, long data2
)
510 const otk::OBProperty
*property
= Openbox::instance
->property();
512 if (!(action
== State_Add
|| action
== State_Remove
||
513 action
== State_Toggle
))
514 return; // an invalid action was passed to the client message, ignore it
516 for (int i
= 0; i
< 2; ++i
) {
517 Atom state
= i
== 0 ? data1
: data2
;
519 if (! state
) continue;
521 // if toggling, then pick whether we're adding or removing
522 if (action
== State_Toggle
) {
523 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
))
524 action
= _modal
? State_Remove
: State_Add
;
526 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
))
527 action
= _max_vert
? State_Remove
: State_Add
;
529 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
))
530 action
= _max_horz
? State_Remove
: State_Add
;
531 else if (state
== property
->atom(otk::OBProperty::net_wm_state_shaded
))
532 action
= _shaded
? State_Remove
: State_Add
;
534 property
->atom(otk::OBProperty::net_wm_state_fullscreen
))
535 action
= _fullscreen
? State_Remove
: State_Add
;
536 else if (state
== property
->atom(otk::OBProperty::net_wm_state_floating
))
537 action
= _floating
? State_Remove
: State_Add
;
540 if (action
== State_Add
) {
541 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
542 if (_modal
) continue;
544 // XXX: give it focus if another window has focus that shouldnt now
546 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
547 if (_max_vert
) continue;
549 // XXX: resize the window etc
551 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
552 if (_max_horz
) continue;
554 // XXX: resize the window etc
556 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
557 if (_shaded
) continue;
559 // XXX: hide the client window
561 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
562 if (_fullscreen
) continue;
564 // XXX: raise the window n shit
566 property
->atom(otk::OBProperty::net_wm_state_floating
)) {
567 if (_floating
) continue;
569 // XXX: raise the window n shit
572 } else { // action == State_Remove
573 if (state
== property
->atom(otk::OBProperty::net_wm_state_modal
)) {
574 if (!_modal
) continue;
577 property
->atom(otk::OBProperty::net_wm_state_maximized_vert
)){
578 if (!_max_vert
) continue;
580 // XXX: resize the window etc
582 property
->atom(otk::OBProperty::net_wm_state_maximized_horz
)){
583 if (!_max_horz
) continue;
585 // XXX: resize the window etc
587 property
->atom(otk::OBProperty::net_wm_state_shaded
)) {
588 if (!_shaded
) continue;
590 // XXX: show the client window
592 property
->atom(otk::OBProperty::net_wm_state_fullscreen
)) {
593 if (!_fullscreen
) continue;
595 // XXX: lower the window to its proper layer
597 property
->atom(otk::OBProperty::net_wm_state_floating
)) {
598 if (!_floating
) continue;
600 // XXX: lower the window to its proper layer
607 void OBClient::update(const XClientMessageEvent
&e
)
609 if (e
.format
!= 32) return;
611 const otk::OBProperty
*property
= Openbox::instance
->property();
613 if (e
.message_type
== property
->atom(otk::OBProperty::wm_change_state
))
614 setWMState(e
.data
.l
[0]);
615 else if (e
.message_type
==
616 property
->atom(otk::OBProperty::net_wm_desktop
))
617 setDesktop(e
.data
.l
[0]);
618 else if (e
.message_type
== property
->atom(otk::OBProperty::net_wm_state
))
619 setState((StateAction
)e
.data
.l
[0], e
.data
.l
[1], e
.data
.l
[2]);
623 void OBClient::setArea(const otk::Rect
&area
)