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