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