]> Dogcows Code - chaz/openbox/blob - src/client.cc
changeState after a setState request
[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 // You can't shade without a titlebar
313 if (!(_decorations & Decor_Titlebar))
314 _functions &= ~Func_Shade;
315
316 changeAllowedActions();
317
318 if (frame) {
319 frame->adjustSize(); // change the decors on the frame
320 frame->adjustPosition(); // with more/less decorations, we may need to be
321 // moved
322 }
323 }
324
325
326 void Client::getMwmHints()
327 {
328 unsigned long num = MwmHints::elements;
329 unsigned long *hints;
330
331 _mwmhints.flags = 0; // default to none
332
333 if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
334 otk::Property::atoms.motif_wm_hints, &num,
335 (unsigned long **)&hints))
336 return;
337
338 if (num >= MwmHints::elements) {
339 // retrieved the hints
340 _mwmhints.flags = hints[0];
341 _mwmhints.functions = hints[1];
342 _mwmhints.decorations = hints[2];
343 }
344
345 delete [] hints;
346 }
347
348
349 void Client::getArea()
350 {
351 XWindowAttributes wattrib;
352 Status ret;
353
354 ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
355 assert(ret != BadWindow);
356
357 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
358 _border_width = wattrib.border_width;
359 }
360
361
362 void Client::getState()
363 {
364 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
365 _iconic = _skip_taskbar = _skip_pager = false;
366
367 unsigned long *state;
368 unsigned long num = (unsigned) -1;
369
370 if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
371 otk::Property::atoms.atom, &num, &state)) {
372 for (unsigned long i = 0; i < num; ++i) {
373 if (state[i] == otk::Property::atoms.net_wm_state_modal)
374 _modal = true;
375 else if (state[i] == otk::Property::atoms.net_wm_state_shaded)
376 _shaded = true;
377 else if (state[i] == otk::Property::atoms.net_wm_state_hidden)
378 _iconic = true;
379 else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
380 _skip_taskbar = true;
381 else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
382 _skip_pager = true;
383 else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
384 _fullscreen = true;
385 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
386 _max_vert = true;
387 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
388 _max_horz = true;
389 else if (state[i] == otk::Property::atoms.net_wm_state_above)
390 _above = true;
391 else if (state[i] == otk::Property::atoms.net_wm_state_below)
392 _below = true;
393 }
394
395 delete [] state;
396 }
397 }
398
399
400 void Client::getShaped()
401 {
402 _shaped = false;
403 #ifdef SHAPE
404 if (otk::display->shape()) {
405 int foo;
406 unsigned int ufoo;
407 int s;
408
409 XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
410
411 XShapeQueryExtents(**otk::display, _window, &s, &foo,
412 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
413 _shaped = (s != 0);
414 }
415 #endif // SHAPE
416 }
417
418
419 void Client::calcLayer() {
420 StackLayer l;
421
422 if (_iconic) l = Layer_Icon;
423 else if (_fullscreen) l = Layer_Fullscreen;
424 else if (_type == Type_Desktop) l = Layer_Desktop;
425 else if (_type == Type_Dock) {
426 if (!_below) l = Layer_Top;
427 else l = Layer_Normal;
428 }
429 else if (_above) l = Layer_Above;
430 else if (_below) l = Layer_Below;
431 else l = Layer_Normal;
432
433 if (l != _layer) {
434 _layer = l;
435 if (frame) {
436 /*
437 if we don't have a frame, then we aren't mapped yet (and this would
438 SIGSEGV :)
439 */
440 openbox->screen(_screen)->raiseWindow(this);
441 }
442 }
443 }
444
445
446 void Client::updateProtocols()
447 {
448 Atom *proto;
449 int num_return = 0;
450
451 _focus_notify = false;
452 _delete_window = false;
453
454 if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
455 for (int i = 0; i < num_return; ++i) {
456 if (proto[i] == otk::Property::atoms.wm_delete_window) {
457 // this means we can request the window to close
458 _delete_window = true;
459 } else if (proto[i] == otk::Property::atoms.wm_take_focus)
460 // if this protocol is requested, then the window will be notified
461 // by the window manager whenever it receives focus
462 _focus_notify = true;
463 }
464 XFree(proto);
465 }
466 }
467
468
469 void Client::updateNormalHints()
470 {
471 XSizeHints size;
472 long ret;
473 int oldgravity = _gravity;
474
475 // defaults
476 _min_ratio = 0.0;
477 _max_ratio = 0.0;
478 _size_inc.setPoint(1, 1);
479 _base_size.setPoint(0, 0);
480 _min_size.setPoint(0, 0);
481 _max_size.setPoint(INT_MAX, INT_MAX);
482
483 // XXX: might want to cancel any interactive resizing of the window at this
484 // point..
485
486 // get the hints from the window
487 if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
488 _positioned = (size.flags & (PPosition|USPosition));
489
490 if (size.flags & PWinGravity) {
491 _gravity = size.win_gravity;
492
493 // if the client has a frame, i.e. has already been mapped and is
494 // changing its gravity
495 if (frame && _gravity != oldgravity) {
496 // move our idea of the client's position based on its new gravity
497 int x, y;
498 frame->frameGravity(x, y);
499 _area.setPos(x, y);
500 }
501 }
502
503 if (size.flags & PAspect) {
504 if (size.min_aspect.y) _min_ratio = size.min_aspect.x/size.min_aspect.y;
505 if (size.max_aspect.y) _max_ratio = size.max_aspect.x/size.max_aspect.y;
506 }
507
508 if (size.flags & PMinSize)
509 _min_size.setPoint(size.min_width, size.min_height);
510
511 if (size.flags & PMaxSize)
512 _max_size.setPoint(size.max_width, size.max_height);
513
514 if (size.flags & PBaseSize)
515 _base_size.setPoint(size.base_width, size.base_height);
516
517 if (size.flags & PResizeInc)
518 _size_inc.setPoint(size.width_inc, size.height_inc);
519 }
520 }
521
522
523 void Client::updateWMHints(bool initstate)
524 {
525 XWMHints *hints;
526
527 // assume a window takes input if it doesnt specify
528 _can_focus = true;
529 bool ur = false;
530
531 if ((hints = XGetWMHints(**otk::display, _window)) != NULL) {
532 if (hints->flags & InputHint)
533 _can_focus = hints->input;
534
535 // only do this when initstate is true!
536 if (initstate && (hints->flags & StateHint))
537 _iconic = hints->initial_state == IconicState;
538
539 if (hints->flags & XUrgencyHint)
540 ur = true;
541
542 if (hints->flags & WindowGroupHint) {
543 if (hints->window_group != _group) {
544 // XXX: remove from the old group if there was one
545 _group = hints->window_group;
546 // XXX: do stuff with the group
547 }
548 } else // no group!
549 _group = None;
550
551 XFree(hints);
552 }
553
554 if (ur != _urgent) {
555 _urgent = ur;
556 #ifdef DEBUG
557 printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
558 (long)_window, _urgent ? "ON" : "OFF");
559 #endif
560 // fire the urgent callback if we're mapped, otherwise, wait until after
561 // we're mapped
562 if (frame)
563 fireUrgent();
564 }
565 }
566
567
568 void Client::updateTitle()
569 {
570 _title = "";
571
572 // try netwm
573 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_name,
574 otk::Property::utf8, &_title)) {
575 // try old x stuff
576 otk::Property::get(_window, otk::Property::atoms.wm_name,
577 otk::Property::ascii, &_title);
578 }
579
580 if (_title.empty())
581 _title = _("Unnamed Window");
582
583 if (frame)
584 frame->setTitle(_title);
585 }
586
587
588 void Client::updateIconTitle()
589 {
590 _icon_title = "";
591
592 // try netwm
593 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon_name,
594 otk::Property::utf8, &_icon_title)) {
595 // try old x stuff
596 otk::Property::get(_window, otk::Property::atoms.wm_icon_name,
597 otk::Property::ascii, &_icon_title);
598 }
599
600 if (_title.empty())
601 _icon_title = _("Unnamed Window");
602 }
603
604
605 void Client::updateClass()
606 {
607 // set the defaults
608 _app_name = _app_class = _role = "";
609
610 otk::Property::StringVect v;
611 unsigned long num = 2;
612
613 if (otk::Property::get(_window, otk::Property::atoms.wm_class,
614 otk::Property::ascii, &num, &v)) {
615 if (num > 0) _app_name = v[0].c_str();
616 if (num > 1) _app_class = v[1].c_str();
617 }
618
619 v.clear();
620 num = 1;
621 if (otk::Property::get(_window, otk::Property::atoms.wm_window_role,
622 otk::Property::ascii, &num, &v)) {
623 if (num > 0) _role = v[0].c_str();
624 }
625 }
626
627
628 void Client::updateStrut()
629 {
630 unsigned long num = 4;
631 unsigned long *data;
632 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_strut,
633 otk::Property::atoms.cardinal, &num, &data))
634 return;
635
636 if (num == 4) {
637 _strut.left = data[0];
638 _strut.right = data[1];
639 _strut.top = data[2];
640 _strut.bottom = data[3];
641
642 // updating here is pointless while we're being mapped cuz we're not in
643 // the screen's client list yet
644 if (frame)
645 openbox->screen(_screen)->updateStrut();
646 }
647
648 delete [] data;
649 }
650
651
652 void Client::updateTransientFor()
653 {
654 Window t = 0;
655 Client *c = 0;
656
657 if (XGetTransientForHint(**otk::display, _window, &t) &&
658 t != _window) { // cant be transient to itself!
659 c = openbox->findClient(t);
660 assert(c != this); // if this happens then we need to check for it
661
662 if (!c /*XXX: && _group*/) {
663 // not transient to a client, see if it is transient for a group
664 if (//t == _group->leader() ||
665 t == None ||
666 t == otk::display->screenInfo(_screen)->rootWindow()) {
667 // window is a transient for its group!
668 // XXX: for now this is treated as non-transient.
669 // this needs to be fixed!
670 }
671 }
672 }
673
674 // if anything has changed...
675 if (c != _transient_for) {
676 if (_transient_for)
677 _transient_for->_transients.remove(this); // remove from old parent
678 _transient_for = c;
679 if (_transient_for)
680 _transient_for->_transients.push_back(this); // add to new parent
681
682 // XXX: change decor status?
683 }
684 }
685
686
687 void Client::propertyHandler(const XPropertyEvent &e)
688 {
689 otk::EventHandler::propertyHandler(e);
690
691 // validate cuz we query stuff off the client here
692 if (!validate()) return;
693
694 // compress changes to a single property into a single change
695 XEvent ce;
696 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
697 // XXX: it would be nice to compress ALL changes to a property, not just
698 // changes in a row without other props between.
699 if (ce.xproperty.atom != e.atom) {
700 XPutBackEvent(**otk::display, &ce);
701 break;
702 }
703 }
704
705 if (e.atom == XA_WM_NORMAL_HINTS) {
706 updateNormalHints();
707 setupDecorAndFunctions(); // normal hints can make a window non-resizable
708 } else if (e.atom == XA_WM_HINTS)
709 updateWMHints();
710 else if (e.atom == XA_WM_TRANSIENT_FOR) {
711 updateTransientFor();
712 getType();
713 calcLayer(); // type may have changed, so update the layer
714 setupDecorAndFunctions();
715 }
716 else if (e.atom == otk::Property::atoms.net_wm_name ||
717 e.atom == otk::Property::atoms.wm_name)
718 updateTitle();
719 else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
720 e.atom == otk::Property::atoms.wm_icon_name)
721 updateIconTitle();
722 else if (e.atom == otk::Property::atoms.wm_class)
723 updateClass();
724 else if (e.atom == otk::Property::atoms.wm_protocols) {
725 updateProtocols();
726 setupDecorAndFunctions();
727 }
728 else if (e.atom == otk::Property::atoms.net_wm_strut)
729 updateStrut();
730 }
731
732
733 void Client::setWMState(long state)
734 {
735 if (state == _wmstate) return; // no change
736
737 switch (state) {
738 case IconicState:
739 setDesktop(ICONIC_DESKTOP);
740 break;
741 case NormalState:
742 setDesktop(openbox->screen(_screen)->desktop());
743 break;
744 }
745 }
746
747
748 void Client::setDesktop(long target)
749 {
750 if (target == _desktop) return;
751
752 printf("Setting desktop %ld\n", target);
753
754 if (!(target >= 0 || target == (signed)0xffffffff ||
755 target == ICONIC_DESKTOP))
756 return;
757
758 _desktop = target;
759
760 // set the desktop hint, but not if we're iconifying
761 if (_desktop != ICONIC_DESKTOP)
762 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
763 otk::Property::atoms.cardinal, (unsigned)_desktop);
764
765 // 'move' the window to the new desktop
766 if (_desktop == openbox->screen(_screen)->desktop() ||
767 _desktop == (signed)0xffffffff)
768 frame->show();
769 else
770 frame->hide();
771
772 // Handle Iconic state. Iconic state is maintained by the client being a
773 // member of the ICONIC_DESKTOP, so this is where we make iconifying and
774 // uniconifying happen.
775 bool i = _desktop == ICONIC_DESKTOP;
776 if (i != _iconic) { // has the state changed?
777 _iconic = i;
778 if (_iconic) {
779 _wmstate = IconicState;
780 ignore_unmaps++;
781 // we unmap the client itself so that we can get MapRequest events, and
782 // because the ICCCM tells us to!
783 XUnmapWindow(**otk::display, _window);
784 } else {
785 _wmstate = NormalState;
786 XMapWindow(**otk::display, _window);
787 }
788 changeState();
789 }
790
791 frame->adjustState();
792 }
793
794
795 void Client::setState(StateAction action, long data1, long data2)
796 {
797 bool shadestate = _shaded;
798 bool fsstate = _fullscreen;
799
800 if (!(action == State_Add || action == State_Remove ||
801 action == State_Toggle))
802 return; // an invalid action was passed to the client message, ignore it
803
804 for (int i = 0; i < 2; ++i) {
805 Atom state = i == 0 ? data1 : data2;
806
807 if (! state) continue;
808
809 // if toggling, then pick whether we're adding or removing
810 if (action == State_Toggle) {
811 if (state == otk::Property::atoms.net_wm_state_modal)
812 action = _modal ? State_Remove : State_Add;
813 else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
814 action = _max_vert ? State_Remove : State_Add;
815 else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
816 action = _max_horz ? State_Remove : State_Add;
817 else if (state == otk::Property::atoms.net_wm_state_shaded)
818 action = _shaded ? State_Remove : State_Add;
819 else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
820 action = _skip_taskbar ? State_Remove : State_Add;
821 else if (state == otk::Property::atoms.net_wm_state_skip_pager)
822 action = _skip_pager ? State_Remove : State_Add;
823 else if (state == otk::Property::atoms.net_wm_state_fullscreen)
824 action = _fullscreen ? State_Remove : State_Add;
825 else if (state == otk::Property::atoms.net_wm_state_above)
826 action = _above ? State_Remove : State_Add;
827 else if (state == otk::Property::atoms.net_wm_state_below)
828 action = _below ? State_Remove : State_Add;
829 }
830
831 if (action == State_Add) {
832 if (state == otk::Property::atoms.net_wm_state_modal) {
833 if (_modal) continue;
834 _modal = true;
835 // XXX: give it focus if another window has focus that shouldnt now
836 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
837 if (_max_vert) continue;
838 _max_vert = true;
839 // XXX: resize the window etc
840 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
841 if (_max_horz) continue;
842 _max_horz = true;
843 // XXX: resize the window etc
844 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
845 shadestate = true;
846 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
847 _skip_taskbar = true;
848 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
849 _skip_pager = true;
850 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
851 fsstate = true;
852 } else if (state == otk::Property::atoms.net_wm_state_above) {
853 if (_above) continue;
854 _above = true;
855 } else if (state == otk::Property::atoms.net_wm_state_below) {
856 if (_below) continue;
857 _below = true;
858 }
859
860 } else { // action == State_Remove
861 if (state == otk::Property::atoms.net_wm_state_modal) {
862 if (!_modal) continue;
863 _modal = false;
864 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
865 if (!_max_vert) continue;
866 _max_vert = false;
867 // XXX: resize the window etc
868 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
869 if (!_max_horz) continue;
870 _max_horz = false;
871 // XXX: resize the window etc
872 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
873 shadestate = false;
874 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
875 _skip_taskbar = false;
876 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
877 _skip_pager = false;
878 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
879 fsstate = false;
880 } else if (state == otk::Property::atoms.net_wm_state_above) {
881 if (!_above) continue;
882 _above = false;
883 } else if (state == otk::Property::atoms.net_wm_state_below) {
884 if (!_below) continue;
885 _below = false;
886 }
887 }
888 }
889 // change fullscreen state before shading, as it will affect if the window
890 // can shade or not
891 if (fsstate != _fullscreen)
892 fullscreen(fsstate);
893 if (shadestate != _shaded)
894 shade(shadestate);
895 calcLayer();
896 changeState(); // change the hint to relect these changes
897 }
898
899
900 void Client::toggleClientBorder(bool addborder)
901 {
902 // adjust our idea of where the client is, based on its border. When the
903 // border is removed, the client should now be considered to be in a
904 // different position.
905 // when re-adding the border to the client, the same operation needs to be
906 // reversed.
907 int oldx = _area.x(), oldy = _area.y();
908 int x = oldx, y = oldy;
909 switch(_gravity) {
910 default:
911 case NorthWestGravity:
912 case WestGravity:
913 case SouthWestGravity:
914 break;
915 case NorthEastGravity:
916 case EastGravity:
917 case SouthEastGravity:
918 if (addborder) x -= _border_width * 2;
919 else x += _border_width * 2;
920 break;
921 case NorthGravity:
922 case SouthGravity:
923 case CenterGravity:
924 case ForgetGravity:
925 case StaticGravity:
926 if (addborder) x -= _border_width;
927 else x += _border_width;
928 break;
929 }
930 switch(_gravity) {
931 default:
932 case NorthWestGravity:
933 case NorthGravity:
934 case NorthEastGravity:
935 break;
936 case SouthWestGravity:
937 case SouthGravity:
938 case SouthEastGravity:
939 if (addborder) y -= _border_width * 2;
940 else y += _border_width * 2;
941 break;
942 case WestGravity:
943 case EastGravity:
944 case CenterGravity:
945 case ForgetGravity:
946 case StaticGravity:
947 if (addborder) y -= _border_width;
948 else y += _border_width;
949 break;
950 }
951 _area.setPos(x, y);
952
953 if (addborder) {
954 XSetWindowBorderWidth(**otk::display, _window, _border_width);
955
956 // move the client so it is back it the right spot _with_ its border!
957 if (x != oldx || y != oldy)
958 XMoveWindow(**otk::display, _window, x, y);
959 } else
960 XSetWindowBorderWidth(**otk::display, _window, 0);
961 }
962
963
964 void Client::clientMessageHandler(const XClientMessageEvent &e)
965 {
966 otk::EventHandler::clientMessageHandler(e);
967
968 // validate cuz we query stuff off the client here
969 if (!validate()) return;
970
971 if (e.format != 32) return;
972
973 if (e.message_type == otk::Property::atoms.wm_change_state) {
974 // compress changes into a single change
975 bool compress = false;
976 XEvent ce;
977 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
978 // XXX: it would be nice to compress ALL messages of a type, not just
979 // messages in a row without other message types between.
980 if (ce.xclient.message_type != e.message_type) {
981 XPutBackEvent(**otk::display, &ce);
982 break;
983 }
984 compress = true;
985 }
986 if (compress)
987 setWMState(ce.xclient.data.l[0]); // use the found event
988 else
989 setWMState(e.data.l[0]); // use the original event
990 } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
991 // compress changes into a single change
992 bool compress = false;
993 XEvent ce;
994 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
995 // XXX: it would be nice to compress ALL messages of a type, not just
996 // messages in a row without other message types between.
997 if (ce.xclient.message_type != e.message_type) {
998 XPutBackEvent(**otk::display, &ce);
999 break;
1000 }
1001 compress = true;
1002 }
1003 if (compress)
1004 setDesktop(e.data.l[0]); // use the found event
1005 else
1006 setDesktop(e.data.l[0]); // use the original event
1007 } else if (e.message_type == otk::Property::atoms.net_wm_state) {
1008 // can't compress these
1009 #ifdef DEBUG
1010 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1011 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
1012 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
1013 e.data.l[1], e.data.l[2], _window);
1014 #endif
1015 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
1016 } else if (e.message_type == otk::Property::atoms.net_close_window) {
1017 #ifdef DEBUG
1018 printf("net_close_window for 0x%lx\n", _window);
1019 #endif
1020 close();
1021 } else if (e.message_type == otk::Property::atoms.net_active_window) {
1022 #ifdef DEBUG
1023 printf("net_active_window for 0x%lx\n", _window);
1024 #endif
1025 if (_iconic)
1026 setDesktop(openbox->screen(_screen)->desktop());
1027 if (_shaded)
1028 shade(false);
1029 // XXX: deiconify
1030 focus();
1031 openbox->screen(_screen)->raiseWindow(this);
1032 }
1033 }
1034
1035
1036 #if defined(SHAPE)
1037 void Client::shapeHandler(const XShapeEvent &e)
1038 {
1039 otk::EventHandler::shapeHandler(e);
1040
1041 if (e.kind == ShapeBounding) {
1042 _shaped = e.shaped;
1043 frame->adjustShape();
1044 }
1045 }
1046 #endif
1047
1048
1049 void Client::resize(Corner anchor, int w, int h)
1050 {
1051 if (!(_functions & Func_Resize)) return;
1052 internal_resize(anchor, w, h);
1053 }
1054
1055
1056 void Client::internal_resize(Corner anchor, int w, int h, bool user,
1057 int x, int y)
1058 {
1059 w -= _base_size.x();
1060 h -= _base_size.y();
1061
1062 // for interactive resizing. have to move half an increment in each
1063 // direction.
1064 w += _size_inc.x() / 2;
1065 h += _size_inc.y() / 2;
1066
1067 if (user) {
1068 // if this is a user-requested resize, then check against min/max sizes
1069 // and aspect ratios
1070
1071 // smaller than min size or bigger than max size?
1072 if (w < _min_size.x()) w = _min_size.x();
1073 else if (w > _max_size.x()) w = _max_size.x();
1074 if (h < _min_size.y()) h = _min_size.y();
1075 else if (h > _max_size.y()) h = _max_size.y();
1076
1077 // adjust the height ot match the width for the aspect ratios
1078 if (_min_ratio)
1079 if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
1080 if (_max_ratio)
1081 if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
1082 }
1083
1084 // keep to the increments
1085 w /= _size_inc.x();
1086 h /= _size_inc.y();
1087
1088 // you cannot resize to nothing
1089 if (w < 1) w = 1;
1090 if (h < 1) h = 1;
1091
1092 // store the logical size
1093 _logical_size.setPoint(w, h);
1094
1095 w *= _size_inc.x();
1096 h *= _size_inc.y();
1097
1098 w += _base_size.x();
1099 h += _base_size.y();
1100
1101 if (x == INT_MIN || y == INT_MIN) {
1102 x = _area.x();
1103 y = _area.y();
1104 switch (anchor) {
1105 case TopLeft:
1106 break;
1107 case TopRight:
1108 x -= w - _area.width();
1109 break;
1110 case BottomLeft:
1111 y -= h - _area.height();
1112 break;
1113 case BottomRight:
1114 x -= w - _area.width();
1115 y -= h - _area.height();
1116 break;
1117 }
1118 }
1119
1120 _area.setSize(w, h);
1121
1122 XResizeWindow(**otk::display, _window, w, h);
1123
1124 // resize the frame to match the request
1125 frame->adjustSize();
1126 internal_move(x, y);
1127 }
1128
1129
1130 void Client::move(int x, int y)
1131 {
1132 if (!(_functions & Func_Move)) return;
1133 internal_move(x, y);
1134 }
1135
1136
1137 void Client::internal_move(int x, int y)
1138 {
1139 _area.setPos(x, y);
1140
1141 // move the frame to be in the requested position
1142 if (frame) { // this can be called while mapping, before frame exists
1143 frame->adjustPosition();
1144
1145 // send synthetic configure notify (we don't need to if we aren't mapped
1146 // yet)
1147 XEvent event;
1148 event.type = ConfigureNotify;
1149 event.xconfigure.display = **otk::display;
1150 event.xconfigure.event = _window;
1151 event.xconfigure.window = _window;
1152
1153 // root window coords with border in mind
1154 event.xconfigure.x = x - _border_width + frame->size().left;
1155 event.xconfigure.y = y - _border_width + frame->size().top;
1156
1157 event.xconfigure.width = _area.width();
1158 event.xconfigure.height = _area.height();
1159 event.xconfigure.border_width = _border_width;
1160 event.xconfigure.above = frame->plate();
1161 event.xconfigure.override_redirect = False;
1162 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1163 StructureNotifyMask, &event);
1164 #if 0//def DEBUG
1165 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1166 event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
1167 event.xconfigure.height, event.xconfigure.window);
1168 #endif
1169 }
1170 }
1171
1172
1173 void Client::close()
1174 {
1175 XEvent ce;
1176
1177 if (!(_functions & Func_Close)) return;
1178
1179 // XXX: itd be cool to do timeouts and shit here for killing the client's
1180 // process off
1181 // like... if the window is around after 5 seconds, then the close button
1182 // turns a nice red, and if this function is called again, the client is
1183 // explicitly killed.
1184
1185 ce.xclient.type = ClientMessage;
1186 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1187 ce.xclient.display = **otk::display;
1188 ce.xclient.window = _window;
1189 ce.xclient.format = 32;
1190 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1191 ce.xclient.data.l[1] = CurrentTime;
1192 ce.xclient.data.l[2] = 0l;
1193 ce.xclient.data.l[3] = 0l;
1194 ce.xclient.data.l[4] = 0l;
1195 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1196 }
1197
1198
1199 void Client::changeState()
1200 {
1201 unsigned long state[2];
1202 state[0] = _wmstate;
1203 state[1] = None;
1204 otk::Property::set(_window, otk::Property::atoms.wm_state,
1205 otk::Property::atoms.wm_state, state, 2);
1206
1207 Atom netstate[10];
1208 int num = 0;
1209 if (_modal)
1210 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1211 if (_shaded)
1212 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1213 if (_iconic)
1214 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1215 if (_skip_taskbar)
1216 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1217 if (_skip_pager)
1218 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1219 if (_fullscreen)
1220 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1221 if (_max_vert)
1222 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1223 if (_max_horz)
1224 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1225 if (_above)
1226 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1227 if (_below)
1228 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1229 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1230 otk::Property::atoms.atom, netstate, num);
1231
1232 calcLayer();
1233
1234 if (frame)
1235 frame->adjustState();
1236 }
1237
1238
1239 void Client::changeAllowedActions(void)
1240 {
1241 Atom actions[9];
1242 int num = 0;
1243
1244 actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1245
1246 if (_functions & Func_Shade)
1247 actions[num++] = otk::Property::atoms.net_wm_action_shade;
1248 if (_functions & Func_Close)
1249 actions[num++] = otk::Property::atoms.net_wm_action_close;
1250 if (_functions & Func_Move)
1251 actions[num++] = otk::Property::atoms.net_wm_action_move;
1252 if (_functions & Func_Iconify)
1253 actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1254 if (_functions & Func_Resize)
1255 actions[num++] = otk::Property::atoms.net_wm_action_resize;
1256 if (_functions & Func_Fullscreen)
1257 actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1258 if (_functions & Func_Maximize) {
1259 actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1260 actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1261 }
1262
1263 otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1264 otk::Property::atoms.atom, actions, num);
1265 }
1266
1267
1268 void Client::applyStartupState()
1269 {
1270 // these are in a carefully crafted order..
1271
1272 if (_iconic) {
1273 _iconic = false;
1274 setDesktop(ICONIC_DESKTOP);
1275 }
1276 if (_fullscreen) {
1277 _fullscreen = false;
1278 fullscreen(true);
1279 }
1280 if (_shaded) {
1281 _shaded = false;
1282 shade(true);
1283 }
1284 if (_urgent)
1285 fireUrgent();
1286
1287 if (_max_vert); // XXX: incomplete
1288 if (_max_horz); // XXX: incomplete
1289
1290 if (_skip_taskbar); // nothing to do for this
1291 if (_skip_pager); // nothing to do for this
1292 if (_modal); // nothing to do for this
1293 if (_above); // nothing to do for this
1294 if (_below); // nothing to do for this
1295 }
1296
1297
1298 void Client::fireUrgent()
1299 {
1300 // call the python UrgentWindow callbacks
1301 EventData data(_screen, this, EventAction::UrgentWindow, 0);
1302 openbox->bindings()->fireEvent(&data);
1303 }
1304
1305
1306 void Client::shade(bool shade)
1307 {
1308 if (!(_functions & Func_Shade) || // can't
1309 _shaded == shade) return; // already done
1310
1311 // when we're iconic, don't change the wmstate
1312 if (!_iconic)
1313 _wmstate = shade ? IconicState : NormalState;
1314 _shaded = shade;
1315 changeState();
1316 frame->adjustSize();
1317 }
1318
1319
1320 void Client::fullscreen(bool fs)
1321 {
1322 static FunctionFlags saved_func;
1323 static DecorationFlags saved_decor;
1324 static otk::Rect saved_area;
1325 static otk::Point saved_logical_size;
1326
1327 if (!(_functions & Func_Fullscreen) || // can't
1328 _fullscreen == fs) return; // already done
1329
1330 _fullscreen = fs;
1331 changeState(); // change the state hints on the client
1332
1333 if (fs) {
1334 // save the functions and remove them
1335 saved_func = _functions;
1336 _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1337 // save the decorations and remove them
1338 saved_decor = _decorations;
1339 _decorations = 0;
1340 // save the area and adjust it (we don't call internal resize here for
1341 // constraints on the size, etc, we just make it fullscreen).
1342 saved_area = _area;
1343 const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1344 _area.setRect(0, 0, info->width(), info->height());
1345 saved_logical_size = _logical_size;
1346 _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
1347 (info->height() - _base_size.y()) / _size_inc.y());
1348 } else {
1349 _functions = saved_func;
1350 _decorations = saved_decor;
1351 _area = saved_area;
1352 _logical_size = saved_logical_size;
1353 }
1354
1355 changeAllowedActions(); // based on the new _functions
1356
1357 frame->adjustSize(); // drop/replace the decor's and resize
1358 frame->adjustPosition(); // get (back) in position!
1359
1360 // raise (back) into our stacking layer
1361 openbox->screen(_screen)->raiseWindow(this);
1362
1363 // try focus us when we go into fullscreen mode
1364 if (fs) focus();
1365 }
1366
1367
1368 void Client::disableDecorations(DecorationFlags flags)
1369 {
1370 _disabled_decorations = flags;
1371 setupDecorAndFunctions();
1372 }
1373
1374
1375 bool Client::focus()
1376 {
1377 // won't try focus if the client doesn't want it, or if the window isn't
1378 // visible on the screen
1379 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1380
1381 if (_focused) return true;
1382
1383 // do a check to see if the window has already been unmapped or destroyed
1384 // do this intelligently while watching out for unmaps we've generated
1385 // (ignore_unmaps > 0)
1386 XEvent ev;
1387 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1388 XPutBackEvent(**otk::display, &ev);
1389 return false;
1390 }
1391 while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1392 if (ignore_unmaps) {
1393 unmapHandler(ev.xunmap);
1394 } else {
1395 XPutBackEvent(**otk::display, &ev);
1396 return false;
1397 }
1398 }
1399
1400 if (_can_focus)
1401 XSetInputFocus(**otk::display, _window,
1402 RevertToNone, CurrentTime);
1403
1404 if (_focus_notify) {
1405 XEvent ce;
1406 ce.xclient.type = ClientMessage;
1407 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1408 ce.xclient.display = **otk::display;
1409 ce.xclient.window = _window;
1410 ce.xclient.format = 32;
1411 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1412 ce.xclient.data.l[1] = openbox->lastTime();
1413 ce.xclient.data.l[2] = 0l;
1414 ce.xclient.data.l[3] = 0l;
1415 ce.xclient.data.l[4] = 0l;
1416 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1417 }
1418
1419 XSync(**otk::display, False);
1420 return true;
1421 }
1422
1423
1424 void Client::unfocus() const
1425 {
1426 if (!_focused) return;
1427
1428 assert(openbox->focusedClient() == this);
1429 openbox->setFocusedClient(0);
1430 }
1431
1432
1433 void Client::focusHandler(const XFocusChangeEvent &e)
1434 {
1435 #ifdef DEBUG
1436 // printf("FocusIn for 0x%lx\n", e.window);
1437 #endif // DEBUG
1438
1439 otk::EventHandler::focusHandler(e);
1440
1441 frame->focus();
1442 _focused = true;
1443
1444 openbox->setFocusedClient(this);
1445 }
1446
1447
1448 void Client::unfocusHandler(const XFocusChangeEvent &e)
1449 {
1450 #ifdef DEBUG
1451 // printf("FocusOut for 0x%lx\n", e.window);
1452 #endif // DEBUG
1453
1454 otk::EventHandler::unfocusHandler(e);
1455
1456 frame->unfocus();
1457 _focused = false;
1458
1459 if (openbox->focusedClient() == this)
1460 openbox->setFocusedClient(0);
1461 }
1462
1463
1464 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1465 {
1466 #ifdef DEBUG
1467 printf("ConfigureRequest for 0x%lx\n", e.window);
1468 #endif // DEBUG
1469
1470 otk::EventHandler::configureRequestHandler(e);
1471
1472 // if we are iconic (or shaded (fvwm does this)) ignore the event
1473 if (_iconic || _shaded) return;
1474
1475 if (e.value_mask & CWBorderWidth)
1476 _border_width = e.border_width;
1477
1478 // resize, then move, as specified in the EWMH section 7.7
1479 if (e.value_mask & (CWWidth | CWHeight)) {
1480 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1481 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1482
1483 Corner corner;
1484 switch (_gravity) {
1485 case NorthEastGravity:
1486 case EastGravity:
1487 corner = TopRight;
1488 break;
1489 case SouthWestGravity:
1490 case SouthGravity:
1491 corner = BottomLeft;
1492 break;
1493 case SouthEastGravity:
1494 corner = BottomRight;
1495 break;
1496 default: // NorthWest, Static, etc
1497 corner = TopLeft;
1498 }
1499
1500 // if moving AND resizing ...
1501 if (e.value_mask & (CWX | CWY)) {
1502 int x = (e.value_mask & CWX) ? e.x : _area.x();
1503 int y = (e.value_mask & CWY) ? e.y : _area.y();
1504 internal_resize(corner, w, h, false, x, y);
1505 } else // if JUST resizing...
1506 internal_resize(corner, w, h, false);
1507 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1508 int x = (e.value_mask & CWX) ? e.x : _area.x();
1509 int y = (e.value_mask & CWY) ? e.y : _area.y();
1510 internal_move(x, y);
1511 }
1512
1513 if (e.value_mask & CWStackMode) {
1514 switch (e.detail) {
1515 case Below:
1516 case BottomIf:
1517 openbox->screen(_screen)->lowerWindow(this);
1518 break;
1519
1520 case Above:
1521 case TopIf:
1522 default:
1523 openbox->screen(_screen)->raiseWindow(this);
1524 break;
1525 }
1526 }
1527 }
1528
1529
1530 void Client::unmapHandler(const XUnmapEvent &e)
1531 {
1532 if (ignore_unmaps) {
1533 #ifdef DEBUG
1534 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1535 #endif // DEBUG
1536 ignore_unmaps--;
1537 return;
1538 }
1539
1540 #ifdef DEBUG
1541 printf("UnmapNotify for 0x%lx\n", e.window);
1542 #endif // DEBUG
1543
1544 otk::EventHandler::unmapHandler(e);
1545
1546 // this deletes us etc
1547 openbox->screen(_screen)->unmanageWindow(this);
1548 }
1549
1550
1551 void Client::destroyHandler(const XDestroyWindowEvent &e)
1552 {
1553 #ifdef DEBUG
1554 printf("DestroyNotify for 0x%lx\n", e.window);
1555 #endif // DEBUG
1556
1557 otk::EventHandler::destroyHandler(e);
1558
1559 // this deletes us etc
1560 openbox->screen(_screen)->unmanageWindow(this);
1561 }
1562
1563
1564 void Client::reparentHandler(const XReparentEvent &e)
1565 {
1566 // this is when the client is first taken captive in the frame
1567 if (e.parent == frame->plate()) return;
1568
1569 #ifdef DEBUG
1570 printf("ReparentNotify for 0x%lx\n", e.window);
1571 #endif // DEBUG
1572
1573 otk::EventHandler::reparentHandler(e);
1574
1575 /*
1576 This event is quite rare and is usually handled in unmapHandler.
1577 However, if the window is unmapped when the reparent event occurs,
1578 the window manager never sees it because an unmap event is not sent
1579 to an already unmapped window.
1580 */
1581
1582 // we don't want the reparent event, put it back on the stack for the X
1583 // server to deal with after we unmanage the window
1584 XEvent ev;
1585 ev.xreparent = e;
1586 XPutBackEvent(**otk::display, &ev);
1587
1588 // this deletes us etc
1589 openbox->screen(_screen)->unmanageWindow(this);
1590 }
1591
1592 void Client::mapRequestHandler(const XMapRequestEvent &e)
1593 {
1594 #ifdef DEBUG
1595 printf("MapRequest for already managed 0x%lx\n", e.window);
1596 #endif // DEBUG
1597
1598 assert(_iconic); // we shouldn't be able to get this unless we're iconic
1599
1600 // move to the current desktop (uniconify)
1601 setDesktop(openbox->screen(_screen)->desktop());
1602 // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1603 }
1604
1605 }
This page took 0.109668 seconds and 5 git commands to generate.