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