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)