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