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