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