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