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