]> Dogcows Code - chaz/openbox/blob - src/client.cc
new/better/cleaner scripting interface
[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("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 _iconic = false;
1185 setDesktop(ICONIC_DESKTOP);
1186 }
1187 if (_fullscreen) {
1188 _fullscreen = false;
1189 fullscreen(true);
1190 }
1191 if (_shaded) {
1192 _shaded = false;
1193 shade(true);
1194 }
1195 if (_urgent)
1196 fireUrgent();
1197
1198 if (_max_vert); // XXX: incomplete
1199 if (_max_horz); // XXX: incomplete
1200
1201 if (_skip_taskbar); // nothing to do for this
1202 if (_skip_pager); // nothing to do for this
1203 if (_modal); // nothing to do for this
1204 if (_above); // nothing to do for this
1205 if (_below); // nothing to do for this
1206 }
1207
1208
1209 void Client::fireUrgent()
1210 {
1211 // call the python UrgentWindow callbacks
1212 EventData data(_screen, this, EventAction::UrgentWindow, 0);
1213 openbox->bindings()->fireEvent(&data);
1214 }
1215
1216
1217 void Client::shade(bool shade)
1218 {
1219 if (!(_functions & Func_Shade) || // can't
1220 _shaded == shade) return; // already done
1221
1222 // when we're iconic, don't change the wmstate
1223 if (!_iconic)
1224 _wmstate = shade ? IconicState : NormalState;
1225 _shaded = shade;
1226 changeState();
1227 frame->adjustSize();
1228 }
1229
1230
1231 void Client::fullscreen(bool fs)
1232 {
1233 static FunctionFlags saved_func;
1234 static DecorationFlags saved_decor;
1235 static otk::Rect saved_area;
1236 static otk::Point saved_logical_size;
1237
1238 if (!(_functions & Func_Fullscreen) || // can't
1239 _fullscreen == fs) return; // already done
1240
1241 _fullscreen = fs;
1242 changeState(); // change the state hints on the client
1243
1244 if (fs) {
1245 // save the functions and remove them
1246 saved_func = _functions;
1247 _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1248 // save the decorations and remove them
1249 saved_decor = _decorations;
1250 _decorations = 0;
1251 // save the area and adjust it (we don't call internal resize here for
1252 // constraints on the size, etc, we just make it fullscreen).
1253 saved_area = _area;
1254 const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1255 _area.setRect(0, 0, info->width(), info->height());
1256 saved_logical_size = _logical_size;
1257 _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(),
1258 (info->height() - _base_size.y()) / _size_inc.y());
1259 } else {
1260 _functions = saved_func;
1261 _decorations = saved_decor;
1262 _area = saved_area;
1263 _logical_size = saved_logical_size;
1264 }
1265
1266 changeAllowedActions(); // based on the new _functions
1267
1268 frame->adjustSize(); // drop/replace the decor's and resize
1269 frame->adjustPosition(); // get (back) in position!
1270
1271 // raise (back) into our stacking layer
1272 openbox->screen(_screen)->raiseWindow(this);
1273
1274 // try focus us when we go into fullscreen mode
1275 if (fs) focus();
1276 }
1277
1278
1279 bool Client::focus()
1280 {
1281 // won't try focus if the client doesn't want it, or if the window isn't
1282 // visible on the screen
1283 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1284
1285 if (_focused) return true;
1286
1287 // do a check to see if the window has already been unmapped or destroyed
1288 // do this intelligently while watching out for unmaps we've generated
1289 // (ignore_unmaps > 0)
1290 XEvent ev;
1291 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1292 XPutBackEvent(**otk::display, &ev);
1293 return false;
1294 }
1295 while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1296 if (ignore_unmaps) {
1297 unmapHandler(ev.xunmap);
1298 } else {
1299 XPutBackEvent(**otk::display, &ev);
1300 return false;
1301 }
1302 }
1303
1304 if (_can_focus)
1305 XSetInputFocus(**otk::display, _window,
1306 RevertToNone, CurrentTime);
1307
1308 if (_focus_notify) {
1309 XEvent ce;
1310 ce.xclient.type = ClientMessage;
1311 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1312 ce.xclient.display = **otk::display;
1313 ce.xclient.window = _window;
1314 ce.xclient.format = 32;
1315 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1316 ce.xclient.data.l[1] = openbox->lastTime();
1317 ce.xclient.data.l[2] = 0l;
1318 ce.xclient.data.l[3] = 0l;
1319 ce.xclient.data.l[4] = 0l;
1320 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1321 }
1322
1323 return true;
1324 }
1325
1326
1327 void Client::unfocus() const
1328 {
1329 if (!_focused) return;
1330
1331 assert(openbox->focusedClient() == this);
1332 openbox->setFocusedClient(0);
1333 }
1334
1335
1336 void Client::focusHandler(const XFocusChangeEvent &e)
1337 {
1338 #ifdef DEBUG
1339 // printf("FocusIn for 0x%lx\n", e.window);
1340 #endif // DEBUG
1341
1342 otk::EventHandler::focusHandler(e);
1343
1344 frame->focus();
1345 _focused = true;
1346
1347 openbox->setFocusedClient(this);
1348 }
1349
1350
1351 void Client::unfocusHandler(const XFocusChangeEvent &e)
1352 {
1353 #ifdef DEBUG
1354 // printf("FocusOut for 0x%lx\n", e.window);
1355 #endif // DEBUG
1356
1357 otk::EventHandler::unfocusHandler(e);
1358
1359 frame->unfocus();
1360 _focused = false;
1361
1362 if (openbox->focusedClient() == this)
1363 openbox->setFocusedClient(0);
1364 }
1365
1366
1367 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1368 {
1369 #ifdef DEBUG
1370 printf("ConfigureRequest for 0x%lx\n", e.window);
1371 #endif // DEBUG
1372
1373 otk::EventHandler::configureRequestHandler(e);
1374
1375 // if we are iconic (or shaded (fvwm does this)) ignore the event
1376 if (_iconic || _shaded) return;
1377
1378 if (e.value_mask & CWBorderWidth)
1379 _border_width = e.border_width;
1380
1381 // resize, then move, as specified in the EWMH section 7.7
1382 if (e.value_mask & (CWWidth | CWHeight)) {
1383 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1384 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1385
1386 Corner corner;
1387 switch (_gravity) {
1388 case NorthEastGravity:
1389 case EastGravity:
1390 corner = TopRight;
1391 break;
1392 case SouthWestGravity:
1393 case SouthGravity:
1394 corner = BottomLeft;
1395 break;
1396 case SouthEastGravity:
1397 corner = BottomRight;
1398 break;
1399 default: // NorthWest, Static, etc
1400 corner = TopLeft;
1401 }
1402
1403 // if moving AND resizing ...
1404 if (e.value_mask & (CWX | CWY)) {
1405 int x = (e.value_mask & CWX) ? e.x : _area.x();
1406 int y = (e.value_mask & CWY) ? e.y : _area.y();
1407 internal_resize(corner, w, h, x, y);
1408 } else // if JUST resizing...
1409 internal_resize(corner, w, h);
1410 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1411 int x = (e.value_mask & CWX) ? e.x : _area.x();
1412 int y = (e.value_mask & CWY) ? e.y : _area.y();
1413 internal_move(x, y);
1414 }
1415
1416 if (e.value_mask & CWStackMode) {
1417 switch (e.detail) {
1418 case Below:
1419 case BottomIf:
1420 openbox->screen(_screen)->lowerWindow(this);
1421 break;
1422
1423 case Above:
1424 case TopIf:
1425 default:
1426 openbox->screen(_screen)->raiseWindow(this);
1427 break;
1428 }
1429 }
1430 }
1431
1432
1433 void Client::unmapHandler(const XUnmapEvent &e)
1434 {
1435 if (ignore_unmaps) {
1436 #ifdef DEBUG
1437 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1438 #endif // DEBUG
1439 ignore_unmaps--;
1440 return;
1441 }
1442
1443 #ifdef DEBUG
1444 printf("UnmapNotify for 0x%lx\n", e.window);
1445 #endif // DEBUG
1446
1447 otk::EventHandler::unmapHandler(e);
1448
1449 // this deletes us etc
1450 openbox->screen(_screen)->unmanageWindow(this);
1451 }
1452
1453
1454 void Client::destroyHandler(const XDestroyWindowEvent &e)
1455 {
1456 #ifdef DEBUG
1457 printf("DestroyNotify for 0x%lx\n", e.window);
1458 #endif // DEBUG
1459
1460 otk::EventHandler::destroyHandler(e);
1461
1462 // this deletes us etc
1463 openbox->screen(_screen)->unmanageWindow(this);
1464 }
1465
1466
1467 void Client::reparentHandler(const XReparentEvent &e)
1468 {
1469 // this is when the client is first taken captive in the frame
1470 if (e.parent == frame->plate()) return;
1471
1472 #ifdef DEBUG
1473 printf("ReparentNotify for 0x%lx\n", e.window);
1474 #endif // DEBUG
1475
1476 otk::EventHandler::reparentHandler(e);
1477
1478 /*
1479 This event is quite rare and is usually handled in unmapHandler.
1480 However, if the window is unmapped when the reparent event occurs,
1481 the window manager never sees it because an unmap event is not sent
1482 to an already unmapped window.
1483 */
1484
1485 // we don't want the reparent event, put it back on the stack for the X
1486 // server to deal with after we unmanage the window
1487 XEvent ev;
1488 ev.xreparent = e;
1489 XPutBackEvent(**otk::display, &ev);
1490
1491 // this deletes us etc
1492 openbox->screen(_screen)->unmanageWindow(this);
1493 }
1494
1495 void Client::mapRequestHandler(const XMapRequestEvent &e)
1496 {
1497 #ifdef DEBUG
1498 printf("MapRequest for already managed 0x%lx\n", e.window);
1499 #endif // DEBUG
1500
1501 assert(_iconic); // we shouldn't be able to get this unless we're iconic
1502
1503 // move to the current desktop (uniconify)
1504 setDesktop(openbox->screen(_screen)->desktop());
1505 // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1506 }
1507
1508 }
This page took 0.102133 seconds and 5 git commands to generate.