]> Dogcows Code - chaz/openbox/blob - src/client.cc
provide the strut for the frame's size instead of an area rect
[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 // XXX: 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, int x, int y)
905 {
906 w -= _base_size.x();
907 h -= _base_size.y();
908
909 // for interactive resizing. have to move half an increment in each
910 // direction.
911 w += _size_inc.x() / 2;
912 h += _size_inc.y() / 2;
913
914 // is the window resizable? if it is not, then don't check its sizes, the
915 // client can do what it wants and the user can't change it anyhow
916 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
917 // smaller than min size or bigger than max size?
918 if (w < _min_size.x()) w = _min_size.x();
919 else if (w > _max_size.x()) w = _max_size.x();
920 if (h < _min_size.y()) h = _min_size.y();
921 else if (h > _max_size.y()) h = _max_size.y();
922 }
923
924 // keep to the increments
925 w /= _size_inc.x();
926 h /= _size_inc.y();
927
928 // you cannot resize to nothing
929 if (w < 1) w = 1;
930 if (h < 1) h = 1;
931
932 // store the logical size
933 _logical_size.setPoint(w, h);
934
935 w *= _size_inc.x();
936 h *= _size_inc.y();
937
938 w += _base_size.x();
939 h += _base_size.y();
940
941 if (x == INT_MIN || y == INT_MIN) {
942 x = _area.x();
943 y = _area.y();
944 switch (anchor) {
945 case TopLeft:
946 break;
947 case TopRight:
948 x -= w - _area.width();
949 break;
950 case BottomLeft:
951 y -= h - _area.height();
952 break;
953 case BottomRight:
954 x -= w - _area.width();
955 y -= h - _area.height();
956 break;
957 }
958 }
959
960 _area.setSize(w, h);
961
962 XResizeWindow(**otk::display, _window, w, h);
963
964 // resize the frame to match the request
965 frame->adjustSize();
966 move(x, y);
967 }
968
969
970 void Client::move(int x, int y)
971 {
972 _area.setPos(x, y);
973
974 // move the frame to be in the requested position
975 if (frame) { // this can be called while mapping, before frame exists
976 frame->adjustPosition();
977
978 // send synthetic configure notify (we don't need to if we aren't mapped
979 // yet)
980 XEvent event;
981 event.type = ConfigureNotify;
982 event.xconfigure.display = **otk::display;
983 event.xconfigure.event = _window;
984 event.xconfigure.window = _window;
985 event.xconfigure.x = x;
986 event.xconfigure.y = y;
987 event.xconfigure.width = _area.width();
988 event.xconfigure.height = _area.height();
989 event.xconfigure.border_width = _border_width;
990 event.xconfigure.above = frame->window();
991 event.xconfigure.override_redirect = False;
992 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
993 StructureNotifyMask, &event);
994 }
995 }
996
997
998 void Client::close()
999 {
1000 XEvent ce;
1001
1002 if (!(_functions & Func_Close)) return;
1003
1004 // XXX: itd be cool to do timeouts and shit here for killing the client's
1005 // process off
1006 // like... if the window is around after 5 seconds, then the close button
1007 // turns a nice red, and if this function is called again, the client is
1008 // explicitly killed.
1009
1010 ce.xclient.type = ClientMessage;
1011 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1012 ce.xclient.display = **otk::display;
1013 ce.xclient.window = _window;
1014 ce.xclient.format = 32;
1015 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1016 ce.xclient.data.l[1] = CurrentTime;
1017 ce.xclient.data.l[2] = 0l;
1018 ce.xclient.data.l[3] = 0l;
1019 ce.xclient.data.l[4] = 0l;
1020 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1021 }
1022
1023
1024 void Client::changeState()
1025 {
1026 unsigned long state[2];
1027 state[0] = _wmstate;
1028 state[1] = None;
1029 otk::Property::set(_window, otk::Property::atoms.wm_state,
1030 otk::Property::atoms.wm_state, state, 2);
1031
1032 Atom netstate[10];
1033 int num = 0;
1034 if (_modal)
1035 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1036 if (_shaded)
1037 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1038 if (_iconic)
1039 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1040 if (_skip_taskbar)
1041 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1042 if (_skip_pager)
1043 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1044 if (_fullscreen)
1045 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1046 if (_max_vert)
1047 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1048 if (_max_horz)
1049 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1050 if (_above)
1051 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1052 if (_below)
1053 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1054 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1055 otk::Property::atoms.atom, netstate, num);
1056
1057 calcLayer();
1058 }
1059
1060
1061 void Client::shade(bool shade)
1062 {
1063 if (shade == _shaded) return; // already done
1064
1065 _wmstate = shade ? IconicState : NormalState;
1066 _shaded = shade;
1067 changeState();
1068 frame->adjustSize();
1069 }
1070
1071
1072 bool Client::focus() const
1073 {
1074 // won't try focus if the client doesn't want it, or if the window isn't
1075 // visible on the screen
1076 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1077
1078 if (_focused) return true;
1079
1080 if (_can_focus)
1081 XSetInputFocus(**otk::display, _window,
1082 RevertToNone, CurrentTime);
1083
1084 if (_focus_notify) {
1085 XEvent ce;
1086 ce.xclient.type = ClientMessage;
1087 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1088 ce.xclient.display = **otk::display;
1089 ce.xclient.window = _window;
1090 ce.xclient.format = 32;
1091 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1092 ce.xclient.data.l[1] = openbox->lastTime();
1093 ce.xclient.data.l[2] = 0l;
1094 ce.xclient.data.l[3] = 0l;
1095 ce.xclient.data.l[4] = 0l;
1096 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1097 }
1098
1099 return true;
1100 }
1101
1102
1103 void Client::unfocus() const
1104 {
1105 if (!_focused) return;
1106
1107 assert(openbox->focusedClient() == this);
1108 openbox->setFocusedClient(0);
1109 }
1110
1111
1112 void Client::focusHandler(const XFocusChangeEvent &e)
1113 {
1114 #ifdef DEBUG
1115 // printf("FocusIn for 0x%lx\n", e.window);
1116 #endif // DEBUG
1117
1118 otk::EventHandler::focusHandler(e);
1119
1120 frame->focus();
1121 _focused = true;
1122
1123 openbox->setFocusedClient(this);
1124 }
1125
1126
1127 void Client::unfocusHandler(const XFocusChangeEvent &e)
1128 {
1129 #ifdef DEBUG
1130 // printf("FocusOut for 0x%lx\n", e.window);
1131 #endif // DEBUG
1132
1133 otk::EventHandler::unfocusHandler(e);
1134
1135 frame->unfocus();
1136 _focused = false;
1137
1138 if (openbox->focusedClient() == this)
1139 openbox->setFocusedClient(0);
1140 }
1141
1142
1143 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1144 {
1145 #ifdef DEBUG
1146 printf("ConfigureRequest for 0x%lx\n", e.window);
1147 #endif // DEBUG
1148
1149 otk::EventHandler::configureRequestHandler(e);
1150
1151 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1152
1153 if (e.value_mask & CWBorderWidth)
1154 _border_width = e.border_width;
1155
1156 // resize, then move, as specified in the EWMH section 7.7
1157 if (e.value_mask & (CWWidth | CWHeight)) {
1158 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1159 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1160
1161 Corner corner;
1162 switch (_gravity) {
1163 case NorthEastGravity:
1164 case EastGravity:
1165 corner = TopRight;
1166 break;
1167 case SouthWestGravity:
1168 case SouthGravity:
1169 corner = BottomLeft;
1170 break;
1171 case SouthEastGravity:
1172 corner = BottomRight;
1173 break;
1174 default: // NorthWest, Static, etc
1175 corner = TopLeft;
1176 }
1177
1178 // if moving AND resizing ...
1179 if (e.value_mask & (CWX | CWY)) {
1180 int x = (e.value_mask & CWX) ? e.x : _area.x();
1181 int y = (e.value_mask & CWY) ? e.y : _area.y();
1182 resize(corner, w, h, x, y);
1183 } else // if JUST resizing...
1184 resize(corner, w, h);
1185 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1186 int x = (e.value_mask & CWX) ? e.x : _area.x();
1187 int y = (e.value_mask & CWY) ? e.y : _area.y();
1188 move(x, y);
1189 }
1190
1191 if (e.value_mask & CWStackMode) {
1192 switch (e.detail) {
1193 case Below:
1194 case BottomIf:
1195 openbox->screen(_screen)->lowerWindow(this);
1196 break;
1197
1198 case Above:
1199 case TopIf:
1200 default:
1201 openbox->screen(_screen)->raiseWindow(this);
1202 break;
1203 }
1204 }
1205 }
1206
1207
1208 void Client::unmapHandler(const XUnmapEvent &e)
1209 {
1210 if (ignore_unmaps) {
1211 #ifdef DEBUG
1212 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1213 #endif // DEBUG
1214 ignore_unmaps--;
1215 return;
1216 }
1217
1218 #ifdef DEBUG
1219 printf("UnmapNotify for 0x%lx\n", e.window);
1220 #endif // DEBUG
1221
1222 otk::EventHandler::unmapHandler(e);
1223
1224 // this deletes us etc
1225 openbox->screen(_screen)->unmanageWindow(this);
1226 }
1227
1228
1229 void Client::destroyHandler(const XDestroyWindowEvent &e)
1230 {
1231 #ifdef DEBUG
1232 printf("DestroyNotify for 0x%lx\n", e.window);
1233 #endif // DEBUG
1234
1235 otk::EventHandler::destroyHandler(e);
1236
1237 // this deletes us etc
1238 openbox->screen(_screen)->unmanageWindow(this);
1239 }
1240
1241
1242 void Client::reparentHandler(const XReparentEvent &e)
1243 {
1244 // this is when the client is first taken captive in the frame
1245 if (e.parent == frame->plate()) return;
1246
1247 #ifdef DEBUG
1248 printf("ReparentNotify for 0x%lx\n", e.window);
1249 #endif // DEBUG
1250
1251 otk::EventHandler::reparentHandler(e);
1252
1253 /*
1254 This event is quite rare and is usually handled in unmapHandler.
1255 However, if the window is unmapped when the reparent event occurs,
1256 the window manager never sees it because an unmap event is not sent
1257 to an already unmapped window.
1258 */
1259
1260 // we don't want the reparent event, put it back on the stack for the X
1261 // server to deal with after we unmanage the window
1262 XEvent ev;
1263 ev.xreparent = e;
1264 XPutBackEvent(**otk::display, &ev);
1265
1266 // this deletes us etc
1267 openbox->screen(_screen)->unmanageWindow(this);
1268 }
1269
1270 }
This page took 0.094716 seconds and 4 git commands to generate.