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