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