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