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