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