]> Dogcows Code - chaz/openbox/blob - src/client.cc
centered gravities lose one borderwidth when mapping too
[chaz/openbox] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "client.hh"
8 #include "frame.hh"
9 #include "screen.hh"
10 #include "openbox.hh"
11 #include "otk/display.hh"
12 #include "otk/property.hh"
13
14 extern "C" {
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17 #include <X11/Xatom.h>
18
19 #include <assert.h>
20
21 #include "gettext.h"
22 #define _(str) gettext(str)
23 }
24
25 namespace ob {
26
27 Client::Client(int screen, Window window)
28 : otk::EventHandler(),
29 WidgetBase(WidgetBase::Type_Client),
30 frame(0), _screen(screen), _window(window)
31 {
32 assert(screen >= 0);
33 assert(window);
34
35 ignore_unmaps = 0;
36
37 // update EVERYTHING the first time!!
38
39 // the state is kinda assumed to be normal. is this right? XXX
40 _wmstate = NormalState; _iconic = false;
41 // no default decors or functions, each has to be enabled
42 _decorations = _functions = 0;
43 // start unfocused
44 _focused = false;
45 // not a transient by default of course
46 _transient_for = 0;
47 // pick a layer to start from
48 _layer = Layer_Normal;
49
50 getGravity();
51 getArea();
52 getDesktop();
53
54 updateTransientFor();
55 getType();
56 getMwmHints();
57
58 setupDecorAndFunctions();
59
60 getState();
61 getShaped();
62
63 updateProtocols();
64 updateWMHints();
65 updateTitle();
66 updateIconTitle();
67 updateClass();
68 updateStrut();
69
70 changeState();
71 }
72
73
74 Client::~Client()
75 {
76 // clean up childrens' references
77 while (!_transients.empty()) {
78 _transients.front()->_transient_for = 0;
79 _transients.pop_front();
80 }
81
82 // clean up parents reference to this
83 if (_transient_for)
84 _transient_for->_transients.remove(this); // remove from old parent
85
86 if (openbox->state() != Openbox::State_Exiting) {
87 // these values should not be persisted across a window unmapping/mapping
88 otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
89 otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
90 }
91 }
92
93
94 void Client::getGravity()
95 {
96 XSizeHints size;
97 XWindowAttributes wattrib;
98 Status ret;
99 long junk;
100
101 if (XGetWMNormalHints(**otk::display, _window, &size, &junk) &&
102 size.flags & PWinGravity) {
103 // first try the normal hints
104 _gravity = size.win_gravity;
105 } else {
106 // then fall back to the attribute
107 ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
108 assert(ret != BadWindow);
109 _gravity = wattrib.win_gravity;
110 }
111 }
112
113
114 void Client::getDesktop()
115 {
116 // defaults to the current desktop
117 _desktop = openbox->screen(_screen)->desktop();
118
119 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
120 otk::Property::atoms.cardinal,
121 (long unsigned*)&_desktop)) {
122 // make sure the hint exists
123 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
124 otk::Property::atoms.cardinal, (unsigned)_desktop);
125 }
126 }
127
128
129 void Client::getType()
130 {
131 _type = (WindowType) -1;
132
133 unsigned long *val;
134 unsigned long num = (unsigned) -1;
135 if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
136 otk::Property::atoms.atom, &num, &val)) {
137 // use the first value that we know about in the array
138 for (unsigned long i = 0; i < num; ++i) {
139 if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
140 _type = Type_Desktop;
141 else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
142 _type = Type_Dock;
143 else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
144 _type = Type_Toolbar;
145 else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
146 _type = Type_Menu;
147 else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
148 _type = Type_Utility;
149 else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
150 _type = Type_Splash;
151 else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
152 _type = Type_Dialog;
153 else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
154 _type = Type_Normal;
155 // XXX: make this work again
156 // else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override)
157 // mwm_decorations = 0; // prevent this window from getting any decor
158 if (_type != (WindowType) -1)
159 break; // grab the first known type
160 }
161 delete val;
162 }
163
164 if (_type == (WindowType) -1) {
165 /*
166 * the window type hint was not set, which means we either classify ourself
167 * as a normal window or a dialog, depending on if we are a transient.
168 */
169 if (_transient_for)
170 _type = Type_Dialog;
171 else
172 _type = Type_Normal;
173 }
174 }
175
176
177 void Client::setupDecorAndFunctions()
178 {
179 // start with everything
180 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
181 Decor_Iconify | Decor_Maximize;
182 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
183
184 switch (_type) {
185 case Type_Normal:
186 // normal windows retain all of the possible decorations and
187 // functionality
188
189 case Type_Dialog:
190 // dialogs cannot be maximized
191 _decorations &= ~Decor_Maximize;
192 _functions &= ~Func_Maximize;
193 break;
194
195 case Type_Menu:
196 case Type_Toolbar:
197 case Type_Utility:
198 // these windows get less functionality
199 _decorations &= ~(Decor_Iconify | Decor_Handle);
200 _functions &= ~(Func_Iconify | Func_Resize);
201 break;
202
203 case Type_Desktop:
204 case Type_Dock:
205 case Type_Splash:
206 // none of these windows are manipulated by the window manager
207 _decorations = 0;
208 _functions = 0;
209 break;
210 }
211
212 // Mwm Hints are applied subtractively to what has already been chosen for
213 // decor and functionality
214 if (_mwmhints.flags & MwmFlag_Decorations) {
215 if (! (_mwmhints.decorations & MwmDecor_All)) {
216 if (! (_mwmhints.decorations & MwmDecor_Border))
217 _decorations &= ~Decor_Border;
218 if (! (_mwmhints.decorations & MwmDecor_Handle))
219 _decorations &= ~Decor_Handle;
220 if (! (_mwmhints.decorations & MwmDecor_Title))
221 _decorations &= ~Decor_Titlebar;
222 if (! (_mwmhints.decorations & MwmDecor_Iconify))
223 _decorations &= ~Decor_Iconify;
224 if (! (_mwmhints.decorations & MwmDecor_Maximize))
225 _decorations &= ~Decor_Maximize;
226 }
227 }
228
229 if (_mwmhints.flags & MwmFlag_Functions) {
230 if (! (_mwmhints.functions & MwmFunc_All)) {
231 if (! (_mwmhints.functions & MwmFunc_Resize))
232 _functions &= ~Func_Resize;
233 if (! (_mwmhints.functions & MwmFunc_Move))
234 _functions &= ~Func_Move;
235 if (! (_mwmhints.functions & MwmFunc_Iconify))
236 _functions &= ~Func_Iconify;
237 if (! (_mwmhints.functions & MwmFunc_Maximize))
238 _functions &= ~Func_Maximize;
239 // dont let mwm hints kill the close button
240 //if (! (_mwmhints.functions & MwmFunc_Close))
241 // _functions &= ~Func_Close;
242 }
243 }
244
245 // XXX: changeAllowedActions();
246 }
247
248
249 void Client::getMwmHints()
250 {
251 unsigned long num = MwmHints::elements;
252 unsigned long *hints;
253
254 _mwmhints.flags = 0; // default to none
255
256 if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
257 otk::Property::atoms.motif_wm_hints, &num,
258 (unsigned long **)&hints))
259 return;
260
261 if (num >= MwmHints::elements) {
262 // retrieved the hints
263 _mwmhints.flags = hints[0];
264 _mwmhints.functions = hints[1];
265 _mwmhints.decorations = hints[2];
266 }
267
268 delete [] hints;
269 }
270
271
272 void Client::getArea()
273 {
274 XWindowAttributes wattrib;
275 Status ret;
276
277 ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
278 assert(ret != BadWindow);
279
280 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
281 _border_width = wattrib.border_width;
282 }
283
284
285 void Client::getState()
286 {
287 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
288 _skip_taskbar = _skip_pager = false;
289
290 unsigned long *state;
291 unsigned long num = (unsigned) -1;
292
293 if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
294 otk::Property::atoms.atom, &num, &state)) {
295 for (unsigned long i = 0; i < num; ++i) {
296 if (state[i] == otk::Property::atoms.net_wm_state_modal)
297 _modal = true;
298 else if (state[i] == otk::Property::atoms.net_wm_state_shaded) {
299 _shaded = true;
300 _wmstate = IconicState;
301 } else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
302 _skip_taskbar = true;
303 else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
304 _skip_pager = true;
305 else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
306 _fullscreen = true;
307 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
308 _max_vert = true;
309 else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
310 _max_horz = true;
311 else if (state[i] == otk::Property::atoms.net_wm_state_above)
312 _above = true;
313 else if (state[i] == otk::Property::atoms.net_wm_state_below)
314 _below = true;
315 }
316
317 delete [] state;
318 }
319 }
320
321
322 void Client::getShaped()
323 {
324 _shaped = false;
325 #ifdef SHAPE
326 if (otk::display->shape()) {
327 int foo;
328 unsigned int ufoo;
329 int s;
330
331 XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
332
333 XShapeQueryExtents(**otk::display, _window, &s, &foo,
334 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
335 _shaped = (s != 0);
336 }
337 #endif // SHAPE
338 }
339
340
341 void Client::calcLayer() {
342 StackLayer l;
343
344 if (_iconic) l = Layer_Icon;
345 else if (_fullscreen) l = Layer_Fullscreen;
346 else if (_type == Type_Desktop) l = Layer_Desktop;
347 else if (_type == Type_Dock) {
348 if (!_below) l = Layer_Top;
349 else l = Layer_Normal;
350 }
351 else if (_above) l = Layer_Above;
352 else if (_below) l = Layer_Below;
353 else l = Layer_Normal;
354
355 if (l != _layer) {
356 _layer = l;
357 if (frame) {
358 /*
359 if we don't have a frame, then we aren't mapped yet (and this would
360 SIGSEGV :)
361 */
362 openbox->screen(_screen)->raiseWindow(this);
363 }
364 }
365 }
366
367
368 void Client::updateProtocols()
369 {
370 Atom *proto;
371 int num_return = 0;
372
373 _focus_notify = false;
374 _decorations &= ~Decor_Close;
375 _functions &= ~Func_Close;
376
377 if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
378 for (int i = 0; i < num_return; ++i) {
379 if (proto[i] == otk::Property::atoms.wm_delete_window) {
380 _decorations |= Decor_Close;
381 _functions |= Func_Close;
382 if (frame)
383 frame->adjustSize(); // update the decorations
384 } else if (proto[i] == otk::Property::atoms.wm_take_focus)
385 // if this protocol is requested, then the window will be notified
386 // by the window manager whenever it receives focus
387 _focus_notify = true;
388 }
389 XFree(proto);
390 }
391 }
392
393
394 void Client::updateNormalHints()
395 {
396 XSizeHints size;
397 long ret;
398 int oldgravity = _gravity;
399
400 // defaults
401 _size_inc.setPoint(1, 1);
402 _base_size.setPoint(0, 0);
403 _min_size.setPoint(0, 0);
404 _max_size.setPoint(INT_MAX, INT_MAX);
405
406 // XXX: might want to cancel any interactive resizing of the window at this
407 // point..
408
409 // get the hints from the window
410 if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
411 _positioned = (size.flags & (PPosition|USPosition));
412
413 if (size.flags & PWinGravity) {
414 _gravity = size.win_gravity;
415
416 // if the client has a frame, i.e. has already been mapped and is
417 // changing its gravity
418 if (_gravity != oldgravity) {
419 // move our idea of the client's position based on its new gravity
420 int x, y;
421 frame->frameGravity(x, y);
422 _area.setPos(x, y);
423 }
424 }
425
426 if (size.flags & PMinSize)
427 _min_size.setPoint(size.min_width, size.min_height);
428
429 if (size.flags & PMaxSize)
430 _max_size.setPoint(size.max_width, size.max_height);
431
432 if (size.flags & PBaseSize)
433 _base_size.setPoint(size.base_width, size.base_height);
434
435 if (size.flags & PResizeInc)
436 _size_inc.setPoint(size.width_inc, size.height_inc);
437 }
438 }
439
440
441 void Client::updateWMHints()
442 {
443 XWMHints *hints;
444
445 // assume a window takes input if it doesnt specify
446 _can_focus = true;
447 _urgent = false;
448
449 if ((hints = XGetWMHints(**otk::display, _window)) != NULL) {
450 if (hints->flags & InputHint)
451 _can_focus = hints->input;
452
453 if (hints->flags & XUrgencyHint)
454 _urgent = true;
455
456 if (hints->flags & WindowGroupHint) {
457 if (hints->window_group != _group) {
458 // XXX: remove from the old group if there was one
459 _group = hints->window_group;
460 // XXX: do stuff with the group
461 }
462 } else // no group!
463 _group = None;
464
465 XFree(hints);
466 }
467 }
468
469
470 void Client::updateTitle()
471 {
472 _title = "";
473
474 // try netwm
475 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_name,
476 otk::Property::utf8, &_title)) {
477 // try old x stuff
478 otk::Property::get(_window, otk::Property::atoms.wm_name,
479 otk::Property::ascii, &_title);
480 }
481
482 if (_title.empty())
483 _title = _("Unnamed Window");
484
485 if (frame)
486 frame->setTitle(_title);
487 }
488
489
490 void Client::updateIconTitle()
491 {
492 _icon_title = "";
493
494 // try netwm
495 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon_name,
496 otk::Property::utf8, &_icon_title)) {
497 // try old x stuff
498 otk::Property::get(_window, otk::Property::atoms.wm_icon_name,
499 otk::Property::ascii, &_icon_title);
500 }
501
502 if (_title.empty())
503 _icon_title = _("Unnamed Window");
504 }
505
506
507 void Client::updateClass()
508 {
509 // set the defaults
510 _app_name = _app_class = _role = "";
511
512 otk::Property::StringVect v;
513 unsigned long num = 2;
514
515 if (otk::Property::get(_window, otk::Property::atoms.wm_class,
516 otk::Property::ascii, &num, &v)) {
517 if (num > 0) _app_name = v[0].c_str();
518 if (num > 1) _app_class = v[1].c_str();
519 }
520
521 v.clear();
522 num = 1;
523 if (otk::Property::get(_window, otk::Property::atoms.wm_window_role,
524 otk::Property::ascii, &num, &v)) {
525 if (num > 0) _role = v[0].c_str();
526 }
527 }
528
529
530 void Client::updateStrut()
531 {
532 unsigned long num = 4;
533 unsigned long *data;
534 if (!otk::Property::get(_window, otk::Property::atoms.net_wm_strut,
535 otk::Property::atoms.cardinal, &num, &data))
536 return;
537
538 if (num == 4) {
539 _strut.left = data[0];
540 _strut.right = data[1];
541 _strut.top = data[2];
542 _strut.bottom = data[3];
543
544 openbox->screen(_screen)->updateStrut();
545 }
546
547 delete [] data;
548 }
549
550
551 void Client::updateTransientFor()
552 {
553 Window t = 0;
554 Client *c = 0;
555
556 if (XGetTransientForHint(**otk::display, _window, &t) &&
557 t != _window) { // cant be transient to itself!
558 c = openbox->findClient(t);
559 assert(c != this); // if this happens then we need to check for it
560
561 if (!c /*XXX: && _group*/) {
562 // not transient to a client, see if it is transient for a group
563 if (//t == _group->leader() ||
564 t == None ||
565 t == otk::display->screenInfo(_screen)->rootWindow()) {
566 // window is a transient for its group!
567 // XXX: for now this is treated as non-transient.
568 // this needs to be fixed!
569 }
570 }
571 }
572
573 // if anything has changed...
574 if (c != _transient_for) {
575 if (_transient_for)
576 _transient_for->_transients.remove(this); // remove from old parent
577 _transient_for = c;
578 if (_transient_for)
579 _transient_for->_transients.push_back(this); // add to new parent
580
581 // XXX: change decor status?
582 }
583 }
584
585
586 void Client::propertyHandler(const XPropertyEvent &e)
587 {
588 otk::EventHandler::propertyHandler(e);
589
590 // compress changes to a single property into a single change
591 XEvent ce;
592 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
593 // XXX: it would be nice to compress ALL changes to a property, not just
594 // changes in a row without other props between.
595 if (ce.xproperty.atom != e.atom) {
596 XPutBackEvent(**otk::display, &ce);
597 break;
598 }
599 }
600
601 if (e.atom == XA_WM_NORMAL_HINTS)
602 updateNormalHints();
603 else if (e.atom == XA_WM_HINTS)
604 updateWMHints();
605 else if (e.atom == XA_WM_TRANSIENT_FOR) {
606 updateTransientFor();
607 getType();
608 calcLayer(); // type may have changed, so update the layer
609 setupDecorAndFunctions();
610 frame->adjustSize(); // this updates the frame for any new decor settings
611 }
612 else if (e.atom == otk::Property::atoms.net_wm_name ||
613 e.atom == otk::Property::atoms.wm_name)
614 updateTitle();
615 else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
616 e.atom == otk::Property::atoms.wm_icon_name)
617 updateIconTitle();
618 else if (e.atom == otk::Property::atoms.wm_class)
619 updateClass();
620 else if (e.atom == otk::Property::atoms.wm_protocols)
621 updateProtocols();
622 else if (e.atom == otk::Property::atoms.net_wm_strut)
623 updateStrut();
624 }
625
626
627 void Client::setWMState(long state)
628 {
629 if (state == _wmstate) return; // no change
630
631 _wmstate = state;
632 switch (_wmstate) {
633 case IconicState:
634 // XXX: cause it to iconify
635 break;
636 case NormalState:
637 // XXX: cause it to uniconify
638 break;
639 }
640 }
641
642
643 void Client::setDesktop(long target)
644 {
645 if (target == _desktop) return;
646
647 printf("Setting desktop %ld\n", target);
648
649 if (!(target >= 0 || target == (signed)0xffffffff)) return;
650
651 _desktop = target;
652
653 otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
654 otk::Property::atoms.cardinal, (unsigned)_desktop);
655
656 // 'move' the window to the new desktop
657 if (_desktop == openbox->screen(_screen)->desktop() ||
658 _desktop == (signed)0xffffffff)
659 frame->show();
660 else
661 frame->hide();
662 }
663
664
665 void Client::setState(StateAction action, long data1, long data2)
666 {
667 bool shadestate = _shaded;
668
669 if (!(action == State_Add || action == State_Remove ||
670 action == State_Toggle))
671 return; // an invalid action was passed to the client message, ignore it
672
673 for (int i = 0; i < 2; ++i) {
674 Atom state = i == 0 ? data1 : data2;
675
676 if (! state) continue;
677
678 // if toggling, then pick whether we're adding or removing
679 if (action == State_Toggle) {
680 if (state == otk::Property::atoms.net_wm_state_modal)
681 action = _modal ? State_Remove : State_Add;
682 else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
683 action = _max_vert ? State_Remove : State_Add;
684 else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
685 action = _max_horz ? State_Remove : State_Add;
686 else if (state == otk::Property::atoms.net_wm_state_shaded)
687 action = _shaded ? State_Remove : State_Add;
688 else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
689 action = _skip_taskbar ? State_Remove : State_Add;
690 else if (state == otk::Property::atoms.net_wm_state_skip_pager)
691 action = _skip_pager ? State_Remove : State_Add;
692 else if (state == otk::Property::atoms.net_wm_state_fullscreen)
693 action = _fullscreen ? State_Remove : State_Add;
694 else if (state == otk::Property::atoms.net_wm_state_above)
695 action = _above ? State_Remove : State_Add;
696 else if (state == otk::Property::atoms.net_wm_state_below)
697 action = _below ? State_Remove : State_Add;
698 }
699
700 if (action == State_Add) {
701 if (state == otk::Property::atoms.net_wm_state_modal) {
702 if (_modal) continue;
703 _modal = true;
704 // XXX: give it focus if another window has focus that shouldnt now
705 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
706 if (_max_vert) continue;
707 _max_vert = true;
708 // XXX: resize the window etc
709 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
710 if (_max_horz) continue;
711 _max_horz = true;
712 // XXX: resize the window etc
713 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
714 if (_shaded) continue;
715 // shade when we're all thru here
716 shadestate = true;
717 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
718 _skip_taskbar = true;
719 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
720 _skip_pager = true;
721 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
722 if (_fullscreen) continue;
723 _fullscreen = true;
724 } else if (state == otk::Property::atoms.net_wm_state_above) {
725 if (_above) continue;
726 _above = true;
727 } else if (state == otk::Property::atoms.net_wm_state_below) {
728 if (_below) continue;
729 _below = true;
730 }
731
732 } else { // action == State_Remove
733 if (state == otk::Property::atoms.net_wm_state_modal) {
734 if (!_modal) continue;
735 _modal = false;
736 } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
737 if (!_max_vert) continue;
738 _max_vert = false;
739 // XXX: resize the window etc
740 } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
741 if (!_max_horz) continue;
742 _max_horz = false;
743 // XXX: resize the window etc
744 } else if (state == otk::Property::atoms.net_wm_state_shaded) {
745 if (!_shaded) continue;
746 // unshade when we're all thru here
747 shadestate = false;
748 } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
749 _skip_taskbar = false;
750 } else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
751 _skip_pager = false;
752 } else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
753 if (!_fullscreen) continue;
754 _fullscreen = false;
755 } else if (state == otk::Property::atoms.net_wm_state_above) {
756 if (!_above) continue;
757 _above = false;
758 } else if (state == otk::Property::atoms.net_wm_state_below) {
759 if (!_below) continue;
760 _below = false;
761 }
762 }
763 }
764 if (shadestate != _shaded)
765 shade(shadestate);
766 calcLayer();
767 }
768
769
770 void Client::toggleClientBorder(bool addborder)
771 {
772 // adjust our idea of where the client is, based on its border. When the
773 // border is removed, the client should now be considered to be in a
774 // different position.
775 // when re-adding the border to the client, the same operation needs to be
776 // reversed.
777 int x = _area.x(), y = _area.y();
778 switch(_gravity) {
779 default:
780 case NorthWestGravity:
781 case WestGravity:
782 case SouthWestGravity:
783 break;
784 case NorthEastGravity:
785 case EastGravity:
786 case SouthEastGravity:
787 if (addborder) x -= _border_width * 2;
788 else x += _border_width * 2;
789 break;
790 case NorthGravity:
791 case SouthGravity:
792 case CenterGravity:
793 case ForgetGravity:
794 case StaticGravity:
795 if (addborder) x -= _border_width;
796 else x += _border_width;
797 break;
798 }
799 switch(_gravity) {
800 default:
801 case NorthWestGravity:
802 case NorthGravity:
803 case NorthEastGravity:
804 break;
805 case SouthWestGravity:
806 case SouthGravity:
807 case SouthEastGravity:
808 if (addborder) y -= _border_width * 2;
809 else y += _border_width * 2;
810 break;
811 case WestGravity:
812 case EastGravity:
813 case CenterGravity:
814 case ForgetGravity:
815 case StaticGravity:
816 if (addborder) y -= _border_width;
817 else y += _border_width;
818 break;
819 }
820 _area.setPos(x, y);
821
822 if (addborder) {
823 XSetWindowBorderWidth(**otk::display, _window, _border_width);
824
825 // move the client so it is back it the right spot _with_ its border!
826 XMoveWindow(**otk::display, _window, x, y);
827 } else
828 XSetWindowBorderWidth(**otk::display, _window, 0);
829 }
830
831
832 void Client::clientMessageHandler(const XClientMessageEvent &e)
833 {
834 otk::EventHandler::clientMessageHandler(e);
835
836 if (e.format != 32) return;
837
838 if (e.message_type == otk::Property::atoms.wm_change_state) {
839 // compress changes into a single change
840 bool compress = false;
841 XEvent ce;
842 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
843 // XXX: it would be nice to compress ALL messages of a type, not just
844 // messages in a row without other message types between.
845 if (ce.xclient.message_type != e.message_type) {
846 XPutBackEvent(**otk::display, &ce);
847 break;
848 }
849 compress = true;
850 }
851 if (compress)
852 setWMState(ce.xclient.data.l[0]); // use the found event
853 else
854 setWMState(e.data.l[0]); // use the original event
855 } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
856 // compress changes into a single change
857 bool compress = false;
858 XEvent ce;
859 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
860 // XXX: it would be nice to compress ALL messages of a type, not just
861 // messages in a row without other message types between.
862 if (ce.xclient.message_type != e.message_type) {
863 XPutBackEvent(**otk::display, &ce);
864 break;
865 }
866 compress = true;
867 }
868 if (compress)
869 setDesktop(e.data.l[0]); // use the found event
870 else
871 setDesktop(e.data.l[0]); // use the original event
872 } else if (e.message_type == otk::Property::atoms.net_wm_state) {
873 // can't compress these
874 #ifdef DEBUG
875 printf("net_wm_state %s %ld %ld for 0x%lx\n",
876 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
877 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
878 e.data.l[1], e.data.l[2], _window);
879 #endif
880 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
881 } else if (e.message_type == otk::Property::atoms.net_close_window) {
882 #ifdef DEBUG
883 printf("net_close_window for 0x%lx\n", _window);
884 #endif
885 close();
886 } else if (e.message_type == otk::Property::atoms.net_active_window) {
887 #ifdef DEBUG
888 printf("net_active_window for 0x%lx\n", _window);
889 #endif
890 if (_shaded)
891 shade(false);
892 // XXX: deiconify
893 focus();
894 openbox->screen(_screen)->raiseWindow(this);
895 }
896 }
897
898
899 #if defined(SHAPE)
900 void Client::shapeHandler(const XShapeEvent &e)
901 {
902 otk::EventHandler::shapeHandler(e);
903
904 if (e.kind == ShapeBounding) {
905 _shaped = e.shaped;
906 frame->adjustShape();
907 }
908 }
909 #endif
910
911
912 void Client::resize(Corner anchor, int w, int h, int x, int y)
913 {
914 w -= _base_size.x();
915 h -= _base_size.y();
916
917 // for interactive resizing. have to move half an increment in each
918 // direction.
919 w += _size_inc.x() / 2;
920 h += _size_inc.y() / 2;
921
922 // is the window resizable? if it is not, then don't check its sizes, the
923 // client can do what it wants and the user can't change it anyhow
924 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
925 // smaller than min size or bigger than max size?
926 if (w < _min_size.x()) w = _min_size.x();
927 else if (w > _max_size.x()) w = _max_size.x();
928 if (h < _min_size.y()) h = _min_size.y();
929 else if (h > _max_size.y()) h = _max_size.y();
930 }
931
932 // keep to the increments
933 w /= _size_inc.x();
934 h /= _size_inc.y();
935
936 // you cannot resize to nothing
937 if (w < 1) w = 1;
938 if (h < 1) h = 1;
939
940 // store the logical size
941 _logical_size.setPoint(w, h);
942
943 w *= _size_inc.x();
944 h *= _size_inc.y();
945
946 w += _base_size.x();
947 h += _base_size.y();
948
949 if (x == INT_MIN || y == INT_MIN) {
950 x = _area.x();
951 y = _area.y();
952 switch (anchor) {
953 case TopLeft:
954 break;
955 case TopRight:
956 x -= w - _area.width();
957 break;
958 case BottomLeft:
959 y -= h - _area.height();
960 break;
961 case BottomRight:
962 x -= w - _area.width();
963 y -= h - _area.height();
964 break;
965 }
966 }
967
968 _area.setSize(w, h);
969
970 XResizeWindow(**otk::display, _window, w, h);
971
972 // resize the frame to match the request
973 frame->adjustSize();
974 move(x, y);
975 }
976
977
978 void Client::move(int x, int y)
979 {
980 _area.setPos(x, y);
981
982 // move the frame to be in the requested position
983 if (frame) { // this can be called while mapping, before frame exists
984 frame->adjustPosition();
985
986 // send synthetic configure notify (we don't need to if we aren't mapped
987 // yet)
988 XEvent event;
989 event.type = ConfigureNotify;
990 event.xconfigure.display = **otk::display;
991 event.xconfigure.event = _window;
992 event.xconfigure.window = _window;
993 event.xconfigure.x = x;
994 event.xconfigure.y = y;
995 event.xconfigure.width = _area.width();
996 event.xconfigure.height = _area.height();
997 event.xconfigure.border_width = _border_width;
998 event.xconfigure.above = frame->window();
999 event.xconfigure.override_redirect = False;
1000 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1001 StructureNotifyMask, &event);
1002 }
1003 }
1004
1005
1006 void Client::close()
1007 {
1008 XEvent ce;
1009
1010 if (!(_functions & Func_Close)) return;
1011
1012 // XXX: itd be cool to do timeouts and shit here for killing the client's
1013 // process off
1014 // like... if the window is around after 5 seconds, then the close button
1015 // turns a nice red, and if this function is called again, the client is
1016 // explicitly killed.
1017
1018 ce.xclient.type = ClientMessage;
1019 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1020 ce.xclient.display = **otk::display;
1021 ce.xclient.window = _window;
1022 ce.xclient.format = 32;
1023 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
1024 ce.xclient.data.l[1] = CurrentTime;
1025 ce.xclient.data.l[2] = 0l;
1026 ce.xclient.data.l[3] = 0l;
1027 ce.xclient.data.l[4] = 0l;
1028 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1029 }
1030
1031
1032 void Client::changeState()
1033 {
1034 unsigned long state[2];
1035 state[0] = _wmstate;
1036 state[1] = None;
1037 otk::Property::set(_window, otk::Property::atoms.wm_state,
1038 otk::Property::atoms.wm_state, state, 2);
1039
1040 Atom netstate[10];
1041 int num = 0;
1042 if (_modal)
1043 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1044 if (_shaded)
1045 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1046 if (_iconic)
1047 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1048 if (_skip_taskbar)
1049 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1050 if (_skip_pager)
1051 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1052 if (_fullscreen)
1053 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1054 if (_max_vert)
1055 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1056 if (_max_horz)
1057 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1058 if (_above)
1059 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1060 if (_below)
1061 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1062 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1063 otk::Property::atoms.atom, netstate, num);
1064
1065 calcLayer();
1066 }
1067
1068
1069 void Client::shade(bool shade)
1070 {
1071 if (shade == _shaded) return; // already done
1072
1073 _wmstate = shade ? IconicState : NormalState;
1074 _shaded = shade;
1075 changeState();
1076 frame->adjustSize();
1077 }
1078
1079
1080 bool Client::focus() const
1081 {
1082 // won't try focus if the client doesn't want it, or if the window isn't
1083 // visible on the screen
1084 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1085
1086 if (_focused) return true;
1087
1088 if (_can_focus)
1089 XSetInputFocus(**otk::display, _window,
1090 RevertToNone, CurrentTime);
1091
1092 if (_focus_notify) {
1093 XEvent ce;
1094 ce.xclient.type = ClientMessage;
1095 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1096 ce.xclient.display = **otk::display;
1097 ce.xclient.window = _window;
1098 ce.xclient.format = 32;
1099 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1100 ce.xclient.data.l[1] = openbox->lastTime();
1101 ce.xclient.data.l[2] = 0l;
1102 ce.xclient.data.l[3] = 0l;
1103 ce.xclient.data.l[4] = 0l;
1104 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1105 }
1106
1107 return true;
1108 }
1109
1110
1111 void Client::unfocus() const
1112 {
1113 if (!_focused) return;
1114
1115 assert(openbox->focusedClient() == this);
1116 openbox->setFocusedClient(0);
1117 }
1118
1119
1120 void Client::focusHandler(const XFocusChangeEvent &e)
1121 {
1122 #ifdef DEBUG
1123 // printf("FocusIn for 0x%lx\n", e.window);
1124 #endif // DEBUG
1125
1126 otk::EventHandler::focusHandler(e);
1127
1128 frame->focus();
1129 _focused = true;
1130
1131 openbox->setFocusedClient(this);
1132 }
1133
1134
1135 void Client::unfocusHandler(const XFocusChangeEvent &e)
1136 {
1137 #ifdef DEBUG
1138 // printf("FocusOut for 0x%lx\n", e.window);
1139 #endif // DEBUG
1140
1141 otk::EventHandler::unfocusHandler(e);
1142
1143 frame->unfocus();
1144 _focused = false;
1145
1146 if (openbox->focusedClient() == this)
1147 openbox->setFocusedClient(0);
1148 }
1149
1150
1151 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1152 {
1153 #ifdef DEBUG
1154 printf("ConfigureRequest for 0x%lx\n", e.window);
1155 #endif // DEBUG
1156
1157 otk::EventHandler::configureRequestHandler(e);
1158
1159 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1160
1161 if (e.value_mask & CWBorderWidth)
1162 _border_width = e.border_width;
1163
1164 // resize, then move, as specified in the EWMH section 7.7
1165 if (e.value_mask & (CWWidth | CWHeight)) {
1166 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1167 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1168
1169 Corner corner;
1170 switch (_gravity) {
1171 case NorthEastGravity:
1172 case EastGravity:
1173 corner = TopRight;
1174 break;
1175 case SouthWestGravity:
1176 case SouthGravity:
1177 corner = BottomLeft;
1178 break;
1179 case SouthEastGravity:
1180 corner = BottomRight;
1181 break;
1182 default: // NorthWest, Static, etc
1183 corner = TopLeft;
1184 }
1185
1186 // if moving AND resizing ...
1187 if (e.value_mask & (CWX | CWY)) {
1188 int x = (e.value_mask & CWX) ? e.x : _area.x();
1189 int y = (e.value_mask & CWY) ? e.y : _area.y();
1190 resize(corner, w, h, x, y);
1191 } else // if JUST resizing...
1192 resize(corner, w, h);
1193 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1194 int x = (e.value_mask & CWX) ? e.x : _area.x();
1195 int y = (e.value_mask & CWY) ? e.y : _area.y();
1196 move(x, y);
1197 }
1198
1199 if (e.value_mask & CWStackMode) {
1200 switch (e.detail) {
1201 case Below:
1202 case BottomIf:
1203 openbox->screen(_screen)->lowerWindow(this);
1204 break;
1205
1206 case Above:
1207 case TopIf:
1208 default:
1209 openbox->screen(_screen)->raiseWindow(this);
1210 break;
1211 }
1212 }
1213 }
1214
1215
1216 void Client::unmapHandler(const XUnmapEvent &e)
1217 {
1218 if (ignore_unmaps) {
1219 #ifdef DEBUG
1220 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1221 #endif // DEBUG
1222 ignore_unmaps--;
1223 return;
1224 }
1225
1226 #ifdef DEBUG
1227 printf("UnmapNotify for 0x%lx\n", e.window);
1228 #endif // DEBUG
1229
1230 otk::EventHandler::unmapHandler(e);
1231
1232 // this deletes us etc
1233 openbox->screen(_screen)->unmanageWindow(this);
1234 }
1235
1236
1237 void Client::destroyHandler(const XDestroyWindowEvent &e)
1238 {
1239 #ifdef DEBUG
1240 printf("DestroyNotify for 0x%lx\n", e.window);
1241 #endif // DEBUG
1242
1243 otk::EventHandler::destroyHandler(e);
1244
1245 // this deletes us etc
1246 openbox->screen(_screen)->unmanageWindow(this);
1247 }
1248
1249
1250 void Client::reparentHandler(const XReparentEvent &e)
1251 {
1252 // this is when the client is first taken captive in the frame
1253 if (e.parent == frame->plate()) return;
1254
1255 #ifdef DEBUG
1256 printf("ReparentNotify for 0x%lx\n", e.window);
1257 #endif // DEBUG
1258
1259 otk::EventHandler::reparentHandler(e);
1260
1261 /*
1262 This event is quite rare and is usually handled in unmapHandler.
1263 However, if the window is unmapped when the reparent event occurs,
1264 the window manager never sees it because an unmap event is not sent
1265 to an already unmapped window.
1266 */
1267
1268 // we don't want the reparent event, put it back on the stack for the X
1269 // server to deal with after we unmanage the window
1270 XEvent ev;
1271 ev.xreparent = e;
1272 XPutBackEvent(**otk::display, &ev);
1273
1274 // this deletes us etc
1275 openbox->screen(_screen)->unmanageWindow(this);
1276 }
1277
1278 }
This page took 0.091241 seconds and 5 git commands to generate.