]> Dogcows Code - chaz/openbox/blob - src/client.cc
new raise/lower window code. only restacks 2 windows (1 move) at a time, ever. ManMow...
[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 case NorthWestGravity:
760 case WestGravity:
761 case SouthWestGravity:
762 break;
763 case NorthEastGravity:
764 case EastGravity:
765 case SouthEastGravity:
766 if (addborder) x -= _border_width * 2;
767 else x += _border_width * 2;
768 break;
769 }
770 switch(_gravity) {
771 case NorthWestGravity:
772 case NorthGravity:
773 case NorthEastGravity:
774 break;
775 case SouthWestGravity:
776 case SouthGravity:
777 case SouthEastGravity:
778 if (addborder) y -= _border_width * 2;
779 else y += _border_width * 2;
780 break;
781 default:
782 // no change for StaticGravity etc.
783 break;
784 }
785 _area.setPos(x, y);
786
787 if (addborder) {
788 XSetWindowBorderWidth(**otk::display, _window, _border_width);
789
790 // move the client so it is back it the right spot _with_ its border!
791 XMoveWindow(**otk::display, _window, x, y);
792 } else
793 XSetWindowBorderWidth(**otk::display, _window, 0);
794 }
795
796
797 void Client::clientMessageHandler(const XClientMessageEvent &e)
798 {
799 otk::EventHandler::clientMessageHandler(e);
800
801 if (e.format != 32) return;
802
803 if (e.message_type == otk::Property::atoms.wm_change_state) {
804 // compress changes into a single change
805 bool compress = false;
806 XEvent ce;
807 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
808 // XXX: it would be nice to compress ALL messages of a type, not just
809 // messages in a row without other message types between.
810 if (ce.xclient.message_type != e.message_type) {
811 XPutBackEvent(**otk::display, &ce);
812 break;
813 }
814 compress = true;
815 }
816 if (compress)
817 setWMState(ce.xclient.data.l[0]); // use the found event
818 else
819 setWMState(e.data.l[0]); // use the original event
820 } else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
821 // compress changes into a single change
822 bool compress = false;
823 XEvent ce;
824 while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
825 // XXX: it would be nice to compress ALL messages of a type, not just
826 // messages in a row without other message types between.
827 if (ce.xclient.message_type != e.message_type) {
828 XPutBackEvent(**otk::display, &ce);
829 break;
830 }
831 compress = true;
832 }
833 if (compress)
834 setDesktop(e.data.l[0]); // use the found event
835 else
836 setDesktop(e.data.l[0]); // use the original event
837 } else if (e.message_type == otk::Property::atoms.net_wm_state) {
838 // can't compress these
839 #ifdef DEBUG
840 printf("net_wm_state %s %ld %ld for 0x%lx\n",
841 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
842 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
843 e.data.l[1], e.data.l[2], _window);
844 #endif
845 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
846 } else if (e.message_type == otk::Property::atoms.net_close_window) {
847 #ifdef DEBUG
848 printf("net_close_window for 0x%lx\n", _window);
849 #endif
850 close();
851 } else if (e.message_type == otk::Property::atoms.net_active_window) {
852 #ifdef DEBUG
853 printf("net_active_window for 0x%lx\n", _window);
854 #endif
855 if (_shaded)
856 shade(false);
857 // XXX: deiconify
858 focus();
859 openbox->screen(_screen)->raiseWindow(this);
860 }
861 }
862
863
864 #if defined(SHAPE)
865 void Client::shapeHandler(const XShapeEvent &e)
866 {
867 otk::EventHandler::shapeHandler(e);
868
869 if (e.kind == ShapeBounding) {
870 _shaped = e.shaped;
871 frame->adjustShape();
872 }
873 }
874 #endif
875
876
877 void Client::resize(Corner anchor, int w, int h, int x, int y)
878 {
879 w -= _base_size.x();
880 h -= _base_size.y();
881
882 // for interactive resizing. have to move half an increment in each
883 // direction.
884 w += _size_inc.x() / 2;
885 h += _size_inc.y() / 2;
886
887 // is the window resizable? if it is not, then don't check its sizes, the
888 // client can do what it wants and the user can't change it anyhow
889 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
890 // smaller than min size or bigger than max size?
891 if (w < _min_size.x()) w = _min_size.x();
892 else if (w > _max_size.x()) w = _max_size.x();
893 if (h < _min_size.y()) h = _min_size.y();
894 else if (h > _max_size.y()) h = _max_size.y();
895 }
896
897 // keep to the increments
898 w /= _size_inc.x();
899 h /= _size_inc.y();
900
901 // you cannot resize to nothing
902 if (w < 1) w = 1;
903 if (h < 1) h = 1;
904
905 // store the logical size
906 _logical_size.setPoint(w, h);
907
908 w *= _size_inc.x();
909 h *= _size_inc.y();
910
911 w += _base_size.x();
912 h += _base_size.y();
913
914 if (x == INT_MIN || y == INT_MIN) {
915 x = _area.x();
916 y = _area.y();
917 switch (anchor) {
918 case TopLeft:
919 break;
920 case TopRight:
921 x -= w - _area.width();
922 break;
923 case BottomLeft:
924 y -= h - _area.height();
925 break;
926 case BottomRight:
927 x -= w - _area.width();
928 y -= h - _area.height();
929 break;
930 }
931 }
932
933 _area.setSize(w, h);
934
935 XResizeWindow(**otk::display, _window, w, h);
936
937 // resize the frame to match the request
938 frame->adjustSize();
939 move(x, y);
940 }
941
942
943 void Client::move(int x, int y)
944 {
945 _area.setPos(x, y);
946
947 // move the frame to be in the requested position
948 if (frame) { // this can be called while mapping, before frame exists
949 frame->adjustPosition();
950
951 // send synthetic configure notify (we don't need to if we aren't mapped
952 // yet)
953 XEvent event;
954 event.type = ConfigureNotify;
955 event.xconfigure.display = **otk::display;
956 event.xconfigure.event = _window;
957 event.xconfigure.window = _window;
958 event.xconfigure.x = x;
959 event.xconfigure.y = y;
960 event.xconfigure.width = _area.width();
961 event.xconfigure.height = _area.height();
962 event.xconfigure.border_width = _border_width;
963 event.xconfigure.above = frame->window();
964 event.xconfigure.override_redirect = False;
965 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
966 StructureNotifyMask, &event);
967 }
968 }
969
970
971 void Client::close()
972 {
973 XEvent ce;
974
975 if (!(_functions & Func_Close)) return;
976
977 // XXX: itd be cool to do timeouts and shit here for killing the client's
978 // process off
979 // like... if the window is around after 5 seconds, then the close button
980 // turns a nice red, and if this function is called again, the client is
981 // explicitly killed.
982
983 ce.xclient.type = ClientMessage;
984 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
985 ce.xclient.display = **otk::display;
986 ce.xclient.window = _window;
987 ce.xclient.format = 32;
988 ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
989 ce.xclient.data.l[1] = CurrentTime;
990 ce.xclient.data.l[2] = 0l;
991 ce.xclient.data.l[3] = 0l;
992 ce.xclient.data.l[4] = 0l;
993 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
994 }
995
996
997 void Client::changeState()
998 {
999 unsigned long state[2];
1000 state[0] = _wmstate;
1001 state[1] = None;
1002 otk::Property::set(_window, otk::Property::atoms.wm_state,
1003 otk::Property::atoms.wm_state, state, 2);
1004
1005 Atom netstate[10];
1006 int num = 0;
1007 if (_modal)
1008 netstate[num++] = otk::Property::atoms.net_wm_state_modal;
1009 if (_shaded)
1010 netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
1011 if (_iconic)
1012 netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
1013 if (_skip_taskbar)
1014 netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
1015 if (_skip_pager)
1016 netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
1017 if (_fullscreen)
1018 netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
1019 if (_max_vert)
1020 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
1021 if (_max_horz)
1022 netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
1023 if (_above)
1024 netstate[num++] = otk::Property::atoms.net_wm_state_above;
1025 if (_below)
1026 netstate[num++] = otk::Property::atoms.net_wm_state_below;
1027 otk::Property::set(_window, otk::Property::atoms.net_wm_state,
1028 otk::Property::atoms.atom, netstate, num);
1029
1030 calcLayer();
1031 }
1032
1033
1034 void Client::shade(bool shade)
1035 {
1036 if (shade == _shaded) return; // already done
1037
1038 _wmstate = shade ? IconicState : NormalState;
1039 _shaded = shade;
1040 changeState();
1041 frame->adjustSize();
1042 }
1043
1044
1045 bool Client::focus() const
1046 {
1047 // won't try focus if the client doesn't want it, or if the window isn't
1048 // visible on the screen
1049 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1050
1051 if (_focused) return true;
1052
1053 if (_can_focus)
1054 XSetInputFocus(**otk::display, _window,
1055 RevertToNone, CurrentTime);
1056
1057 if (_focus_notify) {
1058 XEvent ce;
1059 ce.xclient.type = ClientMessage;
1060 ce.xclient.message_type = otk::Property::atoms.wm_protocols;
1061 ce.xclient.display = **otk::display;
1062 ce.xclient.window = _window;
1063 ce.xclient.format = 32;
1064 ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
1065 ce.xclient.data.l[1] = openbox->lastTime();
1066 ce.xclient.data.l[2] = 0l;
1067 ce.xclient.data.l[3] = 0l;
1068 ce.xclient.data.l[4] = 0l;
1069 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1070 }
1071
1072 return true;
1073 }
1074
1075
1076 void Client::unfocus() const
1077 {
1078 if (!_focused) return;
1079
1080 assert(openbox->focusedClient() == this);
1081 openbox->setFocusedClient(0);
1082 }
1083
1084
1085 void Client::focusHandler(const XFocusChangeEvent &e)
1086 {
1087 #ifdef DEBUG
1088 // printf("FocusIn for 0x%lx\n", e.window);
1089 #endif // DEBUG
1090
1091 otk::EventHandler::focusHandler(e);
1092
1093 frame->focus();
1094 _focused = true;
1095
1096 openbox->setFocusedClient(this);
1097 }
1098
1099
1100 void Client::unfocusHandler(const XFocusChangeEvent &e)
1101 {
1102 #ifdef DEBUG
1103 // printf("FocusOut for 0x%lx\n", e.window);
1104 #endif // DEBUG
1105
1106 otk::EventHandler::unfocusHandler(e);
1107
1108 frame->unfocus();
1109 _focused = false;
1110
1111 if (openbox->focusedClient() == this)
1112 openbox->setFocusedClient(0);
1113 }
1114
1115
1116 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1117 {
1118 #ifdef DEBUG
1119 printf("ConfigureRequest for 0x%lx\n", e.window);
1120 #endif // DEBUG
1121
1122 otk::EventHandler::configureRequestHandler(e);
1123
1124 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1125
1126 if (e.value_mask & CWBorderWidth)
1127 _border_width = e.border_width;
1128
1129 // resize, then move, as specified in the EWMH section 7.7
1130 if (e.value_mask & (CWWidth | CWHeight)) {
1131 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1132 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1133
1134 Corner corner;
1135 switch (_gravity) {
1136 case NorthEastGravity:
1137 case EastGravity:
1138 corner = TopRight;
1139 break;
1140 case SouthWestGravity:
1141 case SouthGravity:
1142 corner = BottomLeft;
1143 break;
1144 case SouthEastGravity:
1145 corner = BottomRight;
1146 break;
1147 default: // NorthWest, Static, etc
1148 corner = TopLeft;
1149 }
1150
1151 // if moving AND resizing ...
1152 if (e.value_mask & (CWX | CWY)) {
1153 int x = (e.value_mask & CWX) ? e.x : _area.x();
1154 int y = (e.value_mask & CWY) ? e.y : _area.y();
1155 resize(corner, w, h, x, y);
1156 } else // if JUST resizing...
1157 resize(corner, w, h);
1158 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1159 int x = (e.value_mask & CWX) ? e.x : _area.x();
1160 int y = (e.value_mask & CWY) ? e.y : _area.y();
1161 move(x, y);
1162 }
1163
1164 if (e.value_mask & CWStackMode) {
1165 switch (e.detail) {
1166 case Below:
1167 case BottomIf:
1168 openbox->screen(_screen)->lowerWindow(this);
1169 break;
1170
1171 case Above:
1172 case TopIf:
1173 default:
1174 openbox->screen(_screen)->raiseWindow(this);
1175 break;
1176 }
1177 }
1178 }
1179
1180
1181 void Client::unmapHandler(const XUnmapEvent &e)
1182 {
1183 if (ignore_unmaps) {
1184 #ifdef DEBUG
1185 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1186 #endif // DEBUG
1187 ignore_unmaps--;
1188 return;
1189 }
1190
1191 #ifdef DEBUG
1192 printf("UnmapNotify for 0x%lx\n", e.window);
1193 #endif // DEBUG
1194
1195 otk::EventHandler::unmapHandler(e);
1196
1197 // this deletes us etc
1198 openbox->screen(_screen)->unmanageWindow(this);
1199 }
1200
1201
1202 void Client::destroyHandler(const XDestroyWindowEvent &e)
1203 {
1204 #ifdef DEBUG
1205 printf("DestroyNotify for 0x%lx\n", e.window);
1206 #endif // DEBUG
1207
1208 otk::EventHandler::destroyHandler(e);
1209
1210 // this deletes us etc
1211 openbox->screen(_screen)->unmanageWindow(this);
1212 }
1213
1214
1215 void Client::reparentHandler(const XReparentEvent &e)
1216 {
1217 // this is when the client is first taken captive in the frame
1218 if (e.parent == frame->plate()) return;
1219
1220 #ifdef DEBUG
1221 printf("ReparentNotify for 0x%lx\n", e.window);
1222 #endif // DEBUG
1223
1224 otk::EventHandler::reparentHandler(e);
1225
1226 /*
1227 This event is quite rare and is usually handled in unmapHandler.
1228 However, if the window is unmapped when the reparent event occurs,
1229 the window manager never sees it because an unmap event is not sent
1230 to an already unmapped window.
1231 */
1232
1233 // we don't want the reparent event, put it back on the stack for the X
1234 // server to deal with after we unmanage the window
1235 XEvent ev;
1236 ev.xreparent = e;
1237 XPutBackEvent(**otk::display, &ev);
1238
1239 // this deletes us etc
1240 openbox->screen(_screen)->unmanageWindow(this);
1241 }
1242
1243 }
This page took 0.086926 seconds and 5 git commands to generate.