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