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