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