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