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