]> Dogcows Code - chaz/openbox/blob - src/client.cc
bd7f7b83c82a29b68302de4782088d7219f28f72
[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("DEBUG: 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 // set the default icon(s) XXX load these from the py
747 _nicons = 1;
748 _icons = new Icon[1];
749 _icons[i].w = 0;
750 _icons[i].h = 0;
751 _icons[i].data = 0;
752 }
753
754 assert(_nicons > 0); // there should always be a default..
755
756 if (frame) frame->adjustIcon();
757 }
758
759 void Client::propertyHandler(const XPropertyEvent &e)
760 {
761 otk::EventHandler::propertyHandler(e);
762
763 // validate cuz we query stuff off the client here
764 if (!validate()) return;
765
766 // compress changes to a single property into a single change
767 XEvent ce;
768 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
769 // XXX: it would be nice to compress ALL changes to a property, not just
770 // changes in a row without other props between.
771 if (ce.xproperty.atom != e.atom) {
772 XPutBackEvent(**otk::display, &ce);
773 break;
774 }
775 }
776
777 if (e.atom == XA_WM_NORMAL_HINTS) {
778 updateNormalHints();
779 setupDecorAndFunctions(); // normal hints can make a window non-resizable
780 } else if (e.atom == XA_WM_HINTS)
781 updateWMHints();
782 else if (e.atom == XA_WM_TRANSIENT_FOR) {
783 updateTransientFor();
784 getType();
785 calcLayer(); // type may have changed, so update the layer
786 setupDecorAndFunctions();
787 }
788 else if (e.atom == otk::Property::atoms.net_wm_name ||
789 e.atom == otk::Property::atoms.wm_name)
790 updateTitle();
791 else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
792 e.atom == otk::Property::atoms.wm_icon_name)
793 updateIconTitle();
794 else if (e.atom == otk::Property::atoms.wm_class)
795 updateClass();
796 else if (e.atom == otk::Property::atoms.wm_protocols) {
797 updateProtocols();
798 setupDecorAndFunctions();
799 }
800 else if (e.atom == otk::Property::atoms.net_wm_strut)
801 updateStrut();
802 else if (e.atom == otk::Property::atoms.net_wm_icon)
803 updateIcons();
804 }
805
806 void Client::setWMState(long state)
807 {
808 if (state == _wmstate) return; // no change
809
810 switch (state) {
811 case IconicState:
812 iconify(true);
813 break;
814 case NormalState:
815 iconify(false);
816 break;
817 }
818 }
819
820 void Client::setDesktop(unsigned int target)
821 {
822 if (target == _desktop) return;
823
824 printf("Setting desktop %u\n", target);
825
826 if (!(target < openbox->screen(_screen)->numDesktops() ||
827 target == 0xffffffff))
828 return;
829
830 _desktop = target;
831 // set the desktop hint
832 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
833 otk::Property::atoms.cardinal, _desktop);
834 frame->adjustState(); // the frame can display the current desktop state
835 // 'move' the window to the new desktop
836 showhide();
837 openbox->screen(_screen)->updateStruts();
838 }
839
840 void Client::showhide()
841 {
842 bool show;
843 Screen *s = openbox->screen(_screen);
844
845 if (_iconic) show = false;
846 else if (!(_desktop == s->desktop() ||
847 _desktop == 0xffffffff)) show = false;
848 else if (normal() && s->showingDesktop()) show = false;
849 else show = true;
850
851 if (show) frame->show();
852 else frame->hide();
853 }
854
855 void Client::setState(StateAction action, long data1, long data2)
856 {
857 bool shadestate = _shaded;
858 bool fsstate = _fullscreen;
859 bool maxh = _max_horz;
860 bool maxv = _max_vert;
861
862 if (!(action == State_Add || action == State_Remove ||
863 action == State_Toggle))
864 return; // an invalid action was passed to the client message, ignore it
865
866 for (int i = 0; i < 2; ++i) {
867 Atom state = i == 0 ? data1 : data2;
868
869 if (! state) continue;
870
871 // if toggling, then pick whether we're adding or removing
872 if (action == State_Toggle) {
873 if (state == otk::Property::atoms.net_wm_state_modal)
874 action = _modal ? State_Remove : State_Add;
875 else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
876 action = _max_vert ? State_Remove : State_Add;
877 else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
878 action = _max_horz ? State_Remove : State_Add;
879 else if (state == otk::Property::atoms.net_wm_state_shaded)
880 action = _shaded ? State_Remove : State_Add;
881 else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
882 action = _skip_taskbar ? State_Remove : State_Add;
883 else if (state == otk::Property::atoms.net_wm_state_skip_pager)
884 action = _skip_pager ? State_Remove : State_Add;
885 else if (state == otk::Property::atoms.net_wm_state_fullscreen)
886 action = _fullscreen ? State_Remove : State_Add;
887 else if (state == otk::Property::atoms.net_wm_state_above)
888 action = _above ? State_Remove : State_Add;
889 else if (state == otk::Property::atoms.net_wm_state_below)
890 action = _below ? State_Remove : State_Add;
891 }
892
893 if (action == State_Add) {
894 if (state == otk::Property::atoms.net_wm_state_modal) {
895 if (_modal) continue;
896 _modal = true;
897 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
898 maxv = true;
899 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
900 if (_max_horz) continue;
901 maxh = true;
902 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
903 shadestate = true;
904 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
905 _skip_taskbar = true;
906 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
907 _skip_pager = true;
908 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
909 fsstate = true;
910 } else if (state == otk::Property::atoms.net_wm_state_above) {
911 if (_above) continue;
912 _above = true;
913 } else if (state == otk::Property::atoms.net_wm_state_below) {
914 if (_below) continue;
915 _below = true;
916 }
917
918 } else { // action == State_Remove
919 if (state == otk::Property::atoms.net_wm_state_modal) {
920 if (!_modal) continue;
921 _modal = false;
922 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
923 maxv = false;
924 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
925 maxh = false;
926 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
927 shadestate = false;
928 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
929 _skip_taskbar = false;
930 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
931 _skip_pager = false;
932 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
933 fsstate = false;
934 } else if (state == otk::Property::atoms.net_wm_state_above) {
935 if (!_above) continue;
936 _above = false;
937 } else if (state == otk::Property::atoms.net_wm_state_below) {
938 if (!_below) continue;
939 _below = false;
940 }
941 }
942 }
943 if (maxh != _max_horz || maxv != _max_vert) {
944 if (maxh != _max_horz && maxv != _max_vert) { // toggling both
945 if (maxh == maxv) { // both going the same way
946 maximize(maxh, 0, true);
947 } else {
948 maximize(maxh, 1, true);
949 maximize(maxv, 2, true);
950 }
951 } else { // toggling one
952 if (maxh != _max_horz)
953 maximize(maxh, 1, true);
954 else
955 maximize(maxv, 2, true);
956 }
957 }
958 // change fullscreen state before shading, as it will affect if the window
959 // can shade or not
960 if (fsstate != _fullscreen)
961 fullscreen(fsstate, true);
962 if (shadestate != _shaded)
963 shade(shadestate);
964 calcLayer();
965 changeState(); // change the hint to relect these changes
966 }
967
968 void Client::toggleClientBorder(bool addborder)
969 {
970 // adjust our idea of where the client is, based on its border. When the
971 // border is removed, the client should now be considered to be in a
972 // different position.
973 // when re-adding the border to the client, the same operation needs to be
974 // reversed.
975 int oldx = _area.x(), oldy = _area.y();
976 int x = oldx, y = oldy;
977 switch(_gravity) {
978 default:
979 case NorthWestGravity:
980 case WestGravity:
981 case SouthWestGravity:
982 break;
983 case NorthEastGravity:
984 case EastGravity:
985 case SouthEastGravity:
986 if (addborder) x -= _border_width * 2;
987 else x += _border_width * 2;
988 break;
989 case NorthGravity:
990 case SouthGravity:
991 case CenterGravity:
992 case ForgetGravity:
993 case StaticGravity:
994 if (addborder) x -= _border_width;
995 else x += _border_width;
996 break;
997 }
998 switch(_gravity) {
999 default:
1000 case NorthWestGravity:
1001 case NorthGravity:
1002 case NorthEastGravity:
1003 break;
1004 case SouthWestGravity:
1005 case SouthGravity:
1006 case SouthEastGravity:
1007 if (addborder) y -= _border_width * 2;
1008 else y += _border_width * 2;
1009 break;
1010 case WestGravity:
1011 case EastGravity:
1012 case CenterGravity:
1013 case ForgetGravity:
1014 case StaticGravity:
1015 if (addborder) y -= _border_width;
1016 else y += _border_width;
1017 break;
1018 }
1019 _area = otk::Rect(otk::Point(x, y), _area.size());
1020
1021 if (addborder) {
1022 XSetWindowBorderWidth(**otk::display, _window, _border_width);
1023
1024 // move the client so it is back it the right spot _with_ its border!
1025 if (x != oldx || y != oldy)
1026 XMoveWindow(**otk::display, _window, x, y);
1027 } else
1028 XSetWindowBorderWidth(**otk::display, _window, 0);
1029 }
1030
1031 void Client::clientMessageHandler(const XClientMessageEvent &e)
1032 {
1033 otk::EventHandler::clientMessageHandler(e);
1034
1035 // validate cuz we query stuff off the client here
1036 if (!validate()) return;
1037
1038 if (e.format != 32) return;
1039
1040 if (e.message_type == otk::Property::atoms.wm_change_state) {
1041 // compress changes into a single change
1042 bool compress = false;
1043 XEvent ce;
1044 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
1045 // XXX: it would be nice to compress ALL messages of a type, not just
1046 // messages in a row without other message types between.
1047 if (ce.xclient.message_type != e.message_type) {
1048 XPutBackEvent(**otk::display, &ce);
1049 break;
1050 }
1051 compress = true;
1052 }
1053 if (compress)
1054 setWMState(ce.xclient.data.l[0]); // use the found event
1055 else
1056 setWMState(e.data.l[0]); // use the original event
1057 } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
1058 // compress changes into a single change
1059 bool compress = false;
1060 XEvent ce;
1061 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
1062 // XXX: it would be nice to compress ALL messages of a type, not just
1063 // messages in a row without other message types between.
1064 if (ce.xclient.message_type != e.message_type) {
1065 XPutBackEvent(**otk::display, &ce);
1066 break;
1067 }
1068 compress = true;
1069 }
1070 if (compress)
1071 setDesktop(e.data.l[0]); // use the found event
1072 else
1073 setDesktop(e.data.l[0]); // use the original event
1074 } else if (e.message_type == otk::Property::atoms.net_wm_state) {
1075 // can't compress these
1076 #ifdef DEBUG
1077 printf("net_wm_state %s %ld %ld for 0x%lx\n",
1078 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
1079 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
1080 e.data.l[1], e.data.l[2], _window);
1081 #endif
1082 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
1083 } else if (e.message_type == otk::Property::atoms.net_close_window) {
1084 #ifdef DEBUG
1085 printf("net_close_window for 0x%lx\n", _window);
1086 #endif
1087 close();
1088 } else if (e.message_type == otk::Property::atoms.net_active_window) {
1089 #ifdef DEBUG
1090 printf("net_active_window for 0x%lx\n", _window);
1091 #endif
1092 if (openbox->screen(_screen)->showingDesktop())
1093 openbox->screen(_screen)->showDesktop(false);
1094 if (_iconic)
1095 iconify(false);
1096 else if (!frame->visible()) // if its not visible for other reasons, then
1097 return; // don't mess with it
1098 if (_shaded)
1099 shade(false);
1100 focus();
1101 openbox->screen(_screen)->raiseWindow(this);
1102 } else if (e.message_type == otk::Property::atoms.openbox_active_window) {
1103 if (openbox->screen(_screen)->showingDesktop())
1104 openbox->screen(_screen)->showDesktop(false);
1105 if (_iconic)
1106 iconify(false);
1107 else if (!frame->visible()) // if its not visible for other reasons, then
1108 return; // don't mess with it
1109 if (e.data.l[0] && _shaded)
1110 shade(false);
1111 focus();
1112 if (e.data.l[1])
1113 openbox->screen(_screen)->raiseWindow(this);
1114 }
1115 }
1116
1117 #if defined(SHAPE)
1118 void Client::shapeHandler(const XShapeEvent &e)
1119 {
1120 otk::EventHandler::shapeHandler(e);
1121
1122 if (e.kind == ShapeBounding) {
1123 _shaped = e.shaped;
1124 frame->adjustShape();
1125 }
1126 }
1127 #endif
1128
1129 void Client::resize(Corner anchor, int w, int h)
1130 {
1131 if (!(_functions & Func_Resize)) return;
1132 internal_resize(anchor, w, h);
1133 }
1134
1135 void Client::internal_resize(Corner anchor, int w, int h,
1136 bool user, int x, int y)
1137 {
1138 w -= _base_size.width();
1139 h -= _base_size.height();
1140
1141 if (user) {
1142 // for interactive resizing. have to move half an increment in each
1143 // direction.
1144 int mw = w % _size_inc.width(); // how far we are towards the next size inc
1145 int mh = h % _size_inc.height();
1146 int aw = _size_inc.width() / 2; // amount to add
1147 int ah = _size_inc.height() / 2;
1148 // don't let us move into a new size increment
1149 if (mw + aw >= _size_inc.width()) aw = _size_inc.width() - mw - 1;
1150 if (mh + ah >= _size_inc.height()) ah = _size_inc.height() - mh - 1;
1151 w += aw;
1152 h += ah;
1153
1154 // if this is a user-requested resize, then check against min/max sizes
1155 // and aspect ratios
1156
1157 // smaller than min size or bigger than max size?
1158 if (w > _max_size.width()) w = _max_size.width();
1159 if (w < _min_size.width()) w = _min_size.width();
1160 if (h > _max_size.height()) h = _max_size.height();
1161 if (h < _min_size.height()) h = _min_size.height();
1162
1163 // adjust the height ot match the width for the aspect ratios
1164 if (_min_ratio)
1165 if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
1166 if (_max_ratio)
1167 if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
1168 }
1169
1170 // keep to the increments
1171 w /= _size_inc.width();
1172 h /= _size_inc.height();
1173
1174 // you cannot resize to nothing
1175 if (w < 1) w = 1;
1176 if (h < 1) h = 1;
1177
1178 // store the logical size
1179 _logical_size = otk::Size(w, h);
1180
1181 w *= _size_inc.width();
1182 h *= _size_inc.height();
1183
1184 w += _base_size.width();
1185 h += _base_size.height();
1186
1187 if (x == INT_MIN || y == INT_MIN) {
1188 x = _area.x();
1189 y = _area.y();
1190 switch (anchor) {
1191 case TopLeft:
1192 break;
1193 case TopRight:
1194 x -= w - _area.width();
1195 break;
1196 case BottomLeft:
1197 y -= h - _area.height();
1198 break;
1199 case BottomRight:
1200 x -= w - _area.width();
1201 y -= h - _area.height();
1202 break;
1203 }
1204 }
1205
1206 _area = otk::Rect(_area.position(), otk::Size(w, h));
1207
1208 XResizeWindow(**otk::display, _window, w, h);
1209
1210 // resize the frame to match the request
1211 frame->adjustSize();
1212 internal_move(x, y);
1213 }
1214
1215 const Icon *Client::icon(const otk::Size &s) const
1216 {
1217 unsigned long req = s.width() * s.height();
1218 // si is the smallest image >= req
1219 // li is the largest image < req
1220 unsigned long smallest = 0xffffffff, largest = 0, si = 0, li = 0;
1221
1222 assert(_nicons > 0); // there should always be a default..
1223 for (int i = 0; i < _nicons; ++i) {
1224 unsigned long size = _icons[i].w * _icons[i].h;
1225 if (size < smallest && size >= req) {
1226 smallest = size;
1227 si = i;
1228 }
1229 if (size > largest && size <= req) {
1230 largest = size;
1231 li = i;
1232 }
1233 }
1234 if (smallest == 0xffffffff) // didnt find one bigger than us...
1235 return &_icons[li];
1236 return &_icons[si];
1237 }
1238
1239 void Client::move(int x, int y)
1240 {
1241 if (!(_functions & Func_Move)) return;
1242 frame->frameGravity(x, y); // get the client's position based on x,y for the
1243 // frame
1244 internal_move(x, y);
1245 }
1246
1247 void Client::internal_move(int x, int y)
1248 {
1249 _area = otk::Rect(otk::Point(x, y), _area.size());
1250
1251 // move the frame to be in the requested position
1252 if (frame) { // this can be called while mapping, before frame exists
1253 frame->adjustPosition();
1254
1255 // send synthetic configure notify (we don't need to if we aren't mapped
1256 // yet)
1257 XEvent event;
1258 event.type = ConfigureNotify;
1259 event.xconfigure.display = **otk::display;
1260 event.xconfigure.event = _window;
1261 event.xconfigure.window = _window;
1262
1263 // root window coords with border in mind
1264 event.xconfigure.x = x - _border_width + frame->size().left;
1265 event.xconfigure.y = y - _border_width + frame->size().top;
1266
1267 event.xconfigure.width = _area.width();
1268 event.xconfigure.height = _area.height();
1269 event.xconfigure.border_width = _border_width;
1270 event.xconfigure.above = frame->plate();
1271 event.xconfigure.override_redirect = False;
1272 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1273 StructureNotifyMask, &event);
1274 #if 0//def DEBUG
1275 printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
1276 event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
1277 event.xconfigure.height, event.xconfigure.window);
1278 #endif
1279 }
1280 }
1281
1282 void Client::close()
1283 {
1284 XEvent ce;
1285
1286 if (!(_functions & Func_Close)) return;
1287
1288 // XXX: itd be cool to do timeouts and shit here for killing the client's
1289 // process off
1290 // like... if the window is around after 5 seconds, then the close button
1291 // turns a nice red, and if this function is called again, the client is
1292 // explicitly killed.
1293
1294 ce.xclient.type = ClientMessage;
1295 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1296 ce.xclient.display = **otk::display;
1297 ce.xclient.window = _window;
1298 ce.xclient.format = 32;
1299 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1300 ce.xclient.data.l[1] = CurrentTime;
1301 ce.xclient.data.l[2] = 0l;
1302 ce.xclient.data.l[3] = 0l;
1303 ce.xclient.data.l[4] = 0l;
1304 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1305 }
1306
1307 void Client::changeState()
1308 {
1309 unsigned long state[2];
1310 state[0] = _wmstate;
1311 state[1] = None;
1312 otk::Property::set(_window, otk::Property::atoms.wm_state,
1313 otk::Property::atoms.wm_state, state, 2);
1314
1315 Atom netstate[10];
1316 int num = 0;
1317 if (_modal)
1318 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1319 if (_shaded)
1320 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1321 if (_iconic)
1322 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1323 if (_skip_taskbar)
1324 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1325 if (_skip_pager)
1326 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1327 if (_fullscreen)
1328 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1329 if (_max_vert)
1330 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1331 if (_max_horz)
1332 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1333 if (_above)
1334 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1335 if (_below)
1336 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1337 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1338 otk::Property::atoms.atom, netstate, num);
1339
1340 calcLayer();
1341
1342 if (frame)
1343 frame->adjustState();
1344 }
1345
1346 void Client::changeAllowedActions(void)
1347 {
1348 Atom actions[9];
1349 int num = 0;
1350
1351 actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
1352
1353 if (_functions & Func_Shade)
1354 actions[num++] = otk::Property::atoms.net_wm_action_shade;
1355 if (_functions & Func_Close)
1356 actions[num++] = otk::Property::atoms.net_wm_action_close;
1357 if (_functions & Func_Move)
1358 actions[num++] = otk::Property::atoms.net_wm_action_move;
1359 if (_functions & Func_Iconify)
1360 actions[num++] = otk::Property::atoms.net_wm_action_minimize;
1361 if (_functions & Func_Resize)
1362 actions[num++] = otk::Property::atoms.net_wm_action_resize;
1363 if (_functions & Func_Fullscreen)
1364 actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
1365 if (_functions & Func_Maximize) {
1366 actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
1367 actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
1368 }
1369
1370 otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
1371 otk::Property::atoms.atom, actions, num);
1372
1373 // make sure the window isn't breaking any rules now
1374
1375 if (!(_functions & Func_Shade) && _shaded)
1376 if (frame) shade(false);
1377 else _shaded = false;
1378 if (!(_functions & Func_Iconify) && _iconic)
1379 if (frame) setDesktop(openbox->screen(_screen)->desktop());
1380 else _iconic = false;
1381 if (!(_functions & Func_Fullscreen) && _fullscreen)
1382 if (frame) fullscreen(false);
1383 else _fullscreen = false;
1384 if (!(_functions & Func_Maximize) && (_max_horz || _max_vert))
1385 if (frame) maximize(false, 0);
1386 else _max_vert = _max_horz = false;
1387 }
1388
1389 void Client::remaximize()
1390 {
1391 int dir;
1392 if (_max_horz && _max_vert)
1393 dir = 0;
1394 else if (_max_horz)
1395 dir = 1;
1396 else if (_max_vert)
1397 dir = 2;
1398 else
1399 return; // not maximized
1400 _max_horz = _max_vert = false;
1401 maximize(true, dir, false);
1402 }
1403
1404 void Client::applyStartupState()
1405 {
1406 // these are in a carefully crafted order..
1407
1408 if (_iconic) {
1409 _iconic = false;
1410 iconify(true);
1411 }
1412 if (_fullscreen) {
1413 _fullscreen = false;
1414 fullscreen(true, false);
1415 }
1416 if (_shaded) {
1417 _shaded = false;
1418 shade(true);
1419 }
1420 if (_urgent)
1421 fireUrgent();
1422
1423 if (_max_vert && _max_horz) {
1424 _max_vert = _max_horz = false;
1425 maximize(true, 0, false);
1426 } else if (_max_vert) {
1427 _max_vert = false;
1428 maximize(true, 2, false);
1429 } else if (_max_horz) {
1430 _max_horz = false;
1431 maximize(true, 1, false);
1432 }
1433
1434 if (_skip_taskbar); // nothing to do for this
1435 if (_skip_pager); // nothing to do for this
1436 if (_modal); // nothing to do for this
1437 if (_above); // nothing to do for this
1438 if (_below); // nothing to do for this
1439 }
1440
1441 void Client::fireUrgent()
1442 {
1443 // call the python UrgentWindow callbacks
1444 EventData data(_screen, this, EventAction::UrgentWindow, 0);
1445 openbox->bindings()->fireEvent(&data);
1446 }
1447
1448 void Client::shade(bool shade)
1449 {
1450 if (!(_functions & Func_Shade) || // can't
1451 _shaded == shade) return; // already done
1452
1453 // when we're iconic, don't change the wmstate
1454 if (!_iconic)
1455 _wmstate = shade ? IconicState : NormalState;
1456 _shaded = shade;
1457 changeState();
1458 frame->adjustSize();
1459 }
1460
1461 void Client::maximize(bool max, int dir, bool savearea)
1462 {
1463 assert(dir == 0 || dir == 1 || dir == 2);
1464 if (!(_functions & Func_Maximize)) return; // can't
1465
1466 // check if already done
1467 if (max) {
1468 if (dir == 0 && _max_horz && _max_vert) return;
1469 if (dir == 1 && _max_horz) return;
1470 if (dir == 2 && _max_vert) return;
1471 } else {
1472 if (dir == 0 && !_max_horz && !_max_vert) return;
1473 if (dir == 1 && !_max_horz) return;
1474 if (dir == 2 && !_max_vert) return;
1475 }
1476
1477 const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
1478 int x = frame->area().x(), y = frame->area().y(),
1479 w = _area.width(), h = _area.height();
1480
1481 if (max) {
1482 if (savearea) {
1483 long dimensions[4];
1484 long *readdim;
1485 unsigned long n = 4;
1486
1487 dimensions[0] = x;
1488 dimensions[1] = y;
1489 dimensions[2] = w;
1490 dimensions[3] = h;
1491
1492 // get the property off the window and use it for the dimentions we are
1493 // already maxed on
1494 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1495 otk::Property::atoms.cardinal, &n,
1496 (long unsigned**) &readdim)) {
1497 if (n >= 4) {
1498 if (_max_horz) {
1499 dimensions[0] = readdim[0];
1500 dimensions[2] = readdim[2];
1501 }
1502 if (_max_vert) {
1503 dimensions[1] = readdim[1];
1504 dimensions[3] = readdim[3];
1505 }
1506 }
1507 delete readdim;
1508 }
1509
1510 otk::Property::set(_window, otk::Property::atoms.openbox_premax,
1511 otk::Property::atoms.cardinal,
1512 (long unsigned*)dimensions, 4);
1513 }
1514 if (dir == 0 || dir == 1) { // horz
1515 x = a.x();
1516 w = a.width();
1517 }
1518 if (dir == 0 || dir == 2) { // vert
1519 y = a.y();
1520 h = a.height() - frame->size().top - frame->size().bottom;
1521 }
1522 } else {
1523 long *dimensions;
1524 long unsigned n = 4;
1525
1526 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1527 otk::Property::atoms.cardinal, &n,
1528 (long unsigned**) &dimensions)) {
1529 if (n >= 4) {
1530 if (dir == 0 || dir == 1) { // horz
1531 x = (signed int)dimensions[0];
1532 w = (signed int)dimensions[2];
1533 }
1534 if (dir == 0 || dir == 2) { // vert
1535 y = (signed int)dimensions[1];
1536 h = (signed int)dimensions[3];
1537 }
1538 }
1539 delete dimensions;
1540 } else {
1541 // pick some fallbacks...
1542 if (dir == 0 || dir == 1) { // horz
1543 x = a.x() + a.width() / 4;
1544 w = a.width() / 2;
1545 }
1546 if (dir == 0 || dir == 2) { // vert
1547 y = a.y() + a.height() / 4;
1548 h = a.height() / 2;
1549 }
1550 }
1551 }
1552
1553 if (dir == 0 || dir == 1) // horz
1554 _max_horz = max;
1555 if (dir == 0 || dir == 2) // vert
1556 _max_vert = max;
1557
1558 if (!_max_horz && !_max_vert)
1559 otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
1560
1561 changeState(); // change the state hints on the client
1562
1563 frame->frameGravity(x, y); // figure out where the client should be going
1564 internal_resize(TopLeft, w, h, true, x, y);
1565 }
1566
1567 void Client::fullscreen(bool fs, bool savearea)
1568 {
1569 static FunctionFlags saved_func;
1570 static DecorationFlags saved_decor;
1571
1572 if (!(_functions & Func_Fullscreen) || // can't
1573 _fullscreen == fs) return; // already done
1574
1575 _fullscreen = fs;
1576 changeState(); // change the state hints on the client
1577
1578 int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
1579
1580 if (fs) {
1581 // save the functions and remove them
1582 saved_func = _functions;
1583 _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
1584 // save the decorations and remove them
1585 saved_decor = _decorations;
1586 _decorations = 0;
1587 if (savearea) {
1588 long dimensions[4];
1589 dimensions[0] = _area.x();
1590 dimensions[1] = _area.y();
1591 dimensions[2] = _area.width();
1592 dimensions[3] = _area.height();
1593 otk::Property::set(_window, otk::Property::atoms.openbox_premax,
1594 otk::Property::atoms.cardinal,
1595 (long unsigned*)dimensions, 4);
1596 }
1597 const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
1598 x = 0;
1599 y = 0;
1600 w = info->size().width();
1601 h = info->size().height();
1602 } else {
1603 _functions = saved_func;
1604 _decorations = saved_decor;
1605
1606 long *dimensions;
1607 long unsigned n = 4;
1608
1609 if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
1610 otk::Property::atoms.cardinal, &n,
1611 (long unsigned**) &dimensions)) {
1612 if (n >= 4) {
1613 x = dimensions[0];
1614 y = dimensions[1];
1615 w = dimensions[2];
1616 h = dimensions[3];
1617 }
1618 delete dimensions;
1619 } else {
1620 // pick some fallbacks...
1621 const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
1622 x = a.x() + a.width() / 4;
1623 y = a.y() + a.height() / 4;
1624 w = a.width() / 2;
1625 h = a.height() / 2;
1626 }
1627 }
1628
1629 changeAllowedActions(); // based on the new _functions
1630
1631 // when fullscreening, don't obey things like increments, fill the screen
1632 internal_resize(TopLeft, w, h, !fs, x, y);
1633
1634 // raise (back) into our stacking layer
1635 openbox->screen(_screen)->raiseWindow(this);
1636
1637 // try focus us when we go into fullscreen mode
1638 if (fs) focus();
1639 }
1640
1641 void Client::iconify(bool iconic, bool curdesk)
1642 {
1643 if (_iconic == iconic) return; // nothing to do
1644
1645 #ifdef DEBUG
1646 printf("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"), _window);
1647 #endif
1648
1649 _iconic = iconic;
1650
1651 if (_iconic) {
1652 _wmstate = IconicState;
1653 ignore_unmaps++;
1654 // we unmap the client itself so that we can get MapRequest events, and
1655 // because the ICCCM tells us to!
1656 XUnmapWindow(**otk::display, _window);
1657 } else {
1658 if (curdesk)
1659 setDesktop(openbox->screen(_screen)->desktop());
1660 _wmstate = NormalState;
1661 XMapWindow(**otk::display, _window);
1662 }
1663 changeState();
1664 showhide();
1665 openbox->screen(_screen)->updateStruts();
1666 }
1667
1668 void Client::disableDecorations(DecorationFlags flags)
1669 {
1670 _disabled_decorations = flags;
1671 setupDecorAndFunctions();
1672 }
1673
1674 void Client::installColormap(bool install) const
1675 {
1676 XWindowAttributes wa;
1677 if (XGetWindowAttributes(**otk::display, _window, &wa)) {
1678 if (install)
1679 XInstallColormap(**otk::display, wa.colormap);
1680 else
1681 XUninstallColormap(**otk::display, wa.colormap);
1682 }
1683 }
1684
1685 Client *Client::searchModalTree(Client *node, Client *skip)
1686 {
1687 List::const_iterator it, end = node->_transients.end();
1688 Client *ret;
1689
1690 for (it = node->_transients.begin(); it != end; ++it) {
1691 if (*it == skip) continue; // circular?
1692 if ((ret = searchModalTree(*it, skip))) return ret; // got one
1693 if ((*it)->_modal) return *it; // got one
1694 }
1695 return 0;
1696 }
1697
1698 Client *Client::findModalChild()
1699 {
1700 return searchModalTree(this, this);
1701 }
1702
1703
1704 bool Client::focus()
1705 {
1706 // if we have a modal child, then focus it, not us
1707 Client *c = findModalChild();
1708 if (c) return c->focus();
1709
1710 // won't try focus if the client doesn't want it, or if the window isn't
1711 // visible on the screen
1712 if (!(frame->visible() && (_can_focus || _focus_notify))) return false;
1713
1714 if (_focused) return true;
1715
1716 // do a check to see if the window has already been unmapped or destroyed
1717 // do this intelligently while watching out for unmaps we've generated
1718 // (ignore_unmaps > 0)
1719 XEvent ev;
1720 if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
1721 XPutBackEvent(**otk::display, &ev);
1722 return false;
1723 }
1724 while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
1725 if (ignore_unmaps) {
1726 unmapHandler(ev.xunmap);
1727 } else {
1728 XPutBackEvent(**otk::display, &ev);
1729 return false;
1730 }
1731 }
1732
1733 if (_can_focus)
1734 XSetInputFocus(**otk::display, _window,
1735 RevertToNone, CurrentTime);
1736
1737 if (_focus_notify) {
1738 XEvent ce;
1739 ce.xclient.type = ClientMessage;
1740 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1741 ce.xclient.display = **otk::display;
1742 ce.xclient.window = _window;
1743 ce.xclient.format = 32;
1744 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1745 ce.xclient.data.l[1] = openbox->lastTime();
1746 ce.xclient.data.l[2] = 0l;
1747 ce.xclient.data.l[3] = 0l;
1748 ce.xclient.data.l[4] = 0l;
1749 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1750 }
1751
1752 XSync(**otk::display, False);
1753 return true;
1754 }
1755
1756
1757 void Client::unfocus() const
1758 {
1759 if (!_focused) return;
1760
1761 assert(openbox->focusedClient() == this);
1762 openbox->setFocusedClient(0);
1763 }
1764
1765
1766 void Client::focusHandler(const XFocusChangeEvent &e)
1767 {
1768 #ifdef DEBUG
1769 // printf("FocusIn for 0x%lx\n", e.window);
1770 #endif // DEBUG
1771
1772 otk::EventHandler::focusHandler(e);
1773
1774 _focused = true;
1775 frame->adjustFocus();
1776
1777 calcLayer(); // focus state can affect the stacking layer
1778
1779 openbox->setFocusedClient(this);
1780 }
1781
1782
1783 void Client::unfocusHandler(const XFocusChangeEvent &e)
1784 {
1785 #ifdef DEBUG
1786 // printf("FocusOut for 0x%lx\n", e.window);
1787 #endif // DEBUG
1788
1789 otk::EventHandler::unfocusHandler(e);
1790
1791 _focused = false;
1792 frame->adjustFocus();
1793
1794 calcLayer(); // focus state can affect the stacking layer
1795
1796 if (openbox->focusedClient() == this)
1797 openbox->setFocusedClient(0);
1798 }
1799
1800
1801 void Client::configureRequestHandler(const XConfigureRequestEvent &ec)
1802 {
1803 #ifdef DEBUG
1804 printf("ConfigureRequest for 0x%lx\n", ec.window);
1805 #endif // DEBUG
1806
1807 otk::EventHandler::configureRequestHandler(ec);
1808
1809 // compress these
1810 XConfigureRequestEvent e = ec;
1811 XEvent ev;
1812 while (XCheckTypedWindowEvent(**otk::display, window(), ConfigureRequest,
1813 &ev)) {
1814 // XXX if this causes bad things.. we can compress config req's with the
1815 // same mask.
1816 e.value_mask |= ev.xconfigurerequest.value_mask;
1817 if (ev.xconfigurerequest.value_mask & CWX)
1818 e.x = ev.xconfigurerequest.x;
1819 if (ev.xconfigurerequest.value_mask & CWY)
1820 e.y = ev.xconfigurerequest.y;
1821 if (ev.xconfigurerequest.value_mask & CWWidth)
1822 e.width = ev.xconfigurerequest.width;
1823 if (ev.xconfigurerequest.value_mask & CWHeight)
1824 e.height = ev.xconfigurerequest.height;
1825 if (ev.xconfigurerequest.value_mask & CWBorderWidth)
1826 e.border_width = ev.xconfigurerequest.border_width;
1827 if (ev.xconfigurerequest.value_mask & CWStackMode)
1828 e.detail = ev.xconfigurerequest.detail;
1829 }
1830
1831 // if we are iconic (or shaded (fvwm does this)) ignore the event
1832 if (_iconic || _shaded) return;
1833
1834 if (e.value_mask & CWBorderWidth)
1835 _border_width = e.border_width;
1836
1837 // resize, then move, as specified in the EWMH section 7.7
1838 if (e.value_mask & (CWWidth | CWHeight)) {
1839 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1840 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1841
1842 Corner corner;
1843 switch (_gravity) {
1844 case NorthEastGravity:
1845 case EastGravity:
1846 corner = TopRight;
1847 break;
1848 case SouthWestGravity:
1849 case SouthGravity:
1850 corner = BottomLeft;
1851 break;
1852 case SouthEastGravity:
1853 corner = BottomRight;
1854 break;
1855 default: // NorthWest, Static, etc
1856 corner = TopLeft;
1857 }
1858
1859 // if moving AND resizing ...
1860 if (e.value_mask & (CWX | CWY)) {
1861 int x = (e.value_mask & CWX) ? e.x : _area.x();
1862 int y = (e.value_mask & CWY) ? e.y : _area.y();
1863 internal_resize(corner, w, h, false, x, y);
1864 } else // if JUST resizing...
1865 internal_resize(corner, w, h, false);
1866 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1867 int x = (e.value_mask & CWX) ? e.x : _area.x();
1868 int y = (e.value_mask & CWY) ? e.y : _area.y();
1869 internal_move(x, y);
1870 }
1871
1872 if (e.value_mask & CWStackMode) {
1873 switch (e.detail) {
1874 case Below:
1875 case BottomIf:
1876 openbox->screen(_screen)->lowerWindow(this);
1877 break;
1878
1879 case Above:
1880 case TopIf:
1881 default:
1882 openbox->screen(_screen)->raiseWindow(this);
1883 break;
1884 }
1885 }
1886 }
1887
1888
1889 void Client::unmapHandler(const XUnmapEvent &e)
1890 {
1891 if (ignore_unmaps) {
1892 #ifdef DEBUG
1893 // printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1894 #endif // DEBUG
1895 ignore_unmaps--;
1896 return;
1897 }
1898
1899 #ifdef DEBUG
1900 printf("UnmapNotify for 0x%lx\n", e.window);
1901 #endif // DEBUG
1902
1903 otk::EventHandler::unmapHandler(e);
1904
1905 // this deletes us etc
1906 openbox->screen(_screen)->unmanageWindow(this);
1907 }
1908
1909
1910 void Client::destroyHandler(const XDestroyWindowEvent &e)
1911 {
1912 #ifdef DEBUG
1913 printf("DestroyNotify for 0x%lx\n", e.window);
1914 #endif // DEBUG
1915
1916 otk::EventHandler::destroyHandler(e);
1917
1918 // this deletes us etc
1919 openbox->screen(_screen)->unmanageWindow(this);
1920 }
1921
1922
1923 void Client::reparentHandler(const XReparentEvent &e)
1924 {
1925 // this is when the client is first taken captive in the frame
1926 if (e.parent == frame->plate()) return;
1927
1928 #ifdef DEBUG
1929 printf("ReparentNotify for 0x%lx\n", e.window);
1930 #endif // DEBUG
1931
1932 otk::EventHandler::reparentHandler(e);
1933
1934 /*
1935 This event is quite rare and is usually handled in unmapHandler.
1936 However, if the window is unmapped when the reparent event occurs,
1937 the window manager never sees it because an unmap event is not sent
1938 to an already unmapped window.
1939 */
1940
1941 // we don't want the reparent event, put it back on the stack for the X
1942 // server to deal with after we unmanage the window
1943 XEvent ev;
1944 ev.xreparent = e;
1945 XPutBackEvent(**otk::display, &ev);
1946
1947 // this deletes us etc
1948 openbox->screen(_screen)->unmanageWindow(this);
1949 }
1950
1951 void Client::mapRequestHandler(const XMapRequestEvent &e)
1952 {
1953 #ifdef DEBUG
1954 printf("MapRequest for already managed 0x%lx\n", e.window);
1955 #endif // DEBUG
1956
1957 assert(_iconic); // we shouldn't be able to get this unless we're iconic
1958
1959 // move to the current desktop (uniconify)
1960 iconify(false);
1961 // XXX: should we focus/raise the window? (basically a net_wm_active_window)
1962 }
1963
1964 }
This page took 0.13217 seconds and 4 git commands to generate.