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