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