]> Dogcows Code - chaz/openbox/blob - src/client.cc
manages windows that exist before running.
[chaz/openbox] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "client.hh"
8 #include "bbscreen.hh"
9 #include "openbox.hh"
10 #include "otk/display.hh"
11 #include "otk/property.hh"
12
13 extern "C" {
14 #include <X11/Xlib.h>
15 #include <X11/Xutil.h>
16
17 #include <assert.h>
18
19 #include "gettext.h"
20 #define _(str) gettext(str)
21 }
22
23 namespace ob {
24
25 OBClient::OBClient(int screen, Window window)
26 : _screen(screen), _window(window)
27 {
28 assert(window);
29
30 ignore_unmaps = 0;
31
32 // update EVERYTHING the first time!!
33
34 // the state is kinda assumed to be normal. is this right? XXX
35 _wmstate = NormalState;
36 // no default decors or functions, each has to be enabled
37 _decorations = _functions = 0;
38
39 getArea();
40 getDesktop();
41 getType();
42
43 // set the decorations and functions
44 switch (_type) {
45 case Type_Normal:
46 // normal windows retain all of the possible decorations and
47 // functionality
48 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
49 Decor_Iconify | Decor_Maximize;
50 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
51
52 case Type_Dialog:
53 // dialogs cannot be maximized
54 _decorations &= ~Decor_Maximize;
55 _functions &= ~Func_Maximize;
56 break;
57
58 case Type_Menu:
59 case Type_Toolbar:
60 case Type_Utility:
61 // these windows get less functionality
62 _decorations &= ~(Decor_Iconify | Decor_Handle);
63 _functions &= ~(Func_Iconify | Func_Resize);
64 break;
65
66 case Type_Desktop:
67 case Type_Dock:
68 case Type_Splash:
69 // none of these windows are manipulated by the window manager
70 _decorations = 0;
71 _functions = 0;
72 break;
73 }
74
75 getMwmHints(); // this fucks (in good ways) with the decors and functions
76 getState();
77 getShaped();
78
79 updateProtocols();
80 updateNormalHints();
81 updateWMHints();
82 // XXX: updateTransientFor();
83 updateTitle();
84 updateIconTitle();
85 updateClass();
86
87 /*
88 #ifdef DEBUG
89 printf("Mapped window: 0x%lx\n"
90 " title: \t%s\t icon title: \t%s\n"
91 " app name: \t%s\t\t class: \t%s\n"
92 " position: \t%d, %d\t\t size: \t%d, %d\n"
93 " desktop: \t%lu\t\t group: \t0x%lx\n"
94 " type: \t%d\t\t min size \t%d, %d\n"
95 " base size \t%d, %d\t\t max size \t%d, %d\n"
96 " size incr \t%d, %d\t\t gravity \t%d\n"
97 " wm state \t%ld\t\t can be focused:\t%s\n"
98 " notify focus: \t%s\t\t urgent: \t%s\n"
99 " shaped: \t%s\t\t modal: \t%s\n"
100 " shaded: \t%s\t\t iconic: \t%s\n"
101 " vert maximized:\t%s\t\t horz maximized:\t%s\n"
102 " fullscreen: \t%s\t\t floating: \t%s\n"
103 " requested pos: \t%s\n",
104 _window,
105 _title.c_str(),
106 _icon_title.c_str(),
107 _app_name.c_str(),
108 _app_class.c_str(),
109 _area.x(), _area.y(),
110 _area.width(), _area.height(),
111 _desktop,
112 _group,
113 _type,
114 _min_x, _min_y,
115 _base_x, _base_y,
116 _max_x, _max_y,
117 _inc_x, _inc_y,
118 _gravity,
119 _wmstate,
120 _can_focus ? "yes" : "no",
121 _focus_notify ? "yes" : "no",
122 _urgent ? "yes" : "no",
123 _shaped ? "yes" : "no",
124 _modal ? "yes" : "no",
125 _shaded ? "yes" : "no",
126 _iconic ? "yes" : "no",
127 _max_vert ? "yes" : "no",
128 _max_horz ? "yes" : "no",
129 _fullscreen ? "yes" : "no",
130 _floating ? "yes" : "no",
131 _positioned ? "yes" : "no");
132 #endif
133 */
134 }
135
136
137 OBClient::~OBClient()
138 {
139 const otk::OBProperty *property = Openbox::instance->property();
140
141 // these values should not be persisted across a window unmapping/mapping
142 property->erase(_window, otk::OBProperty::net_wm_desktop);
143 property->erase(_window, otk::OBProperty::net_wm_state);
144 }
145
146
147 void OBClient::getDesktop()
148 {
149 const otk::OBProperty *property = Openbox::instance->property();
150
151 // defaults to the current desktop
152 _desktop = 0; // XXX: change this to the current desktop!
153
154 property->get(_window, otk::OBProperty::net_wm_desktop,
155 otk::OBProperty::Atom_Cardinal,
156 &_desktop);
157 }
158
159
160 void OBClient::getType()
161 {
162 const otk::OBProperty *property = Openbox::instance->property();
163
164 _type = (WindowType) -1;
165
166 unsigned long *val;
167 unsigned long num = (unsigned) -1;
168 if (property->get(_window, otk::OBProperty::net_wm_window_type,
169 otk::OBProperty::Atom_Atom,
170 &num, &val)) {
171 // use the first value that we know about in the array
172 for (unsigned long i = 0; i < num; ++i) {
173 if (val[i] ==
174 property->atom(otk::OBProperty::net_wm_window_type_desktop))
175 _type = Type_Desktop;
176 else if (val[i] ==
177 property->atom(otk::OBProperty::net_wm_window_type_dock))
178 _type = Type_Dock;
179 else if (val[i] ==
180 property->atom(otk::OBProperty::net_wm_window_type_toolbar))
181 _type = Type_Toolbar;
182 else if (val[i] ==
183 property->atom(otk::OBProperty::net_wm_window_type_menu))
184 _type = Type_Menu;
185 else if (val[i] ==
186 property->atom(otk::OBProperty::net_wm_window_type_utility))
187 _type = Type_Utility;
188 else if (val[i] ==
189 property->atom(otk::OBProperty::net_wm_window_type_splash))
190 _type = Type_Splash;
191 else if (val[i] ==
192 property->atom(otk::OBProperty::net_wm_window_type_dialog))
193 _type = Type_Dialog;
194 else if (val[i] ==
195 property->atom(otk::OBProperty::net_wm_window_type_normal))
196 _type = Type_Normal;
197 // else if (val[i] ==
198 // property->atom(otk::OBProperty::kde_net_wm_window_type_override))
199 // mwm_decorations = 0; // prevent this window from getting any decor
200 // XXX: make this work again
201 }
202 delete val;
203 }
204
205 if (_type == (WindowType) -1) {
206 /*
207 * the window type hint was not set, which means we either classify ourself
208 * as a normal window or a dialog, depending on if we are a transient.
209 */
210 // XXX: make this code work!
211 //if (isTransient())
212 // _type = Type_Dialog;
213 //else
214 _type = Type_Normal;
215 }
216 }
217
218
219 void OBClient::getMwmHints()
220 {
221 const otk::OBProperty *property = Openbox::instance->property();
222
223 unsigned long num;
224 MwmHints *hints;
225
226 num = MwmHints::elements;
227 if (!property->get(_window, otk::OBProperty::motif_wm_hints,
228 otk::OBProperty::motif_wm_hints, &num,
229 (unsigned long **)&hints))
230 return;
231
232 if (num < MwmHints::elements) {
233 delete [] hints;
234 return;
235 }
236
237 // retrieved the hints
238 // Mwm Hints are applied subtractively to what has already been chosen for
239 // decor and functionality
240
241 if (hints->flags & MwmFlag_Decorations) {
242 if (! (hints->decorations & MwmDecor_All)) {
243 if (! (hints->decorations & MwmDecor_Border))
244 _decorations &= ~Decor_Border;
245 if (! (hints->decorations & MwmDecor_Handle))
246 _decorations &= ~Decor_Handle;
247 if (! (hints->decorations & MwmDecor_Title))
248 _decorations &= ~Decor_Titlebar;
249 if (! (hints->decorations & MwmDecor_Iconify))
250 _decorations &= ~Decor_Iconify;
251 if (! (hints->decorations & MwmDecor_Maximize))
252 _decorations &= ~Decor_Maximize;
253 }
254 }
255
256 if (hints->flags & MwmFlag_Functions) {
257 if (! (hints->functions & MwmFunc_All)) {
258 if (! (hints->functions & MwmFunc_Resize))
259 _functions &= ~Func_Resize;
260 if (! (hints->functions & MwmFunc_Move))
261 _functions &= ~Func_Move;
262 if (! (hints->functions & MwmFunc_Iconify))
263 _functions &= ~Func_Iconify;
264 if (! (hints->functions & MwmFunc_Maximize))
265 _functions &= ~Func_Maximize;
266 //if (! (hints->functions & MwmFunc_Close))
267 // _functions &= ~Func_Close;
268 }
269 }
270 delete [] hints;
271 }
272
273
274 void OBClient::getArea()
275 {
276 XWindowAttributes wattrib;
277 Status ret;
278
279 ret = XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib);
280 assert(ret != BadWindow);
281
282 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
283 _border_width = wattrib.border_width;
284 }
285
286
287 void OBClient::getState()
288 {
289 const otk::OBProperty *property = Openbox::instance->property();
290
291 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _floating = false;
292
293 unsigned long *state;
294 unsigned long num = (unsigned) -1;
295
296 if (property->get(_window, otk::OBProperty::net_wm_state,
297 otk::OBProperty::Atom_Atom, &num, &state)) {
298 for (unsigned long i = 0; i < num; ++i) {
299 if (state[i] == property->atom(otk::OBProperty::net_wm_state_modal))
300 _modal = true;
301 else if (state[i] ==
302 property->atom(otk::OBProperty::net_wm_state_shaded))
303 _shaded = true;
304 else if (state[i] ==
305 property->atom(otk::OBProperty::net_wm_state_fullscreen))
306 _fullscreen = true;
307 else if (state[i] ==
308 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
309 _max_vert = true;
310 else if (state[i] ==
311 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
312 _max_horz = true;
313 }
314
315 delete [] state;
316 }
317 }
318
319
320 void OBClient::getShaped()
321 {
322 _shaped = false;
323 #ifdef SHAPE
324 if (otk::OBDisplay::shape()) {
325 int foo;
326 unsigned int ufoo;
327 int s;
328
329 XShapeSelectInput(otk::OBDisplay::display, _window, ShapeNotifyMask);
330
331 XShapeQueryExtents(otk::OBDisplay::display, _window, &s, &foo,
332 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
333 _shaped = (s != 0);
334 }
335 #endif // SHAPE
336 }
337
338
339 void OBClient::updateProtocols()
340 {
341 const otk::OBProperty *property = Openbox::instance->property();
342
343 Atom *proto;
344 int num_return = 0;
345
346 _focus_notify = false;
347 _decorations &= ~Decor_Close;
348 _functions &= ~Func_Close;
349
350 if (XGetWMProtocols(otk::OBDisplay::display, _window, &proto, &num_return)) {
351 for (int i = 0; i < num_return; ++i) {
352 if (proto[i] == property->atom(otk::OBProperty::wm_delete_window)) {
353 _decorations |= Decor_Close;
354 _functions |= Func_Close;
355 // XXX: update the decor?
356 } else if (proto[i] == property->atom(otk::OBProperty::wm_take_focus))
357 // if this protocol is requested, then the window will be notified
358 // by the window manager whenever it receives focus
359 _focus_notify = true;
360 }
361 XFree(proto);
362 }
363 }
364
365
366 void OBClient::updateNormalHints()
367 {
368 XSizeHints size;
369 long ret;
370
371 // defaults
372 _gravity = NorthWestGravity;
373 _inc_x = _inc_y = 1;
374 _base_x = _base_y = 0;
375 _min_x = _min_y = 0;
376 _max_x = _max_y = INT_MAX;
377
378 // XXX: might want to cancel any interactive resizing of the window at this
379 // point..
380
381 // get the hints from the window
382 if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
383 _positioned = (size.flags & (PPosition|USPosition));
384
385 if (size.flags & PWinGravity)
386 _gravity = size.win_gravity;
387
388 if (size.flags & PMinSize) {
389 _min_x = size.min_width;
390 _min_y = size.min_height;
391 }
392
393 if (size.flags & PMaxSize) {
394 _max_x = size.max_width;
395 _max_y = size.max_height;
396 }
397
398 if (size.flags & PBaseSize) {
399 _base_x = size.base_width;
400 _base_y = size.base_height;
401 }
402
403 if (size.flags & PResizeInc) {
404 _inc_x = size.width_inc;
405 _inc_y = size.height_inc;
406 }
407 }
408 }
409
410
411 void OBClient::updateWMHints()
412 {
413 XWMHints *hints;
414
415 // assume a window takes input if it doesnt specify
416 _can_focus = true;
417 _urgent = false;
418
419 if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
420 if (hints->flags & InputHint)
421 _can_focus = hints->input;
422
423 if (hints->flags & XUrgencyHint)
424 _urgent = true;
425
426 if (hints->flags & WindowGroupHint) {
427 if (hints->window_group != _group) {
428 // XXX: remove from the old group if there was one
429 _group = hints->window_group;
430 // XXX: do stuff with the group
431 }
432 } else // no group!
433 _group = None;
434
435 XFree(hints);
436 }
437 }
438
439
440 void OBClient::updateTitle()
441 {
442 const otk::OBProperty *property = Openbox::instance->property();
443
444 _title = "";
445
446 // try netwm
447 if (! property->get(_window, otk::OBProperty::net_wm_name,
448 otk::OBProperty::utf8, &_title)) {
449 // try old x stuff
450 property->get(_window, otk::OBProperty::wm_name,
451 otk::OBProperty::ascii, &_title);
452 }
453
454 if (_title.empty())
455 _title = _("Unnamed Window");
456 }
457
458
459 void OBClient::updateIconTitle()
460 {
461 const otk::OBProperty *property = Openbox::instance->property();
462
463 _icon_title = "";
464
465 // try netwm
466 if (! property->get(_window, otk::OBProperty::net_wm_icon_name,
467 otk::OBProperty::utf8, &_icon_title)) {
468 // try old x stuff
469 property->get(_window, otk::OBProperty::wm_icon_name,
470 otk::OBProperty::ascii, &_icon_title);
471 }
472
473 if (_title.empty())
474 _icon_title = _("Unnamed Window");
475 }
476
477
478 void OBClient::updateClass()
479 {
480 const otk::OBProperty *property = Openbox::instance->property();
481
482 // set the defaults
483 _app_name = _app_class = "";
484
485 otk::OBProperty::StringVect v;
486 unsigned long num = 2;
487
488 if (! property->get(_window, otk::OBProperty::wm_class,
489 otk::OBProperty::ascii, &num, &v))
490 return;
491
492 if (num > 0) _app_name = v[0];
493 if (num > 1) _app_class = v[1];
494 }
495
496
497 void OBClient::update(const XPropertyEvent &e)
498 {
499 const otk::OBProperty *property = Openbox::instance->property();
500
501 if (e.atom == XA_WM_NORMAL_HINTS)
502 updateNormalHints();
503 else if (e.atom == XA_WM_HINTS)
504 updateWMHints();
505 else if (e.atom == property->atom(otk::OBProperty::net_wm_name) ||
506 e.atom == property->atom(otk::OBProperty::wm_name))
507 updateTitle();
508 else if (e.atom == property->atom(otk::OBProperty::net_wm_icon_name) ||
509 e.atom == property->atom(otk::OBProperty::wm_icon_name))
510 updateIconTitle();
511 else if (e.atom == property->atom(otk::OBProperty::wm_class))
512 updateClass();
513 else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
514 updateProtocols();
515 // XXX: transient for hint
516 // XXX: strut hint
517 }
518
519
520 void OBClient::setWMState(long state)
521 {
522 if (state == _wmstate) return; // no change
523
524 switch (state) {
525 case IconicState:
526 // XXX: cause it to iconify
527 break;
528 case NormalState:
529 // XXX: cause it to uniconify
530 break;
531 }
532 _wmstate = state;
533 }
534
535
536 void OBClient::setDesktop(long target)
537 {
538 assert(target >= 0);
539 //assert(target == 0xffffffff || target < MAX);
540
541 // XXX: move the window to the new desktop
542 _desktop = target;
543 }
544
545
546 void OBClient::setState(StateAction action, long data1, long data2)
547 {
548 const otk::OBProperty *property = Openbox::instance->property();
549
550 if (!(action == State_Add || action == State_Remove ||
551 action == State_Toggle))
552 return; // an invalid action was passed to the client message, ignore it
553
554 for (int i = 0; i < 2; ++i) {
555 Atom state = i == 0 ? data1 : data2;
556
557 if (! state) continue;
558
559 // if toggling, then pick whether we're adding or removing
560 if (action == State_Toggle) {
561 if (state == property->atom(otk::OBProperty::net_wm_state_modal))
562 action = _modal ? State_Remove : State_Add;
563 else if (state ==
564 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
565 action = _max_vert ? State_Remove : State_Add;
566 else if (state ==
567 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
568 action = _max_horz ? State_Remove : State_Add;
569 else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
570 action = _shaded ? State_Remove : State_Add;
571 else if (state ==
572 property->atom(otk::OBProperty::net_wm_state_fullscreen))
573 action = _fullscreen ? State_Remove : State_Add;
574 else if (state == property->atom(otk::OBProperty::net_wm_state_floating))
575 action = _floating ? State_Remove : State_Add;
576 }
577
578 if (action == State_Add) {
579 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
580 if (_modal) continue;
581 _modal = true;
582 // XXX: give it focus if another window has focus that shouldnt now
583 } else if (state ==
584 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
585 if (_max_vert) continue;
586 _max_vert = true;
587 // XXX: resize the window etc
588 } else if (state ==
589 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
590 if (_max_horz) continue;
591 _max_horz = true;
592 // XXX: resize the window etc
593 } else if (state ==
594 property->atom(otk::OBProperty::net_wm_state_shaded)) {
595 if (_shaded) continue;
596 _shaded = true;
597 // XXX: hide the client window
598 } else if (state ==
599 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
600 if (_fullscreen) continue;
601 _fullscreen = true;
602 // XXX: raise the window n shit
603 } else if (state ==
604 property->atom(otk::OBProperty::net_wm_state_floating)) {
605 if (_floating) continue;
606 _floating = true;
607 // XXX: raise the window n shit
608 }
609
610 } else { // action == State_Remove
611 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
612 if (!_modal) continue;
613 _modal = false;
614 } else if (state ==
615 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
616 if (!_max_vert) continue;
617 _max_vert = false;
618 // XXX: resize the window etc
619 } else if (state ==
620 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
621 if (!_max_horz) continue;
622 _max_horz = false;
623 // XXX: resize the window etc
624 } else if (state ==
625 property->atom(otk::OBProperty::net_wm_state_shaded)) {
626 if (!_shaded) continue;
627 _shaded = false;
628 // XXX: show the client window
629 } else if (state ==
630 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
631 if (!_fullscreen) continue;
632 _fullscreen = false;
633 // XXX: lower the window to its proper layer
634 } else if (state ==
635 property->atom(otk::OBProperty::net_wm_state_floating)) {
636 if (!_floating) continue;
637 _floating = false;
638 // XXX: lower the window to its proper layer
639 }
640 }
641 }
642 }
643
644
645 void OBClient::update(const XClientMessageEvent &e)
646 {
647 if (e.format != 32) return;
648
649 const otk::OBProperty *property = Openbox::instance->property();
650
651 if (e.message_type == property->atom(otk::OBProperty::wm_change_state))
652 setWMState(e.data.l[0]);
653 else if (e.message_type ==
654 property->atom(otk::OBProperty::net_wm_desktop))
655 setDesktop(e.data.l[0]);
656 else if (e.message_type == property->atom(otk::OBProperty::net_wm_state))
657 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
658 }
659
660
661 #if defined(SHAPE) || defined(DOXYGEN_IGNORE)
662 void OBClient::update(const XShapeEvent &e)
663 {
664 _shaped = e.shaped;
665 }
666 #endif
667
668
669 void OBClient::setArea(const otk::Rect &area)
670 {
671 _area = area;
672 }
673
674 }
This page took 0.066744 seconds and 4 git commands to generate.