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