]> Dogcows Code - chaz/openbox/blob - src/client.cc
874b1f3dddf36b372c2cb8aca33d7d460d1618db
[chaz/openbox] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "client.hh"
8 #include "frame.hh"
9 #include "screen.hh"
10 #include "openbox.hh"
11 #include "otk/display.hh"
12 #include "otk/property.hh"
13
14 extern "C" {
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17
18 #include <assert.h>
19
20 #include "gettext.h"
21 #define _(str) gettext(str)
22 }
23
24 namespace ob {
25
26 OBClient::OBClient(int screen, Window window)
27 : otk::OtkEventHandler(),
28 OBWidget(OBWidget::Type_Client),
29 frame(0), _screen(screen), _window(window)
30 {
31 assert(screen >= 0);
32 assert(window);
33
34 ignore_unmaps = 0;
35
36 // update EVERYTHING the first time!!
37
38 // the state is kinda assumed to be normal. is this right? XXX
39 _wmstate = NormalState; _iconic = false;
40 // no default decors or functions, each has to be enabled
41 _decorations = _functions = 0;
42 // start unfocused
43 _focused = false;
44
45 getArea();
46 getDesktop();
47 getType();
48
49 // set the decorations and functions
50 switch (_type) {
51 case Type_Normal:
52 // normal windows retain all of the possible decorations and
53 // functionality
54 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
55 Decor_Iconify | Decor_Maximize;
56 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
57
58 case Type_Dialog:
59 // dialogs cannot be maximized
60 _decorations &= ~Decor_Maximize;
61 _functions &= ~Func_Maximize;
62 break;
63
64 case Type_Menu:
65 case Type_Toolbar:
66 case Type_Utility:
67 // these windows get less functionality
68 _decorations &= ~(Decor_Iconify | Decor_Handle);
69 _functions &= ~(Func_Iconify | Func_Resize);
70 break;
71
72 case Type_Desktop:
73 case Type_Dock:
74 case Type_Splash:
75 // none of these windows are manipulated by the window manager
76 _decorations = 0;
77 _functions = 0;
78 break;
79 }
80
81 getMwmHints(); // this fucks (in good ways) with the decors and functions
82 getState();
83 getShaped();
84
85 updateProtocols();
86 updateNormalHints();
87 updateWMHints();
88 // XXX: updateTransientFor();
89 updateTitle();
90 updateIconTitle();
91 updateClass();
92
93 calcLayer();
94 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 // XXX: update the decor?
343 } else if (proto[i] == property->atom(otk::OBProperty::wm_take_focus))
344 // if this protocol is requested, then the window will be notified
345 // by the window manager whenever it receives focus
346 _focus_notify = true;
347 }
348 XFree(proto);
349 }
350 }
351
352
353 void OBClient::updateNormalHints()
354 {
355 XSizeHints size;
356 long ret;
357 int oldgravity = _gravity;
358
359 // defaults
360 _gravity = NorthWestGravity;
361 _size_inc.setPoint(1, 1);
362 _base_size.setPoint(0, 0);
363 _min_size.setPoint(0, 0);
364 _max_size.setPoint(INT_MAX, INT_MAX);
365
366 // XXX: might want to cancel any interactive resizing of the window at this
367 // point..
368
369 // get the hints from the window
370 if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
371 _positioned = (size.flags & (PPosition|USPosition));
372
373 if (size.flags & PWinGravity)
374 _gravity = size.win_gravity;
375
376 if (size.flags & PMinSize)
377 _min_size.setPoint(size.min_width, size.min_height);
378
379 if (size.flags & PMaxSize)
380 _max_size.setPoint(size.max_width, size.max_height);
381
382 if (size.flags & PBaseSize)
383 _base_size.setPoint(size.base_width, size.base_height);
384
385 if (size.flags & PResizeInc)
386 _size_inc.setPoint(size.width_inc, size.height_inc);
387 }
388
389 // if the client has a frame, i.e. has already been mapped and is
390 // changing its gravity
391 if (frame && _gravity != oldgravity) {
392 // move our idea of the client's position based on its new gravity
393 int x, y;
394 frame->frameGravity(x, y);
395 _area.setPos(x, y);
396 }
397 }
398
399
400 void OBClient::updateWMHints()
401 {
402 XWMHints *hints;
403
404 // assume a window takes input if it doesnt specify
405 _can_focus = true;
406 _urgent = false;
407
408 if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
409 if (hints->flags & InputHint)
410 _can_focus = hints->input;
411
412 if (hints->flags & XUrgencyHint)
413 _urgent = true;
414
415 if (hints->flags & WindowGroupHint) {
416 if (hints->window_group != _group) {
417 // XXX: remove from the old group if there was one
418 _group = hints->window_group;
419 // XXX: do stuff with the group
420 }
421 } else // no group!
422 _group = None;
423
424 XFree(hints);
425 }
426 }
427
428
429 void OBClient::updateTitle()
430 {
431 const otk::OBProperty *property = Openbox::instance->property();
432
433 _title = "";
434
435 // try netwm
436 if (! property->get(_window, otk::OBProperty::net_wm_name,
437 otk::OBProperty::utf8, &_title)) {
438 // try old x stuff
439 property->get(_window, otk::OBProperty::wm_name,
440 otk::OBProperty::ascii, &_title);
441 }
442
443 if (_title.empty())
444 _title = _("Unnamed Window");
445
446 if (frame)
447 frame->setTitle(_title);
448 }
449
450
451 void OBClient::updateIconTitle()
452 {
453 const otk::OBProperty *property = Openbox::instance->property();
454
455 _icon_title = "";
456
457 // try netwm
458 if (! property->get(_window, otk::OBProperty::net_wm_icon_name,
459 otk::OBProperty::utf8, &_icon_title)) {
460 // try old x stuff
461 property->get(_window, otk::OBProperty::wm_icon_name,
462 otk::OBProperty::ascii, &_icon_title);
463 }
464
465 if (_title.empty())
466 _icon_title = _("Unnamed Window");
467 }
468
469
470 void OBClient::updateClass()
471 {
472 const otk::OBProperty *property = Openbox::instance->property();
473
474 // set the defaults
475 _app_name = _app_class = "";
476
477 otk::OBProperty::StringVect v;
478 unsigned long num = 2;
479
480 if (! property->get(_window, otk::OBProperty::wm_class,
481 otk::OBProperty::ascii, &num, &v))
482 return;
483
484 if (num > 0) _app_name = v[0];
485 if (num > 1) _app_class = v[1];
486 }
487
488
489 void OBClient::propertyHandler(const XPropertyEvent &e)
490 {
491 otk::OtkEventHandler::propertyHandler(e);
492
493 const otk::OBProperty *property = Openbox::instance->property();
494
495 // compress changes to a single property into a single change
496 XEvent ce;
497 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
498 // XXX: it would be nice to compress ALL changes to a property, not just
499 // changes in a row without other props between.
500 if (ce.xproperty.atom != e.atom) {
501 XPutBackEvent(otk::OBDisplay::display, &ce);
502 break;
503 }
504 }
505
506 if (e.atom == XA_WM_NORMAL_HINTS)
507 updateNormalHints();
508 else if (e.atom == XA_WM_HINTS)
509 updateWMHints();
510 else if (e.atom == property->atom(otk::OBProperty::net_wm_name) ||
511 e.atom == property->atom(otk::OBProperty::wm_name))
512 updateTitle();
513 else if (e.atom == property->atom(otk::OBProperty::net_wm_icon_name) ||
514 e.atom == property->atom(otk::OBProperty::wm_icon_name))
515 updateIconTitle();
516 else if (e.atom == property->atom(otk::OBProperty::wm_class))
517 updateClass();
518 else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
519 updateProtocols();
520 // XXX: transient for hint
521 // XXX: strut hint
522 }
523
524
525 void OBClient::setWMState(long state)
526 {
527 if (state == _wmstate) return; // no change
528
529 switch (state) {
530 case IconicState:
531 // XXX: cause it to iconify
532 break;
533 case NormalState:
534 // XXX: cause it to uniconify
535 break;
536 }
537 _wmstate = state;
538 }
539
540
541 void OBClient::setDesktop(long target)
542 {
543 printf("Setting desktop %ld\n", target);
544 assert(target >= 0 || target == (signed)0xffffffff);
545 //assert(target == 0xffffffff || target < MAX);
546
547 // XXX: move the window to the new desktop (and set root property)
548 _desktop = target;
549 }
550
551
552 void OBClient::setState(StateAction action, long data1, long data2)
553 {
554 const otk::OBProperty *property = Openbox::instance->property();
555 bool restack = false, shadestate = _shaded;
556
557 if (!(action == State_Add || action == State_Remove ||
558 action == State_Toggle))
559 return; // an invalid action was passed to the client message, ignore it
560
561 for (int i = 0; i < 2; ++i) {
562 Atom state = i == 0 ? data1 : data2;
563
564 if (! state) continue;
565
566 // if toggling, then pick whether we're adding or removing
567 if (action == State_Toggle) {
568 if (state == property->atom(otk::OBProperty::net_wm_state_modal))
569 action = _modal ? State_Remove : State_Add;
570 else if (state ==
571 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
572 action = _max_vert ? State_Remove : State_Add;
573 else if (state ==
574 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
575 action = _max_horz ? State_Remove : State_Add;
576 else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
577 action = _shaded ? State_Remove : State_Add;
578 else if (state ==
579 property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
580 action = _skip_taskbar ? State_Remove : State_Add;
581 else if (state ==
582 property->atom(otk::OBProperty::net_wm_state_skip_pager))
583 action = _skip_pager ? State_Remove : State_Add;
584 else if (state ==
585 property->atom(otk::OBProperty::net_wm_state_fullscreen))
586 action = _fullscreen ? State_Remove : State_Add;
587 else if (state == property->atom(otk::OBProperty::net_wm_state_above))
588 action = _above ? State_Remove : State_Add;
589 else if (state == property->atom(otk::OBProperty::net_wm_state_below))
590 action = _below ? State_Remove : State_Add;
591 }
592
593 if (action == State_Add) {
594 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
595 if (_modal) continue;
596 _modal = true;
597 // XXX: give it focus if another window has focus that shouldnt now
598 } else if (state ==
599 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
600 if (_max_vert) continue;
601 _max_vert = true;
602 // XXX: resize the window etc
603 } else if (state ==
604 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
605 if (_max_horz) continue;
606 _max_horz = true;
607 // XXX: resize the window etc
608 } else if (state ==
609 property->atom(otk::OBProperty::net_wm_state_shaded)) {
610 if (_shaded) continue;
611 // shade when we're all thru here
612 shadestate = true;
613 } else if (state ==
614 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
615 _skip_taskbar = true;
616 } else if (state ==
617 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
618 _skip_pager = true;
619 } else if (state ==
620 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
621 if (_fullscreen) continue;
622 _fullscreen = true;
623 restack = false;
624 } else if (state ==
625 property->atom(otk::OBProperty::net_wm_state_above)) {
626 if (_above) continue;
627 _above = true;
628 restack = true;
629 } else if (state ==
630 property->atom(otk::OBProperty::net_wm_state_below)) {
631 if (_below) continue;
632 _below = true;
633 restack = true;
634 }
635
636 } else { // action == State_Remove
637 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
638 if (!_modal) continue;
639 _modal = false;
640 } else if (state ==
641 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
642 if (!_max_vert) continue;
643 _max_vert = false;
644 // XXX: resize the window etc
645 } else if (state ==
646 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
647 if (!_max_horz) continue;
648 _max_horz = false;
649 // XXX: resize the window etc
650 } else if (state ==
651 property->atom(otk::OBProperty::net_wm_state_shaded)) {
652 if (!_shaded) continue;
653 // unshade when we're all thru here
654 shadestate = false;
655 } else if (state ==
656 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
657 _skip_taskbar = false;
658 } else if (state ==
659 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
660 _skip_pager = false;
661 } else if (state ==
662 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
663 if (!_fullscreen) continue;
664 _fullscreen = false;
665 restack = true;
666 } else if (state ==
667 property->atom(otk::OBProperty::net_wm_state_above)) {
668 if (!_above) continue;
669 _above = false;
670 restack = true;
671 } else if (state ==
672 property->atom(otk::OBProperty::net_wm_state_below)) {
673 if (!_below) continue;
674 _below = false;
675 restack = true;
676 }
677 }
678 }
679 if (shadestate != _shaded)
680 shade(shadestate);
681 if (restack) {
682 calcLayer();
683 Openbox::instance->screen(_screen)->restack(true, this); // raise
684 }
685 }
686
687
688 void OBClient::toggleClientBorder(bool addborder)
689 {
690 // adjust our idea of where the client is, based on its border. When the
691 // border is removed, the client should now be considered to be in a
692 // different position.
693 // when re-adding the border to the client, the same operation needs to be
694 // reversed.
695 int x = _area.x(), y = _area.y();
696 switch(_gravity) {
697 case NorthWestGravity:
698 case WestGravity:
699 case SouthWestGravity:
700 break;
701 case NorthEastGravity:
702 case EastGravity:
703 case SouthEastGravity:
704 if (addborder) x -= _border_width * 2;
705 else x += _border_width * 2;
706 break;
707 }
708 switch(_gravity) {
709 case NorthWestGravity:
710 case NorthGravity:
711 case NorthEastGravity:
712 break;
713 case SouthWestGravity:
714 case SouthGravity:
715 case SouthEastGravity:
716 if (addborder) y -= _border_width * 2;
717 else y += _border_width * 2;
718 break;
719 default:
720 // no change for StaticGravity etc.
721 break;
722 }
723 _area.setPos(x, y);
724
725 if (addborder) {
726 XSetWindowBorderWidth(otk::OBDisplay::display, _window, _border_width);
727
728 // move the client so it is back it the right spot _with_ its border!
729 XMoveWindow(otk::OBDisplay::display, _window, x, y);
730 } else
731 XSetWindowBorderWidth(otk::OBDisplay::display, _window, 0);
732 }
733
734
735 void OBClient::clientMessageHandler(const XClientMessageEvent &e)
736 {
737 otk::OtkEventHandler::clientMessageHandler(e);
738
739 if (e.format != 32) return;
740
741 const otk::OBProperty *property = Openbox::instance->property();
742
743 if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) {
744 // compress changes into a single change
745 bool compress = false;
746 XEvent ce;
747 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
748 // XXX: it would be nice to compress ALL messages of a type, not just
749 // messages in a row without other message types between.
750 if (ce.xclient.message_type != e.message_type) {
751 XPutBackEvent(otk::OBDisplay::display, &ce);
752 break;
753 }
754 compress = true;
755 }
756 if (compress)
757 setWMState(ce.xclient.data.l[0]); // use the found event
758 else
759 setWMState(e.data.l[0]); // use the original event
760 } else if (e.message_type ==
761 property->atom(otk::OBProperty::net_wm_desktop)) {
762 // compress changes into a single change
763 bool compress = false;
764 XEvent ce;
765 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
766 // XXX: it would be nice to compress ALL messages of a type, not just
767 // messages in a row without other message types between.
768 if (ce.xclient.message_type != e.message_type) {
769 XPutBackEvent(otk::OBDisplay::display, &ce);
770 break;
771 }
772 compress = true;
773 }
774 if (compress)
775 setDesktop(e.data.l[0]); // use the found event
776 else
777 setDesktop(e.data.l[0]); // use the original event
778 } else if (e.message_type == property->atom(otk::OBProperty::net_wm_state)) {
779 // can't compress these
780 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
781 } else if (e.message_type ==
782 property->atom(otk::OBProperty::net_close_window)) {
783 close();
784 } else if (e.message_type ==
785 property->atom(otk::OBProperty::net_active_window)) {
786 focus();
787 Openbox::instance->screen(_screen)->restack(true, this); // raise
788 }
789 }
790
791
792 #if defined(SHAPE) || defined(DOXYGEN_IGNORE)
793 void OBClient::shapeHandler(const XShapeEvent &e)
794 {
795 otk::OtkEventHandler::shapeHandler(e);
796
797 _shaped = e.shaped;
798 }
799 #endif
800
801
802 void OBClient::resize(Corner anchor, int w, int h)
803 {
804 w -= _base_size.x();
805 h -= _base_size.y();
806
807 // for interactive resizing. have to move half an increment in each
808 // direction.
809 w += _size_inc.x() / 2;
810 h += _size_inc.y() / 2;
811
812 // is the window resizable? if it is not, then don't check its sizes, the
813 // client can do what it wants and the user can't change it anyhow
814 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
815 // smaller than min size or bigger than max size?
816 if (w < _min_size.x()) w = _min_size.x();
817 else if (w > _max_size.x()) w = _max_size.x();
818 if (h < _min_size.y()) h = _min_size.y();
819 else if (h > _max_size.y()) h = _max_size.y();
820 }
821
822 // keep to the increments
823 w /= _size_inc.x();
824 h /= _size_inc.y();
825
826 // store the logical size
827 _logical_size.setPoint(w, h);
828
829 w *= _size_inc.x();
830 h *= _size_inc.y();
831
832 w += _base_size.x();
833 h += _base_size.y();
834
835 int x = _area.x(), y = _area.y();
836 switch (anchor) {
837 case TopLeft:
838 break;
839 case TopRight:
840 x -= w - _area.width();
841 break;
842 case BottomLeft:
843 y -= h - _area.height();
844 break;
845 case BottomRight:
846 x -= w - _area.width();
847 y -= h - _area.height();
848 break;
849 }
850
851 _area.setSize(w, h);
852 XResizeWindow(otk::OBDisplay::display, _window, w, h);
853
854 // resize the frame to match the request
855 frame->adjustSize();
856 move(x, y);
857 }
858
859
860 void OBClient::move(int x, int y)
861 {
862 _area.setPos(x, y);
863 // move the frame to be in the requested position
864 frame->adjustPosition();
865 }
866
867
868 void OBClient::close()
869 {
870 XEvent ce;
871 const otk::OBProperty *property = Openbox::instance->property();
872
873 if (!(_functions & Func_Close)) return;
874
875 // XXX: itd be cool to do timeouts and shit here for killing the client's
876 // process off
877
878 ce.xclient.type = ClientMessage;
879 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
880 ce.xclient.display = otk::OBDisplay::display;
881 ce.xclient.window = _window;
882 ce.xclient.format = 32;
883 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_delete_window);
884 ce.xclient.data.l[1] = CurrentTime;
885 ce.xclient.data.l[2] = 0l;
886 ce.xclient.data.l[3] = 0l;
887 ce.xclient.data.l[4] = 0l;
888 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
889 }
890
891
892 void OBClient::changeState()
893 {
894 const otk::OBProperty *property = Openbox::instance->property();
895
896 unsigned long state[2];
897 state[0] = _wmstate;
898 state[1] = None;
899 property->set(_window, otk::OBProperty::wm_state, otk::OBProperty::wm_state,
900 state, 2);
901
902 Atom netstate[10];
903 int num = 0;
904 if (_modal)
905 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_modal);
906 if (_shaded)
907 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_shaded);
908 if (_iconic)
909 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_hidden);
910 if (_skip_taskbar)
911 netstate[num++] =
912 property->atom(otk::OBProperty::net_wm_state_skip_taskbar);
913 if (_skip_pager)
914 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_skip_pager);
915 if (_fullscreen)
916 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_fullscreen);
917 if (_max_vert)
918 netstate[num++] =
919 property->atom(otk::OBProperty::net_wm_state_maximized_vert);
920 if (_max_horz)
921 netstate[num++] =
922 property->atom(otk::OBProperty::net_wm_state_maximized_horz);
923 if (_above)
924 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_above);
925 if (_below)
926 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_below);
927 property->set(_window, otk::OBProperty::net_wm_state,
928 otk::OBProperty::Atom_Atom, netstate, num);
929
930 }
931
932 void OBClient::shade(bool shade)
933 {
934 if (shade == _shaded) return; // already done
935
936 _wmstate = shade ? IconicState : NormalState;
937 _shaded = shade;
938 changeState();
939 frame->adjustSize();
940 }
941
942
943 bool OBClient::focus()
944 {
945 if (!(_can_focus || _focus_notify) || _focused) return false;
946
947 if (_can_focus)
948 XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
949
950 if (_focus_notify) {
951 XEvent ce;
952 const otk::OBProperty *property = Openbox::instance->property();
953
954 ce.xclient.type = ClientMessage;
955 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
956 ce.xclient.display = otk::OBDisplay::display;
957 ce.xclient.window = _window;
958 ce.xclient.format = 32;
959 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_take_focus);
960 ce.xclient.data.l[1] = Openbox::instance->lastTime();
961 ce.xclient.data.l[2] = 0l;
962 ce.xclient.data.l[3] = 0l;
963 ce.xclient.data.l[4] = 0l;
964 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
965 }
966
967 return true;
968 }
969
970
971 void OBClient::unfocus()
972 {
973 if (!_focused) return;
974
975 assert(Openbox::instance->focusedClient() == this);
976 Openbox::instance->setFocusedClient(0);
977 }
978
979
980 void OBClient::focusHandler(const XFocusChangeEvent &e)
981 {
982 #ifdef DEBUG
983 printf("FocusIn for 0x%lx\n", e.window);
984 #endif // DEBUG
985
986 OtkEventHandler::focusHandler(e);
987
988 frame->focus();
989 _focused = true;
990
991 Openbox::instance->setFocusedClient(this);
992 }
993
994
995 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
996 {
997 #ifdef DEBUG
998 printf("FocusOut for 0x%lx\n", e.window);
999 #endif // DEBUG
1000
1001 OtkEventHandler::unfocusHandler(e);
1002
1003 frame->unfocus();
1004 _focused = false;
1005
1006 if (Openbox::instance->focusedClient() == this) {
1007 printf("UNFOCUSED!\n");
1008 Openbox::instance->setFocusedClient(this);
1009 }
1010 }
1011
1012
1013 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
1014 {
1015 #ifdef DEBUG
1016 printf("ConfigureRequest for 0x%lx\n", e.window);
1017 #endif // DEBUG
1018
1019 OtkEventHandler::configureRequestHandler(e);
1020
1021 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1022
1023 if (e.value_mask & CWBorderWidth)
1024 _border_width = e.border_width;
1025
1026 // resize, then move, as specified in the EWMH section 7.7
1027 if (e.value_mask & (CWWidth | CWHeight)) {
1028 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1029 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1030
1031 Corner corner;
1032 switch (_gravity) {
1033 case NorthEastGravity:
1034 case EastGravity:
1035 corner = TopRight;
1036 break;
1037 case SouthWestGravity:
1038 case SouthGravity:
1039 corner = BottomLeft;
1040 break;
1041 case SouthEastGravity:
1042 corner = BottomRight;
1043 break;
1044 default: // NorthWest, Static, etc
1045 corner = TopLeft;
1046 }
1047
1048 resize(corner, w, h);
1049 }
1050
1051 if (e.value_mask & (CWX | CWY)) {
1052 int x = (e.value_mask & CWX) ? e.x : _area.x();
1053 int y = (e.value_mask & CWY) ? e.y : _area.y();
1054 move(x, y);
1055 }
1056
1057 if (e.value_mask & CWStackMode) {
1058 switch (e.detail) {
1059 case Below:
1060 case BottomIf:
1061 // XXX: lower the window
1062 break;
1063
1064 case Above:
1065 case TopIf:
1066 default:
1067 // XXX: raise the window
1068 break;
1069 }
1070 }
1071 }
1072
1073
1074 void OBClient::unmapHandler(const XUnmapEvent &e)
1075 {
1076 #ifdef DEBUG
1077 printf("UnmapNotify for 0x%lx\n", e.window);
1078 #endif // DEBUG
1079
1080 if (ignore_unmaps) {
1081 ignore_unmaps--;
1082 return;
1083 }
1084
1085 OtkEventHandler::unmapHandler(e);
1086
1087 // this deletes us etc
1088 Openbox::instance->screen(_screen)->unmanageWindow(this);
1089 }
1090
1091
1092 void OBClient::destroyHandler(const XDestroyWindowEvent &e)
1093 {
1094 #ifdef DEBUG
1095 printf("DestroyNotify for 0x%lx\n", e.window);
1096 #endif // DEBUG
1097
1098 OtkEventHandler::destroyHandler(e);
1099
1100 // this deletes us etc
1101 Openbox::instance->screen(_screen)->unmanageWindow(this);
1102 }
1103
1104
1105 void OBClient::reparentHandler(const XReparentEvent &e)
1106 {
1107 // this is when the client is first taken captive in the frame
1108 if (e.parent == frame->plate()) return;
1109
1110 #ifdef DEBUG
1111 printf("ReparentNotify for 0x%lx\n", e.window);
1112 #endif // DEBUG
1113
1114 OtkEventHandler::reparentHandler(e);
1115
1116 /*
1117 This event is quite rare and is usually handled in unmapHandler.
1118 However, if the window is unmapped when the reparent event occurs,
1119 the window manager never sees it because an unmap event is not sent
1120 to an already unmapped window.
1121 */
1122
1123 // this deletes us etc
1124 Openbox::instance->screen(_screen)->unmanageWindow(this);
1125 }
1126
1127 }
This page took 0.085637 seconds and 4 git commands to generate.