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