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