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