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