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