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