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