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