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