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