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