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