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