]> Dogcows Code - chaz/openbox/blob - src/client.cc
set the net support atoms
[chaz/openbox] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "client.hh"
8 #include "frame.hh"
9 #include "screen.hh"
10 #include "openbox.hh"
11 #include "otk/display.hh"
12 #include "otk/property.hh"
13
14 extern "C" {
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17
18 #include <assert.h>
19
20 #include "gettext.h"
21 #define _(str) gettext(str)
22 }
23
24 namespace ob {
25
26 OBClient::OBClient(int screen, Window window)
27 : otk::OtkEventHandler(),
28 OBWidget(OBWidget::Type_Client),
29 frame(0), _screen(screen), _window(window)
30 {
31 assert(screen >= 0);
32 assert(window);
33
34 ignore_unmaps = 0;
35
36 // update EVERYTHING the first time!!
37
38 // the state is kinda assumed to be normal. is this right? XXX
39 _wmstate = NormalState; _iconic = false;
40 // no default decors or functions, each has to be enabled
41 _decorations = _functions = 0;
42 // start unfocused
43 _focused = false;
44
45 getArea();
46 getDesktop();
47 getType();
48
49 // set the decorations and functions
50 switch (_type) {
51 case Type_Normal:
52 // normal windows retain all of the possible decorations and
53 // functionality
54 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
55 Decor_Iconify | Decor_Maximize;
56 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
57
58 case Type_Dialog:
59 // dialogs cannot be maximized
60 _decorations &= ~Decor_Maximize;
61 _functions &= ~Func_Maximize;
62 break;
63
64 case Type_Menu:
65 case Type_Toolbar:
66 case Type_Utility:
67 // these windows get less functionality
68 _decorations &= ~(Decor_Iconify | Decor_Handle);
69 _functions &= ~(Func_Iconify | Func_Resize);
70 break;
71
72 case Type_Desktop:
73 case Type_Dock:
74 case Type_Splash:
75 // none of these windows are manipulated by the window manager
76 _decorations = 0;
77 _functions = 0;
78 break;
79 }
80
81 getMwmHints(); // this fucks (in good ways) with the decors and functions
82 getState();
83 getShaped();
84
85 updateProtocols();
86 updateNormalHints();
87 updateWMHints();
88 // XXX: updateTransientFor();
89 updateTitle();
90 updateIconTitle();
91 updateClass();
92
93 calcLayer();
94 }
95
96
97 OBClient::~OBClient()
98 {
99 const otk::OBProperty *property = Openbox::instance->property();
100
101 if (Openbox::instance->state() != Openbox::State_Exiting) {
102 // these values should not be persisted across a window unmapping/mapping
103 property->erase(_window, otk::OBProperty::net_wm_desktop);
104 property->erase(_window, otk::OBProperty::net_wm_state);
105 }
106 }
107
108
109 void OBClient::getDesktop()
110 {
111 const otk::OBProperty *property = Openbox::instance->property();
112
113 // defaults to the current desktop
114 _desktop = 0; // XXX: change this to the current desktop!
115
116 property->get(_window, otk::OBProperty::net_wm_desktop,
117 otk::OBProperty::Atom_Cardinal,
118 &_desktop);
119 }
120
121
122 void OBClient::getType()
123 {
124 const otk::OBProperty *property = Openbox::instance->property();
125
126 _type = (WindowType) -1;
127
128 unsigned long *val;
129 unsigned long num = (unsigned) -1;
130 if (property->get(_window, otk::OBProperty::net_wm_window_type,
131 otk::OBProperty::Atom_Atom,
132 &num, &val)) {
133 // use the first value that we know about in the array
134 for (unsigned long i = 0; i < num; ++i) {
135 if (val[i] ==
136 property->atom(otk::OBProperty::net_wm_window_type_desktop))
137 _type = Type_Desktop;
138 else if (val[i] ==
139 property->atom(otk::OBProperty::net_wm_window_type_dock))
140 _type = Type_Dock;
141 else if (val[i] ==
142 property->atom(otk::OBProperty::net_wm_window_type_toolbar))
143 _type = Type_Toolbar;
144 else if (val[i] ==
145 property->atom(otk::OBProperty::net_wm_window_type_menu))
146 _type = Type_Menu;
147 else if (val[i] ==
148 property->atom(otk::OBProperty::net_wm_window_type_utility))
149 _type = Type_Utility;
150 else if (val[i] ==
151 property->atom(otk::OBProperty::net_wm_window_type_splash))
152 _type = Type_Splash;
153 else if (val[i] ==
154 property->atom(otk::OBProperty::net_wm_window_type_dialog))
155 _type = Type_Dialog;
156 else if (val[i] ==
157 property->atom(otk::OBProperty::net_wm_window_type_normal))
158 _type = Type_Normal;
159 // else if (val[i] ==
160 // property->atom(otk::OBProperty::kde_net_wm_window_type_override))
161 // mwm_decorations = 0; // prevent this window from getting any decor
162 // XXX: make this work again
163 }
164 delete val;
165 }
166
167 if (_type == (WindowType) -1) {
168 /*
169 * the window type hint was not set, which means we either classify ourself
170 * as a normal window or a dialog, depending on if we are a transient.
171 */
172 // XXX: make this code work!
173 //if (isTransient())
174 // _type = Type_Dialog;
175 //else
176 _type = Type_Normal;
177 }
178 }
179
180
181 void OBClient::getMwmHints()
182 {
183 const otk::OBProperty *property = Openbox::instance->property();
184
185 unsigned long num;
186 MwmHints *hints;
187
188 num = MwmHints::elements;
189 if (!property->get(_window, otk::OBProperty::motif_wm_hints,
190 otk::OBProperty::motif_wm_hints, &num,
191 (unsigned long **)&hints))
192 return;
193
194 if (num < MwmHints::elements) {
195 delete [] hints;
196 return;
197 }
198
199 // retrieved the hints
200 // Mwm Hints are applied subtractively to what has already been chosen for
201 // decor and functionality
202
203 if (hints->flags & MwmFlag_Decorations) {
204 if (! (hints->decorations & MwmDecor_All)) {
205 if (! (hints->decorations & MwmDecor_Border))
206 _decorations &= ~Decor_Border;
207 if (! (hints->decorations & MwmDecor_Handle))
208 _decorations &= ~Decor_Handle;
209 if (! (hints->decorations & MwmDecor_Title))
210 _decorations &= ~Decor_Titlebar;
211 if (! (hints->decorations & MwmDecor_Iconify))
212 _decorations &= ~Decor_Iconify;
213 if (! (hints->decorations & MwmDecor_Maximize))
214 _decorations &= ~Decor_Maximize;
215 }
216 }
217
218 if (hints->flags & MwmFlag_Functions) {
219 if (! (hints->functions & MwmFunc_All)) {
220 if (! (hints->functions & MwmFunc_Resize))
221 _functions &= ~Func_Resize;
222 if (! (hints->functions & MwmFunc_Move))
223 _functions &= ~Func_Move;
224 if (! (hints->functions & MwmFunc_Iconify))
225 _functions &= ~Func_Iconify;
226 if (! (hints->functions & MwmFunc_Maximize))
227 _functions &= ~Func_Maximize;
228 //if (! (hints->functions & MwmFunc_Close))
229 // _functions &= ~Func_Close;
230 }
231 }
232 delete [] hints;
233 }
234
235
236 void OBClient::getArea()
237 {
238 XWindowAttributes wattrib;
239 Status ret;
240
241 ret = XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib);
242 assert(ret != BadWindow);
243
244 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
245 _border_width = wattrib.border_width;
246 }
247
248
249 void OBClient::getState()
250 {
251 const otk::OBProperty *property = Openbox::instance->property();
252
253 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
254 _skip_taskbar = _skip_pager = false;
255
256 unsigned long *state;
257 unsigned long num = (unsigned) -1;
258
259 if (property->get(_window, otk::OBProperty::net_wm_state,
260 otk::OBProperty::Atom_Atom, &num, &state)) {
261 for (unsigned long i = 0; i < num; ++i) {
262 if (state[i] == property->atom(otk::OBProperty::net_wm_state_modal))
263 _modal = true;
264 else if (state[i] ==
265 property->atom(otk::OBProperty::net_wm_state_shaded))
266 _shaded = true;
267 else if (state[i] ==
268 property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
269 _skip_taskbar = true;
270 else if (state[i] ==
271 property->atom(otk::OBProperty::net_wm_state_skip_pager))
272 _skip_pager = true;
273 else if (state[i] ==
274 property->atom(otk::OBProperty::net_wm_state_fullscreen))
275 _fullscreen = true;
276 else if (state[i] ==
277 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
278 _max_vert = true;
279 else if (state[i] ==
280 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
281 _max_horz = true;
282 else if (state[i] ==
283 property->atom(otk::OBProperty::net_wm_state_above))
284 _above = true;
285 else if (state[i] ==
286 property->atom(otk::OBProperty::net_wm_state_below))
287 _below = true;
288 }
289
290 delete [] state;
291 }
292 }
293
294
295 void OBClient::getShaped()
296 {
297 _shaped = false;
298 #ifdef SHAPE
299 if (otk::OBDisplay::shape()) {
300 int foo;
301 unsigned int ufoo;
302 int s;
303
304 XShapeSelectInput(otk::OBDisplay::display, _window, ShapeNotifyMask);
305
306 XShapeQueryExtents(otk::OBDisplay::display, _window, &s, &foo,
307 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
308 _shaped = (s != 0);
309 }
310 #endif // SHAPE
311 }
312
313
314 void OBClient::calcLayer() {
315 if (_iconic) _layer = OBScreen::Layer_Icon;
316 else if (_type == Type_Desktop) _layer = OBScreen::Layer_Desktop;
317 else if (_type == Type_Dock) _layer = OBScreen::Layer_Top;
318 else if (_fullscreen) _layer = OBScreen::Layer_Fullscreen;
319 else if (_above) _layer = OBScreen::Layer_Above;
320 else if (_below) _layer = OBScreen::Layer_Below;
321 else _layer = OBScreen::Layer_Normal;
322 }
323
324
325 void OBClient::updateProtocols()
326 {
327 const otk::OBProperty *property = Openbox::instance->property();
328
329 Atom *proto;
330 int num_return = 0;
331
332 _focus_notify = false;
333 _decorations &= ~Decor_Close;
334 _functions &= ~Func_Close;
335
336 if (XGetWMProtocols(otk::OBDisplay::display, _window, &proto, &num_return)) {
337 for (int i = 0; i < num_return; ++i) {
338 if (proto[i] == property->atom(otk::OBProperty::wm_delete_window)) {
339 _decorations |= Decor_Close;
340 _functions |= Func_Close;
341 // XXX: update the decor?
342 } else if (proto[i] == property->atom(otk::OBProperty::wm_take_focus))
343 // if this protocol is requested, then the window will be notified
344 // by the window manager whenever it receives focus
345 _focus_notify = true;
346 }
347 XFree(proto);
348 }
349 }
350
351
352 void OBClient::updateNormalHints()
353 {
354 XSizeHints size;
355 long ret;
356 int oldgravity = _gravity;
357
358 // defaults
359 _gravity = NorthWestGravity;
360 _size_inc.setPoint(1, 1);
361 _base_size.setPoint(0, 0);
362 _min_size.setPoint(0, 0);
363 _max_size.setPoint(INT_MAX, INT_MAX);
364
365 // XXX: might want to cancel any interactive resizing of the window at this
366 // point..
367
368 // get the hints from the window
369 if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
370 _positioned = (size.flags & (PPosition|USPosition));
371
372 if (size.flags & PWinGravity)
373 _gravity = size.win_gravity;
374
375 if (size.flags & PMinSize)
376 _min_size.setPoint(size.min_width, size.min_height);
377
378 if (size.flags & PMaxSize)
379 _max_size.setPoint(size.max_width, size.max_height);
380
381 if (size.flags & PBaseSize)
382 _base_size.setPoint(size.base_width, size.base_height);
383
384 if (size.flags & PResizeInc)
385 _size_inc.setPoint(size.width_inc, size.height_inc);
386 }
387
388 // if the client has a frame, i.e. has already been mapped and is
389 // changing its gravity
390 if (frame && _gravity != oldgravity) {
391 // move our idea of the client's position based on its new gravity
392 int x, y;
393 frame->frameGravity(x, y);
394 _area.setPos(x, y);
395 }
396 }
397
398
399 void OBClient::updateWMHints()
400 {
401 XWMHints *hints;
402
403 // assume a window takes input if it doesnt specify
404 _can_focus = true;
405 _urgent = false;
406
407 if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
408 if (hints->flags & InputHint)
409 _can_focus = hints->input;
410
411 if (hints->flags & XUrgencyHint)
412 _urgent = true;
413
414 if (hints->flags & WindowGroupHint) {
415 if (hints->window_group != _group) {
416 // XXX: remove from the old group if there was one
417 _group = hints->window_group;
418 // XXX: do stuff with the group
419 }
420 } else // no group!
421 _group = None;
422
423 XFree(hints);
424 }
425 }
426
427
428 void OBClient::updateTitle()
429 {
430 const otk::OBProperty *property = Openbox::instance->property();
431
432 _title = "";
433
434 // try netwm
435 if (! property->get(_window, otk::OBProperty::net_wm_name,
436 otk::OBProperty::utf8, &_title)) {
437 // try old x stuff
438 property->get(_window, otk::OBProperty::wm_name,
439 otk::OBProperty::ascii, &_title);
440 }
441
442 if (_title.empty())
443 _title = _("Unnamed Window");
444
445 if (frame)
446 frame->setTitle(_title);
447 }
448
449
450 void OBClient::updateIconTitle()
451 {
452 const otk::OBProperty *property = Openbox::instance->property();
453
454 _icon_title = "";
455
456 // try netwm
457 if (! property->get(_window, otk::OBProperty::net_wm_icon_name,
458 otk::OBProperty::utf8, &_icon_title)) {
459 // try old x stuff
460 property->get(_window, otk::OBProperty::wm_icon_name,
461 otk::OBProperty::ascii, &_icon_title);
462 }
463
464 if (_title.empty())
465 _icon_title = _("Unnamed Window");
466 }
467
468
469 void OBClient::updateClass()
470 {
471 const otk::OBProperty *property = Openbox::instance->property();
472
473 // set the defaults
474 _app_name = _app_class = "";
475
476 otk::OBProperty::StringVect v;
477 unsigned long num = 2;
478
479 if (! property->get(_window, otk::OBProperty::wm_class,
480 otk::OBProperty::ascii, &num, &v))
481 return;
482
483 if (num > 0) _app_name = v[0];
484 if (num > 1) _app_class = v[1];
485 }
486
487
488 void OBClient::propertyHandler(const XPropertyEvent &e)
489 {
490 otk::OtkEventHandler::propertyHandler(e);
491
492 const otk::OBProperty *property = Openbox::instance->property();
493
494 // compress changes to a single property into a single change
495 XEvent ce;
496 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
497 // XXX: it would be nice to compress ALL changes to a property, not just
498 // changes in a row without other props between.
499 if (ce.xproperty.atom != e.atom) {
500 XPutBackEvent(otk::OBDisplay::display, &ce);
501 break;
502 }
503 }
504
505 if (e.atom == XA_WM_NORMAL_HINTS)
506 updateNormalHints();
507 else if (e.atom == XA_WM_HINTS)
508 updateWMHints();
509 else if (e.atom == property->atom(otk::OBProperty::net_wm_name) ||
510 e.atom == property->atom(otk::OBProperty::wm_name))
511 updateTitle();
512 else if (e.atom == property->atom(otk::OBProperty::net_wm_icon_name) ||
513 e.atom == property->atom(otk::OBProperty::wm_icon_name))
514 updateIconTitle();
515 else if (e.atom == property->atom(otk::OBProperty::wm_class))
516 updateClass();
517 else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
518 updateProtocols();
519 // XXX: transient for hint
520 // XXX: strut hint
521 }
522
523
524 void OBClient::setWMState(long state)
525 {
526 if (state == _wmstate) return; // no change
527
528 switch (state) {
529 case IconicState:
530 // XXX: cause it to iconify
531 break;
532 case NormalState:
533 // XXX: cause it to uniconify
534 break;
535 }
536 _wmstate = state;
537 }
538
539
540 void OBClient::setDesktop(long target)
541 {
542 assert(target >= 0);
543 //assert(target == 0xffffffff || target < MAX);
544
545 // XXX: move the window to the new desktop (and set root property)
546 _desktop = target;
547 }
548
549
550 void OBClient::setState(StateAction action, long data1, long data2)
551 {
552 const otk::OBProperty *property = Openbox::instance->property();
553
554 if (!(action == State_Add || action == State_Remove ||
555 action == State_Toggle))
556 return; // an invalid action was passed to the client message, ignore it
557
558 for (int i = 0; i < 2; ++i) {
559 Atom state = i == 0 ? data1 : data2;
560
561 if (! state) continue;
562
563 // if toggling, then pick whether we're adding or removing
564 if (action == State_Toggle) {
565 if (state == property->atom(otk::OBProperty::net_wm_state_modal))
566 action = _modal ? State_Remove : State_Add;
567 else if (state ==
568 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
569 action = _max_vert ? State_Remove : State_Add;
570 else if (state ==
571 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
572 action = _max_horz ? State_Remove : State_Add;
573 else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
574 action = _shaded ? State_Remove : State_Add;
575 else if (state ==
576 property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
577 action = _skip_taskbar ? State_Remove : State_Add;
578 else if (state ==
579 property->atom(otk::OBProperty::net_wm_state_skip_pager))
580 action = _skip_pager ? State_Remove : State_Add;
581 else if (state ==
582 property->atom(otk::OBProperty::net_wm_state_fullscreen))
583 action = _fullscreen ? State_Remove : State_Add;
584 else if (state == property->atom(otk::OBProperty::net_wm_state_above))
585 action = _above ? State_Remove : State_Add;
586 else if (state == property->atom(otk::OBProperty::net_wm_state_below))
587 action = _below ? State_Remove : State_Add;
588 }
589
590 if (action == State_Add) {
591 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
592 if (_modal) continue;
593 _modal = true;
594 // XXX: give it focus if another window has focus that shouldnt now
595 } else if (state ==
596 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
597 if (_max_vert) continue;
598 _max_vert = true;
599 // XXX: resize the window etc
600 } else if (state ==
601 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
602 if (_max_horz) continue;
603 _max_horz = true;
604 // XXX: resize the window etc
605 } else if (state ==
606 property->atom(otk::OBProperty::net_wm_state_shaded)) {
607 if (_shaded) continue;
608 _shaded = true;
609 // XXX: hide the client window
610 } else if (state ==
611 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
612 _skip_taskbar = true;
613 } else if (state ==
614 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
615 _skip_pager = true;
616 } else if (state ==
617 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
618 if (_fullscreen) continue;
619 _fullscreen = true;
620 // XXX: raise the window n shit
621 } else if (state ==
622 property->atom(otk::OBProperty::net_wm_state_above)) {
623 if (_above) continue;
624 _above = true;
625 // XXX: raise the window n shit
626 } else if (state ==
627 property->atom(otk::OBProperty::net_wm_state_below)) {
628 if (_below) continue;
629 _below = true;
630 // XXX: lower the window n shit
631 }
632
633 } else { // action == State_Remove
634 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
635 if (!_modal) continue;
636 _modal = false;
637 } else if (state ==
638 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
639 if (!_max_vert) continue;
640 _max_vert = false;
641 // XXX: resize the window etc
642 } else if (state ==
643 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
644 if (!_max_horz) continue;
645 _max_horz = false;
646 // XXX: resize the window etc
647 } else if (state ==
648 property->atom(otk::OBProperty::net_wm_state_shaded)) {
649 if (!_shaded) continue;
650 _shaded = false;
651 // XXX: show the client window
652 } else if (state ==
653 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
654 _skip_taskbar = false;
655 } else if (state ==
656 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
657 _skip_pager = false;
658 } else if (state ==
659 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
660 if (!_fullscreen) continue;
661 _fullscreen = false;
662 // XXX: lower the window to its proper layer
663 } else if (state ==
664 property->atom(otk::OBProperty::net_wm_state_above)) {
665 if (!_above) continue;
666 _above = false;
667 // XXX: lower the window to its proper layer
668 } else if (state ==
669 property->atom(otk::OBProperty::net_wm_state_below)) {
670 if (!_below) continue;
671 _below = false;
672 // XXX: raise the window to its proper layer
673 }
674 }
675 }
676 calcLayer();
677 Openbox::instance->screen(_screen)->restack(true, this); // raise
678 }
679
680
681 void OBClient::toggleClientBorder(bool addborder)
682 {
683 // adjust our idea of where the client is, based on its border. When the
684 // border is removed, the client should now be considered to be in a
685 // different position.
686 // when re-adding the border to the client, the same operation needs to be
687 // reversed.
688 int x = _area.x(), y = _area.y();
689 switch(_gravity) {
690 case NorthWestGravity:
691 case WestGravity:
692 case SouthWestGravity:
693 break;
694 case NorthEastGravity:
695 case EastGravity:
696 case SouthEastGravity:
697 if (addborder) x -= _border_width * 2;
698 else x += _border_width * 2;
699 break;
700 }
701 switch(_gravity) {
702 case NorthWestGravity:
703 case NorthGravity:
704 case NorthEastGravity:
705 break;
706 case SouthWestGravity:
707 case SouthGravity:
708 case SouthEastGravity:
709 if (addborder) y -= _border_width * 2;
710 else y += _border_width * 2;
711 break;
712 default:
713 // no change for StaticGravity etc.
714 break;
715 }
716 _area.setPos(x, y);
717
718 if (addborder) {
719 XSetWindowBorderWidth(otk::OBDisplay::display, _window, _border_width);
720
721 // move the client so it is back it the right spot _with_ its border!
722 XMoveWindow(otk::OBDisplay::display, _window, x, y);
723 } else
724 XSetWindowBorderWidth(otk::OBDisplay::display, _window, 0);
725 }
726
727
728 void OBClient::clientMessageHandler(const XClientMessageEvent &e)
729 {
730 otk::OtkEventHandler::clientMessageHandler(e);
731
732 if (e.format != 32) return;
733
734 const otk::OBProperty *property = Openbox::instance->property();
735
736 if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) {
737 // compress changes into a single change
738 bool compress = false;
739 XEvent ce;
740 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
741 // XXX: it would be nice to compress ALL messages of a type, not just
742 // messages in a row without other message types between.
743 if (ce.xclient.message_type != e.message_type) {
744 XPutBackEvent(otk::OBDisplay::display, &ce);
745 break;
746 }
747 compress = true;
748 }
749 if (compress)
750 setWMState(ce.xclient.data.l[0]); // use the found event
751 else
752 setWMState(e.data.l[0]); // use the original event
753 } else if (e.message_type ==
754 property->atom(otk::OBProperty::net_wm_desktop)) {
755 // compress changes into a single change
756 bool compress = false;
757 XEvent ce;
758 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
759 // XXX: it would be nice to compress ALL messages of a type, not just
760 // messages in a row without other message types between.
761 if (ce.xclient.message_type != e.message_type) {
762 XPutBackEvent(otk::OBDisplay::display, &ce);
763 break;
764 }
765 compress = true;
766 }
767 if (compress)
768 setDesktop(e.data.l[0]); // use the found event
769 else
770 setDesktop(e.data.l[0]); // use the original event
771 } else if (e.message_type == property->atom(otk::OBProperty::net_wm_state)) {
772 // can't compress these
773 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
774 } else if (e.message_type ==
775 property->atom(otk::OBProperty::net_close_window)) {
776 close();
777 } else if (e.message_type ==
778 property->atom(otk::OBProperty::net_active_window)) {
779 focus();
780 }
781 }
782
783
784 #if defined(SHAPE) || defined(DOXYGEN_IGNORE)
785 void OBClient::shapeHandler(const XShapeEvent &e)
786 {
787 otk::OtkEventHandler::shapeHandler(e);
788
789 _shaped = e.shaped;
790 }
791 #endif
792
793
794 void OBClient::resize(Corner anchor, int w, int h)
795 {
796 w -= _base_size.x();
797 h -= _base_size.y();
798
799 // for interactive resizing. have to move half an increment in each
800 // direction.
801 w += _size_inc.x() / 2;
802 h += _size_inc.y() / 2;
803
804 // is the window resizable? if it is not, then don't check its sizes, the
805 // client can do what it wants and the user can't change it anyhow
806 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
807 // smaller than min size or bigger than max size?
808 if (w < _min_size.x()) w = _min_size.x();
809 else if (w > _max_size.x()) w = _max_size.x();
810 if (h < _min_size.y()) h = _min_size.y();
811 else if (h > _max_size.y()) h = _max_size.y();
812 }
813
814 // keep to the increments
815 w /= _size_inc.x();
816 h /= _size_inc.y();
817
818 // store the logical size
819 _logical_size.setPoint(w, h);
820
821 w *= _size_inc.x();
822 h *= _size_inc.y();
823
824 w += _base_size.x();
825 h += _base_size.y();
826
827 int x = _area.x(), y = _area.y();
828 switch (anchor) {
829 case TopLeft:
830 break;
831 case TopRight:
832 x -= w - _area.width();
833 break;
834 case BottomLeft:
835 y -= h - _area.height();
836 break;
837 case BottomRight:
838 x -= w - _area.width();
839 y -= h - _area.height();
840 break;
841 }
842
843 _area.setSize(w, h);
844 XResizeWindow(otk::OBDisplay::display, _window, w, h);
845
846 // resize the frame to match the request
847 frame->adjustSize();
848 move(x, y);
849 }
850
851
852 void OBClient::move(int x, int y)
853 {
854 _area.setPos(x, y);
855 // move the frame to be in the requested position
856 frame->adjustPosition();
857 }
858
859
860 void OBClient::close()
861 {
862 XEvent ce;
863 const otk::OBProperty *property = Openbox::instance->property();
864
865 if (!(_functions & Func_Close)) return;
866
867 // XXX: itd be cool to do timeouts and shit here for killing the client's
868 // process off
869
870 ce.xclient.type = ClientMessage;
871 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
872 ce.xclient.display = otk::OBDisplay::display;
873 ce.xclient.window = _window;
874 ce.xclient.format = 32;
875 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_delete_window);
876 ce.xclient.data.l[1] = CurrentTime;
877 ce.xclient.data.l[2] = 0l;
878 ce.xclient.data.l[3] = 0l;
879 ce.xclient.data.l[4] = 0l;
880 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
881 }
882
883
884 void OBClient::changeState()
885 {
886 const otk::OBProperty *property = Openbox::instance->property();
887
888 unsigned long state[2];
889 state[0] = _wmstate;
890 state[1] = None;
891 property->set(_window, otk::OBProperty::wm_state, otk::OBProperty::wm_state,
892 state, 2);
893
894 Atom netstate[10];
895 int num = 0;
896 if (_modal)
897 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_modal);
898 if (_shaded)
899 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_shaded);
900 if (_iconic)
901 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_hidden);
902 if (_skip_taskbar)
903 netstate[num++] =
904 property->atom(otk::OBProperty::net_wm_state_skip_taskbar);
905 if (_skip_pager)
906 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_skip_pager);
907 if (_fullscreen)
908 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_fullscreen);
909 if (_max_vert)
910 netstate[num++] =
911 property->atom(otk::OBProperty::net_wm_state_maximized_vert);
912 if (_max_horz)
913 netstate[num++] =
914 property->atom(otk::OBProperty::net_wm_state_maximized_horz);
915 if (_above)
916 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_above);
917 if (_below)
918 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_below);
919 property->set(_window, otk::OBProperty::net_wm_state,
920 otk::OBProperty::Atom_Atom, netstate, num);
921
922 }
923
924 void OBClient::shade(bool shade)
925 {
926 if (shade == _shaded) return; // already done
927
928 _wmstate = shade ? IconicState : NormalState;
929 _shaded = shade;
930 changeState();
931 frame->adjustSize();
932 }
933
934
935 bool OBClient::focus()
936 {
937 if (!_can_focus || _focused) return false;
938
939 XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
940 return true;
941 }
942
943
944 void OBClient::unfocus()
945 {
946 if (!_focused) return;
947
948 assert(Openbox::instance->focusedClient() == this);
949 Openbox::instance->setFocusedClient(0);
950 }
951
952
953 void OBClient::focusHandler(const XFocusChangeEvent &e)
954 {
955 #ifdef DEBUG
956 printf("FocusIn for 0x%lx\n", e.window);
957 #endif // DEBUG
958
959 OtkEventHandler::focusHandler(e);
960
961 frame->focus();
962 _focused = true;
963
964 Openbox::instance->setFocusedClient(this);
965 }
966
967
968 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
969 {
970 #ifdef DEBUG
971 printf("FocusOut for 0x%lx\n", e.window);
972 #endif // DEBUG
973
974 OtkEventHandler::unfocusHandler(e);
975
976 frame->unfocus();
977 _focused = false;
978
979 if (Openbox::instance->focusedClient() == this) {
980 printf("UNFOCUSED!\n");
981 Openbox::instance->setFocusedClient(this);
982 }
983 }
984
985
986 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
987 {
988 #ifdef DEBUG
989 printf("ConfigureRequest for 0x%lx\n", e.window);
990 #endif // DEBUG
991
992 OtkEventHandler::configureRequestHandler(e);
993
994 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
995
996 if (e.value_mask & CWBorderWidth)
997 _border_width = e.border_width;
998
999 // resize, then move, as specified in the EWMH section 7.7
1000 if (e.value_mask & (CWWidth | CWHeight)) {
1001 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1002 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1003
1004 Corner corner;
1005 switch (_gravity) {
1006 case NorthEastGravity:
1007 case EastGravity:
1008 corner = TopRight;
1009 break;
1010 case SouthWestGravity:
1011 case SouthGravity:
1012 corner = BottomLeft;
1013 break;
1014 case SouthEastGravity:
1015 corner = BottomRight;
1016 break;
1017 default: // NorthWest, Static, etc
1018 corner = TopLeft;
1019 }
1020
1021 resize(corner, w, h);
1022 }
1023
1024 if (e.value_mask & (CWX | CWY)) {
1025 int x = (e.value_mask & CWX) ? e.x : _area.x();
1026 int y = (e.value_mask & CWY) ? e.y : _area.y();
1027 move(x, y);
1028 }
1029
1030 if (e.value_mask & CWStackMode) {
1031 switch (e.detail) {
1032 case Below:
1033 case BottomIf:
1034 // XXX: lower the window
1035 break;
1036
1037 case Above:
1038 case TopIf:
1039 default:
1040 // XXX: raise the window
1041 break;
1042 }
1043 }
1044 }
1045
1046
1047 void OBClient::unmapHandler(const XUnmapEvent &e)
1048 {
1049 #ifdef DEBUG
1050 printf("UnmapNotify for 0x%lx\n", e.window);
1051 #endif // DEBUG
1052
1053 if (ignore_unmaps) {
1054 ignore_unmaps--;
1055 return;
1056 }
1057
1058 OtkEventHandler::unmapHandler(e);
1059
1060 // this deletes us etc
1061 Openbox::instance->screen(_screen)->unmanageWindow(this);
1062 }
1063
1064
1065 void OBClient::destroyHandler(const XDestroyWindowEvent &e)
1066 {
1067 #ifdef DEBUG
1068 printf("DestroyNotify for 0x%lx\n", e.window);
1069 #endif // DEBUG
1070
1071 OtkEventHandler::destroyHandler(e);
1072
1073 // this deletes us etc
1074 Openbox::instance->screen(_screen)->unmanageWindow(this);
1075 }
1076
1077
1078 void OBClient::reparentHandler(const XReparentEvent &e)
1079 {
1080 // this is when the client is first taken captive in the frame
1081 if (e.parent == frame->plate()) return;
1082
1083 #ifdef DEBUG
1084 printf("ReparentNotify for 0x%lx\n", e.window);
1085 #endif // DEBUG
1086
1087 OtkEventHandler::reparentHandler(e);
1088
1089 /*
1090 This event is quite rare and is usually handled in unmapHandler.
1091 However, if the window is unmapped when the reparent event occurs,
1092 the window manager never sees it because an unmap event is not sent
1093 to an already unmapped window.
1094 */
1095
1096 // this deletes us etc
1097 Openbox::instance->screen(_screen)->unmanageWindow(this);
1098 }
1099
1100 }
This page took 0.081063 seconds and 4 git commands to generate.