]> Dogcows Code - chaz/openbox/blob - src/client.cc
finally set up handle right based on max/min sizes
[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
57 getArea();
58 getDesktop();
59
60 updateTransientFor();
61 getType();
62 getMwmHints();
63
64 getState();
65 getShaped();
66
67 updateProtocols();
68
69 getGravity(); // get the attribute gravity
70 updateNormalHints(); // this may override the attribute gravity
71
72 // got the type, the mwmhints, the protocols, and the normal hints (min/max
73 // sizes), so we're ready to set up
74 // the decorations/functions
75 setupDecorAndFunctions();
76
77 // also get the initial_state and set _iconic if we aren't "starting"
78 // when we're "starting" that means we should use whatever state was already
79 // on the window over the initial map state, because it was already mapped
80 updateWMHints(openbox->state() != Openbox::State_Starting);
81 updateTitle();
82 updateIconTitle();
83 updateClass();
84 updateStrut();
85
86 // this makes sure that these windows appear on all desktops
87 if (/*_type == Type_Dock ||*/ _type == Type_Desktop)
88 _desktop = 0xffffffff;
89
90 // set the desktop hint, to make sure that it always exists, and to reflect
91 // any changes we've made here
92 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
93 otk::Property::atoms.cardinal, (unsigned)_desktop);
94
95 changeState();
96 }
97
98
99 Client::~Client()
100 {
101 // clean up childrens' references
102 while (!_transients.empty()) {
103 _transients.front()->_transient_for = 0;
104 _transients.pop_front();
105 }
106
107 // clean up parents reference to this
108 if (_transient_for)
109 _transient_for->_transients.remove(this); // remove from old parent
110
111 if (openbox->state() != Openbox::State_Exiting) {
112 // these values should not be persisted across a window unmapping/mapping
113 otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
114 otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
115 } else {
116 // if we're left in an iconic state, the client wont be mapped. this is
117 // bad, since we will no longer be managing the window on restart
118 if (_iconic)
119 XMapWindow(**otk::display, _window);
120 }
121 }
122
123
124 bool Client::validate() const
125 {
126 XSync(**otk::display, false); // get all events on the server
127
128 XEvent e;
129 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &e) ||
130 XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &e)) {
131 XPutBackEvent(**otk::display, &e);
132 return false;
133 }
134
135 return true;
136 }
137
138
139 void Client::getGravity()
140 {
141 XWindowAttributes wattrib;
142 Status ret;
143
144 ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
145 assert(ret != BadWindow);
146 _gravity = wattrib.win_gravity;
147 }
148
149
150 void Client::getDesktop()
151 {
152 // defaults to the current desktop
153 _desktop = openbox->screen(_screen)->desktop();
154
155 if (otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
156 otk::Property::atoms.cardinal,
157 (long unsigned*)&_desktop)) {
158 #ifdef DEBUG
159 // printf("Window requested desktop: %ld\n", _desktop);
160 #endif
161 }
162 }
163
164
165 void Client::getType()
166 {
167 _type = (WindowType) -1;
168
169 unsigned long *val;
170 unsigned long num = (unsigned) -1;
171 if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
172 otk::Property::atoms.atom, &num, &val)) {
173 // use the first value that we know about in the array
174 for (unsigned long i = 0; i < num; ++i) {
175 if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
176 _type = Type_Desktop;
177 else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
178 _type = Type_Dock;
179 else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
180 _type = Type_Toolbar;
181 else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
182 _type = Type_Menu;
183 else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
184 _type = Type_Utility;
185 else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
186 _type = Type_Splash;
187 else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
188 _type = Type_Dialog;
189 else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
190 _type = Type_Normal;
191 // XXX: make this work again
192 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
193 // mwm_decorations = 0; // prevent this window from getting any decor
194 if (_type != (WindowType) -1)
195 break; // grab the first known type
196 }
197 delete val;
198 }
199
200 if (_type == (WindowType) -1) {
201 /*
202 * the window type hint was not set, which means we either classify ourself
203 * as a normal window or a dialog, depending on if we are a transient.
204 */
205 if (_transient_for)
206 _type = Type_Dialog;
207 else
208 _type = Type_Normal;
209 }
210 }
211
212
213 void Client::setupDecorAndFunctions()
214 {
215 // start with everything (cept fullscreen)
216 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
217 Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
218 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
219 Func_Shade;
220 if (_delete_window) {
221 _decorations |= Decor_Close;
222 _functions |= Func_Close;
223 }
224
225 if (!(_min_size.x() < _max_size.x() || _min_size.y() < _max_size.y())) {
226 _decorations &= ~(Decor_Maximize | Decor_Handle);
227 _functions &= ~(Func_Resize | Func_Maximize);
228 }
229
230 switch (_type) {
231 case Type_Normal:
232 // normal windows retain all of the possible decorations and
233 // functionality, and are the only windows that you can fullscreen
234 _functions |= Func_Fullscreen;
235
236 case Type_Dialog:
237 // dialogs cannot be maximized
238 _decorations &= ~Decor_Maximize;
239 _functions &= ~Func_Maximize;
240 break;
241
242 case Type_Menu:
243 case Type_Toolbar:
244 case Type_Utility:
245 // these windows get less functionality
246 _decorations &= ~(Decor_Iconify | Decor_Handle);
247 _functions &= ~(Func_Iconify | Func_Resize);
248 break;
249
250 case Type_Desktop:
251 case Type_Dock:
252 case Type_Splash:
253 // none of these windows are manipulated by the window manager
254 _decorations = 0;
255 _functions = 0;
256 break;
257 }
258
259 // Mwm Hints are applied subtractively to what has already been chosen for
260 // decor and functionality
261 if (_mwmhints.flags & MwmFlag_Decorations) {
262 if (! (_mwmhints.decorations & MwmDecor_All)) {
263 if (! (_mwmhints.decorations & MwmDecor_Border))
264 _decorations &= ~Decor_Border;
265 if (! (_mwmhints.decorations & MwmDecor_Handle))
266 _decorations &= ~Decor_Handle;
267 if (! (_mwmhints.decorations & MwmDecor_Title)) {
268 _decorations &= ~Decor_Titlebar;
269 // if we don't have a titlebar, then we cannot shade!
270 _functions &= ~Func_Shade;
271 }
272 if (! (_mwmhints.decorations & MwmDecor_Iconify))
273 _decorations &= ~Decor_Iconify;
274 if (! (_mwmhints.decorations & MwmDecor_Maximize))
275 _decorations &= ~Decor_Maximize;
276 }
277 }
278
279 if (_mwmhints.flags & MwmFlag_Functions) {
280 if (! (_mwmhints.functions & MwmFunc_All)) {
281 if (! (_mwmhints.functions & MwmFunc_Resize))
282 _functions &= ~Func_Resize;
283 if (! (_mwmhints.functions & MwmFunc_Move))
284 _functions &= ~Func_Move;
285 if (! (_mwmhints.functions & MwmFunc_Iconify))
286 _functions &= ~Func_Iconify;
287 if (! (_mwmhints.functions & MwmFunc_Maximize))
288 _functions &= ~Func_Maximize;
289 // dont let mwm hints kill the close button
290 //if (! (_mwmhints.functions & MwmFunc_Close))
291 // _functions &= ~Func_Close;
292 }
293 }
294
295 // finally, user specified disabled decorations are applied to subtract
296 // decorations
297 if (_disabled_decorations & Decor_Titlebar)
298 _decorations &= ~Decor_Titlebar;
299 if (_disabled_decorations & Decor_Handle)
300 _decorations &= ~Decor_Handle;
301 if (_disabled_decorations & Decor_Border)
302 _decorations &= ~Decor_Border;
303 if (_disabled_decorations & Decor_Iconify)
304 _decorations &= ~Decor_Iconify;
305 if (_disabled_decorations & Decor_Maximize)
306 _decorations &= ~Decor_Maximize;
307 if (_disabled_decorations & Decor_AllDesktops)
308 _decorations &= ~Decor_AllDesktops;
309 if (_disabled_decorations & Decor_Close)
310 _decorations &= ~Decor_Close;
311
312 changeAllowedActions();
313
314 if (frame) {
315 frame->adjustSize(); // change the decors on the frame
316 frame->adjustPosition(); // with more/less decorations, we may need to be
317 // moved
318 }
319 }
320
321
322 void Client::getMwmHints()
323 {
324 unsigned long num = MwmHints::elements;
325 unsigned long *hints;
326
327 _mwmhints.flags = 0; // default to none
328
329 if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
330 otk::Property::atoms.motif_wm_hints, &num,
331 (unsigned long **)&hints))
332 return;
333
334 if (num >= MwmHints::elements) {
335 // retrieved the hints
336 _mwmhints.flags = hints[0];
337 _mwmhints.functions = hints[1];
338 _mwmhints.decorations = hints[2];
339 }
340
341 delete [] hints;
342 }
343
344
345 void Client::getArea()
346 {
347 XWindowAttributes wattrib;
348 Status ret;
349
350 ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
351 assert(ret != BadWindow);
352
353 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
354 _border_width = wattrib.border_width;
355 }
356
357
358 void Client::getState()
359 {
360 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
361 _iconic = _skip_taskbar = _skip_pager = false;
362
363 unsigned long *state;
364 unsigned long num = (unsigned) -1;
365
366 if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
367 otk::Property::atoms.atom, &num, &state)) {
368 for (unsigned long i = 0; i < num; ++i) {
369 if (state[i] == otk::Property::atoms.net_wm_state_modal)
370 _modal = true;
371 else if (state[i] == otk::Property::atoms.net_wm_state_shaded)
372 _shaded = true;
373 else if (state[i] == otk::Property::atoms.net_wm_state_hidden)
374 _iconic = true;
375 else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
376 _skip_taskbar = true;
377 else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
378 _skip_pager = true;
379 else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
380 _fullscreen = true;
381 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
382 _max_vert = true;
383 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
384 _max_horz = true;
385 else if (state[i] == otk::Property::atoms.net_wm_state_above)
386 _above = true;
387 else if (state[i] == otk::Property::atoms.net_wm_state_below)
388 _below = true;
389 }
390
391 delete [] state;
392 }
393 }
394
395
396 void Client::getShaped()
397 {
398 _shaped = false;
399 #ifdef SHAPE
400 if (otk::display->shape()) {
401 int foo;
402 unsigned int ufoo;
403 int s;
404
405 XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
406
407 XShapeQueryExtents(**otk::display, _window, &s, &foo,
408 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
409 _shaped = (s != 0);
410 }
411 #endif // SHAPE
412 }
413
414
415 void Client::calcLayer() {
416 StackLayer l;
417
418 if (_iconic) l = Layer_Icon;
419 else if (_fullscreen) l = Layer_Fullscreen;
420 else if (_type == Type_Desktop) l = Layer_Desktop;
421 else if (_type == Type_Dock) {
422 if (!_below) l = Layer_Top;
423 else l = Layer_Normal;
424 }
425 else if (_above) l = Layer_Above;
426 else if (_below) l = Layer_Below;
427 else l = Layer_Normal;
428
429 if (l != _layer) {
430 _layer = l;
431 if (frame) {
432 /*
433 if we don't have a frame, then we aren't mapped yet (and this would
434 SIGSEGV :)
435 */
436 openbox->screen(_screen)->raiseWindow(this);
437 }
438 }
439 }
440
441
442 void Client::updateProtocols()
443 {
444 Atom *proto;
445 int num_return = 0;
446
447 _focus_notify = false;
448 _delete_window = false;
449
450 if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
451 for (int i = 0; i < num_return; ++i) {
452 if (proto[i] == otk::Property::atoms.wm_delete_window) {
453 // this means we can request the window to close
454 _delete_window = true;
455 } else if (proto[i] == otk::Property::atoms.wm_take_focus)
456 // if this protocol is requested, then the window will be notified
457 // by the window manager whenever it receives focus
458 _focus_notify = true;
459 }
460 XFree(proto);
461 }
462 }
463
464
465 void Client::updateNormalHints()
466 {
467 XSizeHints size;
468 long ret;
469 int oldgravity = _gravity;
470
471 // defaults
472 _min_ratio = 0.0;
473 _max_ratio = 0.0;
474 _size_inc.setPoint(1, 1);
475 _base_size.setPoint(0, 0);
476 _min_size.setPoint(0, 0);
477 _max_size.setPoint(INT_MAX, INT_MAX);
478
479 // XXX: might want to cancel any interactive resizing of the window at this
480 // point..
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, y;
494 frame->frameGravity(x, y);
495 _area.setPos(x, y);
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.setPoint(size.min_width, size.min_height);
506
507 if (size.flags & PMaxSize)
508 _max_size.setPoint(size.max_width, size.max_height);
509
510 if (size.flags & PBaseSize)
511 _base_size.setPoint(size.base_width, size.base_height);
512
513 if (size.flags & PResizeInc)
514 _size_inc.setPoint(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->setTitle(_title);
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)->updateStrut();
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 // XXX: change decor status?
679 }
680 }
681
682
683 void Client::propertyHandler(const XPropertyEvent &e)
684 {
685 otk::EventHandler::propertyHandler(e);
686
687 // validate cuz we query stuff off the client here
688 if (!validate()) return;
689
690 // compress changes to a single property into a single change
691 XEvent ce;
692 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
693 // XXX: it would be nice to compress ALL changes to a property, not just
694 // changes in a row without other props between.
695 if (ce.xproperty.atom != e.atom) {
696 XPutBackEvent(**otk::display, &ce);
697 break;
698 }
699 }
700
701 if (e.atom == XA_WM_NORMAL_HINTS) {
702 updateNormalHints();
703 setupDecorAndFunctions(); // normal hints can make a window non-resizable
704 } else if (e.atom == XA_WM_HINTS)
705 updateWMHints();
706 else if (e.atom == XA_WM_TRANSIENT_FOR) {
707 updateTransientFor();
708 getType();
709 calcLayer(); // type may have changed, so update the layer
710 setupDecorAndFunctions();
711 }
712 else if (e.atom == otk::Property::atoms.net_wm_name ||
713 e.atom == otk::Property::atoms.wm_name)
714 updateTitle();
715 else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
716 e.atom == otk::Property::atoms.wm_icon_name)
717 updateIconTitle();
718 else if (e.atom == otk::Property::atoms.wm_class)
719 updateClass();
720 else if (e.atom == otk::Property::atoms.wm_protocols) {
721 updateProtocols();
722 setupDecorAndFunctions();
723 }
724 else if (e.atom == otk::Property::atoms.net_wm_strut)
725 updateStrut();
726 }
727
728
729 void Client::setWMState(long state)
730 {
731 if (state == _wmstate) return; // no change
732
733 switch (state) {
734 case IconicState:
735 setDesktop(ICONIC_DESKTOP);
736 break;
737 case NormalState:
738 setDesktop(openbox->screen(_screen)->desktop());
739 break;
740 }
741 }
742
743
744 void Client::setDesktop(long target)
745 {
746 if (target == _desktop) return;
747
748 printf("Setting desktop %ld\n", target);
749
750 if (!(target >= 0 || target == (signed)0xffffffff ||
751 target == ICONIC_DESKTOP))
752 return;
753
754 _desktop = target;
755
756 // set the desktop hint, but not if we're iconifying
757 if (_desktop != ICONIC_DESKTOP)
758 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
759 otk::Property::atoms.cardinal, (unsigned)_desktop);
760
761 // 'move' the window to the new desktop
762 if (_desktop == openbox->screen(_screen)->desktop() ||
763 _desktop == (signed)0xffffffff)
764 frame->show();
765 else
766 frame->hide();
767
768 // Handle Iconic state. Iconic state is maintained by the client being a
769 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
770 // uniconifying happen.
771 bool i = _desktop == ICONIC_DESKTOP;
772 if (i != _iconic) { // has the state changed?
773 _iconic = i;
774 if (_iconic) {
775 _wmstate = IconicState;
776 ignore_unmaps++;
777 // we unmap the client itself so that we can get MapRequest events, and
778 // because the ICCCM tells us to!
779 XUnmapWindow(**otk::display, _window);
780 } else {
781 _wmstate = NormalState;
782 XMapWindow(**otk::display, _window);
783 }
784 changeState();
785 }
786
787 frame->adjustState();
788 }
789
790
791 void Client::setState(StateAction action, long data1, long data2)
792 {
793 bool shadestate = _shaded;
794 bool fsstate = _fullscreen;
795
796 if (!(action == State_Add || action == State_Remove ||
797 action == State_Toggle))
798 return; // an invalid action was passed to the client message, ignore it
799
800 for (int i = 0; i < 2; ++i) {
801 Atom state = i == 0 ? data1 : data2;
802
803 if (! state) continue;
804
805 // if toggling, then pick whether we're adding or removing
806 if (action == State_Toggle) {
807 if (state == otk::Property::atoms.net_wm_state_modal)
808 action = _modal ? State_Remove : State_Add;
809 else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
810 action = _max_vert ? State_Remove : State_Add;
811 else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
812 action = _max_horz ? State_Remove : State_Add;
813 else if (state == otk::Property::atoms.net_wm_state_shaded)
814 action = _shaded ? State_Remove : State_Add;
815 else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
816 action = _skip_taskbar ? State_Remove : State_Add;
817 else if (state == otk::Property::atoms.net_wm_state_skip_pager)
818 action = _skip_pager ? State_Remove : State_Add;
819 else if (state == otk::Property::atoms.net_wm_state_fullscreen)
820 action = _fullscreen ? State_Remove : State_Add;
821 else if (state == otk::Property::atoms.net_wm_state_above)
822 action = _above ? State_Remove : State_Add;
823 else if (state == otk::Property::atoms.net_wm_state_below)
824 action = _below ? State_Remove : State_Add;
825 }
826
827 if (action == State_Add) {
828 if (state == otk::Property::atoms.net_wm_state_modal) {
829 if (_modal) continue;
830 _modal = true;
831 // XXX: give it focus if another window has focus that shouldnt now
832 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
833 if (_max_vert) continue;
834 _max_vert = true;
835 // XXX: resize the window etc
836 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
837 if (_max_horz) continue;
838 _max_horz = true;
839 // XXX: resize the window etc
840 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
841 shadestate = true;
842 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
843 _skip_taskbar = true;
844 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
845 _skip_pager = true;
846 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
847 fsstate = true;
848 } else if (state == otk::Property::atoms.net_wm_state_above) {
849 if (_above) continue;
850 _above = true;
851 } else if (state == otk::Property::atoms.net_wm_state_below) {
852 if (_below) continue;
853 _below = true;
854 }
855
856 } else { // action == State_Remove
857 if (state == otk::Property::atoms.net_wm_state_modal) {
858 if (!_modal) continue;
859 _modal = false;
860 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
861 if (!_max_vert) continue;
862 _max_vert = false;
863 // XXX: resize the window etc
864 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
865 if (!_max_horz) continue;
866 _max_horz = false;
867 // XXX: resize the window etc
868 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
869 shadestate = false;
870 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
871 _skip_taskbar = false;
872 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
873 _skip_pager = false;
874 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
875 fsstate = false;
876 } else if (state == otk::Property::atoms.net_wm_state_above) {
877 if (!_above) continue;
878 _above = false;
879 } else if (state == otk::Property::atoms.net_wm_state_below) {
880 if (!_below) continue;
881 _below = false;
882 }
883 }
884 }
885 // change fullscreen state before shading, as it will affect if the window
886 // can shade or not
887 if (fsstate != _fullscreen)
888 fullscreen(fsstate);
889 if (shadestate != _shaded)
890 shade(shadestate);
891 calcLayer();
892 }
893
894
895 void Client::toggleClientBorder(bool addborder)
896 {
897 // adjust our idea of where the client is, based on its border. When the
898 // border is removed, the client should now be considered to be in a
899 // different position.
900 // when re-adding the border to the client, the same operation needs to be
901 // reversed.
902 int x = _area.x(), y = _area.y();
903 switch(_gravity) {
904 default:
905 case NorthWestGravity:
906 case WestGravity:
907 case SouthWestGravity:
908 break;
909 case NorthEastGravity:
910 case EastGravity:
911 case SouthEastGravity:
912 if (addborder) x -= _border_width * 2;
913 else x += _border_width * 2;
914 break;
915 case NorthGravity:
916 case SouthGravity:
917 case CenterGravity:
918 case ForgetGravity:
919 case StaticGravity:
920 if (addborder) x -= _border_width;
921 else x += _border_width;
922 break;
923 }
924 switch(_gravity) {
925 default:
926 case NorthWestGravity:
927 case NorthGravity:
928 case NorthEastGravity:
929 break;
930 case SouthWestGravity:
931 case SouthGravity:
932 case SouthEastGravity:
933 if (addborder) y -= _border_width * 2;
934 else y += _border_width * 2;
935 break;
936 case WestGravity:
937 case EastGravity:
938 case CenterGravity:
939 case ForgetGravity:
940 case StaticGravity:
941 if (addborder) y -= _border_width;
942 else y += _border_width;
943 break;
944 }
945 _area.setPos(x, y);
946
947 if (addborder) {
948 XSetWindowBorderWidth(**otk::display, _window, _border_width);
949
950 // move the client so it is back it the right spot _with_ its border!
951 XMoveWindow(**otk::display, _window, x, y);
952 } else
953 XSetWindowBorderWidth(**otk::display, _window, 0);
954 }
955
956
957 void Client::clientMessageHandler(const XClientMessageEvent &e)
958 {
959 otk::EventHandler::clientMessageHandler(e);
960
961 // validate cuz we query stuff off the client here
962 if (!validate()) return;
963
964 if (e.format != 32) return;
965
966 if (e.message_type == otk::Property::atoms.wm_change_state) {
967 // compress changes into a single change
968 bool compress = false;
969 XEvent ce;
970 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
971 // XXX: it would be nice to compress ALL messages of a type, not just
972 // messages in a row without other message types between.
973 if (ce.xclient.message_type != e.message_type) {
974 XPutBackEvent(**otk::display, &ce);
975 break;
976 }
977 compress = true;
978 }
979 if (compress)
980 setWMState(ce.xclient.data.l[0]); // use the found event
981 else
982 setWMState(e.data.l[0]); // use the original event
983 } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
984 // compress changes into a single change
985 bool compress = false;
986 XEvent ce;
987 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
988 // XXX: it would be nice to compress ALL messages of a type, not just
989 // messages in a row without other message types between.
990 if (ce.xclient.message_type != e.message_type) {
991 XPutBackEvent(**otk::display, &ce);
992 break;
993 }
994 compress = true;
995 }
996 if (compress)
997 setDesktop(e.data.l[0]); // use the found event
998 else
999 setDesktop(e.data.l[0]); // use the original event
1000 } else if (e.message_type == otk::Property::atoms.net_wm_state) {
1001 // can't compress these
1002 #ifdef DEBUG
1003 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1004 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
1005 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
1006 e.data.l[1], e.data.l[2], _window);
1007 #endif
1008 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
1009 } else if (e.message_type == otk::Property::atoms.net_close_window) {
1010 #ifdef DEBUG
1011 printf("net_close_window for 0x%lx\n", _window);
1012 #endif
1013 close();
1014 } else if (e.message_type == otk::Property::atoms.net_active_window) {
1015 #ifdef DEBUG
1016 printf("net_active_window for 0x%lx\n", _window);
1017 #endif
1018 if (_iconic)
1019 setDesktop(openbox->screen(_screen)->desktop());
1020 if (_shaded)
1021 shade(false);
1022 // XXX: deiconify
1023 focus();
1024 openbox->screen(_screen)->raiseWindow(this);
1025 }
1026 }
1027
1028
1029 #if defined(SHAPE)
1030 void Client::shapeHandler(const XShapeEvent &e)
1031 {
1032 otk::EventHandler::shapeHandler(e);
1033
1034 if (e.kind == ShapeBounding) {
1035 _shaped = e.shaped;
1036 frame->adjustShape();
1037 }
1038 }
1039 #endif
1040
1041
1042 void Client::resize(Corner anchor, int w, int h)
1043 {
1044 if (!(_functions & Func_Resize)) return;
1045 internal_resize(anchor, w, h);
1046 }
1047
1048
1049 void Client::internal_resize(Corner anchor, int w, int h, bool user,
1050 int x, int y)
1051 {
1052 w -= _base_size.x();
1053 h -= _base_size.y();
1054
1055 // for interactive resizing. have to move half an increment in each
1056 // direction.
1057 w += _size_inc.x() / 2;
1058 h += _size_inc.y() / 2;
1059
1060 if (user) {
1061 // if this is a user-requested resize, then check against min/max sizes
1062 // and aspect ratios
1063
1064 // smaller than min size or bigger than max size?
1065 if (w < _min_size.x()) w = _min_size.x();
1066 else if (w > _max_size.x()) w = _max_size.x();
1067 if (h < _min_size.y()) h = _min_size.y();
1068 else if (h > _max_size.y()) h = _max_size.y();
1069
1070 // adjust the height ot match the width for the aspect ratios
1071 if (_min_ratio)
1072 if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
1073 if (_max_ratio)
1074 if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
1075 }
1076
1077 // keep to the increments
1078 w /= _size_inc.x();
1079 h /= _size_inc.y();
1080
1081 // you cannot resize to nothing
1082 if (w < 1) w = 1;
1083 if (h < 1) h = 1;
1084
1085 // store the logical size
1086 _logical_size.setPoint(w, h);
1087
1088 w *= _size_inc.x();
1089 h *= _size_inc.y();
1090
1091 w += _base_size.x();
1092 h += _base_size.y();
1093
1094 if (x == INT_MIN || y == INT_MIN) {
1095 x = _area.x();
1096 y = _area.y();
1097 switch (anchor) {
1098 case TopLeft:
1099 break;
1100 case TopRight:
1101 x -= w - _area.width();
1102 break;
1103 case BottomLeft:
1104 y -= h - _area.height();
1105 break;
1106 case BottomRight:
1107 x -= w - _area.width();
1108 y -= h - _area.height();
1109 break;
1110 }
1111 }
1112
1113 _area.setSize(w, h);
1114
1115 XResizeWindow(**otk::display, _window, w, h);
1116
1117 // resize the frame to match the request
1118 frame->adjustSize();
1119 internal_move(x, y);
1120 }
1121
1122
1123 void Client::move(int x, int y)
1124 {
1125 if (!(_functions & Func_Move)) return;
1126 internal_move(x, y);
1127 }
1128
1129
1130 void Client::internal_move(int x, int y)
1131 {
1132 _area.setPos(x, y);
1133
1134 // move the frame to be in the requested position
1135 if (frame) { // this can be called while mapping, before frame exists
1136 frame->adjustPosition();
1137
1138 // send synthetic configure notify (we don't need to if we aren't mapped
1139 // yet)
1140 XEvent event;
1141 event.type = ConfigureNotify;
1142 event.xconfigure.display = **otk::display;
1143 event.xconfigure.event = _window;
1144 event.xconfigure.window = _window;
1145 event.xconfigure.x = x;
1146 event.xconfigure.y = y;
1147 event.xconfigure.width = _area.width();
1148 event.xconfigure.height = _area.height();
1149 event.xconfigure.border_width = _border_width;
1150 event.xconfigure.above = frame->window();
1151 event.xconfigure.override_redirect = False;
1152 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1153 StructureNotifyMask, &event);
1154 }
1155 }
1156
1157
1158 void Client::close()
1159 {
1160 XEvent ce;
1161
1162 if (!(_functions & Func_Close)) return;
1163
1164 // XXX: itd be cool to do timeouts and shit here for killing the client's
1165 // process off
1166 // like... if the window is around after 5 seconds, then the close button
1167 // turns a nice red, and if this function is called again, the client is
1168 // explicitly killed.
1169
1170 ce.xclient.type = ClientMessage;
1171 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1172 ce.xclient.display = **otk::display;
1173 ce.xclient.window = _window;
1174 ce.xclient.format = 32;
1175 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1176 ce.xclient.data.l[1] = CurrentTime;
1177 ce.xclient.data.l[2] = 0l;
1178 ce.xclient.data.l[3] = 0l;
1179 ce.xclient.data.l[4] = 0l;
1180 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1181 }
1182
1183
1184 void Client::changeState()
1185 {
1186 unsigned long state[2];
1187 state[0] = _wmstate;
1188 state[1] = None;
1189 otk::Property::set(_window, otk::Property::atoms.wm_state,
1190 otk::Property::atoms.wm_state, state, 2);
1191
1192 Atom netstate[10];
1193 int num = 0;
1194 if (_modal)
1195 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1196 if (_shaded)
1197 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1198 if (_iconic)
1199 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1200 if (_skip_taskbar)
1201 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1202 if (_skip_pager)
1203 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1204 if (_fullscreen)
1205 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1206 if (_max_vert)
1207 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1208 if (_max_horz)
1209 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1210 if (_above)
1211 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1212 if (_below)
1213 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1214 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1215 otk::Property::atoms.atom, netstate, num);
1216
1217 calcLayer();
1218
1219 if (frame)
1220 frame->adjustState();
1221 }
1222
1223
1224 void Client::changeAllowedActions(void)
1225 {
1226 Atom actions[9];
1227 int num = 0;
1228
1229 actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1230
1231 if (_functions & Func_Shade)
1232 actions[num++] = otk::Property::atoms.net_wm_action_shade;
1233 if (_functions & Func_Close)
1234 actions[num++] = otk::Property::atoms.net_wm_action_close;
1235 if (_functions & Func_Move)
1236 actions[num++] = otk::Property::atoms.net_wm_action_move;
1237 if (_functions & Func_Iconify)
1238 actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1239 if (_functions & Func_Resize)
1240 actions[num++] = otk::Property::atoms.net_wm_action_resize;
1241 if (_functions & Func_Fullscreen)
1242 actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1243 if (_functions & Func_Maximize) {
1244 actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1245 actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1246 }
1247
1248 otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1249 otk::Property::atoms.atom, actions, num);
1250 }
1251
1252
1253 void Client::applyStartupState()
1254 {
1255 // these are in a carefully crafted order..
1256
1257 if (_iconic) {
1258 _iconic = false;
1259 setDesktop(ICONIC_DESKTOP);
1260 }
1261 if (_fullscreen) {
1262 _fullscreen = false;
1263 fullscreen(true);
1264 }
1265 if (_shaded) {
1266 _shaded = false;
1267 shade(true);
1268 }
1269 if (_urgent)
1270 fireUrgent();
1271
1272 if (_max_vert); // XXX: incomplete
1273 if (_max_horz); // XXX: incomplete
1274
1275 if (_skip_taskbar); // nothing to do for this
1276 if (_skip_pager); // nothing to do for this
1277 if (_modal); // nothing to do for this
1278 if (_above); // nothing to do for this
1279 if (_below); // nothing to do for this
1280 }
1281
1282
1283 void Client::fireUrgent()
1284 {
1285 // call the python UrgentWindow callbacks
1286 EventData data(_screen, this, EventAction::UrgentWindow, 0);
1287 openbox->bindings()->fireEvent(&data);
1288 }
1289
1290
1291 void Client::shade(bool shade)
1292 {
1293 if (!(_functions & Func_Shade) || // can't
1294 _shaded == shade) return; // already done
1295
1296 // when we're iconic, don't change the wmstate
1297 if (!_iconic)
1298 _wmstate = shade ? IconicState : NormalState;
1299 _shaded = shade;
1300 changeState();
1301 frame->adjustSize();
1302 }
1303
1304
1305 void Client::fullscreen(bool fs)
1306 {
1307 static FunctionFlags saved_func;
1308 static DecorationFlags saved_decor;
1309 static otk::Rect saved_area;
1310 static otk::Point saved_logical_size;
1311
1312 if (!(_functions & Func_Fullscreen) || // can't
1313 _fullscreen == fs) return; // already done
1314
1315 _fullscreen = fs;
1316 changeState(); // change the state hints on the client
1317
1318 if (fs) {
1319 // save the functions and remove them
1320 saved_func = _functions;
1321 _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1322 // save the decorations and remove them
1323 saved_decor = _decorations;
1324 _decorations = 0;
1325 // save the area and adjust it (we don't call internal resize here for
1326 // constraints on the size, etc, we just make it fullscreen).
1327 saved_area = _area;
1328 const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1329 _area.setRect(0, 0, info->width(), info->height());
1330 saved_logical_size = _logical_size;
1331 _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
1332 (info->height() - _base_size.y()) / _size_inc.y());
1333 } else {
1334 _functions = saved_func;
1335 _decorations = saved_decor;
1336 _area = saved_area;
1337 _logical_size = saved_logical_size;
1338 }
1339
1340 changeAllowedActions(); // based on the new _functions
1341
1342 frame->adjustSize(); // drop/replace the decor's and resize
1343 frame->adjustPosition(); // get (back) in position!
1344
1345 // raise (back) into our stacking layer
1346 openbox->screen(_screen)->raiseWindow(this);
1347
1348 // try focus us when we go into fullscreen mode
1349 if (fs) focus();
1350 }
1351
1352
1353 void Client::disableDecorations(DecorationFlags flags)
1354 {
1355 _disabled_decorations = flags;
1356 setupDecorAndFunctions();
1357 }
1358
1359
1360 bool Client::focus()
1361 {
1362 // won't try focus if the client doesn't want it, or if the window isn't
1363 // visible on the screen
1364 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1365
1366 if (_focused) return true;
1367
1368 // do a check to see if the window has already been unmapped or destroyed
1369 // do this intelligently while watching out for unmaps we've generated
1370 // (ignore_unmaps > 0)
1371 XEvent ev;
1372 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1373 XPutBackEvent(**otk::display, &ev);
1374 return false;
1375 }
1376 while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1377 if (ignore_unmaps) {
1378 unmapHandler(ev.xunmap);
1379 } else {
1380 XPutBackEvent(**otk::display, &ev);
1381 return false;
1382 }
1383 }
1384
1385 if (_can_focus)
1386 XSetInputFocus(**otk::display, _window,
1387 RevertToNone, CurrentTime);
1388
1389 if (_focus_notify) {
1390 XEvent ce;
1391 ce.xclient.type = ClientMessage;
1392 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1393 ce.xclient.display = **otk::display;
1394 ce.xclient.window = _window;
1395 ce.xclient.format = 32;
1396 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1397 ce.xclient.data.l[1] = openbox->lastTime();
1398 ce.xclient.data.l[2] = 0l;
1399 ce.xclient.data.l[3] = 0l;
1400 ce.xclient.data.l[4] = 0l;
1401 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1402 }
1403
1404 XSync(**otk::display, False);
1405 return true;
1406 }
1407
1408
1409 void Client::unfocus() const
1410 {
1411 if (!_focused) return;
1412
1413 assert(openbox->focusedClient() == this);
1414 openbox->setFocusedClient(0);
1415 }
1416
1417
1418 void Client::focusHandler(const XFocusChangeEvent &e)
1419 {
1420 #ifdef DEBUG
1421 // printf("FocusIn for 0x%lx\n", e.window);
1422 #endif // DEBUG
1423
1424 otk::EventHandler::focusHandler(e);
1425
1426 frame->focus();
1427 _focused = true;
1428
1429 openbox->setFocusedClient(this);
1430 }
1431
1432
1433 void Client::unfocusHandler(const XFocusChangeEvent &e)
1434 {
1435 #ifdef DEBUG
1436 // printf("FocusOut for 0x%lx\n", e.window);
1437 #endif // DEBUG
1438
1439 otk::EventHandler::unfocusHandler(e);
1440
1441 frame->unfocus();
1442 _focused = false;
1443
1444 if (openbox->focusedClient() == this)
1445 openbox->setFocusedClient(0);
1446 }
1447
1448
1449 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1450 {
1451 #ifdef DEBUG
1452 printf("ConfigureRequest for 0x%lx\n", e.window);
1453 #endif // DEBUG
1454
1455 otk::EventHandler::configureRequestHandler(e);
1456
1457 // if we are iconic (or shaded (fvwm does this)) ignore the event
1458 if (_iconic || _shaded) return;
1459
1460 if (e.value_mask & CWBorderWidth)
1461 _border_width = e.border_width;
1462
1463 // resize, then move, as specified in the EWMH section 7.7
1464 if (e.value_mask & (CWWidth | CWHeight)) {
1465 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1466 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1467
1468 Corner corner;
1469 switch (_gravity) {
1470 case NorthEastGravity:
1471 case EastGravity:
1472 corner = TopRight;
1473 break;
1474 case SouthWestGravity:
1475 case SouthGravity:
1476 corner = BottomLeft;
1477 break;
1478 case SouthEastGravity:
1479 corner = BottomRight;
1480 break;
1481 default: // NorthWest, Static, etc
1482 corner = TopLeft;
1483 }
1484
1485 // if moving AND resizing ...
1486 if (e.value_mask & (CWX | CWY)) {
1487 int x = (e.value_mask & CWX) ? e.x : _area.x();
1488 int y = (e.value_mask & CWY) ? e.y : _area.y();
1489 internal_resize(corner, w, h, false, x, y);
1490 } else // if JUST resizing...
1491 internal_resize(corner, w, h, false);
1492 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1493 int x = (e.value_mask & CWX) ? e.x : _area.x();
1494 int y = (e.value_mask & CWY) ? e.y : _area.y();
1495 internal_move(x, y);
1496 }
1497
1498 if (e.value_mask & CWStackMode) {
1499 switch (e.detail) {
1500 case Below:
1501 case BottomIf:
1502 openbox->screen(_screen)->lowerWindow(this);
1503 break;
1504
1505 case Above:
1506 case TopIf:
1507 default:
1508 openbox->screen(_screen)->raiseWindow(this);
1509 break;
1510 }
1511 }
1512 }
1513
1514
1515 void Client::unmapHandler(const XUnmapEvent &e)
1516 {
1517 if (ignore_unmaps) {
1518 #ifdef DEBUG
1519 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1520 #endif // DEBUG
1521 ignore_unmaps--;
1522 return;
1523 }
1524
1525 #ifdef DEBUG
1526 printf("UnmapNotify for 0x%lx\n", e.window);
1527 #endif // DEBUG
1528
1529 otk::EventHandler::unmapHandler(e);
1530
1531 // this deletes us etc
1532 openbox->screen(_screen)->unmanageWindow(this);
1533 }
1534
1535
1536 void Client::destroyHandler(const XDestroyWindowEvent &e)
1537 {
1538 #ifdef DEBUG
1539 printf("DestroyNotify for 0x%lx\n", e.window);
1540 #endif // DEBUG
1541
1542 otk::EventHandler::destroyHandler(e);
1543
1544 // this deletes us etc
1545 openbox->screen(_screen)->unmanageWindow(this);
1546 }
1547
1548
1549 void Client::reparentHandler(const XReparentEvent &e)
1550 {
1551 // this is when the client is first taken captive in the frame
1552 if (e.parent == frame->plate()) return;
1553
1554 #ifdef DEBUG
1555 printf("ReparentNotify for 0x%lx\n", e.window);
1556 #endif // DEBUG
1557
1558 otk::EventHandler::reparentHandler(e);
1559
1560 /*
1561 This event is quite rare and is usually handled in unmapHandler.
1562 However, if the window is unmapped when the reparent event occurs,
1563 the window manager never sees it because an unmap event is not sent
1564 to an already unmapped window.
1565 */
1566
1567 // we don't want the reparent event, put it back on the stack for the X
1568 // server to deal with after we unmanage the window
1569 XEvent ev;
1570 ev.xreparent = e;
1571 XPutBackEvent(**otk::display, &ev);
1572
1573 // this deletes us etc
1574 openbox->screen(_screen)->unmanageWindow(this);
1575 }
1576
1577 void Client::mapRequestHandler(const XMapRequestEvent &e)
1578 {
1579 #ifdef DEBUG
1580 printf("MapRequest for already managed 0x%lx\n", e.window);
1581 #endif // DEBUG
1582
1583 assert(_iconic); // we shouldn't be able to get this unless we're iconic
1584
1585 // move to the current desktop (uniconify)
1586 setDesktop(openbox->screen(_screen)->desktop());
1587 // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1588 }
1589
1590 }
This page took 0.102447 seconds and 5 git commands to generate.