]> Dogcows Code - chaz/openbox/blob - src/client.cc
c3ab3c63a8740f25f677c38aa7c977fae1195b9b
[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 printf("Setting desktop %ld\n", target);
659 assert(target >= 0 || target == (signed)0xffffffff);
660 //assert(target == 0xffffffff || target < MAX);
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
672 // XXX: move the window to the new desktop
673 }
674
675
676 void OBClient::setState(StateAction action, long data1, long data2)
677 {
678 const otk::OBProperty *property = Openbox::instance->property();
679 bool shadestate = _shaded;
680
681 if (!(action == State_Add || action == State_Remove ||
682 action == State_Toggle))
683 return; // an invalid action was passed to the client message, ignore it
684
685 for (int i = 0; i < 2; ++i) {
686 Atom state = i == 0 ? data1 : data2;
687
688 if (! state) continue;
689
690 // if toggling, then pick whether we're adding or removing
691 if (action == State_Toggle) {
692 if (state == property->atom(otk::OBProperty::net_wm_state_modal))
693 action = _modal ? State_Remove : State_Add;
694 else if (state ==
695 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
696 action = _max_vert ? State_Remove : State_Add;
697 else if (state ==
698 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
699 action = _max_horz ? State_Remove : State_Add;
700 else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
701 action = _shaded ? State_Remove : State_Add;
702 else if (state ==
703 property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
704 action = _skip_taskbar ? State_Remove : State_Add;
705 else if (state ==
706 property->atom(otk::OBProperty::net_wm_state_skip_pager))
707 action = _skip_pager ? State_Remove : State_Add;
708 else if (state ==
709 property->atom(otk::OBProperty::net_wm_state_fullscreen))
710 action = _fullscreen ? State_Remove : State_Add;
711 else if (state == property->atom(otk::OBProperty::net_wm_state_above))
712 action = _above ? State_Remove : State_Add;
713 else if (state == property->atom(otk::OBProperty::net_wm_state_below))
714 action = _below ? State_Remove : State_Add;
715 }
716
717 if (action == State_Add) {
718 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
719 if (_modal) continue;
720 _modal = true;
721 // XXX: give it focus if another window has focus that shouldnt now
722 } else if (state ==
723 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
724 if (_max_vert) continue;
725 _max_vert = true;
726 // XXX: resize the window etc
727 } else if (state ==
728 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
729 if (_max_horz) continue;
730 _max_horz = true;
731 // XXX: resize the window etc
732 } else if (state ==
733 property->atom(otk::OBProperty::net_wm_state_shaded)) {
734 if (_shaded) continue;
735 // shade when we're all thru here
736 shadestate = true;
737 } else if (state ==
738 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
739 _skip_taskbar = true;
740 } else if (state ==
741 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
742 _skip_pager = true;
743 } else if (state ==
744 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
745 if (_fullscreen) continue;
746 _fullscreen = true;
747 } else if (state ==
748 property->atom(otk::OBProperty::net_wm_state_above)) {
749 if (_above) continue;
750 _above = true;
751 } else if (state ==
752 property->atom(otk::OBProperty::net_wm_state_below)) {
753 if (_below) continue;
754 _below = true;
755 }
756
757 } else { // action == State_Remove
758 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
759 if (!_modal) continue;
760 _modal = false;
761 } else if (state ==
762 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
763 if (!_max_vert) continue;
764 _max_vert = false;
765 // XXX: resize the window etc
766 } else if (state ==
767 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
768 if (!_max_horz) continue;
769 _max_horz = false;
770 // XXX: resize the window etc
771 } else if (state ==
772 property->atom(otk::OBProperty::net_wm_state_shaded)) {
773 if (!_shaded) continue;
774 // unshade when we're all thru here
775 shadestate = false;
776 } else if (state ==
777 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
778 _skip_taskbar = false;
779 } else if (state ==
780 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
781 _skip_pager = false;
782 } else if (state ==
783 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
784 if (!_fullscreen) continue;
785 _fullscreen = false;
786 } else if (state ==
787 property->atom(otk::OBProperty::net_wm_state_above)) {
788 if (!_above) continue;
789 _above = false;
790 } else if (state ==
791 property->atom(otk::OBProperty::net_wm_state_below)) {
792 if (!_below) continue;
793 _below = false;
794 }
795 }
796 }
797 if (shadestate != _shaded)
798 shade(shadestate);
799 calcLayer();
800 }
801
802
803 void OBClient::toggleClientBorder(bool addborder)
804 {
805 // adjust our idea of where the client is, based on its border. When the
806 // border is removed, the client should now be considered to be in a
807 // different position.
808 // when re-adding the border to the client, the same operation needs to be
809 // reversed.
810 int x = _area.x(), y = _area.y();
811 switch(_gravity) {
812 case NorthWestGravity:
813 case WestGravity:
814 case SouthWestGravity:
815 break;
816 case NorthEastGravity:
817 case EastGravity:
818 case SouthEastGravity:
819 if (addborder) x -= _border_width * 2;
820 else x += _border_width * 2;
821 break;
822 }
823 switch(_gravity) {
824 case NorthWestGravity:
825 case NorthGravity:
826 case NorthEastGravity:
827 break;
828 case SouthWestGravity:
829 case SouthGravity:
830 case SouthEastGravity:
831 if (addborder) y -= _border_width * 2;
832 else y += _border_width * 2;
833 break;
834 default:
835 // no change for StaticGravity etc.
836 break;
837 }
838 _area.setPos(x, y);
839
840 if (addborder) {
841 XSetWindowBorderWidth(otk::OBDisplay::display, _window, _border_width);
842
843 // move the client so it is back it the right spot _with_ its border!
844 XMoveWindow(otk::OBDisplay::display, _window, x, y);
845 } else
846 XSetWindowBorderWidth(otk::OBDisplay::display, _window, 0);
847 }
848
849
850 void OBClient::clientMessageHandler(const XClientMessageEvent &e)
851 {
852 otk::OtkEventHandler::clientMessageHandler(e);
853
854 if (e.format != 32) return;
855
856 const otk::OBProperty *property = Openbox::instance->property();
857
858 if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) {
859 // compress changes into a single change
860 bool compress = false;
861 XEvent ce;
862 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
863 // XXX: it would be nice to compress ALL messages of a type, not just
864 // messages in a row without other message types between.
865 if (ce.xclient.message_type != e.message_type) {
866 XPutBackEvent(otk::OBDisplay::display, &ce);
867 break;
868 }
869 compress = true;
870 }
871 if (compress)
872 setWMState(ce.xclient.data.l[0]); // use the found event
873 else
874 setWMState(e.data.l[0]); // use the original event
875 } else if (e.message_type ==
876 property->atom(otk::OBProperty::net_wm_desktop)) {
877 // compress changes into a single change
878 bool compress = false;
879 XEvent ce;
880 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
881 // XXX: it would be nice to compress ALL messages of a type, not just
882 // messages in a row without other message types between.
883 if (ce.xclient.message_type != e.message_type) {
884 XPutBackEvent(otk::OBDisplay::display, &ce);
885 break;
886 }
887 compress = true;
888 }
889 if (compress)
890 setDesktop(e.data.l[0]); // use the found event
891 else
892 setDesktop(e.data.l[0]); // use the original event
893 } else if (e.message_type == property->atom(otk::OBProperty::net_wm_state)) {
894 // can't compress these
895 #ifdef DEBUG
896 printf("net_wm_state for 0x%lx\n", _window);
897 #endif
898 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
899 } else if (e.message_type ==
900 property->atom(otk::OBProperty::net_close_window)) {
901 #ifdef DEBUG
902 printf("net_close_window for 0x%lx\n", _window);
903 #endif
904 close();
905 } else if (e.message_type ==
906 property->atom(otk::OBProperty::net_active_window)) {
907 #ifdef DEBUG
908 printf("net_active_window for 0x%lx\n", _window);
909 #endif
910 focus();
911 Openbox::instance->screen(_screen)->restack(true, this); // raise
912 }
913 }
914
915
916 #if defined(SHAPE)
917 void OBClient::shapeHandler(const XShapeEvent &e)
918 {
919 otk::OtkEventHandler::shapeHandler(e);
920
921 _shaped = e.shaped;
922 frame->adjustShape();
923 }
924 #endif
925
926
927 void OBClient::resize(Corner anchor, int w, int h, int x, int y)
928 {
929 w -= _base_size.x();
930 h -= _base_size.y();
931
932 // for interactive resizing. have to move half an increment in each
933 // direction.
934 w += _size_inc.x() / 2;
935 h += _size_inc.y() / 2;
936
937 // is the window resizable? if it is not, then don't check its sizes, the
938 // client can do what it wants and the user can't change it anyhow
939 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
940 // smaller than min size or bigger than max size?
941 if (w < _min_size.x()) w = _min_size.x();
942 else if (w > _max_size.x()) w = _max_size.x();
943 if (h < _min_size.y()) h = _min_size.y();
944 else if (h > _max_size.y()) h = _max_size.y();
945 }
946
947 // keep to the increments
948 w /= _size_inc.x();
949 h /= _size_inc.y();
950
951 // store the logical size
952 _logical_size.setPoint(w, h);
953
954 w *= _size_inc.x();
955 h *= _size_inc.y();
956
957 w += _base_size.x();
958 h += _base_size.y();
959
960 if (x == INT_MIN || y == INT_MIN) {
961 x = _area.x();
962 y = _area.y();
963 switch (anchor) {
964 case TopLeft:
965 break;
966 case TopRight:
967 x -= w - _area.width();
968 break;
969 case BottomLeft:
970 y -= h - _area.height();
971 break;
972 case BottomRight:
973 x -= w - _area.width();
974 y -= h - _area.height();
975 break;
976 }
977 }
978
979 _area.setSize(w, h);
980
981 XResizeWindow(otk::OBDisplay::display, _window, w, h);
982
983 // resize the frame to match the request
984 frame->adjustSize();
985 move(x, y);
986 }
987
988
989 void OBClient::move(int x, int y)
990 {
991 _area.setPos(x, y);
992
993 // move the frame to be in the requested position
994 frame->adjustPosition();
995 }
996
997
998 void OBClient::close()
999 {
1000 XEvent ce;
1001 const otk::OBProperty *property = Openbox::instance->property();
1002
1003 if (!(_functions & Func_Close)) return;
1004
1005 // XXX: itd be cool to do timeouts and shit here for killing the client's
1006 // process off
1007 // like... if the window is around after 5 seconds, then the close button
1008 // turns a nice red, and if this function is called again, the client is
1009 // explicitly killed.
1010
1011 ce.xclient.type = ClientMessage;
1012 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
1013 ce.xclient.display = otk::OBDisplay::display;
1014 ce.xclient.window = _window;
1015 ce.xclient.format = 32;
1016 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_delete_window);
1017 ce.xclient.data.l[1] = CurrentTime;
1018 ce.xclient.data.l[2] = 0l;
1019 ce.xclient.data.l[3] = 0l;
1020 ce.xclient.data.l[4] = 0l;
1021 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
1022 }
1023
1024
1025 void OBClient::changeState()
1026 {
1027 const otk::OBProperty *property = Openbox::instance->property();
1028
1029 unsigned long state[2];
1030 state[0] = _wmstate;
1031 state[1] = None;
1032 property->set(_window, otk::OBProperty::wm_state, otk::OBProperty::wm_state,
1033 state, 2);
1034
1035 Atom netstate[10];
1036 int num = 0;
1037 if (_modal)
1038 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_modal);
1039 if (_shaded)
1040 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_shaded);
1041 if (_iconic)
1042 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_hidden);
1043 if (_skip_taskbar)
1044 netstate[num++] =
1045 property->atom(otk::OBProperty::net_wm_state_skip_taskbar);
1046 if (_skip_pager)
1047 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_skip_pager);
1048 if (_fullscreen)
1049 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_fullscreen);
1050 if (_max_vert)
1051 netstate[num++] =
1052 property->atom(otk::OBProperty::net_wm_state_maximized_vert);
1053 if (_max_horz)
1054 netstate[num++] =
1055 property->atom(otk::OBProperty::net_wm_state_maximized_horz);
1056 if (_above)
1057 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_above);
1058 if (_below)
1059 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_below);
1060 property->set(_window, otk::OBProperty::net_wm_state,
1061 otk::OBProperty::Atom_Atom, netstate, num);
1062
1063 calcLayer();
1064 }
1065
1066
1067 void OBClient::setStackLayer(int l)
1068 {
1069 if (l == 0)
1070 _above = _below = false; // normal
1071 else if (l > 0) {
1072 _above = true;
1073 _below = false; // above
1074 } else {
1075 _above = false;
1076 _below = true; // below
1077 }
1078 changeState();
1079 }
1080
1081
1082 void OBClient::shade(bool shade)
1083 {
1084 if (shade == _shaded) return; // already done
1085
1086 _wmstate = shade ? IconicState : NormalState;
1087 _shaded = shade;
1088 changeState();
1089 frame->adjustSize();
1090 }
1091
1092
1093 bool OBClient::focus()
1094 {
1095 if (!(_can_focus || _focus_notify) || _focused) return false;
1096
1097 if (_can_focus)
1098 XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
1099
1100 if (_focus_notify) {
1101 XEvent ce;
1102 const otk::OBProperty *property = Openbox::instance->property();
1103
1104 ce.xclient.type = ClientMessage;
1105 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
1106 ce.xclient.display = otk::OBDisplay::display;
1107 ce.xclient.window = _window;
1108 ce.xclient.format = 32;
1109 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_take_focus);
1110 ce.xclient.data.l[1] = Openbox::instance->lastTime();
1111 ce.xclient.data.l[2] = 0l;
1112 ce.xclient.data.l[3] = 0l;
1113 ce.xclient.data.l[4] = 0l;
1114 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
1115 }
1116
1117 return true;
1118 }
1119
1120
1121 void OBClient::unfocus()
1122 {
1123 if (!_focused) return;
1124
1125 assert(Openbox::instance->focusedClient() == this);
1126 Openbox::instance->setFocusedClient(0);
1127 }
1128
1129
1130 void OBClient::focusHandler(const XFocusChangeEvent &e)
1131 {
1132 #ifdef DEBUG
1133 // printf("FocusIn for 0x%lx\n", e.window);
1134 #endif // DEBUG
1135
1136 OtkEventHandler::focusHandler(e);
1137
1138 frame->focus();
1139 _focused = true;
1140
1141 Openbox::instance->setFocusedClient(this);
1142 }
1143
1144
1145 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
1146 {
1147 #ifdef DEBUG
1148 // printf("FocusOut for 0x%lx\n", e.window);
1149 #endif // DEBUG
1150
1151 OtkEventHandler::unfocusHandler(e);
1152
1153 frame->unfocus();
1154 _focused = false;
1155
1156 if (Openbox::instance->focusedClient() == this)
1157 Openbox::instance->setFocusedClient(0);
1158 }
1159
1160
1161 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
1162 {
1163 #ifdef DEBUG
1164 printf("ConfigureRequest for 0x%lx\n", e.window);
1165 #endif // DEBUG
1166
1167 OtkEventHandler::configureRequestHandler(e);
1168
1169 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1170
1171 if (e.value_mask & CWBorderWidth)
1172 _border_width = e.border_width;
1173
1174 // resize, then move, as specified in the EWMH section 7.7
1175 if (e.value_mask & (CWWidth | CWHeight)) {
1176 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1177 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1178
1179 Corner corner;
1180 switch (_gravity) {
1181 case NorthEastGravity:
1182 case EastGravity:
1183 corner = TopRight;
1184 break;
1185 case SouthWestGravity:
1186 case SouthGravity:
1187 corner = BottomLeft;
1188 break;
1189 case SouthEastGravity:
1190 corner = BottomRight;
1191 break;
1192 default: // NorthWest, Static, etc
1193 corner = TopLeft;
1194 }
1195
1196 // if moving AND resizing ...
1197 if (e.value_mask & (CWX | CWY)) {
1198 int x = (e.value_mask & CWX) ? e.x : _area.x();
1199 int y = (e.value_mask & CWY) ? e.y : _area.y();
1200 resize(corner, w, h, x, y);
1201 } else // if JUST resizing...
1202 resize(corner, w, h);
1203 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1204 int x = (e.value_mask & CWX) ? e.x : _area.x();
1205 int y = (e.value_mask & CWY) ? e.y : _area.y();
1206 move(x, y);
1207 }
1208
1209 if (e.value_mask & CWStackMode) {
1210 switch (e.detail) {
1211 case Below:
1212 case BottomIf:
1213 Openbox::instance->screen(_screen)->restack(false, this); // lower
1214 break;
1215
1216 case Above:
1217 case TopIf:
1218 default:
1219 Openbox::instance->screen(_screen)->restack(true, this); // raise
1220 break;
1221 }
1222 }
1223 }
1224
1225
1226 void OBClient::unmapHandler(const XUnmapEvent &e)
1227 {
1228 #ifdef DEBUG
1229 printf("UnmapNotify for 0x%lx\n", e.window);
1230 #endif // DEBUG
1231
1232 if (ignore_unmaps) {
1233 ignore_unmaps--;
1234 return;
1235 }
1236
1237 OtkEventHandler::unmapHandler(e);
1238
1239 // this deletes us etc
1240 Openbox::instance->screen(_screen)->unmanageWindow(this);
1241 }
1242
1243
1244 void OBClient::destroyHandler(const XDestroyWindowEvent &e)
1245 {
1246 #ifdef DEBUG
1247 printf("DestroyNotify for 0x%lx\n", e.window);
1248 #endif // DEBUG
1249
1250 OtkEventHandler::destroyHandler(e);
1251
1252 // this deletes us etc
1253 Openbox::instance->screen(_screen)->unmanageWindow(this);
1254 }
1255
1256
1257 void OBClient::reparentHandler(const XReparentEvent &e)
1258 {
1259 // this is when the client is first taken captive in the frame
1260 if (e.parent == frame->plate()) return;
1261
1262 #ifdef DEBUG
1263 printf("ReparentNotify for 0x%lx\n", e.window);
1264 #endif // DEBUG
1265
1266 OtkEventHandler::reparentHandler(e);
1267
1268 /*
1269 This event is quite rare and is usually handled in unmapHandler.
1270 However, if the window is unmapped when the reparent event occurs,
1271 the window manager never sees it because an unmap event is not sent
1272 to an already unmapped window.
1273 */
1274
1275 // this deletes us etc
1276 Openbox::instance->screen(_screen)->unmanageWindow(this);
1277 }
1278
1279 }
This page took 0.097439 seconds and 3 git commands to generate.