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