]> Dogcows Code - chaz/openbox/blob - src/client.cc
shading works from epist
[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 }
787 }
788
789
790 #if defined(SHAPE) || defined(DOXYGEN_IGNORE)
791 void OBClient::shapeHandler(const XShapeEvent &e)
792 {
793 otk::OtkEventHandler::shapeHandler(e);
794
795 _shaped = e.shaped;
796 }
797 #endif
798
799
800 void OBClient::resize(Corner anchor, int w, int h)
801 {
802 w -= _base_size.x();
803 h -= _base_size.y();
804
805 // for interactive resizing. have to move half an increment in each
806 // direction.
807 w += _size_inc.x() / 2;
808 h += _size_inc.y() / 2;
809
810 // is the window resizable? if it is not, then don't check its sizes, the
811 // client can do what it wants and the user can't change it anyhow
812 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
813 // smaller than min size or bigger than max size?
814 if (w < _min_size.x()) w = _min_size.x();
815 else if (w > _max_size.x()) w = _max_size.x();
816 if (h < _min_size.y()) h = _min_size.y();
817 else if (h > _max_size.y()) h = _max_size.y();
818 }
819
820 // keep to the increments
821 w /= _size_inc.x();
822 h /= _size_inc.y();
823
824 // store the logical size
825 _logical_size.setPoint(w, h);
826
827 w *= _size_inc.x();
828 h *= _size_inc.y();
829
830 w += _base_size.x();
831 h += _base_size.y();
832
833 int x = _area.x(), y = _area.y();
834 switch (anchor) {
835 case TopLeft:
836 break;
837 case TopRight:
838 x -= w - _area.width();
839 break;
840 case BottomLeft:
841 y -= h - _area.height();
842 break;
843 case BottomRight:
844 x -= w - _area.width();
845 y -= h - _area.height();
846 break;
847 }
848
849 _area.setSize(w, h);
850 XResizeWindow(otk::OBDisplay::display, _window, w, h);
851
852 // resize the frame to match the request
853 frame->adjustSize();
854 move(x, y);
855 }
856
857
858 void OBClient::move(int x, int y)
859 {
860 _area.setPos(x, y);
861 // move the frame to be in the requested position
862 frame->adjustPosition();
863 }
864
865
866 void OBClient::close()
867 {
868 XEvent ce;
869 const otk::OBProperty *property = Openbox::instance->property();
870
871 if (!(_functions & Func_Close)) return;
872
873 // XXX: itd be cool to do timeouts and shit here for killing the client's
874 // process off
875
876 ce.xclient.type = ClientMessage;
877 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
878 ce.xclient.display = otk::OBDisplay::display;
879 ce.xclient.window = _window;
880 ce.xclient.format = 32;
881 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_delete_window);
882 ce.xclient.data.l[1] = CurrentTime;
883 ce.xclient.data.l[2] = 0l;
884 ce.xclient.data.l[3] = 0l;
885 ce.xclient.data.l[4] = 0l;
886 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
887 }
888
889
890 void OBClient::changeState()
891 {
892 const otk::OBProperty *property = Openbox::instance->property();
893
894 unsigned long state[2];
895 state[0] = _wmstate;
896 state[1] = None;
897 property->set(_window, otk::OBProperty::wm_state, otk::OBProperty::wm_state,
898 state, 2);
899
900 Atom netstate[10];
901 int num = 0;
902 if (_modal)
903 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_modal);
904 if (_shaded)
905 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_shaded);
906 if (_iconic)
907 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_hidden);
908 if (_skip_taskbar)
909 netstate[num++] =
910 property->atom(otk::OBProperty::net_wm_state_skip_taskbar);
911 if (_skip_pager)
912 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_skip_pager);
913 if (_fullscreen)
914 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_fullscreen);
915 if (_max_vert)
916 netstate[num++] =
917 property->atom(otk::OBProperty::net_wm_state_maximized_vert);
918 if (_max_horz)
919 netstate[num++] =
920 property->atom(otk::OBProperty::net_wm_state_maximized_horz);
921 if (_above)
922 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_above);
923 if (_below)
924 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_below);
925 property->set(_window, otk::OBProperty::net_wm_state,
926 otk::OBProperty::Atom_Atom, netstate, num);
927
928 }
929
930 void OBClient::shade(bool shade)
931 {
932 if (shade == _shaded) return; // already done
933
934 _wmstate = shade ? IconicState : NormalState;
935 _shaded = shade;
936 changeState();
937 frame->adjustSize();
938 }
939
940
941 bool OBClient::focus()
942 {
943 if (!(_can_focus || _focus_notify) || _focused) return false;
944
945 if (_can_focus)
946 XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
947
948 if (_focus_notify) {
949 XEvent ce;
950 const otk::OBProperty *property = Openbox::instance->property();
951
952 ce.xclient.type = ClientMessage;
953 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
954 ce.xclient.display = otk::OBDisplay::display;
955 ce.xclient.window = _window;
956 ce.xclient.format = 32;
957 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_take_focus);
958 ce.xclient.data.l[1] = Openbox::instance->lastTime();
959 ce.xclient.data.l[2] = 0l;
960 ce.xclient.data.l[3] = 0l;
961 ce.xclient.data.l[4] = 0l;
962 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
963 }
964
965 return true;
966 }
967
968
969 void OBClient::unfocus()
970 {
971 if (!_focused) return;
972
973 assert(Openbox::instance->focusedClient() == this);
974 Openbox::instance->setFocusedClient(0);
975 }
976
977
978 void OBClient::focusHandler(const XFocusChangeEvent &e)
979 {
980 #ifdef DEBUG
981 printf("FocusIn for 0x%lx\n", e.window);
982 #endif // DEBUG
983
984 OtkEventHandler::focusHandler(e);
985
986 frame->focus();
987 _focused = true;
988
989 Openbox::instance->setFocusedClient(this);
990 }
991
992
993 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
994 {
995 #ifdef DEBUG
996 printf("FocusOut for 0x%lx\n", e.window);
997 #endif // DEBUG
998
999 OtkEventHandler::unfocusHandler(e);
1000
1001 frame->unfocus();
1002 _focused = false;
1003
1004 if (Openbox::instance->focusedClient() == this) {
1005 printf("UNFOCUSED!\n");
1006 Openbox::instance->setFocusedClient(this);
1007 }
1008 }
1009
1010
1011 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
1012 {
1013 #ifdef DEBUG
1014 printf("ConfigureRequest for 0x%lx\n", e.window);
1015 #endif // DEBUG
1016
1017 OtkEventHandler::configureRequestHandler(e);
1018
1019 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1020
1021 if (e.value_mask & CWBorderWidth)
1022 _border_width = e.border_width;
1023
1024 // resize, then move, as specified in the EWMH section 7.7
1025 if (e.value_mask & (CWWidth | CWHeight)) {
1026 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1027 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1028
1029 Corner corner;
1030 switch (_gravity) {
1031 case NorthEastGravity:
1032 case EastGravity:
1033 corner = TopRight;
1034 break;
1035 case SouthWestGravity:
1036 case SouthGravity:
1037 corner = BottomLeft;
1038 break;
1039 case SouthEastGravity:
1040 corner = BottomRight;
1041 break;
1042 default: // NorthWest, Static, etc
1043 corner = TopLeft;
1044 }
1045
1046 resize(corner, w, h);
1047 }
1048
1049 if (e.value_mask & (CWX | CWY)) {
1050 int x = (e.value_mask & CWX) ? e.x : _area.x();
1051 int y = (e.value_mask & CWY) ? e.y : _area.y();
1052 move(x, y);
1053 }
1054
1055 if (e.value_mask & CWStackMode) {
1056 switch (e.detail) {
1057 case Below:
1058 case BottomIf:
1059 // XXX: lower the window
1060 break;
1061
1062 case Above:
1063 case TopIf:
1064 default:
1065 // XXX: raise the window
1066 break;
1067 }
1068 }
1069 }
1070
1071
1072 void OBClient::unmapHandler(const XUnmapEvent &e)
1073 {
1074 #ifdef DEBUG
1075 printf("UnmapNotify for 0x%lx\n", e.window);
1076 #endif // DEBUG
1077
1078 if (ignore_unmaps) {
1079 ignore_unmaps--;
1080 return;
1081 }
1082
1083 OtkEventHandler::unmapHandler(e);
1084
1085 // this deletes us etc
1086 Openbox::instance->screen(_screen)->unmanageWindow(this);
1087 }
1088
1089
1090 void OBClient::destroyHandler(const XDestroyWindowEvent &e)
1091 {
1092 #ifdef DEBUG
1093 printf("DestroyNotify for 0x%lx\n", e.window);
1094 #endif // DEBUG
1095
1096 OtkEventHandler::destroyHandler(e);
1097
1098 // this deletes us etc
1099 Openbox::instance->screen(_screen)->unmanageWindow(this);
1100 }
1101
1102
1103 void OBClient::reparentHandler(const XReparentEvent &e)
1104 {
1105 // this is when the client is first taken captive in the frame
1106 if (e.parent == frame->plate()) return;
1107
1108 #ifdef DEBUG
1109 printf("ReparentNotify for 0x%lx\n", e.window);
1110 #endif // DEBUG
1111
1112 OtkEventHandler::reparentHandler(e);
1113
1114 /*
1115 This event is quite rare and is usually handled in unmapHandler.
1116 However, if the window is unmapped when the reparent event occurs,
1117 the window manager never sees it because an unmap event is not sent
1118 to an already unmapped window.
1119 */
1120
1121 // this deletes us etc
1122 Openbox::instance->screen(_screen)->unmanageWindow(this);
1123 }
1124
1125 }
This page took 0.085894 seconds and 4 git commands to generate.