]> Dogcows Code - chaz/openbox/blob - src/client.cc
6b67f3c290aa9f1a3909169b84bb2fa2238f2de1
[chaz/openbox] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "client.hh"
4 #include "screen.hh"
5 #include "openbox.hh"
6 #include "otk/display.hh"
7 #include "otk/property.hh"
8
9 extern "C" {
10 #include <X11/Xlib.h>
11 #include <X11/Xutil.h>
12
13 #include <assert.h>
14
15 #include "gettext.h"
16 #define _(str) gettext(str)
17 }
18
19 namespace ob {
20
21 OBClient::OBClient(Window window)
22 : _window(window)
23 {
24 assert(window);
25
26 // update EVERYTHING the first time!!
27
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;
32
33 getArea();
34 getDesktop();
35 getType();
36
37 // set the decorations and functions
38 switch (_type) {
39 case Type_Normal:
40 // normal windows retain all of the possible decorations and
41 // functionality
42 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
43 Decor_Iconify | Decor_Maximize;
44 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
45
46 case Type_Dialog:
47 // dialogs cannot be maximized
48 _decorations &= ~Decor_Maximize;
49 _functions &= ~Func_Maximize;
50 break;
51
52 case Type_Menu:
53 case Type_Toolbar:
54 case Type_Utility:
55 // these windows get less functionality
56 _decorations &= ~(Decor_Iconify | Decor_Handle);
57 _functions &= ~(Func_Iconify | Func_Resize);
58 break;
59
60 case Type_Desktop:
61 case Type_Dock:
62 case Type_Splash:
63 // none of these windows are manipulated by the window manager
64 _decorations = 0;
65 _functions = 0;
66 break;
67 }
68
69 getMwmHints(); // this fucks (in good ways) with the decors and functions
70 getState();
71 getShaped();
72
73 updateProtocols();
74 updateNormalHints();
75 updateWMHints();
76 // XXX: updateTransientFor();
77 updateTitle();
78 updateClass();
79
80 #ifdef DEBUG
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",
96 _window,
97 _title.c_str(),
98 _icon_title.c_str(),
99 _app_name.c_str(),
100 _app_class.c_str(),
101 _area.x(), _area.y(),
102 _area.width(), _area.height(),
103 _desktop,
104 _group,
105 _type,
106 _min_x, _min_y,
107 _base_x, _base_y,
108 _max_x, _max_y,
109 _inc_x, _inc_y,
110 _gravity,
111 _wmstate,
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");
124 #endif
125 }
126
127
128 OBClient::~OBClient()
129 {
130 const otk::OBProperty *property = Openbox::instance->property();
131
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);
135 }
136
137
138 void OBClient::getDesktop()
139 {
140 const otk::OBProperty *property = Openbox::instance->property();
141
142 // defaults to the current desktop
143 _desktop = 0; // XXX: change this to the current desktop!
144
145 property->get(_window, otk::OBProperty::net_wm_desktop,
146 otk::OBProperty::Atom_Cardinal,
147 &_desktop);
148 }
149
150
151 void OBClient::getType()
152 {
153 const otk::OBProperty *property = Openbox::instance->property();
154
155 _type = (WindowType) -1;
156
157 unsigned long *val;
158 unsigned long num = (unsigned) -1;
159 if (property->get(_window, otk::OBProperty::net_wm_window_type,
160 otk::OBProperty::Atom_Atom,
161 &num, &val)) {
162 // use the first value that we know about in the array
163 for (unsigned long i = 0; i < num; ++i) {
164 if (val[i] ==
165 property->atom(otk::OBProperty::net_wm_window_type_desktop))
166 _type = Type_Desktop;
167 else if (val[i] ==
168 property->atom(otk::OBProperty::net_wm_window_type_dock))
169 _type = Type_Dock;
170 else if (val[i] ==
171 property->atom(otk::OBProperty::net_wm_window_type_toolbar))
172 _type = Type_Toolbar;
173 else if (val[i] ==
174 property->atom(otk::OBProperty::net_wm_window_type_menu))
175 _type = Type_Menu;
176 else if (val[i] ==
177 property->atom(otk::OBProperty::net_wm_window_type_utility))
178 _type = Type_Utility;
179 else if (val[i] ==
180 property->atom(otk::OBProperty::net_wm_window_type_splash))
181 _type = Type_Splash;
182 else if (val[i] ==
183 property->atom(otk::OBProperty::net_wm_window_type_dialog))
184 _type = Type_Dialog;
185 else if (val[i] ==
186 property->atom(otk::OBProperty::net_wm_window_type_normal))
187 _type = 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
192 }
193 delete val;
194 }
195
196 if (_type == (WindowType) -1) {
197 /*
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.
200 */
201 // XXX: make this code work!
202 //if (isTransient())
203 // _type = Type_Dialog;
204 //else
205 _type = Type_Normal;
206 }
207 }
208
209
210 void OBClient::getMwmHints()
211 {
212 const otk::OBProperty *property = Openbox::instance->property();
213
214 unsigned long num;
215 MwmHints *hints;
216
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))
221 return;
222
223 if (num < MwmHints::elements) {
224 delete [] hints;
225 return;
226 }
227
228 // retrieved the hints
229 // Mwm Hints are applied subtractively to what has already been chosen for
230 // decor and functionality
231
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;
244 }
245 }
246
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;
259 }
260 }
261 delete [] hints;
262 }
263
264
265 void OBClient::getArea()
266 {
267 XWindowAttributes wattrib;
268 assert(XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib));
269
270 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
271 _border_width = wattrib.border_width;
272 }
273
274
275 void OBClient::getState()
276 {
277 const otk::OBProperty *property = Openbox::instance->property();
278
279 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _floating = false;
280
281 unsigned long *state;
282 unsigned long num = (unsigned) -1;
283
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))
288 _modal = true;
289 else if (state[i] ==
290 property->atom(otk::OBProperty::net_wm_state_shaded))
291 _shaded = true;
292 else if (state[i] ==
293 property->atom(otk::OBProperty::net_wm_state_fullscreen))
294 _fullscreen = true;
295 else if (state[i] ==
296 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
297 _max_vert = true;
298 else if (state[i] ==
299 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
300 _max_horz = true;
301 }
302
303 delete [] state;
304 }
305 }
306
307
308 void OBClient::getShaped()
309 {
310 _shaped = false;
311 #ifdef SHAPE
312 if (otk::OBDisplay::shape()) {
313 int foo;
314 unsigned int ufoo;
315
316 XShapeQueryExtents(otk::OBDisplay::display, client.window, &_shaped, &foo,
317 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
318 }
319 #endif // SHAPE
320 }
321
322
323 void OBClient::updateProtocols()
324 {
325 const otk::OBProperty *property = Openbox::instance->property();
326
327 Atom *proto;
328 int num_return = 0;
329
330 _focus_notify = false;
331
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;
342 }
343 XFree(proto);
344 }
345 }
346
347
348 void OBClient::updateNormalHints()
349 {
350 XSizeHints size;
351 long ret;
352
353 // defaults
354 _gravity = NorthWestGravity;
355 _inc_x = _inc_y = 1;
356 _base_x = _base_y = 0;
357 _min_x = _min_y = 0;
358 _max_x = _max_y = INT_MAX;
359
360 // XXX: might want to cancel any interactive resizing of the window at this
361 // point..
362
363 // get the hints from the window
364 if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
365 _positioned = (size.flags & (PPosition|USPosition));
366
367 if (size.flags & PWinGravity)
368 _gravity = size.win_gravity;
369
370 if (size.flags & PMinSize) {
371 _min_x = size.min_width;
372 _min_y = size.min_height;
373 }
374
375 if (size.flags & PMaxSize) {
376 _max_x = size.max_width;
377 _max_y = size.max_height;
378 }
379
380 if (size.flags & PBaseSize) {
381 _base_x = size.base_width;
382 _base_y = size.base_height;
383 }
384
385 if (size.flags & PResizeInc) {
386 _inc_x = size.width_inc;
387 _inc_y = size.height_inc;
388 }
389 }
390 }
391
392
393 void OBClient::updateWMHints()
394 {
395 XWMHints *hints;
396
397 // assume a window takes input if it doesnt specify
398 _can_focus = true;
399 _urgent = false;
400
401 if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
402 if (hints->flags & InputHint)
403 _can_focus = hints->input;
404
405 if (hints->flags & XUrgencyHint)
406 _urgent = true;
407
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
413 }
414 } else // no group!
415 _group = None;
416
417 XFree(hints);
418 }
419 }
420
421
422 void OBClient::updateTitle()
423 {
424 const otk::OBProperty *property = Openbox::instance->property();
425
426 _title = "";
427
428 // try netwm
429 if (! property->get(_window, otk::OBProperty::net_wm_name,
430 otk::OBProperty::utf8, &_title)) {
431 // try old x stuff
432 property->get(_window, otk::OBProperty::wm_name,
433 otk::OBProperty::ascii, &_title);
434 }
435
436 if (_title.empty())
437 _title = _("Unnamed Window");
438 }
439
440
441 void OBClient::updateClass()
442 {
443 const otk::OBProperty *property = Openbox::instance->property();
444
445 // set the defaults
446 _app_name = _app_class = "";
447
448 otk::OBProperty::StringVect v;
449 unsigned long num = 2;
450
451 if (! property->get(_window, otk::OBProperty::wm_class,
452 otk::OBProperty::ascii, &num, &v))
453 return;
454
455 if (num > 0) _app_name = v[0];
456 if (num > 1) _app_class = v[1];
457 }
458
459
460 void OBClient::update(const XPropertyEvent &e)
461 {
462 const otk::OBProperty *property = Openbox::instance->property();
463
464 if (e.atom == XA_WM_NORMAL_HINTS)
465 updateNormalHints();
466 else if (e.atom == XA_WM_HINTS)
467 updateWMHints();
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))
472 updateTitle();
473 else if (e.atom == property->atom(otk::OBProperty::wm_class))
474 updateClass();
475 else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
476 updateProtocols();
477 // XXX: transient for hint
478 // XXX: strut hint
479 }
480
481
482 void OBClient::setWMState(long state)
483 {
484 if (state == _wmstate) return; // no change
485
486 switch (state) {
487 case IconicState:
488 // XXX: cause it to iconify
489 break;
490 case NormalState:
491 // XXX: cause it to uniconify
492 break;
493 }
494 _wmstate = state;
495 }
496
497
498 void OBClient::setDesktop(long target)
499 {
500 assert(target >= 0);
501 //assert(target == 0xffffffff || target < MAX);
502
503 // XXX: move the window to the new desktop
504 _desktop = target;
505 }
506
507
508 void OBClient::setState(StateAction action, long data1, long data2)
509 {
510 const otk::OBProperty *property = Openbox::instance->property();
511
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
515
516 for (int i = 0; i < 2; ++i) {
517 Atom state = i == 0 ? data1 : data2;
518
519 if (! state) continue;
520
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;
525 else if (state ==
526 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
527 action = _max_vert ? State_Remove : State_Add;
528 else if (state ==
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;
533 else if (state ==
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;
538 }
539
540 if (action == State_Add) {
541 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
542 if (_modal) continue;
543 _modal = true;
544 // XXX: give it focus if another window has focus that shouldnt now
545 } else if (state ==
546 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
547 if (_max_vert) continue;
548 _max_vert = true;
549 // XXX: resize the window etc
550 } else if (state ==
551 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
552 if (_max_horz) continue;
553 _max_horz = true;
554 // XXX: resize the window etc
555 } else if (state ==
556 property->atom(otk::OBProperty::net_wm_state_shaded)) {
557 if (_shaded) continue;
558 _shaded = true;
559 // XXX: hide the client window
560 } else if (state ==
561 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
562 if (_fullscreen) continue;
563 _fullscreen = true;
564 // XXX: raise the window n shit
565 } else if (state ==
566 property->atom(otk::OBProperty::net_wm_state_floating)) {
567 if (_floating) continue;
568 _floating = true;
569 // XXX: raise the window n shit
570 }
571
572 } else { // action == State_Remove
573 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
574 if (!_modal) continue;
575 _modal = false;
576 } else if (state ==
577 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
578 if (!_max_vert) continue;
579 _max_vert = false;
580 // XXX: resize the window etc
581 } else if (state ==
582 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
583 if (!_max_horz) continue;
584 _max_horz = false;
585 // XXX: resize the window etc
586 } else if (state ==
587 property->atom(otk::OBProperty::net_wm_state_shaded)) {
588 if (!_shaded) continue;
589 _shaded = false;
590 // XXX: show the client window
591 } else if (state ==
592 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
593 if (!_fullscreen) continue;
594 _fullscreen = false;
595 // XXX: lower the window to its proper layer
596 } else if (state ==
597 property->atom(otk::OBProperty::net_wm_state_floating)) {
598 if (!_floating) continue;
599 _floating = false;
600 // XXX: lower the window to its proper layer
601 }
602 }
603 }
604 }
605
606
607 void OBClient::update(const XClientMessageEvent &e)
608 {
609 if (e.format != 32) return;
610
611 const otk::OBProperty *property = Openbox::instance->property();
612
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]);
620 }
621
622
623 void OBClient::setArea(const otk::Rect &area)
624 {
625 _area = area;
626 }
627
628 }
This page took 0.067027 seconds and 4 git commands to generate.