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