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