]> Dogcows Code - chaz/openbox/blob - src/client.cc
better focus passing around for now
[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
970 void OBClient::setStackLayer(int l)
971 {
972 if (l == 0)
973 _above = _below = false; // normal
974 else if (l > 0) {
975 _above = true;
976 _below = false; // above
977 } else {
978 _above = false;
979 _below = true; // below
980 }
981 changeState();
982 }
983
984
985 void OBClient::shade(bool shade)
986 {
987 if (shade == _shaded) return; // already done
988
989 _wmstate = shade ? IconicState : NormalState;
990 _shaded = shade;
991 changeState();
992 frame->adjustSize();
993 }
994
995
996 bool OBClient::focus()
997 {
998 if (!(_can_focus || _focus_notify) || _focused) return false;
999
1000 if (_can_focus)
1001 XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
1002
1003 if (_focus_notify) {
1004 XEvent ce;
1005 const otk::OBProperty *property = Openbox::instance->property();
1006
1007 ce.xclient.type = ClientMessage;
1008 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
1009 ce.xclient.display = otk::OBDisplay::display;
1010 ce.xclient.window = _window;
1011 ce.xclient.format = 32;
1012 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_take_focus);
1013 ce.xclient.data.l[1] = Openbox::instance->lastTime();
1014 ce.xclient.data.l[2] = 0l;
1015 ce.xclient.data.l[3] = 0l;
1016 ce.xclient.data.l[4] = 0l;
1017 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
1018 }
1019
1020 return true;
1021 }
1022
1023
1024 void OBClient::unfocus()
1025 {
1026 if (!_focused) return;
1027
1028 assert(Openbox::instance->focusedClient() == this);
1029 Openbox::instance->setFocusedClient(0);
1030 }
1031
1032
1033 void OBClient::focusHandler(const XFocusChangeEvent &e)
1034 {
1035 #ifdef DEBUG
1036 printf("FocusIn for 0x%lx\n", e.window);
1037 #endif // DEBUG
1038
1039 OtkEventHandler::focusHandler(e);
1040
1041 frame->focus();
1042 _focused = true;
1043
1044 Openbox::instance->setFocusedClient(this);
1045 }
1046
1047
1048 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
1049 {
1050 #ifdef DEBUG
1051 printf("FocusOut for 0x%lx\n", e.window);
1052 #endif // DEBUG
1053
1054 OtkEventHandler::unfocusHandler(e);
1055
1056 frame->unfocus();
1057 _focused = false;
1058
1059 if (Openbox::instance->focusedClient() == this) {
1060 printf("UNFOCUSED!\n");
1061 Openbox::instance->setFocusedClient(this);
1062 }
1063 }
1064
1065
1066 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
1067 {
1068 #ifdef DEBUG
1069 printf("ConfigureRequest for 0x%lx\n", e.window);
1070 #endif // DEBUG
1071
1072 OtkEventHandler::configureRequestHandler(e);
1073
1074 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1075
1076 if (e.value_mask & CWBorderWidth)
1077 _border_width = e.border_width;
1078
1079 // resize, then move, as specified in the EWMH section 7.7
1080 if (e.value_mask & (CWWidth | CWHeight)) {
1081 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1082 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1083
1084 Corner corner;
1085 switch (_gravity) {
1086 case NorthEastGravity:
1087 case EastGravity:
1088 corner = TopRight;
1089 break;
1090 case SouthWestGravity:
1091 case SouthGravity:
1092 corner = BottomLeft;
1093 break;
1094 case SouthEastGravity:
1095 corner = BottomRight;
1096 break;
1097 default: // NorthWest, Static, etc
1098 corner = TopLeft;
1099 }
1100
1101 // if moving AND resizing ...
1102 if (e.value_mask & (CWX | CWY)) {
1103 int x = (e.value_mask & CWX) ? e.x : _area.x();
1104 int y = (e.value_mask & CWY) ? e.y : _area.y();
1105 resize(corner, w, h, x, y);
1106 } else // if JUST resizing...
1107 resize(corner, w, h);
1108 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1109 int x = (e.value_mask & CWX) ? e.x : _area.x();
1110 int y = (e.value_mask & CWY) ? e.y : _area.y();
1111 move(x, y);
1112 }
1113
1114 if (e.value_mask & CWStackMode) {
1115 switch (e.detail) {
1116 case Below:
1117 case BottomIf:
1118 // XXX: lower the window
1119 break;
1120
1121 case Above:
1122 case TopIf:
1123 default:
1124 // XXX: raise the window
1125 break;
1126 }
1127 }
1128 }
1129
1130
1131 void OBClient::unmapHandler(const XUnmapEvent &e)
1132 {
1133 #ifdef DEBUG
1134 printf("UnmapNotify for 0x%lx\n", e.window);
1135 #endif // DEBUG
1136
1137 if (ignore_unmaps) {
1138 ignore_unmaps--;
1139 return;
1140 }
1141
1142 OtkEventHandler::unmapHandler(e);
1143
1144 // this deletes us etc
1145 Openbox::instance->screen(_screen)->unmanageWindow(this);
1146 }
1147
1148
1149 void OBClient::destroyHandler(const XDestroyWindowEvent &e)
1150 {
1151 #ifdef DEBUG
1152 printf("DestroyNotify for 0x%lx\n", e.window);
1153 #endif // DEBUG
1154
1155 OtkEventHandler::destroyHandler(e);
1156
1157 // this deletes us etc
1158 Openbox::instance->screen(_screen)->unmanageWindow(this);
1159 }
1160
1161
1162 void OBClient::reparentHandler(const XReparentEvent &e)
1163 {
1164 // this is when the client is first taken captive in the frame
1165 if (e.parent == frame->plate()) return;
1166
1167 #ifdef DEBUG
1168 printf("ReparentNotify for 0x%lx\n", e.window);
1169 #endif // DEBUG
1170
1171 OtkEventHandler::reparentHandler(e);
1172
1173 /*
1174 This event is quite rare and is usually handled in unmapHandler.
1175 However, if the window is unmapped when the reparent event occurs,
1176 the window manager never sees it because an unmap event is not sent
1177 to an already unmapped window.
1178 */
1179
1180 // this deletes us etc
1181 Openbox::instance->screen(_screen)->unmanageWindow(this);
1182 }
1183
1184 }
This page took 0.0857599999999999 seconds and 4 git commands to generate.