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