]> Dogcows Code - chaz/openbox/blob - src/client.cc
remove includes for widgetbase.hh. fix bug with circular modal pointer.
[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 "bindings.hh"
12 #include "otk/display.hh"
13 #include "otk/property.hh"
14
15 extern "C" {
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/Xatom.h>
19
20 #include "gettext.h"
21 #define _(str) gettext(str)
22 }
23
24 #include <climits>
25 #include <cassert>
26 #include <algorithm>
27
28 namespace ob {
29
30 Client::Client(int screen, Window window)
31 : otk::EventHandler(),
32 frame(0), _screen(screen), _window(window)
33 {
34 assert(screen >= 0);
35 assert(window);
36
37 ignore_unmaps = 0;
38
39 // update EVERYTHING the first time!!
40
41 // defaults
42 _wmstate = NormalState;
43 _focused = false;
44 _transient_for = 0;
45 _layer = Layer_Normal;
46 _urgent = false;
47 _positioned = false;
48 _disabled_decorations = 0;
49 _modal_child = 0;
50 _group = None;
51 _desktop = 0;
52
53 getArea();
54 getDesktop();
55 getState(); // do this before updateTransientFor! (for _modal)
56 getShaped();
57
58 updateTransientFor();
59 getMwmHints();
60 getType(); // this can change the mwmhints for special cases
61
62 updateProtocols();
63
64 getGravity(); // get the attribute gravity
65 updateNormalHints(); // this may override the attribute gravity
66
67 // got the type, the mwmhints, the protocols, and the normal hints (min/max
68 // sizes), so we're ready to set up
69 // the decorations/functions
70 setupDecorAndFunctions();
71
72 // also get the initial_state and set _iconic if we aren't "starting"
73 // when we're "starting" that means we should use whatever state was already
74 // on the window over the initial map state, because it was already mapped
75 updateWMHints(openbox->state() != Openbox::State_Starting);
76 updateTitle();
77 updateIconTitle();
78 updateClass();
79 updateStrut();
80
81 // this makes sure that these windows appear on all desktops
82 if (/*_type == Type_Dock ||*/ _type == Type_Desktop)
83 _desktop = 0xffffffff;
84
85 // set the desktop hint, to make sure that it always exists, and to reflect
86 // any changes we've made here
87 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
88 otk::Property::atoms.cardinal, (unsigned)_desktop);
89
90 changeState();
91 }
92
93
94 Client::~Client()
95 {
96 // clean up childrens' references
97 while (!_transients.empty()) {
98 _transients.front()->_transient_for = 0;
99 _transients.pop_front();
100 }
101
102 // clean up parents reference to this
103 if (_transient_for)
104 _transient_for->_transients.remove(this); // remove from old parent
105
106 if (openbox->state() != Openbox::State_Exiting) {
107 // these values should not be persisted across a window unmapping/mapping
108 otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
109 otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
110 } else {
111 // if we're left in an iconic state, the client wont be mapped. this is
112 // bad, since we will no longer be managing the window on restart
113 if (_iconic)
114 XMapWindow(**otk::display, _window);
115 }
116 }
117
118
119 bool Client::validate() const
120 {
121 XSync(**otk::display, false); // get all events on the server
122
123 XEvent e;
124 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &e) ||
125 XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &e)) {
126 XPutBackEvent(**otk::display, &e);
127 return false;
128 }
129
130 return true;
131 }
132
133
134 void Client::getGravity()
135 {
136 XWindowAttributes wattrib;
137 Status ret;
138
139 ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
140 assert(ret != BadWindow);
141 _gravity = wattrib.win_gravity;
142 }
143
144
145 void Client::getDesktop()
146 {
147 // defaults to the current desktop
148 _desktop = openbox->screen(_screen)->desktop();
149
150 if (otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
151 otk::Property::atoms.cardinal,
152 (long unsigned*)&_desktop)) {
153 #ifdef DEBUG
154 // printf("Window requested desktop: %ld\n", _desktop);
155 #endif
156 }
157 }
158
159
160 void Client::getType()
161 {
162 _type = (WindowType) -1;
163
164 unsigned long *val;
165 unsigned long num = (unsigned) -1;
166 if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
167 otk::Property::atoms.atom, &num, &val)) {
168 // use the first value that we know about in the array
169 for (unsigned long i = 0; i < num; ++i) {
170 if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
171 _type = Type_Desktop;
172 else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
173 _type = Type_Dock;
174 else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
175 _type = Type_Toolbar;
176 else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
177 _type = Type_Menu;
178 else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
179 _type = Type_Utility;
180 else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
181 _type = Type_Splash;
182 else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
183 _type = Type_Dialog;
184 else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
185 _type = Type_Normal;
186 else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override){
187 // prevent this window from getting any decor or functionality
188 _mwmhints.flags &= MwmFlag_Functions | MwmFlag_Decorations;
189 _mwmhints.decorations = 0;
190 _mwmhints.functions = 0;
191 }
192 if (_type != (WindowType) -1)
193 break; // grab the first known type
194 }
195 delete val;
196 }
197
198 if (_type == (WindowType) -1) {
199 /*
200 * the window type hint was not set, which means we either classify ourself
201 * as a normal window or a dialog, depending on if we are a transient.
202 */
203 if (_transient_for)
204 _type = Type_Dialog;
205 else
206 _type = Type_Normal;
207 }
208 }
209
210
211 void Client::setupDecorAndFunctions()
212 {
213 // start with everything (cept fullscreen)
214 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
215 Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
216 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
217 Func_Shade;
218 if (_delete_window) {
219 _decorations |= Decor_Close;
220 _functions |= Func_Close;
221 }
222
223 if (!(_min_size.width() < _max_size.width() ||
224 _min_size.height() < _max_size.height())) {
225 _decorations &= ~(Decor_Maximize | Decor_Handle);
226 _functions &= ~(Func_Resize | Func_Maximize);
227 }
228
229 switch (_type) {
230 case Type_Normal:
231 // normal windows retain all of the possible decorations and
232 // functionality, and are the only windows that you can fullscreen
233 _functions |= Func_Fullscreen;
234 break;
235
236 case Type_Dialog:
237 // dialogs cannot be maximized
238 _decorations &= ~Decor_Maximize;
239 _functions &= ~Func_Maximize;
240 break;
241
242 case Type_Menu:
243 case Type_Toolbar:
244 case Type_Utility:
245 // these windows get less functionality
246 _decorations &= ~(Decor_Iconify | Decor_Handle);
247 _functions &= ~(Func_Iconify | Func_Resize);
248 break;
249
250 case Type_Desktop:
251 case Type_Dock:
252 case Type_Splash:
253 // none of these windows are manipulated by the window manager
254 _decorations = 0;
255 _functions = 0;
256 break;
257 }
258
259 // Mwm Hints are applied subtractively to what has already been chosen for
260 // decor and functionality
261 if (_mwmhints.flags & MwmFlag_Decorations) {
262 if (! (_mwmhints.decorations & MwmDecor_All)) {
263 if (! (_mwmhints.decorations & MwmDecor_Border))
264 _decorations &= ~Decor_Border;
265 if (! (_mwmhints.decorations & MwmDecor_Handle))
266 _decorations &= ~Decor_Handle;
267 if (! (_mwmhints.decorations & MwmDecor_Title))
268 _decorations &= ~Decor_Titlebar;
269 if (! (_mwmhints.decorations & MwmDecor_Iconify))
270 _decorations &= ~Decor_Iconify;
271 if (! (_mwmhints.decorations & MwmDecor_Maximize))
272 _decorations &= ~Decor_Maximize;
273 }
274 }
275
276 if (_mwmhints.flags & MwmFlag_Functions) {
277 if (! (_mwmhints.functions & MwmFunc_All)) {
278 if (! (_mwmhints.functions & MwmFunc_Resize))
279 _functions &= ~Func_Resize;
280 if (! (_mwmhints.functions & MwmFunc_Move))
281 _functions &= ~Func_Move;
282 if (! (_mwmhints.functions & MwmFunc_Iconify))
283 _functions &= ~Func_Iconify;
284 if (! (_mwmhints.functions & MwmFunc_Maximize))
285 _functions &= ~Func_Maximize;
286 // dont let mwm hints kill the close button
287 //if (! (_mwmhints.functions & MwmFunc_Close))
288 // _functions &= ~Func_Close;
289 }
290 }
291
292 // can't maximize without moving/resizing
293 if (!((_functions & Func_Move) && (_functions & Func_Resize)))
294 _functions &= ~Func_Maximize;
295
296 // finally, user specified disabled decorations are applied to subtract
297 // decorations
298 if (_disabled_decorations & Decor_Titlebar)
299 _decorations &= ~Decor_Titlebar;
300 if (_disabled_decorations & Decor_Handle)
301 _decorations &= ~Decor_Handle;
302 if (_disabled_decorations & Decor_Border)
303 _decorations &= ~Decor_Border;
304 if (_disabled_decorations & Decor_Iconify)
305 _decorations &= ~Decor_Iconify;
306 if (_disabled_decorations & Decor_Maximize)
307 _decorations &= ~Decor_Maximize;
308 if (_disabled_decorations & Decor_AllDesktops)
309 _decorations &= ~Decor_AllDesktops;
310 if (_disabled_decorations & Decor_Close)
311 _decorations &= ~Decor_Close;
312
313 // if we don't have a titlebar, then we cannot shade!
314 if (!(_decorations & Decor_Titlebar))
315 _functions &= ~Func_Shade;
316
317 changeAllowedActions();
318
319 if (frame) {
320 frame->adjustSize(); // change the decors on the frame
321 frame->adjustPosition(); // with more/less decorations, we may need to be
322 // moved
323 remaximize(); // with new decor, the window's maximized size may change
324 }
325 }
326
327
328 void Client::getMwmHints()
329 {
330 unsigned long num = MwmHints::elements;
331 unsigned long *hints;
332
333 _mwmhints.flags = 0; // default to none
334
335 if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
336 otk::Property::atoms.motif_wm_hints, &num,
337 (unsigned long **)&hints))
338 return;
339
340 if (num >= MwmHints::elements) {
341 // retrieved the hints
342 _mwmhints.flags = hints[0];
343 _mwmhints.functions = hints[1];
344 _mwmhints.decorations = hints[2];
345 }
346
347 delete [] hints;
348 }
349
350
351 void Client::getArea()
352 {
353 XWindowAttributes wattrib;
354 Status ret;
355
356 ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
357 assert(ret != BadWindow);
358
359 _area = otk::Rect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
360 _border_width = wattrib.border_width;
361 }
362
363
364 void Client::getState()
365 {
366 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
367 _iconic = _skip_taskbar = _skip_pager = false;
368
369 unsigned long *state;
370 unsigned long num = (unsigned) -1;
371
372 if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
373 otk::Property::atoms.atom, &num, &state)) {
374 for (unsigned long i = 0; i < num; ++i) {
375 if (state[i] == otk::Property::atoms.net_wm_state_modal)
376 _modal = true;
377 else if (state[i] == otk::Property::atoms.net_wm_state_shaded)
378 _shaded = true;
379 else if (state[i] == otk::Property::atoms.net_wm_state_hidden)
380 _iconic = true;
381 else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
382 _skip_taskbar = true;
383 else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
384 _skip_pager = true;
385 else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
386 _fullscreen = true;
387 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
388 _max_vert = true;
389 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
390 _max_horz = true;
391 else if (state[i] == otk::Property::atoms.net_wm_state_above)
392 _above = true;
393 else if (state[i] == otk::Property::atoms.net_wm_state_below)
394 _below = true;
395 }
396
397 delete [] state;
398 }
399 }
400
401
402 void Client::getShaped()
403 {
404 _shaped = false;
405 #ifdef SHAPE
406 if (otk::display->shape()) {
407 int foo;
408 unsigned int ufoo;
409 int s;
410
411 XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
412
413 XShapeQueryExtents(**otk::display, _window, &s, &foo,
414 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
415 _shaped = (s != 0);
416 }
417 #endif // SHAPE
418 }
419
420
421 void Client::calcLayer() {
422 StackLayer l;
423
424 if (_iconic) l = Layer_Icon;
425 else if (_fullscreen) l = Layer_Fullscreen;
426 else if (_type == Type_Desktop) l = Layer_Desktop;
427 else if (_type == Type_Dock) {
428 if (!_below) l = Layer_Top;
429 else l = Layer_Normal;
430 }
431 else if (_above) l = Layer_Above;
432 else if (_below) l = Layer_Below;
433 else l = Layer_Normal;
434
435 if (l != _layer) {
436 _layer = l;
437 if (frame) {
438 /*
439 if we don't have a frame, then we aren't mapped yet (and this would
440 SIGSEGV :)
441 */
442 openbox->screen(_screen)->raiseWindow(this);
443 }
444 }
445 }
446
447
448 void Client::updateProtocols()
449 {
450 Atom *proto;
451 int num_return = 0;
452
453 _focus_notify = false;
454 _delete_window = false;
455
456 if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
457 for (int i = 0; i < num_return; ++i) {
458 if (proto[i] == otk::Property::atoms.wm_delete_window) {
459 // this means we can request the window to close
460 _delete_window = true;
461 } else if (proto[i] == otk::Property::atoms.wm_take_focus)
462 // if this protocol is requested, then the window will be notified
463 // by the window manager whenever it receives focus
464 _focus_notify = true;
465 }
466 XFree(proto);
467 }
468 }
469
470
471 void Client::updateNormalHints()
472 {
473 XSizeHints size;
474 long ret;
475 int oldgravity = _gravity;
476
477 // defaults
478 _min_ratio = 0.0;
479 _max_ratio = 0.0;
480 _size_inc = otk::Size(1, 1);
481 _base_size = otk::Size(0, 0);
482 _min_size = otk::Size(0, 0);
483 _max_size = otk::Size(UINT_MAX, UINT_MAX);
484
485 // get the hints from the window
486 if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
487 _positioned = (size.flags & (PPosition|USPosition));
488
489 if (size.flags & PWinGravity) {
490 _gravity = size.win_gravity;
491
492 // if the client has a frame, i.e. has already been mapped and is
493 // changing its gravity
494 if (frame && _gravity != oldgravity) {
495 // move our idea of the client's position based on its new gravity
496 int x = frame->area().x(), y = frame->area().y();
497 frame->frameGravity(x, y);
498 _area = otk::Rect(otk::Point(x, y), _area.size());
499 }
500 }
501
502 if (size.flags & PAspect) {
503 if (size.min_aspect.y) _min_ratio = size.min_aspect.x/size.min_aspect.y;
504 if (size.max_aspect.y) _max_ratio = size.max_aspect.x/size.max_aspect.y;
505 }
506
507 if (size.flags & PMinSize)
508 _min_size = otk::Size(size.min_width, size.min_height);
509
510 if (size.flags & PMaxSize)
511 _max_size = otk::Size(size.max_width, size.max_height);
512
513 if (size.flags & PBaseSize)
514 _base_size = otk::Size(size.base_width, size.base_height);
515
516 if (size.flags & PResizeInc)
517 _size_inc = otk::Size(size.width_inc, size.height_inc);
518 }
519 }
520
521
522 void Client::updateWMHints(bool initstate)
523 {
524 XWMHints *hints;
525
526 // assume a window takes input if it doesnt specify
527 _can_focus = true;
528 bool ur = false;
529
530 if ((hints = XGetWMHints(**otk::display, _window)) != NULL) {
531 if (hints->flags & InputHint)
532 _can_focus = hints->input;
533
534 // only do this when initstate is true!
535 if (initstate && (hints->flags & StateHint))
536 _iconic = hints->initial_state == IconicState;
537
538 if (hints->flags & XUrgencyHint)
539 ur = true;
540
541 if (hints->flags & WindowGroupHint) {
542 if (hints->window_group != _group) {
543 // XXX: remove from the old group if there was one
544 _group = hints->window_group;
545 // XXX: do stuff with the group
546 }
547 } else // no group!
548 _group = None;
549
550 XFree(hints);
551 }
552
553 if (ur != _urgent) {
554 _urgent = ur;
555 #ifdef DEBUG
556 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
557 (long)_window, _urgent ? "ON" : "OFF");
558 #endif
559 // fire the urgent callback if we're mapped, otherwise, wait until after
560 // we're mapped
561 if (frame)
562 fireUrgent();
563 }
564 }
565
566
567 void Client::updateTitle()
568 {
569 _title = "";
570
571 // try netwm
572 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_name,
573 otk::Property::utf8, &_title)) {
574 // try old x stuff
575 otk::Property::get(_window, otk::Property::atoms.wm_name,
576 otk::Property::ascii, &_title);
577 }
578
579 if (_title.empty())
580 _title = _("Unnamed Window");
581
582 if (frame)
583 frame->adjustTitle();
584 }
585
586
587 void Client::updateIconTitle()
588 {
589 _icon_title = "";
590
591 // try netwm
592 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon_name,
593 otk::Property::utf8, &_icon_title)) {
594 // try old x stuff
595 otk::Property::get(_window, otk::Property::atoms.wm_icon_name,
596 otk::Property::ascii, &_icon_title);
597 }
598
599 if (_title.empty())
600 _icon_title = _("Unnamed Window");
601 }
602
603
604 void Client::updateClass()
605 {
606 // set the defaults
607 _app_name = _app_class = _role = "";
608
609 otk::Property::StringVect v;
610 unsigned long num = 2;
611
612 if (otk::Property::get(_window, otk::Property::atoms.wm_class,
613 otk::Property::ascii, &num, &v)) {
614 if (num > 0) _app_name = v[0].c_str();
615 if (num > 1) _app_class = v[1].c_str();
616 }
617
618 v.clear();
619 num = 1;
620 if (otk::Property::get(_window, otk::Property::atoms.wm_window_role,
621 otk::Property::ascii, &num, &v)) {
622 if (num > 0) _role = v[0].c_str();
623 }
624 }
625
626
627 void Client::updateStrut()
628 {
629 unsigned long num = 4;
630 unsigned long *data;
631 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_strut,
632 otk::Property::atoms.cardinal, &num, &data))
633 return;
634
635 if (num == 4) {
636 _strut.left = data[0];
637 _strut.right = data[1];
638 _strut.top = data[2];
639 _strut.bottom = data[3];
640
641 // updating here is pointless while we're being mapped cuz we're not in
642 // the screen's client list yet
643 if (frame)
644 openbox->screen(_screen)->updateStrut();
645 }
646
647 delete [] data;
648 }
649
650
651 void Client::updateTransientFor()
652 {
653 Window t = 0;
654 Client *c = 0;
655
656 if (XGetTransientForHint(**otk::display, _window, &t) &&
657 t != _window) { // cant be transient to itself!
658 c = openbox->findClient(t);
659 assert(c != this); // if this happens then we need to check for it
660
661 if (!c /*XXX: && _group*/) {
662 // not transient to a client, see if it is transient for a group
663 if (//t == _group->leader() ||
664 t == None ||
665 t == otk::display->screenInfo(_screen)->rootWindow()) {
666 // window is a transient for its group!
667 // XXX: for now this is treated as non-transient.
668 // this needs to be fixed!
669 }
670 }
671 }
672
673 // if anything has changed...
674 if (c != _transient_for) {
675 bool m = _modal;
676 if (_modal)
677 setModal(false);
678
679 if (_transient_for)
680 _transient_for->_transients.remove(this); // remove from old parent
681 _transient_for = c;
682 if (_transient_for)
683 _transient_for->_transients.push_back(this); // add to new parent
684
685 if (m)
686 setModal(true);
687 }
688 }
689
690
691 void Client::propertyHandler(const XPropertyEvent &e)
692 {
693 otk::EventHandler::propertyHandler(e);
694
695 // validate cuz we query stuff off the client here
696 if (!validate()) return;
697
698 // compress changes to a single property into a single change
699 XEvent ce;
700 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
701 // XXX: it would be nice to compress ALL changes to a property, not just
702 // changes in a row without other props between.
703 if (ce.xproperty.atom != e.atom) {
704 XPutBackEvent(**otk::display, &ce);
705 break;
706 }
707 }
708
709 if (e.atom == XA_WM_NORMAL_HINTS) {
710 updateNormalHints();
711 setupDecorAndFunctions(); // normal hints can make a window non-resizable
712 } else if (e.atom == XA_WM_HINTS)
713 updateWMHints();
714 else if (e.atom == XA_WM_TRANSIENT_FOR) {
715 updateTransientFor();
716 getType();
717 calcLayer(); // type may have changed, so update the layer
718 setupDecorAndFunctions();
719 }
720 else if (e.atom == otk::Property::atoms.net_wm_name ||
721 e.atom == otk::Property::atoms.wm_name)
722 updateTitle();
723 else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
724 e.atom == otk::Property::atoms.wm_icon_name)
725 updateIconTitle();
726 else if (e.atom == otk::Property::atoms.wm_class)
727 updateClass();
728 else if (e.atom == otk::Property::atoms.wm_protocols) {
729 updateProtocols();
730 setupDecorAndFunctions();
731 }
732 else if (e.atom == otk::Property::atoms.net_wm_strut)
733 updateStrut();
734 }
735
736
737 void Client::setWMState(long state)
738 {
739 if (state == _wmstate) return; // no change
740
741 switch (state) {
742 case IconicState:
743 setDesktop(ICONIC_DESKTOP);
744 break;
745 case NormalState:
746 setDesktop(openbox->screen(_screen)->desktop());
747 break;
748 }
749 }
750
751
752 void Client::setDesktop(long target)
753 {
754 if (target == _desktop) return;
755
756 printf("Setting desktop %ld\n", target);
757
758 if (!(target >= 0 || target == (signed)0xffffffff ||
759 target == ICONIC_DESKTOP))
760 return;
761
762 _desktop = target;
763
764 // set the desktop hint, but not if we're iconifying
765 if (_desktop != ICONIC_DESKTOP)
766 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
767 otk::Property::atoms.cardinal, (unsigned)_desktop);
768
769 // 'move' the window to the new desktop
770 if (_desktop == openbox->screen(_screen)->desktop() ||
771 _desktop == (signed)0xffffffff)
772 frame->show();
773 else
774 frame->hide();
775
776 // Handle Iconic state. Iconic state is maintained by the client being a
777 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
778 // uniconifying happen.
779 bool i = _desktop == ICONIC_DESKTOP;
780 if (i != _iconic) { // has the state changed?
781 _iconic = i;
782 if (_iconic) {
783 _wmstate = IconicState;
784 ignore_unmaps++;
785 // we unmap the client itself so that we can get MapRequest events, and
786 // because the ICCCM tells us to!
787 XUnmapWindow(**otk::display, _window);
788 } else {
789 _wmstate = NormalState;
790 XMapWindow(**otk::display, _window);
791 }
792 changeState();
793 }
794
795 frame->adjustState();
796 }
797
798
799 Client *Client::findModalChild(Client *skip) const
800 {
801 Client *ret = 0;
802
803 // find a modal child recursively and try focus it
804 List::const_iterator it, end = _transients.end();
805 for (it = _transients.begin(); it != end; ++it)
806 if ((*it)->_modal && *it != skip)
807 return *it; // got one
808 // none of our direct children are modal, let them try check
809 for (it = _transients.begin(); it != end; ++it)
810 if ((ret = (*it)->findModalChild()))
811 return ret; // got one
812 return ret;
813 }
814
815
816 void Client::setModal(bool modal)
817 {
818 if (modal == _modal) return;
819
820 if (modal) {
821 Client *c = this;
822 while (c->_transient_for) {
823 c = c->_transient_for;
824 if (c == this) break; // circular?
825 if (c->_modal_child) break; // already has a modal child
826 c->_modal_child = this;
827 }
828 } else {
829 // try find a replacement modal dialog
830 Client *replacement = 0;
831
832 Client *c = this;
833 while (c->_transient_for) // go up the tree
834 c = c->_transient_for;
835 replacement = c->findModalChild(this); // find a modal child, skipping this
836
837 c = this;
838 while (c->_transient_for) {
839 c = c->_transient_for;
840 if (c == this) break; // circular?
841 if (c->_modal_child != this) break; // has a different modal child
842 if (c == replacement) break; // found the replacement itself
843 c->_modal_child = replacement;
844 }
845 }
846 _modal = modal;
847 }
848
849
850 void Client::setState(StateAction action, long data1, long data2)
851 {
852 bool shadestate = _shaded;
853 bool fsstate = _fullscreen;
854 bool maxh = _max_horz;
855 bool maxv = _max_vert;
856 bool modal = _modal;
857
858 if (!(action == State_Add || action == State_Remove ||
859 action == State_Toggle))
860 return; // an invalid action was passed to the client message, ignore it
861
862 for (int i = 0; i < 2; ++i) {
863 Atom state = i == 0 ? data1 : data2;
864
865 if (! state) continue;
866
867 // if toggling, then pick whether we're adding or removing
868 if (action == State_Toggle) {
869 if (state == otk::Property::atoms.net_wm_state_modal)
870 action = _modal ? State_Remove : State_Add;
871 else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
872 action = _max_vert ? State_Remove : State_Add;
873 else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
874 action = _max_horz ? State_Remove : State_Add;
875 else if (state == otk::Property::atoms.net_wm_state_shaded)
876 action = _shaded ? State_Remove : State_Add;
877 else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
878 action = _skip_taskbar ? State_Remove : State_Add;
879 else if (state == otk::Property::atoms.net_wm_state_skip_pager)
880 action = _skip_pager ? State_Remove : State_Add;
881 else if (state == otk::Property::atoms.net_wm_state_fullscreen)
882 action = _fullscreen ? State_Remove : State_Add;
883 else if (state == otk::Property::atoms.net_wm_state_above)
884 action = _above ? State_Remove : State_Add;
885 else if (state == otk::Property::atoms.net_wm_state_below)
886 action = _below ? State_Remove : State_Add;
887 }
888
889 if (action == State_Add) {
890 if (state == otk::Property::atoms.net_wm_state_modal) {
891 if (_modal) continue;
892 modal = true;
893 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
894 maxv = true;
895 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
896 if (_max_horz) continue;
897 maxh = true;
898 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
899 shadestate = true;
900 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
901 _skip_taskbar = true;
902 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
903 _skip_pager = true;
904 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
905 fsstate = true;
906 } else if (state == otk::Property::atoms.net_wm_state_above) {
907 if (_above) continue;
908 _above = true;
909 } else if (state == otk::Property::atoms.net_wm_state_below) {
910 if (_below) continue;
911 _below = true;
912 }
913
914 } else { // action == State_Remove
915 if (state == otk::Property::atoms.net_wm_state_modal) {
916 if (!_modal) continue;
917 modal = false;
918 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
919 maxv = false;
920 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
921 maxh = false;
922 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
923 shadestate = false;
924 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
925 _skip_taskbar = false;
926 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
927 _skip_pager = false;
928 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
929 fsstate = false;
930 } else if (state == otk::Property::atoms.net_wm_state_above) {
931 if (!_above) continue;
932 _above = false;
933 } else if (state == otk::Property::atoms.net_wm_state_below) {
934 if (!_below) continue;
935 _below = false;
936 }
937 }
938 }
939 if (maxh != _max_horz || maxv != _max_vert) {
940 if (maxh != _max_horz && maxv != _max_vert) { // toggling both
941 if (maxh == maxv) { // both going the same way
942 maximize(maxh, 0, true);
943 } else {
944 maximize(maxh, 1, true);
945 maximize(maxv, 2, true);
946 }
947 } else { // toggling one
948 if (maxh != _max_horz)
949 maximize(maxh, 1, true);
950 else
951 maximize(maxv, 2, true);
952 }
953 }
954 if (modal != _modal)
955 setModal(modal);
956 // change fullscreen state before shading, as it will affect if the window
957 // can shade or not
958 if (fsstate != _fullscreen)
959 fullscreen(fsstate, true);
960 if (shadestate != _shaded)
961 shade(shadestate);
962 calcLayer();
963 changeState(); // change the hint to relect these changes
964 }
965
966
967 void Client::toggleClientBorder(bool addborder)
968 {
969 // adjust our idea of where the client is, based on its border. When the
970 // border is removed, the client should now be considered to be in a
971 // different position.
972 // when re-adding the border to the client, the same operation needs to be
973 // reversed.
974 int oldx = _area.x(), oldy = _area.y();
975 int x = oldx, y = oldy;
976 switch(_gravity) {
977 default:
978 case NorthWestGravity:
979 case WestGravity:
980 case SouthWestGravity:
981 break;
982 case NorthEastGravity:
983 case EastGravity:
984 case SouthEastGravity:
985 if (addborder) x -= _border_width * 2;
986 else x += _border_width * 2;
987 break;
988 case NorthGravity:
989 case SouthGravity:
990 case CenterGravity:
991 case ForgetGravity:
992 case StaticGravity:
993 if (addborder) x -= _border_width;
994 else x += _border_width;
995 break;
996 }
997 switch(_gravity) {
998 default:
999 case NorthWestGravity:
1000 case NorthGravity:
1001 case NorthEastGravity:
1002 break;
1003 case SouthWestGravity:
1004 case SouthGravity:
1005 case SouthEastGravity:
1006 if (addborder) y -= _border_width * 2;
1007 else y += _border_width * 2;
1008 break;
1009 case WestGravity:
1010 case EastGravity:
1011 case CenterGravity:
1012 case ForgetGravity:
1013 case StaticGravity:
1014 if (addborder) y -= _border_width;
1015 else y += _border_width;
1016 break;
1017 }
1018 _area = otk::Rect(otk::Point(x, y), _area.size());
1019
1020 if (addborder) {
1021 XSetWindowBorderWidth(**otk::display, _window, _border_width);
1022
1023 // move the client so it is back it the right spot _with_ its border!
1024 if (x != oldx || y != oldy)
1025 XMoveWindow(**otk::display, _window, x, y);
1026 } else
1027 XSetWindowBorderWidth(**otk::display, _window, 0);
1028 }
1029
1030
1031 void Client::clientMessageHandler(const XClientMessageEvent &e)
1032 {
1033 otk::EventHandler::clientMessageHandler(e);
1034
1035 // validate cuz we query stuff off the client here
1036 if (!validate()) return;
1037
1038 if (e.format != 32) return;
1039
1040 if (e.message_type == otk::Property::atoms.wm_change_state) {
1041 // compress changes into a single change
1042 bool compress = false;
1043 XEvent ce;
1044 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
1045 // XXX: it would be nice to compress ALL messages of a type, not just
1046 // messages in a row without other message types between.
1047 if (ce.xclient.message_type != e.message_type) {
1048 XPutBackEvent(**otk::display, &ce);
1049 break;
1050 }
1051 compress = true;
1052 }
1053 if (compress)
1054 setWMState(ce.xclient.data.l[0]); // use the found event
1055 else
1056 setWMState(e.data.l[0]); // use the original event
1057 } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
1058 // compress changes into a single change
1059 bool compress = false;
1060 XEvent ce;
1061 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
1062 // XXX: it would be nice to compress ALL messages of a type, not just
1063 // messages in a row without other message types between.
1064 if (ce.xclient.message_type != e.message_type) {
1065 XPutBackEvent(**otk::display, &ce);
1066 break;
1067 }
1068 compress = true;
1069 }
1070 if (compress)
1071 setDesktop(e.data.l[0]); // use the found event
1072 else
1073 setDesktop(e.data.l[0]); // use the original event
1074 } else if (e.message_type == otk::Property::atoms.net_wm_state) {
1075 // can't compress these
1076 #ifdef DEBUG
1077 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1078 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
1079 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
1080 e.data.l[1], e.data.l[2], _window);
1081 #endif
1082 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
1083 } else if (e.message_type == otk::Property::atoms.net_close_window) {
1084 #ifdef DEBUG
1085 printf("net_close_window for 0x%lx\n", _window);
1086 #endif
1087 close();
1088 } else if (e.message_type == otk::Property::atoms.net_active_window) {
1089 #ifdef DEBUG
1090 printf("net_active_window for 0x%lx\n", _window);
1091 #endif
1092 if (_iconic)
1093 setDesktop(openbox->screen(_screen)->desktop());
1094 if (_shaded)
1095 shade(false);
1096 focus();
1097 openbox->screen(_screen)->raiseWindow(this);
1098 } else if (e.message_type == otk::Property::atoms.openbox_active_window) {
1099 if (_iconic)
1100 setDesktop(openbox->screen(_screen)->desktop());
1101 if (e.data.l[0] && _shaded)
1102 shade(false);
1103 focus();
1104 if (e.data.l[1])
1105 openbox->screen(_screen)->raiseWindow(this);
1106 }
1107 }
1108
1109
1110 #if defined(SHAPE)
1111 void Client::shapeHandler(const XShapeEvent &e)
1112 {
1113 otk::EventHandler::shapeHandler(e);
1114
1115 if (e.kind == ShapeBounding) {
1116 _shaped = e.shaped;
1117 frame->adjustShape();
1118 }
1119 }
1120 #endif
1121
1122
1123 void Client::resize(Corner anchor, unsigned int w, unsigned int h)
1124 {
1125 if (!(_functions & Func_Resize)) return;
1126 internal_resize(anchor, w, h);
1127 }
1128
1129
1130 void Client::internal_resize(Corner anchor, unsigned int w, unsigned int h,
1131 bool user, int x, int y)
1132 {
1133 if (_base_size.width() < w)
1134 w -= _base_size.width();
1135 else
1136 w = 0;
1137 if (_base_size.height() < h)
1138 h -= _base_size.height();
1139 else
1140 h = 0;
1141
1142 if (user) {
1143 // for interactive resizing. have to move half an increment in each
1144 // direction.
1145 unsigned int mw = w % _size_inc.width(); // how far we are towards the next
1146 // size inc
1147 unsigned int mh = h % _size_inc.height();
1148 unsigned int aw = _size_inc.width() / 2; // amount to add
1149 unsigned int ah = _size_inc.height() / 2;
1150 // don't let us move into a new size increment
1151 if (mw + aw >= _size_inc.width()) aw = _size_inc.width() - mw - 1;
1152 if (mh + ah >= _size_inc.height()) ah = _size_inc.height() - mh - 1;
1153 w += aw;
1154 h += ah;
1155
1156 // if this is a user-requested resize, then check against min/max sizes
1157 // and aspect ratios
1158
1159 // smaller than min size or bigger than max size?
1160 if (w < _min_size.width()) w = _min_size.width();
1161 else if (w > _max_size.width()) w = _max_size.width();
1162 if (h < _min_size.height()) h = _min_size.height();
1163 else if (h > _max_size.height()) h = _max_size.height();
1164
1165 // adjust the height ot match the width for the aspect ratios
1166 if (_min_ratio)
1167 if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
1168 if (_max_ratio)
1169 if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
1170 }
1171
1172 // keep to the increments
1173 w /= _size_inc.width();
1174 h /= _size_inc.height();
1175
1176 // you cannot resize to nothing
1177 if (w < 1) w = 1;
1178 if (h < 1) h = 1;
1179
1180 // store the logical size
1181 _logical_size = otk::Size(w, h);
1182
1183 w *= _size_inc.width();
1184 h *= _size_inc.height();
1185
1186 w += _base_size.width();
1187 h += _base_size.height();
1188
1189 if (x == INT_MIN || y == INT_MIN) {
1190 x = _area.x();
1191 y = _area.y();
1192 switch (anchor) {
1193 case TopLeft:
1194 break;
1195 case TopRight:
1196 x -= w - _area.width();
1197 break;
1198 case BottomLeft:
1199 y -= h - _area.height();
1200 break;
1201 case BottomRight:
1202 x -= w - _area.width();
1203 y -= h - _area.height();
1204 break;
1205 }
1206 }
1207
1208 _area = otk::Rect(_area.position(), otk::Size(w, h));
1209
1210 XResizeWindow(**otk::display, _window, w, h);
1211
1212 // resize the frame to match the request
1213 frame->adjustSize();
1214 internal_move(x, y);
1215 }
1216
1217
1218 void Client::move(int x, int y)
1219 {
1220 if (!(_functions & Func_Move)) return;
1221 frame->frameGravity(x, y); // get the client's position based on x,y for the
1222 // frame
1223 internal_move(x, y);
1224 }
1225
1226
1227 void Client::internal_move(int x, int y)
1228 {
1229 _area = otk::Rect(otk::Point(x, y), _area.size());
1230
1231 // move the frame to be in the requested position
1232 if (frame) { // this can be called while mapping, before frame exists
1233 frame->adjustPosition();
1234
1235 // send synthetic configure notify (we don't need to if we aren't mapped
1236 // yet)
1237 XEvent event;
1238 event.type = ConfigureNotify;
1239 event.xconfigure.display = **otk::display;
1240 event.xconfigure.event = _window;
1241 event.xconfigure.window = _window;
1242
1243 // root window coords with border in mind
1244 event.xconfigure.x = x - _border_width + frame->size().left;
1245 event.xconfigure.y = y - _border_width + frame->size().top;
1246
1247 event.xconfigure.width = _area.width();
1248 event.xconfigure.height = _area.height();
1249 event.xconfigure.border_width = _border_width;
1250 event.xconfigure.above = frame->plate();
1251 event.xconfigure.override_redirect = False;
1252 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1253 StructureNotifyMask, &event);
1254 #if 0//def DEBUG
1255 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1256 event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
1257 event.xconfigure.height, event.xconfigure.window);
1258 #endif
1259 }
1260 }
1261
1262
1263 void Client::close()
1264 {
1265 XEvent ce;
1266
1267 if (!(_functions & Func_Close)) return;
1268
1269 // XXX: itd be cool to do timeouts and shit here for killing the client's
1270 // process off
1271 // like... if the window is around after 5 seconds, then the close button
1272 // turns a nice red, and if this function is called again, the client is
1273 // explicitly killed.
1274
1275 ce.xclient.type = ClientMessage;
1276 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1277 ce.xclient.display = **otk::display;
1278 ce.xclient.window = _window;
1279 ce.xclient.format = 32;
1280 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1281 ce.xclient.data.l[1] = CurrentTime;
1282 ce.xclient.data.l[2] = 0l;
1283 ce.xclient.data.l[3] = 0l;
1284 ce.xclient.data.l[4] = 0l;
1285 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1286 }
1287
1288
1289 void Client::changeState()
1290 {
1291 unsigned long state[2];
1292 state[0] = _wmstate;
1293 state[1] = None;
1294 otk::Property::set(_window, otk::Property::atoms.wm_state,
1295 otk::Property::atoms.wm_state, state, 2);
1296
1297 Atom netstate[10];
1298 int num = 0;
1299 if (_modal)
1300 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1301 if (_shaded)
1302 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1303 if (_iconic)
1304 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1305 if (_skip_taskbar)
1306 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1307 if (_skip_pager)
1308 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1309 if (_fullscreen)
1310 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1311 if (_max_vert)
1312 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1313 if (_max_horz)
1314 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1315 if (_above)
1316 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1317 if (_below)
1318 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1319 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1320 otk::Property::atoms.atom, netstate, num);
1321
1322 calcLayer();
1323
1324 if (frame)
1325 frame->adjustState();
1326 }
1327
1328
1329 void Client::changeAllowedActions(void)
1330 {
1331 Atom actions[9];
1332 int num = 0;
1333
1334 actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1335
1336 if (_functions & Func_Shade)
1337 actions[num++] = otk::Property::atoms.net_wm_action_shade;
1338 if (_functions & Func_Close)
1339 actions[num++] = otk::Property::atoms.net_wm_action_close;
1340 if (_functions & Func_Move)
1341 actions[num++] = otk::Property::atoms.net_wm_action_move;
1342 if (_functions & Func_Iconify)
1343 actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1344 if (_functions & Func_Resize)
1345 actions[num++] = otk::Property::atoms.net_wm_action_resize;
1346 if (_functions & Func_Fullscreen)
1347 actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1348 if (_functions & Func_Maximize) {
1349 actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1350 actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1351 }
1352
1353 otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1354 otk::Property::atoms.atom, actions, num);
1355
1356 // make sure the window isn't breaking any rules now
1357
1358 if (!(_functions & Func_Shade) && _shaded)
1359 if (frame) shade(false);
1360 else _shaded = false;
1361 if (!(_functions & Func_Iconify) && _iconic)
1362 if (frame) setDesktop(openbox->screen(_screen)->desktop());
1363 else _iconic = false;
1364 if (!(_functions & Func_Fullscreen) && _fullscreen)
1365 if (frame) fullscreen(false);
1366 else _fullscreen = false;
1367 if (!(_functions & Func_Maximize) && (_max_horz || _max_vert))
1368 if (frame) maximize(false, 0);
1369 else _max_vert = _max_horz = false;
1370 }
1371
1372
1373 void Client::remaximize()
1374 {
1375 int dir;
1376 if (_max_horz && _max_vert)
1377 dir = 0;
1378 else if (_max_horz)
1379 dir = 1;
1380 else if (_max_vert)
1381 dir = 2;
1382 else
1383 return; // not maximized
1384 _max_horz = _max_vert = false;
1385 maximize(true, dir, false);
1386 }
1387
1388
1389 void Client::applyStartupState()
1390 {
1391 // these are in a carefully crafted order..
1392
1393 if (_modal) {
1394 _modal = false;
1395 setModal(true);
1396 }
1397
1398 if (_iconic) {
1399 _iconic = false;
1400 setDesktop(ICONIC_DESKTOP);
1401 }
1402 if (_fullscreen) {
1403 _fullscreen = false;
1404 fullscreen(true, false);
1405 }
1406 if (_shaded) {
1407 _shaded = false;
1408 shade(true);
1409 }
1410 if (_urgent)
1411 fireUrgent();
1412
1413 if (_max_vert && _max_horz) {
1414 _max_vert = _max_horz = false;
1415 maximize(true, 0, false);
1416 } else if (_max_vert) {
1417 _max_vert = false;
1418 maximize(true, 2, false);
1419 } else if (_max_horz) {
1420 _max_horz = false;
1421 maximize(true, 1, false);
1422 }
1423
1424 if (_skip_taskbar); // nothing to do for this
1425 if (_skip_pager); // nothing to do for this
1426 if (_modal); // nothing to do for this
1427 if (_above); // nothing to do for this
1428 if (_below); // nothing to do for this
1429 }
1430
1431
1432 void Client::fireUrgent()
1433 {
1434 // call the python UrgentWindow callbacks
1435 EventData data(_screen, this, EventAction::UrgentWindow, 0);
1436 openbox->bindings()->fireEvent(&data);
1437 }
1438
1439
1440 void Client::shade(bool shade)
1441 {
1442 if (!(_functions & Func_Shade) || // can't
1443 _shaded == shade) return; // already done
1444
1445 // when we're iconic, don't change the wmstate
1446 if (!_iconic)
1447 _wmstate = shade ? IconicState : NormalState;
1448 _shaded = shade;
1449 changeState();
1450 frame->adjustSize();
1451 }
1452
1453
1454 void Client::maximize(bool max, int dir, bool savearea)
1455 {
1456 assert(dir == 0 || dir == 1 || dir == 2);
1457 if (!(_functions & Func_Maximize)) return; // can't
1458
1459 // check if already done
1460 if (max) {
1461 if (dir == 0 && _max_horz && _max_vert) return;
1462 if (dir == 1 && _max_horz) return;
1463 if (dir == 2 && _max_vert) return;
1464 } else {
1465 if (dir == 0 && !_max_horz && !_max_vert) return;
1466 if (dir == 1 && !_max_horz) return;
1467 if (dir == 2 && !_max_vert) return;
1468 }
1469
1470 const otk::Rect &a = openbox->screen(_screen)->area();
1471 int x = frame->area().x(), y = frame->area().y(),
1472 w = _area.width(), h = _area.height();
1473
1474 if (max) {
1475 if (savearea) {
1476 long dimensions[4];
1477 long *readdim;
1478 unsigned long n = 4;
1479
1480 dimensions[0] = x;
1481 dimensions[1] = y;
1482 dimensions[2] = w;
1483 dimensions[3] = h;
1484
1485 // get the property off the window and use it for the dimentions we are
1486 // already maxed on
1487 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1488 otk::Property::atoms.cardinal, &n,
1489 (long unsigned**) &readdim)) {
1490 if (n >= 4) {
1491 if (_max_horz) {
1492 dimensions[0] = readdim[0];
1493 dimensions[2] = readdim[2];
1494 }
1495 if (_max_vert) {
1496 dimensions[1] = readdim[1];
1497 dimensions[3] = readdim[3];
1498 }
1499 }
1500 delete readdim;
1501 }
1502
1503 otk::Property::set(_window, otk::Property::atoms.openbox_premax,
1504 otk::Property::atoms.cardinal,
1505 (long unsigned*)dimensions, 4);
1506 }
1507 if (dir == 0 || dir == 1) { // horz
1508 x = a.x();
1509 w = a.width();
1510 }
1511 if (dir == 0 || dir == 2) { // vert
1512 y = a.y();
1513 h = a.height() - frame->size().top - frame->size().bottom;
1514 }
1515 } else {
1516 long *dimensions;
1517 long unsigned n = 4;
1518
1519 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1520 otk::Property::atoms.cardinal, &n,
1521 (long unsigned**) &dimensions)) {
1522 if (n >= 4) {
1523 if (dir == 0 || dir == 1) { // horz
1524 x = (signed int)dimensions[0];
1525 w = (signed int)dimensions[2];
1526 }
1527 if (dir == 0 || dir == 2) { // vert
1528 y = (signed int)dimensions[1];
1529 h = (signed int)dimensions[3];
1530 }
1531 }
1532 delete dimensions;
1533 } else {
1534 // pick some fallbacks...
1535 if (dir == 0 || dir == 1) { // horz
1536 x = a.x() + a.width() / 4;
1537 w = a.width() / 2;
1538 }
1539 if (dir == 0 || dir == 2) { // vert
1540 y = a.y() + a.height() / 4;
1541 h = a.height() / 2;
1542 }
1543 }
1544 }
1545
1546 if (dir == 0 || dir == 1) // horz
1547 _max_horz = max;
1548 if (dir == 0 || dir == 2) // vert
1549 _max_vert = max;
1550
1551 if (!_max_horz && !_max_vert)
1552 otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
1553
1554 changeState(); // change the state hints on the client
1555
1556 frame->frameGravity(x, y); // figure out where the client should be going
1557 internal_resize(TopLeft, w, h, true, x, y);
1558 }
1559
1560
1561 void Client::fullscreen(bool fs, bool savearea)
1562 {
1563 static FunctionFlags saved_func;
1564 static DecorationFlags saved_decor;
1565
1566 if (!(_functions & Func_Fullscreen) || // can't
1567 _fullscreen == fs) return; // already done
1568
1569 _fullscreen = fs;
1570 changeState(); // change the state hints on the client
1571
1572 int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
1573
1574 if (fs) {
1575 // save the functions and remove them
1576 saved_func = _functions;
1577 _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1578 // save the decorations and remove them
1579 saved_decor = _decorations;
1580 _decorations = 0;
1581 if (savearea) {
1582 long dimensions[4];
1583 dimensions[0] = _area.x();
1584 dimensions[1] = _area.y();
1585 dimensions[2] = _area.width();
1586 dimensions[3] = _area.height();
1587 otk::Property::set(_window, otk::Property::atoms.openbox_premax,
1588 otk::Property::atoms.cardinal,
1589 (long unsigned*)dimensions, 4);
1590 }
1591 const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1592 x = 0;
1593 y = 0;
1594 w = info->size().width();
1595 h = info->size().height();
1596 } else {
1597 _functions = saved_func;
1598 _decorations = saved_decor;
1599
1600 long *dimensions;
1601 long unsigned n = 4;
1602
1603 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1604 otk::Property::atoms.cardinal, &n,
1605 (long unsigned**) &dimensions)) {
1606 if (n >= 4) {
1607 x = dimensions[0];
1608 y = dimensions[1];
1609 w = dimensions[2];
1610 h = dimensions[3];
1611 }
1612 delete dimensions;
1613 } else {
1614 // pick some fallbacks...
1615 const otk::Rect &a = openbox->screen(_screen)->area();
1616 x = a.x() + a.width() / 4;
1617 y = a.y() + a.height() / 4;
1618 w = a.width() / 2;
1619 h = a.height() / 2;
1620 }
1621 }
1622
1623 changeAllowedActions(); // based on the new _functions
1624
1625 // when fullscreening, don't obey things like increments, fill the screen
1626 internal_resize(TopLeft, w, h, !fs, x, y);
1627
1628 // raise (back) into our stacking layer
1629 openbox->screen(_screen)->raiseWindow(this);
1630
1631 // try focus us when we go into fullscreen mode
1632 if (fs) focus();
1633 }
1634
1635
1636 void Client::disableDecorations(DecorationFlags flags)
1637 {
1638 _disabled_decorations = flags;
1639 setupDecorAndFunctions();
1640 }
1641
1642
1643 void Client::installColormap(bool install) const
1644 {
1645 XWindowAttributes wa;
1646 if (XGetWindowAttributes(**otk::display, _window, &wa)) {
1647 if (install)
1648 XInstallColormap(**otk::display, wa.colormap);
1649 else
1650 XUninstallColormap(**otk::display, wa.colormap);
1651 }
1652 }
1653
1654
1655 bool Client::focus()
1656 {
1657 // if we have a modal child, then focus it, not us
1658 if (_modal_child)
1659 return _modal_child->focus();
1660
1661 // won't try focus if the client doesn't want it, or if the window isn't
1662 // visible on the screen
1663 if (!(frame->visible() && (_can_focus || _focus_notify))) return false;
1664
1665 if (_focused) return true;
1666
1667 // do a check to see if the window has already been unmapped or destroyed
1668 // do this intelligently while watching out for unmaps we've generated
1669 // (ignore_unmaps > 0)
1670 XEvent ev;
1671 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1672 XPutBackEvent(**otk::display, &ev);
1673 return false;
1674 }
1675 while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1676 if (ignore_unmaps) {
1677 unmapHandler(ev.xunmap);
1678 } else {
1679 XPutBackEvent(**otk::display, &ev);
1680 return false;
1681 }
1682 }
1683
1684 if (_can_focus)
1685 XSetInputFocus(**otk::display, _window,
1686 RevertToNone, CurrentTime);
1687
1688 if (_focus_notify) {
1689 XEvent ce;
1690 ce.xclient.type = ClientMessage;
1691 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1692 ce.xclient.display = **otk::display;
1693 ce.xclient.window = _window;
1694 ce.xclient.format = 32;
1695 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1696 ce.xclient.data.l[1] = openbox->lastTime();
1697 ce.xclient.data.l[2] = 0l;
1698 ce.xclient.data.l[3] = 0l;
1699 ce.xclient.data.l[4] = 0l;
1700 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1701 }
1702
1703 XSync(**otk::display, False);
1704 return true;
1705 }
1706
1707
1708 void Client::unfocus() const
1709 {
1710 if (!_focused) return;
1711
1712 assert(openbox->focusedClient() == this);
1713 openbox->setFocusedClient(0);
1714 }
1715
1716
1717 void Client::focusHandler(const XFocusChangeEvent &e)
1718 {
1719 #ifdef DEBUG
1720 // printf("FocusIn for 0x%lx\n", e.window);
1721 #endif // DEBUG
1722
1723 otk::EventHandler::focusHandler(e);
1724
1725 _focused = true;
1726 frame->adjustFocus();
1727
1728 openbox->setFocusedClient(this);
1729 }
1730
1731
1732 void Client::unfocusHandler(const XFocusChangeEvent &e)
1733 {
1734 #ifdef DEBUG
1735 // printf("FocusOut for 0x%lx\n", e.window);
1736 #endif // DEBUG
1737
1738 otk::EventHandler::unfocusHandler(e);
1739
1740 _focused = false;
1741 frame->adjustFocus();
1742
1743 if (openbox->focusedClient() == this)
1744 openbox->setFocusedClient(0);
1745 }
1746
1747
1748 void Client::configureRequestHandler(const XConfigureRequestEvent &ec)
1749 {
1750 #ifdef DEBUG
1751 printf("ConfigureRequest for 0x%lx\n", ec.window);
1752 #endif // DEBUG
1753
1754 otk::EventHandler::configureRequestHandler(ec);
1755
1756 // compress these
1757 XConfigureRequestEvent e = ec;
1758 XEvent ev;
1759 while (XCheckTypedWindowEvent(**otk::display, window(), ConfigureRequest,
1760 &ev)) {
1761 // XXX if this causes bad things.. we can compress config req's with the
1762 // same mask.
1763 e.value_mask |= ev.xconfigurerequest.value_mask;
1764 if (ev.xconfigurerequest.value_mask & CWX)
1765 e.x = ev.xconfigurerequest.x;
1766 if (ev.xconfigurerequest.value_mask & CWY)
1767 e.y = ev.xconfigurerequest.y;
1768 if (ev.xconfigurerequest.value_mask & CWWidth)
1769 e.width = ev.xconfigurerequest.width;
1770 if (ev.xconfigurerequest.value_mask & CWHeight)
1771 e.height = ev.xconfigurerequest.height;
1772 if (ev.xconfigurerequest.value_mask & CWBorderWidth)
1773 e.border_width = ev.xconfigurerequest.border_width;
1774 if (ev.xconfigurerequest.value_mask & CWStackMode)
1775 e.detail = ev.xconfigurerequest.detail;
1776 }
1777
1778 // if we are iconic (or shaded (fvwm does this)) ignore the event
1779 if (_iconic || _shaded) return;
1780
1781 if (e.value_mask & CWBorderWidth)
1782 _border_width = e.border_width;
1783
1784 // resize, then move, as specified in the EWMH section 7.7
1785 if (e.value_mask & (CWWidth | CWHeight)) {
1786 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1787 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1788
1789 Corner corner;
1790 switch (_gravity) {
1791 case NorthEastGravity:
1792 case EastGravity:
1793 corner = TopRight;
1794 break;
1795 case SouthWestGravity:
1796 case SouthGravity:
1797 corner = BottomLeft;
1798 break;
1799 case SouthEastGravity:
1800 corner = BottomRight;
1801 break;
1802 default: // NorthWest, Static, etc
1803 corner = TopLeft;
1804 }
1805
1806 // if moving AND resizing ...
1807 if (e.value_mask & (CWX | CWY)) {
1808 int x = (e.value_mask & CWX) ? e.x : _area.x();
1809 int y = (e.value_mask & CWY) ? e.y : _area.y();
1810 internal_resize(corner, w, h, false, x, y);
1811 } else // if JUST resizing...
1812 internal_resize(corner, w, h, false);
1813 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1814 int x = (e.value_mask & CWX) ? e.x : _area.x();
1815 int y = (e.value_mask & CWY) ? e.y : _area.y();
1816 internal_move(x, y);
1817 }
1818
1819 if (e.value_mask & CWStackMode) {
1820 switch (e.detail) {
1821 case Below:
1822 case BottomIf:
1823 openbox->screen(_screen)->lowerWindow(this);
1824 break;
1825
1826 case Above:
1827 case TopIf:
1828 default:
1829 openbox->screen(_screen)->raiseWindow(this);
1830 break;
1831 }
1832 }
1833 }
1834
1835
1836 void Client::unmapHandler(const XUnmapEvent &e)
1837 {
1838 if (ignore_unmaps) {
1839 #ifdef DEBUG
1840 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1841 #endif // DEBUG
1842 ignore_unmaps--;
1843 return;
1844 }
1845
1846 #ifdef DEBUG
1847 printf("UnmapNotify for 0x%lx\n", e.window);
1848 #endif // DEBUG
1849
1850 otk::EventHandler::unmapHandler(e);
1851
1852 // this deletes us etc
1853 openbox->screen(_screen)->unmanageWindow(this);
1854 }
1855
1856
1857 void Client::destroyHandler(const XDestroyWindowEvent &e)
1858 {
1859 #ifdef DEBUG
1860 printf("DestroyNotify for 0x%lx\n", e.window);
1861 #endif // DEBUG
1862
1863 otk::EventHandler::destroyHandler(e);
1864
1865 // this deletes us etc
1866 openbox->screen(_screen)->unmanageWindow(this);
1867 }
1868
1869
1870 void Client::reparentHandler(const XReparentEvent &e)
1871 {
1872 // this is when the client is first taken captive in the frame
1873 if (e.parent == frame->plate()) return;
1874
1875 #ifdef DEBUG
1876 printf("ReparentNotify for 0x%lx\n", e.window);
1877 #endif // DEBUG
1878
1879 otk::EventHandler::reparentHandler(e);
1880
1881 /*
1882 This event is quite rare and is usually handled in unmapHandler.
1883 However, if the window is unmapped when the reparent event occurs,
1884 the window manager never sees it because an unmap event is not sent
1885 to an already unmapped window.
1886 */
1887
1888 // we don't want the reparent event, put it back on the stack for the X
1889 // server to deal with after we unmanage the window
1890 XEvent ev;
1891 ev.xreparent = e;
1892 XPutBackEvent(**otk::display, &ev);
1893
1894 // this deletes us etc
1895 openbox->screen(_screen)->unmanageWindow(this);
1896 }
1897
1898 void Client::mapRequestHandler(const XMapRequestEvent &e)
1899 {
1900 #ifdef DEBUG
1901 printf("MapRequest for already managed 0x%lx\n", e.window);
1902 #endif // DEBUG
1903
1904 assert(_iconic); // we shouldn't be able to get this unless we're iconic
1905
1906 // move to the current desktop (uniconify)
1907 setDesktop(openbox->screen(_screen)->desktop());
1908 // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1909 }
1910
1911 }
This page took 0.125834 seconds and 5 git commands to generate.