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