]> Dogcows Code - chaz/openbox/blob - src/client.cc
separate off execute, since its not a callback function
[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 printf("Setting desktop %ld\n", target);
543 assert(target >= 0 || target == (signed)0xffffffff);
544 //assert(target == 0xffffffff || target < MAX);
545
546 // XXX: move the window to the new desktop (and set root property)
547 _desktop = target;
548 }
549
550
551 void OBClient::setState(StateAction action, long data1, long data2)
552 {
553 const otk::OBProperty *property = Openbox::instance->property();
554
555 if (!(action == State_Add || action == State_Remove ||
556 action == State_Toggle))
557 return; // an invalid action was passed to the client message, ignore it
558
559 for (int i = 0; i < 2; ++i) {
560 Atom state = i == 0 ? data1 : data2;
561
562 if (! state) continue;
563
564 // if toggling, then pick whether we're adding or removing
565 if (action == State_Toggle) {
566 if (state == property->atom(otk::OBProperty::net_wm_state_modal))
567 action = _modal ? State_Remove : State_Add;
568 else if (state ==
569 property->atom(otk::OBProperty::net_wm_state_maximized_vert))
570 action = _max_vert ? State_Remove : State_Add;
571 else if (state ==
572 property->atom(otk::OBProperty::net_wm_state_maximized_horz))
573 action = _max_horz ? State_Remove : State_Add;
574 else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
575 action = _shaded ? State_Remove : State_Add;
576 else if (state ==
577 property->atom(otk::OBProperty::net_wm_state_skip_taskbar))
578 action = _skip_taskbar ? State_Remove : State_Add;
579 else if (state ==
580 property->atom(otk::OBProperty::net_wm_state_skip_pager))
581 action = _skip_pager ? State_Remove : State_Add;
582 else if (state ==
583 property->atom(otk::OBProperty::net_wm_state_fullscreen))
584 action = _fullscreen ? State_Remove : State_Add;
585 else if (state == property->atom(otk::OBProperty::net_wm_state_above))
586 action = _above ? State_Remove : State_Add;
587 else if (state == property->atom(otk::OBProperty::net_wm_state_below))
588 action = _below ? State_Remove : State_Add;
589 }
590
591 if (action == State_Add) {
592 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
593 if (_modal) continue;
594 _modal = true;
595 // XXX: give it focus if another window has focus that shouldnt now
596 } else if (state ==
597 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
598 if (_max_vert) continue;
599 _max_vert = true;
600 // XXX: resize the window etc
601 } else if (state ==
602 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
603 if (_max_horz) continue;
604 _max_horz = true;
605 // XXX: resize the window etc
606 } else if (state ==
607 property->atom(otk::OBProperty::net_wm_state_shaded)) {
608 if (_shaded) continue;
609 _shaded = true;
610 // XXX: hide the client window
611 } else if (state ==
612 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
613 _skip_taskbar = true;
614 } else if (state ==
615 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
616 _skip_pager = true;
617 } else if (state ==
618 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
619 if (_fullscreen) continue;
620 _fullscreen = true;
621 // XXX: raise the window n shit
622 } else if (state ==
623 property->atom(otk::OBProperty::net_wm_state_above)) {
624 if (_above) continue;
625 _above = true;
626 // XXX: raise the window n shit
627 } else if (state ==
628 property->atom(otk::OBProperty::net_wm_state_below)) {
629 if (_below) continue;
630 _below = true;
631 // XXX: lower the window n shit
632 }
633
634 } else { // action == State_Remove
635 if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
636 if (!_modal) continue;
637 _modal = false;
638 } else if (state ==
639 property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
640 if (!_max_vert) continue;
641 _max_vert = false;
642 // XXX: resize the window etc
643 } else if (state ==
644 property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
645 if (!_max_horz) continue;
646 _max_horz = false;
647 // XXX: resize the window etc
648 } else if (state ==
649 property->atom(otk::OBProperty::net_wm_state_shaded)) {
650 if (!_shaded) continue;
651 _shaded = false;
652 // XXX: show the client window
653 } else if (state ==
654 property->atom(otk::OBProperty::net_wm_state_skip_taskbar)) {
655 _skip_taskbar = false;
656 } else if (state ==
657 property->atom(otk::OBProperty::net_wm_state_skip_pager)) {
658 _skip_pager = false;
659 } else if (state ==
660 property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
661 if (!_fullscreen) continue;
662 _fullscreen = false;
663 // XXX: lower the window to its proper layer
664 } else if (state ==
665 property->atom(otk::OBProperty::net_wm_state_above)) {
666 if (!_above) continue;
667 _above = false;
668 // XXX: lower the window to its proper layer
669 } else if (state ==
670 property->atom(otk::OBProperty::net_wm_state_below)) {
671 if (!_below) continue;
672 _below = false;
673 // XXX: raise the window to its proper layer
674 }
675 }
676 }
677 calcLayer();
678 Openbox::instance->screen(_screen)->restack(true, this); // raise
679 }
680
681
682 void OBClient::toggleClientBorder(bool addborder)
683 {
684 // adjust our idea of where the client is, based on its border. When the
685 // border is removed, the client should now be considered to be in a
686 // different position.
687 // when re-adding the border to the client, the same operation needs to be
688 // reversed.
689 int x = _area.x(), y = _area.y();
690 switch(_gravity) {
691 case NorthWestGravity:
692 case WestGravity:
693 case SouthWestGravity:
694 break;
695 case NorthEastGravity:
696 case EastGravity:
697 case SouthEastGravity:
698 if (addborder) x -= _border_width * 2;
699 else x += _border_width * 2;
700 break;
701 }
702 switch(_gravity) {
703 case NorthWestGravity:
704 case NorthGravity:
705 case NorthEastGravity:
706 break;
707 case SouthWestGravity:
708 case SouthGravity:
709 case SouthEastGravity:
710 if (addborder) y -= _border_width * 2;
711 else y += _border_width * 2;
712 break;
713 default:
714 // no change for StaticGravity etc.
715 break;
716 }
717 _area.setPos(x, y);
718
719 if (addborder) {
720 XSetWindowBorderWidth(otk::OBDisplay::display, _window, _border_width);
721
722 // move the client so it is back it the right spot _with_ its border!
723 XMoveWindow(otk::OBDisplay::display, _window, x, y);
724 } else
725 XSetWindowBorderWidth(otk::OBDisplay::display, _window, 0);
726 }
727
728
729 void OBClient::clientMessageHandler(const XClientMessageEvent &e)
730 {
731 otk::OtkEventHandler::clientMessageHandler(e);
732
733 if (e.format != 32) return;
734
735 const otk::OBProperty *property = Openbox::instance->property();
736
737 if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) {
738 // compress changes into a single change
739 bool compress = false;
740 XEvent ce;
741 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
742 // XXX: it would be nice to compress ALL messages of a type, not just
743 // messages in a row without other message types between.
744 if (ce.xclient.message_type != e.message_type) {
745 XPutBackEvent(otk::OBDisplay::display, &ce);
746 break;
747 }
748 compress = true;
749 }
750 if (compress)
751 setWMState(ce.xclient.data.l[0]); // use the found event
752 else
753 setWMState(e.data.l[0]); // use the original event
754 } else if (e.message_type ==
755 property->atom(otk::OBProperty::net_wm_desktop)) {
756 // compress changes into a single change
757 bool compress = false;
758 XEvent ce;
759 while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
760 // XXX: it would be nice to compress ALL messages of a type, not just
761 // messages in a row without other message types between.
762 if (ce.xclient.message_type != e.message_type) {
763 XPutBackEvent(otk::OBDisplay::display, &ce);
764 break;
765 }
766 compress = true;
767 }
768 if (compress)
769 setDesktop(e.data.l[0]); // use the found event
770 else
771 setDesktop(e.data.l[0]); // use the original event
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 } else if (e.message_type ==
776 property->atom(otk::OBProperty::net_close_window)) {
777 close();
778 } else if (e.message_type ==
779 property->atom(otk::OBProperty::net_active_window)) {
780 focus();
781 }
782 }
783
784
785 #if defined(SHAPE) || defined(DOXYGEN_IGNORE)
786 void OBClient::shapeHandler(const XShapeEvent &e)
787 {
788 otk::OtkEventHandler::shapeHandler(e);
789
790 _shaped = e.shaped;
791 }
792 #endif
793
794
795 void OBClient::resize(Corner anchor, int w, int h)
796 {
797 w -= _base_size.x();
798 h -= _base_size.y();
799
800 // for interactive resizing. have to move half an increment in each
801 // direction.
802 w += _size_inc.x() / 2;
803 h += _size_inc.y() / 2;
804
805 // is the window resizable? if it is not, then don't check its sizes, the
806 // client can do what it wants and the user can't change it anyhow
807 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
808 // smaller than min size or bigger than max size?
809 if (w < _min_size.x()) w = _min_size.x();
810 else if (w > _max_size.x()) w = _max_size.x();
811 if (h < _min_size.y()) h = _min_size.y();
812 else if (h > _max_size.y()) h = _max_size.y();
813 }
814
815 // keep to the increments
816 w /= _size_inc.x();
817 h /= _size_inc.y();
818
819 // store the logical size
820 _logical_size.setPoint(w, h);
821
822 w *= _size_inc.x();
823 h *= _size_inc.y();
824
825 w += _base_size.x();
826 h += _base_size.y();
827
828 int x = _area.x(), y = _area.y();
829 switch (anchor) {
830 case TopLeft:
831 break;
832 case TopRight:
833 x -= w - _area.width();
834 break;
835 case BottomLeft:
836 y -= h - _area.height();
837 break;
838 case BottomRight:
839 x -= w - _area.width();
840 y -= h - _area.height();
841 break;
842 }
843
844 _area.setSize(w, h);
845 XResizeWindow(otk::OBDisplay::display, _window, w, h);
846
847 // resize the frame to match the request
848 frame->adjustSize();
849 move(x, y);
850 }
851
852
853 void OBClient::move(int x, int y)
854 {
855 _area.setPos(x, y);
856 // move the frame to be in the requested position
857 frame->adjustPosition();
858 }
859
860
861 void OBClient::close()
862 {
863 XEvent ce;
864 const otk::OBProperty *property = Openbox::instance->property();
865
866 if (!(_functions & Func_Close)) return;
867
868 // XXX: itd be cool to do timeouts and shit here for killing the client's
869 // process off
870
871 ce.xclient.type = ClientMessage;
872 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
873 ce.xclient.display = otk::OBDisplay::display;
874 ce.xclient.window = _window;
875 ce.xclient.format = 32;
876 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_delete_window);
877 ce.xclient.data.l[1] = CurrentTime;
878 ce.xclient.data.l[2] = 0l;
879 ce.xclient.data.l[3] = 0l;
880 ce.xclient.data.l[4] = 0l;
881 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
882 }
883
884
885 void OBClient::changeState()
886 {
887 const otk::OBProperty *property = Openbox::instance->property();
888
889 unsigned long state[2];
890 state[0] = _wmstate;
891 state[1] = None;
892 property->set(_window, otk::OBProperty::wm_state, otk::OBProperty::wm_state,
893 state, 2);
894
895 Atom netstate[10];
896 int num = 0;
897 if (_modal)
898 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_modal);
899 if (_shaded)
900 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_shaded);
901 if (_iconic)
902 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_hidden);
903 if (_skip_taskbar)
904 netstate[num++] =
905 property->atom(otk::OBProperty::net_wm_state_skip_taskbar);
906 if (_skip_pager)
907 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_skip_pager);
908 if (_fullscreen)
909 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_fullscreen);
910 if (_max_vert)
911 netstate[num++] =
912 property->atom(otk::OBProperty::net_wm_state_maximized_vert);
913 if (_max_horz)
914 netstate[num++] =
915 property->atom(otk::OBProperty::net_wm_state_maximized_horz);
916 if (_above)
917 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_above);
918 if (_below)
919 netstate[num++] = property->atom(otk::OBProperty::net_wm_state_below);
920 property->set(_window, otk::OBProperty::net_wm_state,
921 otk::OBProperty::Atom_Atom, netstate, num);
922
923 }
924
925 void OBClient::shade(bool shade)
926 {
927 if (shade == _shaded) return; // already done
928
929 _wmstate = shade ? IconicState : NormalState;
930 _shaded = shade;
931 changeState();
932 frame->adjustSize();
933 }
934
935
936 bool OBClient::focus()
937 {
938 if (!(_can_focus || _focus_notify) || _focused) return false;
939
940 if (_can_focus)
941 XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
942
943 if (_focus_notify) {
944 XEvent ce;
945 const otk::OBProperty *property = Openbox::instance->property();
946
947 ce.xclient.type = ClientMessage;
948 ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
949 ce.xclient.display = otk::OBDisplay::display;
950 ce.xclient.window = _window;
951 ce.xclient.format = 32;
952 ce.xclient.data.l[0] = property->atom(otk::OBProperty::wm_take_focus);
953 ce.xclient.data.l[1] = Openbox::instance->lastTime();
954 ce.xclient.data.l[2] = 0l;
955 ce.xclient.data.l[3] = 0l;
956 ce.xclient.data.l[4] = 0l;
957 XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
958 }
959
960 return true;
961 }
962
963
964 void OBClient::unfocus()
965 {
966 if (!_focused) return;
967
968 assert(Openbox::instance->focusedClient() == this);
969 Openbox::instance->setFocusedClient(0);
970 }
971
972
973 void OBClient::focusHandler(const XFocusChangeEvent &e)
974 {
975 #ifdef DEBUG
976 printf("FocusIn for 0x%lx\n", e.window);
977 #endif // DEBUG
978
979 OtkEventHandler::focusHandler(e);
980
981 frame->focus();
982 _focused = true;
983
984 Openbox::instance->setFocusedClient(this);
985 }
986
987
988 void OBClient::unfocusHandler(const XFocusChangeEvent &e)
989 {
990 #ifdef DEBUG
991 printf("FocusOut for 0x%lx\n", e.window);
992 #endif // DEBUG
993
994 OtkEventHandler::unfocusHandler(e);
995
996 frame->unfocus();
997 _focused = false;
998
999 if (Openbox::instance->focusedClient() == this) {
1000 printf("UNFOCUSED!\n");
1001 Openbox::instance->setFocusedClient(this);
1002 }
1003 }
1004
1005
1006 void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
1007 {
1008 #ifdef DEBUG
1009 printf("ConfigureRequest for 0x%lx\n", e.window);
1010 #endif // DEBUG
1011
1012 OtkEventHandler::configureRequestHandler(e);
1013
1014 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1015
1016 if (e.value_mask & CWBorderWidth)
1017 _border_width = e.border_width;
1018
1019 // resize, then move, as specified in the EWMH section 7.7
1020 if (e.value_mask & (CWWidth | CWHeight)) {
1021 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1022 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1023
1024 Corner corner;
1025 switch (_gravity) {
1026 case NorthEastGravity:
1027 case EastGravity:
1028 corner = TopRight;
1029 break;
1030 case SouthWestGravity:
1031 case SouthGravity:
1032 corner = BottomLeft;
1033 break;
1034 case SouthEastGravity:
1035 corner = BottomRight;
1036 break;
1037 default: // NorthWest, Static, etc
1038 corner = TopLeft;
1039 }
1040
1041 resize(corner, w, h);
1042 }
1043
1044 if (e.value_mask & (CWX | CWY)) {
1045 int x = (e.value_mask & CWX) ? e.x : _area.x();
1046 int y = (e.value_mask & CWY) ? e.y : _area.y();
1047 move(x, y);
1048 }
1049
1050 if (e.value_mask & CWStackMode) {
1051 switch (e.detail) {
1052 case Below:
1053 case BottomIf:
1054 // XXX: lower the window
1055 break;
1056
1057 case Above:
1058 case TopIf:
1059 default:
1060 // XXX: raise the window
1061 break;
1062 }
1063 }
1064 }
1065
1066
1067 void OBClient::unmapHandler(const XUnmapEvent &e)
1068 {
1069 #ifdef DEBUG
1070 printf("UnmapNotify for 0x%lx\n", e.window);
1071 #endif // DEBUG
1072
1073 if (ignore_unmaps) {
1074 ignore_unmaps--;
1075 return;
1076 }
1077
1078 OtkEventHandler::unmapHandler(e);
1079
1080 // this deletes us etc
1081 Openbox::instance->screen(_screen)->unmanageWindow(this);
1082 }
1083
1084
1085 void OBClient::destroyHandler(const XDestroyWindowEvent &e)
1086 {
1087 #ifdef DEBUG
1088 printf("DestroyNotify for 0x%lx\n", e.window);
1089 #endif // DEBUG
1090
1091 OtkEventHandler::destroyHandler(e);
1092
1093 // this deletes us etc
1094 Openbox::instance->screen(_screen)->unmanageWindow(this);
1095 }
1096
1097
1098 void OBClient::reparentHandler(const XReparentEvent &e)
1099 {
1100 // this is when the client is first taken captive in the frame
1101 if (e.parent == frame->plate()) return;
1102
1103 #ifdef DEBUG
1104 printf("ReparentNotify for 0x%lx\n", e.window);
1105 #endif // DEBUG
1106
1107 OtkEventHandler::reparentHandler(e);
1108
1109 /*
1110 This event is quite rare and is usually handled in unmapHandler.
1111 However, if the window is unmapped when the reparent event occurs,
1112 the window manager never sees it because an unmap event is not sent
1113 to an already unmapped window.
1114 */
1115
1116 // this deletes us etc
1117 Openbox::instance->screen(_screen)->unmanageWindow(this);
1118 }
1119
1120 }
This page took 0.096582 seconds and 4 git commands to generate.