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