]> Dogcows Code - chaz/openbox/blob - src/client.cc
signed ints instead of unsigned ints again. less pain. pain bad.
[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(INT_MAX, INT_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 assert(replacement != this);
837
838 c = this;
839 while (c->_transient_for) {
840 c = c->_transient_for;
841 if (c == this) break; // circular?
842 if (c->_modal_child != this) break; // has a different modal child
843 if (c == replacement) break; // found the replacement itself
844 c->_modal_child = replacement;
845 }
846 }
847 _modal = modal;
848 }
849
850
851 void Client::setState(StateAction action, long data1, long data2)
852 {
853 bool shadestate = _shaded;
854 bool fsstate = _fullscreen;
855 bool maxh = _max_horz;
856 bool maxv = _max_vert;
857 bool modal = _modal;
858
859 if (!(action == State_Add || action == State_Remove ||
860 action == State_Toggle))
861 return; // an invalid action was passed to the client message, ignore it
862
863 for (int i = 0; i < 2; ++i) {
864 Atom state = i == 0 ? data1 : data2;
865
866 if (! state) continue;
867
868 // if toggling, then pick whether we're adding or removing
869 if (action == State_Toggle) {
870 if (state == otk::Property::atoms.net_wm_state_modal)
871 action = _modal ? State_Remove : State_Add;
872 else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
873 action = _max_vert ? State_Remove : State_Add;
874 else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
875 action = _max_horz ? State_Remove : State_Add;
876 else if (state == otk::Property::atoms.net_wm_state_shaded)
877 action = _shaded ? State_Remove : State_Add;
878 else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
879 action = _skip_taskbar ? State_Remove : State_Add;
880 else if (state == otk::Property::atoms.net_wm_state_skip_pager)
881 action = _skip_pager ? State_Remove : State_Add;
882 else if (state == otk::Property::atoms.net_wm_state_fullscreen)
883 action = _fullscreen ? State_Remove : State_Add;
884 else if (state == otk::Property::atoms.net_wm_state_above)
885 action = _above ? State_Remove : State_Add;
886 else if (state == otk::Property::atoms.net_wm_state_below)
887 action = _below ? State_Remove : State_Add;
888 }
889
890 if (action == State_Add) {
891 if (state == otk::Property::atoms.net_wm_state_modal) {
892 if (_modal) continue;
893 modal = true;
894 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
895 maxv = true;
896 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
897 if (_max_horz) continue;
898 maxh = true;
899 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
900 shadestate = true;
901 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
902 _skip_taskbar = true;
903 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
904 _skip_pager = true;
905 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
906 fsstate = true;
907 } else if (state == otk::Property::atoms.net_wm_state_above) {
908 if (_above) continue;
909 _above = true;
910 } else if (state == otk::Property::atoms.net_wm_state_below) {
911 if (_below) continue;
912 _below = true;
913 }
914
915 } else { // action == State_Remove
916 if (state == otk::Property::atoms.net_wm_state_modal) {
917 if (!_modal) continue;
918 modal = false;
919 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
920 maxv = false;
921 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
922 maxh = false;
923 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
924 shadestate = false;
925 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
926 _skip_taskbar = false;
927 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
928 _skip_pager = false;
929 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
930 fsstate = false;
931 } else if (state == otk::Property::atoms.net_wm_state_above) {
932 if (!_above) continue;
933 _above = false;
934 } else if (state == otk::Property::atoms.net_wm_state_below) {
935 if (!_below) continue;
936 _below = false;
937 }
938 }
939 }
940 if (maxh != _max_horz || maxv != _max_vert) {
941 if (maxh != _max_horz && maxv != _max_vert) { // toggling both
942 if (maxh == maxv) { // both going the same way
943 maximize(maxh, 0, true);
944 } else {
945 maximize(maxh, 1, true);
946 maximize(maxv, 2, true);
947 }
948 } else { // toggling one
949 if (maxh != _max_horz)
950 maximize(maxh, 1, true);
951 else
952 maximize(maxv, 2, true);
953 }
954 }
955 if (modal != _modal)
956 setModal(modal);
957 // change fullscreen state before shading, as it will affect if the window
958 // can shade or not
959 if (fsstate != _fullscreen)
960 fullscreen(fsstate, true);
961 if (shadestate != _shaded)
962 shade(shadestate);
963 calcLayer();
964 changeState(); // change the hint to relect these changes
965 }
966
967
968 void Client::toggleClientBorder(bool addborder)
969 {
970 // adjust our idea of where the client is, based on its border. When the
971 // border is removed, the client should now be considered to be in a
972 // different position.
973 // when re-adding the border to the client, the same operation needs to be
974 // reversed.
975 int oldx = _area.x(), oldy = _area.y();
976 int x = oldx, y = oldy;
977 switch(_gravity) {
978 default:
979 case NorthWestGravity:
980 case WestGravity:
981 case SouthWestGravity:
982 break;
983 case NorthEastGravity:
984 case EastGravity:
985 case SouthEastGravity:
986 if (addborder) x -= _border_width * 2;
987 else x += _border_width * 2;
988 break;
989 case NorthGravity:
990 case SouthGravity:
991 case CenterGravity:
992 case ForgetGravity:
993 case StaticGravity:
994 if (addborder) x -= _border_width;
995 else x += _border_width;
996 break;
997 }
998 switch(_gravity) {
999 default:
1000 case NorthWestGravity:
1001 case NorthGravity:
1002 case NorthEastGravity:
1003 break;
1004 case SouthWestGravity:
1005 case SouthGravity:
1006 case SouthEastGravity:
1007 if (addborder) y -= _border_width * 2;
1008 else y += _border_width * 2;
1009 break;
1010 case WestGravity:
1011 case EastGravity:
1012 case CenterGravity:
1013 case ForgetGravity:
1014 case StaticGravity:
1015 if (addborder) y -= _border_width;
1016 else y += _border_width;
1017 break;
1018 }
1019 _area = otk::Rect(otk::Point(x, y), _area.size());
1020
1021 if (addborder) {
1022 XSetWindowBorderWidth(**otk::display, _window, _border_width);
1023
1024 // move the client so it is back it the right spot _with_ its border!
1025 if (x != oldx || y != oldy)
1026 XMoveWindow(**otk::display, _window, x, y);
1027 } else
1028 XSetWindowBorderWidth(**otk::display, _window, 0);
1029 }
1030
1031
1032 void Client::clientMessageHandler(const XClientMessageEvent &e)
1033 {
1034 otk::EventHandler::clientMessageHandler(e);
1035
1036 // validate cuz we query stuff off the client here
1037 if (!validate()) return;
1038
1039 if (e.format != 32) return;
1040
1041 if (e.message_type == otk::Property::atoms.wm_change_state) {
1042 // compress changes into a single change
1043 bool compress = false;
1044 XEvent ce;
1045 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
1046 // XXX: it would be nice to compress ALL messages of a type, not just
1047 // messages in a row without other message types between.
1048 if (ce.xclient.message_type != e.message_type) {
1049 XPutBackEvent(**otk::display, &ce);
1050 break;
1051 }
1052 compress = true;
1053 }
1054 if (compress)
1055 setWMState(ce.xclient.data.l[0]); // use the found event
1056 else
1057 setWMState(e.data.l[0]); // use the original event
1058 } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
1059 // compress changes into a single change
1060 bool compress = false;
1061 XEvent ce;
1062 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
1063 // XXX: it would be nice to compress ALL messages of a type, not just
1064 // messages in a row without other message types between.
1065 if (ce.xclient.message_type != e.message_type) {
1066 XPutBackEvent(**otk::display, &ce);
1067 break;
1068 }
1069 compress = true;
1070 }
1071 if (compress)
1072 setDesktop(e.data.l[0]); // use the found event
1073 else
1074 setDesktop(e.data.l[0]); // use the original event
1075 } else if (e.message_type == otk::Property::atoms.net_wm_state) {
1076 // can't compress these
1077 #ifdef DEBUG
1078 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1079 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
1080 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
1081 e.data.l[1], e.data.l[2], _window);
1082 #endif
1083 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
1084 } else if (e.message_type == otk::Property::atoms.net_close_window) {
1085 #ifdef DEBUG
1086 printf("net_close_window for 0x%lx\n", _window);
1087 #endif
1088 close();
1089 } else if (e.message_type == otk::Property::atoms.net_active_window) {
1090 #ifdef DEBUG
1091 printf("net_active_window for 0x%lx\n", _window);
1092 #endif
1093 if (_iconic)
1094 setDesktop(openbox->screen(_screen)->desktop());
1095 if (_shaded)
1096 shade(false);
1097 focus();
1098 openbox->screen(_screen)->raiseWindow(this);
1099 } else if (e.message_type == otk::Property::atoms.openbox_active_window) {
1100 if (_iconic)
1101 setDesktop(openbox->screen(_screen)->desktop());
1102 if (e.data.l[0] && _shaded)
1103 shade(false);
1104 focus();
1105 if (e.data.l[1])
1106 openbox->screen(_screen)->raiseWindow(this);
1107 }
1108 }
1109
1110
1111 #if defined(SHAPE)
1112 void Client::shapeHandler(const XShapeEvent &e)
1113 {
1114 otk::EventHandler::shapeHandler(e);
1115
1116 if (e.kind == ShapeBounding) {
1117 _shaped = e.shaped;
1118 frame->adjustShape();
1119 }
1120 }
1121 #endif
1122
1123
1124 void Client::resize(Corner anchor, int w, int h)
1125 {
1126 if (!(_functions & Func_Resize)) return;
1127 internal_resize(anchor, w, h);
1128 }
1129
1130
1131 void Client::internal_resize(Corner anchor, int w, int h,
1132 bool user, int x, int y)
1133 {
1134 w -= _base_size.width();
1135 h -= _base_size.height();
1136
1137 if (user) {
1138 // for interactive resizing. have to move half an increment in each
1139 // direction.
1140 int mw = w % _size_inc.width(); // how far we are towards the next size inc
1141 int mh = h % _size_inc.height();
1142 int aw = _size_inc.width() / 2; // amount to add
1143 int ah = _size_inc.height() / 2;
1144 // don't let us move into a new size increment
1145 if (mw + aw >= _size_inc.width()) aw = _size_inc.width() - mw - 1;
1146 if (mh + ah >= _size_inc.height()) ah = _size_inc.height() - mh - 1;
1147 w += aw;
1148 h += ah;
1149
1150 // if this is a user-requested resize, then check against min/max sizes
1151 // and aspect ratios
1152
1153 // smaller than min size or bigger than max size?
1154 if (w < _min_size.width()) w = _min_size.width();
1155 else if (w > _max_size.width()) w = _max_size.width();
1156 if (h < _min_size.height()) h = _min_size.height();
1157 else if (h > _max_size.height()) h = _max_size.height();
1158
1159 // adjust the height ot match the width for the aspect ratios
1160 if (_min_ratio)
1161 if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
1162 if (_max_ratio)
1163 if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
1164 }
1165
1166 // keep to the increments
1167 w /= _size_inc.width();
1168 h /= _size_inc.height();
1169
1170 // you cannot resize to nothing
1171 if (w < 1) w = 1;
1172 if (h < 1) h = 1;
1173
1174 // store the logical size
1175 _logical_size = otk::Size(w, h);
1176
1177 w *= _size_inc.width();
1178 h *= _size_inc.height();
1179
1180 w += _base_size.width();
1181 h += _base_size.height();
1182
1183 if (x == INT_MIN || y == INT_MIN) {
1184 x = _area.x();
1185 y = _area.y();
1186 switch (anchor) {
1187 case TopLeft:
1188 break;
1189 case TopRight:
1190 x -= w - _area.width();
1191 break;
1192 case BottomLeft:
1193 y -= h - _area.height();
1194 break;
1195 case BottomRight:
1196 x -= w - _area.width();
1197 y -= h - _area.height();
1198 break;
1199 }
1200 }
1201
1202 _area = otk::Rect(_area.position(), otk::Size(w, h));
1203
1204 XResizeWindow(**otk::display, _window, w, h);
1205
1206 // resize the frame to match the request
1207 frame->adjustSize();
1208 internal_move(x, y);
1209 }
1210
1211
1212 void Client::move(int x, int y)
1213 {
1214 if (!(_functions & Func_Move)) return;
1215 frame->frameGravity(x, y); // get the client's position based on x,y for the
1216 // frame
1217 internal_move(x, y);
1218 }
1219
1220
1221 void Client::internal_move(int x, int y)
1222 {
1223 _area = otk::Rect(otk::Point(x, y), _area.size());
1224
1225 // move the frame to be in the requested position
1226 if (frame) { // this can be called while mapping, before frame exists
1227 frame->adjustPosition();
1228
1229 // send synthetic configure notify (we don't need to if we aren't mapped
1230 // yet)
1231 XEvent event;
1232 event.type = ConfigureNotify;
1233 event.xconfigure.display = **otk::display;
1234 event.xconfigure.event = _window;
1235 event.xconfigure.window = _window;
1236
1237 // root window coords with border in mind
1238 event.xconfigure.x = x - _border_width + frame->size().left;
1239 event.xconfigure.y = y - _border_width + frame->size().top;
1240
1241 event.xconfigure.width = _area.width();
1242 event.xconfigure.height = _area.height();
1243 event.xconfigure.border_width = _border_width;
1244 event.xconfigure.above = frame->plate();
1245 event.xconfigure.override_redirect = False;
1246 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1247 StructureNotifyMask, &event);
1248 #if 0//def DEBUG
1249 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1250 event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
1251 event.xconfigure.height, event.xconfigure.window);
1252 #endif
1253 }
1254 }
1255
1256
1257 void Client::close()
1258 {
1259 XEvent ce;
1260
1261 if (!(_functions & Func_Close)) return;
1262
1263 // XXX: itd be cool to do timeouts and shit here for killing the client's
1264 // process off
1265 // like... if the window is around after 5 seconds, then the close button
1266 // turns a nice red, and if this function is called again, the client is
1267 // explicitly killed.
1268
1269 ce.xclient.type = ClientMessage;
1270 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1271 ce.xclient.display = **otk::display;
1272 ce.xclient.window = _window;
1273 ce.xclient.format = 32;
1274 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1275 ce.xclient.data.l[1] = CurrentTime;
1276 ce.xclient.data.l[2] = 0l;
1277 ce.xclient.data.l[3] = 0l;
1278 ce.xclient.data.l[4] = 0l;
1279 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1280 }
1281
1282
1283 void Client::changeState()
1284 {
1285 unsigned long state[2];
1286 state[0] = _wmstate;
1287 state[1] = None;
1288 otk::Property::set(_window, otk::Property::atoms.wm_state,
1289 otk::Property::atoms.wm_state, state, 2);
1290
1291 Atom netstate[10];
1292 int num = 0;
1293 if (_modal)
1294 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1295 if (_shaded)
1296 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1297 if (_iconic)
1298 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1299 if (_skip_taskbar)
1300 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1301 if (_skip_pager)
1302 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1303 if (_fullscreen)
1304 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1305 if (_max_vert)
1306 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1307 if (_max_horz)
1308 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1309 if (_above)
1310 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1311 if (_below)
1312 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1313 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1314 otk::Property::atoms.atom, netstate, num);
1315
1316 calcLayer();
1317
1318 if (frame)
1319 frame->adjustState();
1320 }
1321
1322
1323 void Client::changeAllowedActions(void)
1324 {
1325 Atom actions[9];
1326 int num = 0;
1327
1328 actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1329
1330 if (_functions & Func_Shade)
1331 actions[num++] = otk::Property::atoms.net_wm_action_shade;
1332 if (_functions & Func_Close)
1333 actions[num++] = otk::Property::atoms.net_wm_action_close;
1334 if (_functions & Func_Move)
1335 actions[num++] = otk::Property::atoms.net_wm_action_move;
1336 if (_functions & Func_Iconify)
1337 actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1338 if (_functions & Func_Resize)
1339 actions[num++] = otk::Property::atoms.net_wm_action_resize;
1340 if (_functions & Func_Fullscreen)
1341 actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1342 if (_functions & Func_Maximize) {
1343 actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1344 actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1345 }
1346
1347 otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1348 otk::Property::atoms.atom, actions, num);
1349
1350 // make sure the window isn't breaking any rules now
1351
1352 if (!(_functions & Func_Shade) && _shaded)
1353 if (frame) shade(false);
1354 else _shaded = false;
1355 if (!(_functions & Func_Iconify) && _iconic)
1356 if (frame) setDesktop(openbox->screen(_screen)->desktop());
1357 else _iconic = false;
1358 if (!(_functions & Func_Fullscreen) && _fullscreen)
1359 if (frame) fullscreen(false);
1360 else _fullscreen = false;
1361 if (!(_functions & Func_Maximize) && (_max_horz || _max_vert))
1362 if (frame) maximize(false, 0);
1363 else _max_vert = _max_horz = false;
1364 }
1365
1366
1367 void Client::remaximize()
1368 {
1369 int dir;
1370 if (_max_horz && _max_vert)
1371 dir = 0;
1372 else if (_max_horz)
1373 dir = 1;
1374 else if (_max_vert)
1375 dir = 2;
1376 else
1377 return; // not maximized
1378 _max_horz = _max_vert = false;
1379 maximize(true, dir, false);
1380 }
1381
1382
1383 void Client::applyStartupState()
1384 {
1385 // these are in a carefully crafted order..
1386
1387 if (_modal) {
1388 _modal = false;
1389 setModal(true);
1390 }
1391
1392 if (_iconic) {
1393 _iconic = false;
1394 setDesktop(ICONIC_DESKTOP);
1395 }
1396 if (_fullscreen) {
1397 _fullscreen = false;
1398 fullscreen(true, false);
1399 }
1400 if (_shaded) {
1401 _shaded = false;
1402 shade(true);
1403 }
1404 if (_urgent)
1405 fireUrgent();
1406
1407 if (_max_vert && _max_horz) {
1408 _max_vert = _max_horz = false;
1409 maximize(true, 0, false);
1410 } else if (_max_vert) {
1411 _max_vert = false;
1412 maximize(true, 2, false);
1413 } else if (_max_horz) {
1414 _max_horz = false;
1415 maximize(true, 1, false);
1416 }
1417
1418 if (_skip_taskbar); // nothing to do for this
1419 if (_skip_pager); // nothing to do for this
1420 if (_modal); // nothing to do for this
1421 if (_above); // nothing to do for this
1422 if (_below); // nothing to do for this
1423 }
1424
1425
1426 void Client::fireUrgent()
1427 {
1428 // call the python UrgentWindow callbacks
1429 EventData data(_screen, this, EventAction::UrgentWindow, 0);
1430 openbox->bindings()->fireEvent(&data);
1431 }
1432
1433
1434 void Client::shade(bool shade)
1435 {
1436 if (!(_functions & Func_Shade) || // can't
1437 _shaded == shade) return; // already done
1438
1439 // when we're iconic, don't change the wmstate
1440 if (!_iconic)
1441 _wmstate = shade ? IconicState : NormalState;
1442 _shaded = shade;
1443 changeState();
1444 frame->adjustSize();
1445 }
1446
1447
1448 void Client::maximize(bool max, int dir, bool savearea)
1449 {
1450 assert(dir == 0 || dir == 1 || dir == 2);
1451 if (!(_functions & Func_Maximize)) return; // can't
1452
1453 // check if already done
1454 if (max) {
1455 if (dir == 0 && _max_horz && _max_vert) return;
1456 if (dir == 1 && _max_horz) return;
1457 if (dir == 2 && _max_vert) return;
1458 } else {
1459 if (dir == 0 && !_max_horz && !_max_vert) return;
1460 if (dir == 1 && !_max_horz) return;
1461 if (dir == 2 && !_max_vert) return;
1462 }
1463
1464 const otk::Rect &a = openbox->screen(_screen)->area();
1465 int x = frame->area().x(), y = frame->area().y(),
1466 w = _area.width(), h = _area.height();
1467
1468 if (max) {
1469 if (savearea) {
1470 long dimensions[4];
1471 long *readdim;
1472 unsigned long n = 4;
1473
1474 dimensions[0] = x;
1475 dimensions[1] = y;
1476 dimensions[2] = w;
1477 dimensions[3] = h;
1478
1479 // get the property off the window and use it for the dimentions we are
1480 // already maxed on
1481 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1482 otk::Property::atoms.cardinal, &n,
1483 (long unsigned**) &readdim)) {
1484 if (n >= 4) {
1485 if (_max_horz) {
1486 dimensions[0] = readdim[0];
1487 dimensions[2] = readdim[2];
1488 }
1489 if (_max_vert) {
1490 dimensions[1] = readdim[1];
1491 dimensions[3] = readdim[3];
1492 }
1493 }
1494 delete readdim;
1495 }
1496
1497 otk::Property::set(_window, otk::Property::atoms.openbox_premax,
1498 otk::Property::atoms.cardinal,
1499 (long unsigned*)dimensions, 4);
1500 }
1501 if (dir == 0 || dir == 1) { // horz
1502 x = a.x();
1503 w = a.width();
1504 }
1505 if (dir == 0 || dir == 2) { // vert
1506 y = a.y();
1507 h = a.height() - frame->size().top - frame->size().bottom;
1508 }
1509 } else {
1510 long *dimensions;
1511 long unsigned n = 4;
1512
1513 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1514 otk::Property::atoms.cardinal, &n,
1515 (long unsigned**) &dimensions)) {
1516 if (n >= 4) {
1517 if (dir == 0 || dir == 1) { // horz
1518 x = (signed int)dimensions[0];
1519 w = (signed int)dimensions[2];
1520 }
1521 if (dir == 0 || dir == 2) { // vert
1522 y = (signed int)dimensions[1];
1523 h = (signed int)dimensions[3];
1524 }
1525 }
1526 delete dimensions;
1527 } else {
1528 // pick some fallbacks...
1529 if (dir == 0 || dir == 1) { // horz
1530 x = a.x() + a.width() / 4;
1531 w = a.width() / 2;
1532 }
1533 if (dir == 0 || dir == 2) { // vert
1534 y = a.y() + a.height() / 4;
1535 h = a.height() / 2;
1536 }
1537 }
1538 }
1539
1540 if (dir == 0 || dir == 1) // horz
1541 _max_horz = max;
1542 if (dir == 0 || dir == 2) // vert
1543 _max_vert = max;
1544
1545 if (!_max_horz && !_max_vert)
1546 otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
1547
1548 changeState(); // change the state hints on the client
1549
1550 frame->frameGravity(x, y); // figure out where the client should be going
1551 internal_resize(TopLeft, w, h, true, x, y);
1552 }
1553
1554
1555 void Client::fullscreen(bool fs, bool savearea)
1556 {
1557 static FunctionFlags saved_func;
1558 static DecorationFlags saved_decor;
1559
1560 if (!(_functions & Func_Fullscreen) || // can't
1561 _fullscreen == fs) return; // already done
1562
1563 _fullscreen = fs;
1564 changeState(); // change the state hints on the client
1565
1566 int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
1567
1568 if (fs) {
1569 // save the functions and remove them
1570 saved_func = _functions;
1571 _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1572 // save the decorations and remove them
1573 saved_decor = _decorations;
1574 _decorations = 0;
1575 if (savearea) {
1576 long dimensions[4];
1577 dimensions[0] = _area.x();
1578 dimensions[1] = _area.y();
1579 dimensions[2] = _area.width();
1580 dimensions[3] = _area.height();
1581 otk::Property::set(_window, otk::Property::atoms.openbox_premax,
1582 otk::Property::atoms.cardinal,
1583 (long unsigned*)dimensions, 4);
1584 }
1585 const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1586 x = 0;
1587 y = 0;
1588 w = info->size().width();
1589 h = info->size().height();
1590 } else {
1591 _functions = saved_func;
1592 _decorations = saved_decor;
1593
1594 long *dimensions;
1595 long unsigned n = 4;
1596
1597 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1598 otk::Property::atoms.cardinal, &n,
1599 (long unsigned**) &dimensions)) {
1600 if (n >= 4) {
1601 x = dimensions[0];
1602 y = dimensions[1];
1603 w = dimensions[2];
1604 h = dimensions[3];
1605 }
1606 delete dimensions;
1607 } else {
1608 // pick some fallbacks...
1609 const otk::Rect &a = openbox->screen(_screen)->area();
1610 x = a.x() + a.width() / 4;
1611 y = a.y() + a.height() / 4;
1612 w = a.width() / 2;
1613 h = a.height() / 2;
1614 }
1615 }
1616
1617 changeAllowedActions(); // based on the new _functions
1618
1619 // when fullscreening, don't obey things like increments, fill the screen
1620 internal_resize(TopLeft, w, h, !fs, x, y);
1621
1622 // raise (back) into our stacking layer
1623 openbox->screen(_screen)->raiseWindow(this);
1624
1625 // try focus us when we go into fullscreen mode
1626 if (fs) focus();
1627 }
1628
1629
1630 void Client::disableDecorations(DecorationFlags flags)
1631 {
1632 _disabled_decorations = flags;
1633 setupDecorAndFunctions();
1634 }
1635
1636
1637 void Client::installColormap(bool install) const
1638 {
1639 XWindowAttributes wa;
1640 if (XGetWindowAttributes(**otk::display, _window, &wa)) {
1641 if (install)
1642 XInstallColormap(**otk::display, wa.colormap);
1643 else
1644 XUninstallColormap(**otk::display, wa.colormap);
1645 }
1646 }
1647
1648
1649 bool Client::focus()
1650 {
1651 // if we have a modal child, then focus it, not us
1652 if (_modal_child)
1653 return _modal_child->focus();
1654
1655 // won't try focus if the client doesn't want it, or if the window isn't
1656 // visible on the screen
1657 if (!(frame->visible() && (_can_focus || _focus_notify))) return false;
1658
1659 if (_focused) return true;
1660
1661 // do a check to see if the window has already been unmapped or destroyed
1662 // do this intelligently while watching out for unmaps we've generated
1663 // (ignore_unmaps > 0)
1664 XEvent ev;
1665 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1666 XPutBackEvent(**otk::display, &ev);
1667 return false;
1668 }
1669 while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1670 if (ignore_unmaps) {
1671 unmapHandler(ev.xunmap);
1672 } else {
1673 XPutBackEvent(**otk::display, &ev);
1674 return false;
1675 }
1676 }
1677
1678 if (_can_focus)
1679 XSetInputFocus(**otk::display, _window,
1680 RevertToNone, CurrentTime);
1681
1682 if (_focus_notify) {
1683 XEvent ce;
1684 ce.xclient.type = ClientMessage;
1685 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1686 ce.xclient.display = **otk::display;
1687 ce.xclient.window = _window;
1688 ce.xclient.format = 32;
1689 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1690 ce.xclient.data.l[1] = openbox->lastTime();
1691 ce.xclient.data.l[2] = 0l;
1692 ce.xclient.data.l[3] = 0l;
1693 ce.xclient.data.l[4] = 0l;
1694 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1695 }
1696
1697 XSync(**otk::display, False);
1698 return true;
1699 }
1700
1701
1702 void Client::unfocus() const
1703 {
1704 if (!_focused) return;
1705
1706 assert(openbox->focusedClient() == this);
1707 openbox->setFocusedClient(0);
1708 }
1709
1710
1711 void Client::focusHandler(const XFocusChangeEvent &e)
1712 {
1713 #ifdef DEBUG
1714 // printf("FocusIn for 0x%lx\n", e.window);
1715 #endif // DEBUG
1716
1717 otk::EventHandler::focusHandler(e);
1718
1719 _focused = true;
1720 frame->adjustFocus();
1721
1722 openbox->setFocusedClient(this);
1723 }
1724
1725
1726 void Client::unfocusHandler(const XFocusChangeEvent &e)
1727 {
1728 #ifdef DEBUG
1729 // printf("FocusOut for 0x%lx\n", e.window);
1730 #endif // DEBUG
1731
1732 otk::EventHandler::unfocusHandler(e);
1733
1734 _focused = false;
1735 frame->adjustFocus();
1736
1737 if (openbox->focusedClient() == this)
1738 openbox->setFocusedClient(0);
1739 }
1740
1741
1742 void Client::configureRequestHandler(const XConfigureRequestEvent &ec)
1743 {
1744 #ifdef DEBUG
1745 printf("ConfigureRequest for 0x%lx\n", ec.window);
1746 #endif // DEBUG
1747
1748 otk::EventHandler::configureRequestHandler(ec);
1749
1750 // compress these
1751 XConfigureRequestEvent e = ec;
1752 XEvent ev;
1753 while (XCheckTypedWindowEvent(**otk::display, window(), ConfigureRequest,
1754 &ev)) {
1755 // XXX if this causes bad things.. we can compress config req's with the
1756 // same mask.
1757 e.value_mask |= ev.xconfigurerequest.value_mask;
1758 if (ev.xconfigurerequest.value_mask & CWX)
1759 e.x = ev.xconfigurerequest.x;
1760 if (ev.xconfigurerequest.value_mask & CWY)
1761 e.y = ev.xconfigurerequest.y;
1762 if (ev.xconfigurerequest.value_mask & CWWidth)
1763 e.width = ev.xconfigurerequest.width;
1764 if (ev.xconfigurerequest.value_mask & CWHeight)
1765 e.height = ev.xconfigurerequest.height;
1766 if (ev.xconfigurerequest.value_mask & CWBorderWidth)
1767 e.border_width = ev.xconfigurerequest.border_width;
1768 if (ev.xconfigurerequest.value_mask & CWStackMode)
1769 e.detail = ev.xconfigurerequest.detail;
1770 }
1771
1772 // if we are iconic (or shaded (fvwm does this)) ignore the event
1773 if (_iconic || _shaded) return;
1774
1775 if (e.value_mask & CWBorderWidth)
1776 _border_width = e.border_width;
1777
1778 // resize, then move, as specified in the EWMH section 7.7
1779 if (e.value_mask & (CWWidth | CWHeight)) {
1780 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1781 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1782
1783 Corner corner;
1784 switch (_gravity) {
1785 case NorthEastGravity:
1786 case EastGravity:
1787 corner = TopRight;
1788 break;
1789 case SouthWestGravity:
1790 case SouthGravity:
1791 corner = BottomLeft;
1792 break;
1793 case SouthEastGravity:
1794 corner = BottomRight;
1795 break;
1796 default: // NorthWest, Static, etc
1797 corner = TopLeft;
1798 }
1799
1800 // if moving AND resizing ...
1801 if (e.value_mask & (CWX | CWY)) {
1802 int x = (e.value_mask & CWX) ? e.x : _area.x();
1803 int y = (e.value_mask & CWY) ? e.y : _area.y();
1804 internal_resize(corner, w, h, false, x, y);
1805 } else // if JUST resizing...
1806 internal_resize(corner, w, h, false);
1807 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1808 int x = (e.value_mask & CWX) ? e.x : _area.x();
1809 int y = (e.value_mask & CWY) ? e.y : _area.y();
1810 internal_move(x, y);
1811 }
1812
1813 if (e.value_mask & CWStackMode) {
1814 switch (e.detail) {
1815 case Below:
1816 case BottomIf:
1817 openbox->screen(_screen)->lowerWindow(this);
1818 break;
1819
1820 case Above:
1821 case TopIf:
1822 default:
1823 openbox->screen(_screen)->raiseWindow(this);
1824 break;
1825 }
1826 }
1827 }
1828
1829
1830 void Client::unmapHandler(const XUnmapEvent &e)
1831 {
1832 if (ignore_unmaps) {
1833 #ifdef DEBUG
1834 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1835 #endif // DEBUG
1836 ignore_unmaps--;
1837 return;
1838 }
1839
1840 #ifdef DEBUG
1841 printf("UnmapNotify for 0x%lx\n", e.window);
1842 #endif // DEBUG
1843
1844 otk::EventHandler::unmapHandler(e);
1845
1846 // this deletes us etc
1847 openbox->screen(_screen)->unmanageWindow(this);
1848 }
1849
1850
1851 void Client::destroyHandler(const XDestroyWindowEvent &e)
1852 {
1853 #ifdef DEBUG
1854 printf("DestroyNotify for 0x%lx\n", e.window);
1855 #endif // DEBUG
1856
1857 otk::EventHandler::destroyHandler(e);
1858
1859 // this deletes us etc
1860 openbox->screen(_screen)->unmanageWindow(this);
1861 }
1862
1863
1864 void Client::reparentHandler(const XReparentEvent &e)
1865 {
1866 // this is when the client is first taken captive in the frame
1867 if (e.parent == frame->plate()) return;
1868
1869 #ifdef DEBUG
1870 printf("ReparentNotify for 0x%lx\n", e.window);
1871 #endif // DEBUG
1872
1873 otk::EventHandler::reparentHandler(e);
1874
1875 /*
1876 This event is quite rare and is usually handled in unmapHandler.
1877 However, if the window is unmapped when the reparent event occurs,
1878 the window manager never sees it because an unmap event is not sent
1879 to an already unmapped window.
1880 */
1881
1882 // we don't want the reparent event, put it back on the stack for the X
1883 // server to deal with after we unmanage the window
1884 XEvent ev;
1885 ev.xreparent = e;
1886 XPutBackEvent(**otk::display, &ev);
1887
1888 // this deletes us etc
1889 openbox->screen(_screen)->unmanageWindow(this);
1890 }
1891
1892 void Client::mapRequestHandler(const XMapRequestEvent &e)
1893 {
1894 #ifdef DEBUG
1895 printf("MapRequest for already managed 0x%lx\n", e.window);
1896 #endif // DEBUG
1897
1898 assert(_iconic); // we shouldn't be able to get this unless we're iconic
1899
1900 // move to the current desktop (uniconify)
1901 setDesktop(openbox->screen(_screen)->desktop());
1902 // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1903 }
1904
1905 }
This page took 0.123483 seconds and 4 git commands to generate.