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