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