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