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