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