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