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