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