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