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