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