]> Dogcows Code - chaz/openbox/blob - src/client.cc
8103aafb9d80279aea17d6192a85dc52ce566f0f
[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
31 getArea();
32 getDesktop();
33 getType();
34 getState();
35 getShaped();
36
37 updateNormalHints();
38 updateWMHints();
39 // XXX: updateTransientFor();
40 updateTitle();
41 updateClass();
42
43 #ifdef DEBUG
44 printf("Mapped window: 0x%lx\n"
45 " title: %s\n"
46 " icon title: %s\n"
47 " app name: %s\n"
48 " class: %s\n"
49 " position: %d, %d\n"
50 " size: %d, %d\n"
51 " desktop: %lu\n"
52 " group: 0x%lx\n"
53 " type: %d\n"
54 " min size %d, %d\n"
55 " base size %d, %d\n"
56 " max size %d, %d\n"
57 " size incr %d, %d\n"
58 " gravity %d\n"
59 " wm state %ld\n"
60 " can be focused: %s\n"
61 " notify focus: %s\n"
62 " urgent: %s\n"
63 " shaped: %s\n"
64 " modal: %s\n"
65 " shaded: %s\n"
66 " iconic: %s\n"
67 " vert maximized: %s\n"
68 " horz maximized: %s\n"
69 " fullscreen: %s\n"
70 " floating: %s\n",
71 _window,
72 _title.c_str(),
73 _icon_title.c_str(),
74 _app_name.c_str(),
75 _app_class.c_str(),
76 _area.x(), _area.y(),
77 _area.width(), _area.height(),
78 _desktop,
79 _group,
80 _type,
81 _min_x, _min_y,
82 _base_x, _base_y,
83 _max_x, _max_y,
84 _inc_x, _inc_y,
85 _gravity,
86 _wmstate,
87 _can_focus ? "yes" : "no",
88 _focus_notify ? "yes" : "no",
89 _urgent ? "yes" : "no",
90 _shaped ? "yes" : "no",
91 _modal ? "yes" : "no",
92 _shaded ? "yes" : "no",
93 _iconic ? "yes" : "no",
94 _max_vert ? "yes" : "no",
95 _max_horz ? "yes" : "no",
96 _fullscreen ? "yes" : "no",
97 _floating ? "yes" : "no");
98 #endif
99 }
100
101
102 OBClient::~OBClient()
103 {
104 const otk::OBProperty *property = Openbox::instance->property();
105
106 // these values should not be persisted across a window unmapping/mapping
107 property->erase(_window, otk::OBProperty::net_wm_desktop);
108 property->erase(_window, otk::OBProperty::net_wm_state);
109 }
110
111
112 void OBClient::getDesktop()
113 {
114 const otk::OBProperty *property = Openbox::instance->property();
115
116 // defaults to the current desktop
117 _desktop = 0; // XXX: change this to the current desktop!
118
119 property->get(_window, otk::OBProperty::net_wm_desktop,
120 otk::OBProperty::Atom_Cardinal,
121 &_desktop);
122 }
123
124
125 void OBClient::getType()
126 {
127 const otk::OBProperty *property = Openbox::instance->property();
128
129 _type = (WindowType) -1;
130
131 unsigned long *val;
132 unsigned long num = (unsigned) -1;
133 if (property->get(_window, otk::OBProperty::net_wm_window_type,
134 otk::OBProperty::Atom_Atom,
135 &num, &val)) {
136 // use the first value that we know about in the array
137 for (unsigned long i = 0; i < num; ++i) {
138 if (val[i] ==
139 property->atom(otk::OBProperty::net_wm_window_type_desktop))
140 _type = Type_Desktop;
141 else if (val[i] ==
142 property->atom(otk::OBProperty::net_wm_window_type_dock))
143 _type = Type_Dock;
144 else if (val[i] ==
145 property->atom(otk::OBProperty::net_wm_window_type_toolbar))
146 _type = Type_Toolbar;
147 else if (val[i] ==
148 property->atom(otk::OBProperty::net_wm_window_type_menu))
149 _type = Type_Menu;
150 else if (val[i] ==
151 property->atom(otk::OBProperty::net_wm_window_type_utility))
152 _type = Type_Utility;
153 else if (val[i] ==
154 property->atom(otk::OBProperty::net_wm_window_type_splash))
155 _type = Type_Splash;
156 else if (val[i] ==
157 property->atom(otk::OBProperty::net_wm_window_type_dialog))
158 _type = Type_Dialog;
159 else if (val[i] ==
160 property->atom(otk::OBProperty::net_wm_window_type_normal))
161 _type = Type_Normal;
162 // else if (val[i] ==
163 // property->atom(otk::OBProperty::kde_net_wm_window_type_override))
164 // mwm_decorations = 0; // prevent this window from getting any decor
165 // XXX: make this work again
166 }
167 delete val;
168 }
169
170 if (_type == (WindowType) -1) {
171 /*
172 * the window type hint was not set, which means we either classify ourself
173 * as a normal window or a dialog, depending on if we are a transient.
174 */
175 // XXX: make this code work!
176 //if (isTransient())
177 // _type = Type_Dialog;
178 //else
179 _type = Type_Normal;
180 }
181 }
182
183
184 void OBClient::getArea()
185 {
186 XWindowAttributes wattrib;
187 assert(XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib));
188
189 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
190 }
191
192
193 void OBClient::getState()
194 {
195 const otk::OBProperty *property = Openbox::instance->property();
196
197 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _floating = false;
198
199 unsigned long *state;
200 unsigned long num = (unsigned) -1;
201
202 if (property->get(_window, otk::OBProperty::net_wm_state,
203 otk::OBProperty::Atom_Atom, &num, &state)) {
204 for (unsigned long i = 0; i < num; ++i) {
205 if (state[i] == property->atom(otk::OBProperty::net_wm_state_modal))
206 _modal = true;
207 else if (state[i] ==
208 property->atom(otk::OBProperty::net_wm_state_shaded))
209 _shaded = true;
210 else if (state[i] ==
211 property->atom(otk::OBProperty::net_wm_state_fullscreen))
212 _fullscreen = true;
213 else if (state[i] ==
214 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
215 _max_vert = true;
216 else if (state[i] ==
217 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
218 _max_horz = true;
219 }
220
221 delete [] state;
222 }
223 }
224
225
226 void OBClient::getShaped()
227 {
228 _shaped = false;
229 #ifdef SHAPE
230 if (otk::OBDisplay::shape()) {
231 int foo;
232 unsigned int ufoo;
233
234 XShapeQueryExtents(otk::OBDisplay::display, client.window, &_shaped, &foo,
235 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
236 }
237 #endif // SHAPE
238 }
239
240
241 void OBClient::updateNormalHints()
242 {
243 XSizeHints size;
244 long ret;
245
246 // defaults
247 _gravity = NorthWestGravity;
248 _inc_x = _inc_y = 1;
249 _base_x = _base_y = 0;
250
251 // get the hints from the window
252 if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
253 if (size.flags & PWinGravity)
254 _gravity = size.win_gravity;
255 if (size.flags & PBaseSize) {
256 _base_x = size.base_width;
257 _base_y = size.base_height;
258 }
259 if (size.flags & PResizeInc) {
260 _inc_x = size.width_inc;
261 _inc_y = size.height_inc;
262 }
263 }
264 }
265
266
267 void OBClient::updateWMHints()
268 {
269 XWMHints *hints;
270
271 // assume a window takes input if it doesnt specify
272 _can_focus = true;
273 _urgent = false;
274
275 if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
276 if (hints->flags & InputHint)
277 _can_focus = hints->input;
278
279 if (hints->flags & XUrgencyHint)
280 _urgent = true;
281
282 if (hints->flags & WindowGroupHint) {
283 if (hints->window_group != _group) {
284 // XXX: remove from the old group if there was one
285 _group = hints->window_group;
286 // XXX: do stuff with the group
287 }
288 } else // no group!
289 _group = None;
290
291 XFree(hints);
292 }
293 }
294
295
296 void OBClient::updateTitle()
297 {
298 const otk::OBProperty *property = Openbox::instance->property();
299
300 _title = "";
301
302 // try netwm
303 if (! property->get(_window, otk::OBProperty::net_wm_name,
304 otk::OBProperty::utf8, &_title)) {
305 // try old x stuff
306 property->get(_window, otk::OBProperty::wm_name,
307 otk::OBProperty::ascii, &_title);
308 }
309
310 if (_title.empty())
311 _title = _("Unnamed Window");
312 }
313
314
315 void OBClient::updateClass()
316 {
317 const otk::OBProperty *property = Openbox::instance->property();
318
319 // set the defaults
320 _app_name = _app_class = "";
321
322 otk::OBProperty::StringVect v;
323 unsigned long num = 2;
324
325 if (! property->get(_window, otk::OBProperty::wm_class,
326 otk::OBProperty::ascii, &num, &v))
327 return;
328
329 if (num > 0) _app_name = v[0];
330 if (num > 1) _app_class = v[1];
331 }
332
333
334 void OBClient::update(const XPropertyEvent &e)
335 {
336 const otk::OBProperty *property = Openbox::instance->property();
337
338 if (e.atom == XA_WM_NORMAL_HINTS)
339 updateNormalHints();
340 else if (e.atom == XA_WM_HINTS)
341 updateWMHints();
342 else if (e.atom == property->atom(otk::OBProperty::net_wm_name) ||
343 e.atom == property->atom(otk::OBProperty::wm_name) ||
344 e.atom == property->atom(otk::OBProperty::net_wm_icon_name) ||
345 e.atom == property->atom(otk::OBProperty::wm_icon_name))
346 updateTitle();
347 else if (e.atom == property->atom(otk::OBProperty::wm_class))
348 updateClass();
349 // XXX: transient for hint
350 }
351
352
353 void OBClient::setWMState(long state)
354 {
355 if (state == _wmstate) return; // no change
356
357 switch (state) {
358 case IconicState:
359 // XXX: cause it to iconify
360 break;
361 case NormalState:
362 // XXX: cause it to uniconify
363 break;
364 }
365 _wmstate = state;
366 }
367
368
369 void OBClient::setDesktop(long target)
370 {
371 assert(target >= 0);
372 //assert(target == 0xffffffff || target < MAX);
373
374 // XXX: move the window to the new desktop
375 _desktop = target;
376 }
377
378
379 void OBClient::setState(StateAction action, long data1, long data2)
380 {
381 const otk::OBProperty *property = Openbox::instance->property();
382
383 if (!(action == State_Add || action == State_Remove ||
384 action == State_Toggle))
385 return; // an invalid action was passed to the client message, ignore it
386
387 for (int i = 0; i < 2; ++i) {
388 Atom state = i == 0 ? data1 : data2;
389
390 if (! state) continue;
391
392 // if toggling, then pick whether we're adding or removing
393 if (action == State_Toggle) {
394 if (state == property->atom(otk::OBProperty::net_wm_state_modal))
395 action = _modal ? State_Remove : State_Add;
396 else if (state ==
397 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
398 action = _max_vert ? State_Remove : State_Add;
399 else if (state ==
400 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
401 action = _max_horz ? State_Remove : State_Add;
402 else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
403 action = _shaded ? State_Remove : State_Add;
404 else if (state ==
405 property->atom(otk::OBProperty::net_wm_state_fullscreen))
406 action = _fullscreen ? State_Remove : State_Add;
407 else if (state == property->atom(otk::OBProperty::net_wm_state_floating))
408 action = _floating ? State_Remove : State_Add;
409 }
410
411 if (action == State_Add) {
412 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
413 if (_modal) continue;
414 _modal = true;
415 // XXX: give it focus if another window has focus that shouldnt now
416 } else if (state ==
417 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
418 if (_max_vert) continue;
419 _max_vert = true;
420 // XXX: resize the window etc
421 } else if (state ==
422 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
423 if (_max_horz) continue;
424 _max_horz = true;
425 // XXX: resize the window etc
426 } else if (state ==
427 property->atom(otk::OBProperty::net_wm_state_shaded)) {
428 if (_shaded) continue;
429 _shaded = true;
430 // XXX: hide the client window
431 } else if (state ==
432 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
433 if (_fullscreen) continue;
434 _fullscreen = true;
435 // XXX: raise the window n shit
436 } else if (state ==
437 property->atom(otk::OBProperty::net_wm_state_floating)) {
438 if (_floating) continue;
439 _floating = true;
440 // XXX: raise the window n shit
441 }
442
443 } else { // action == State_Remove
444 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
445 if (!_modal) continue;
446 _modal = false;
447 } else if (state ==
448 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
449 if (!_max_vert) continue;
450 _max_vert = false;
451 // XXX: resize the window etc
452 } else if (state ==
453 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
454 if (!_max_horz) continue;
455 _max_horz = false;
456 // XXX: resize the window etc
457 } else if (state ==
458 property->atom(otk::OBProperty::net_wm_state_shaded)) {
459 if (!_shaded) continue;
460 _shaded = false;
461 // XXX: show the client window
462 } else if (state ==
463 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
464 if (!_fullscreen) continue;
465 _fullscreen = false;
466 // XXX: lower the window to its proper layer
467 } else if (state ==
468 property->atom(otk::OBProperty::net_wm_state_floating)) {
469 if (!_floating) continue;
470 _floating = false;
471 // XXX: lower the window to its proper layer
472 }
473 }
474 }
475 }
476
477
478 void OBClient::update(const XClientMessageEvent &e)
479 {
480 if (e.format != 32) return;
481
482 const otk::OBProperty *property = Openbox::instance->property();
483
484 if (e.message_type == property->atom(otk::OBProperty::wm_change_state))
485 setWMState(e.data.l[0]);
486 else if (e.message_type ==
487 property->atom(otk::OBProperty::net_wm_desktop))
488 setDesktop(e.data.l[0]);
489 else if (e.message_type == property->atom(otk::OBProperty::net_wm_state))
490 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
491 }
492
493
494 void OBClient::setArea(const otk::Rect &area)
495 {
496 _area = area;
497 }
498
499 }
This page took 0.057396 seconds and 3 git commands to generate.