version bump
[blackbox.git] / src / Window.cc
blobb1361ae80bc4db89d01f2e27bf1bf37dc2aacb69
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000, 2002 - 2005
5 // Bradley T Hughes <bhughes at trolltech.com>
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
25 // make sure we get bt::textPropertyToString()
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
29 #include "Window.hh"
30 #include "Screen.hh"
31 #include "WindowGroup.hh"
32 #include "Windowmenu.hh"
33 #include "Workspace.hh"
34 #include "blackbox.hh"
36 #include <Pen.hh>
37 #include <PixmapCache.hh>
38 #include <Unicode.hh>
40 #include <X11/Xatom.h>
41 #ifdef SHAPE
42 # include <X11/extensions/shape.h>
43 #endif
45 #include <assert.h>
48 sometimes C++ is a pain in the ass... it gives us stuff like the
49 default copy constructor and assignment operator (which does member
50 my member copy/assignment), but what we don't get is a default
51 comparison operator... how fucked up is that?
53 the language is designed to handle this:
55 struct foo { int a; int b };
56 foo a = { 0 , 0 }, b = a;
57 foo c;
58 c = a;
60 BUT, BUT, BUT, forget about doing this:
62 a = findFoo();
63 if (c == a)
64 return; // nothing to do
65 c = a;
67 ARGH!@#)(!*@#)(@#*$(!@#
69 bool operator==(const WMNormalHints &x, const WMNormalHints &y);
72 // Event mask used for managed client windows.
73 const unsigned long client_window_event_mask =
74 (PropertyChangeMask | StructureNotifyMask);
78 * Returns the appropriate WindowType based on the _NET_WM_WINDOW_TYPE
80 static WindowType window_type_from_atom(const bt::EWMH &ewmh, Atom atom) {
81 if (atom == ewmh.wmWindowTypeDialog())
82 return WindowTypeDialog;
83 if (atom == ewmh.wmWindowTypeDesktop())
84 return WindowTypeDesktop;
85 if (atom == ewmh.wmWindowTypeDock())
86 return WindowTypeDock;
87 if (atom == ewmh.wmWindowTypeMenu())
88 return WindowTypeMenu;
89 if (atom == ewmh.wmWindowTypeSplash())
90 return WindowTypeSplash;
91 if (atom == ewmh.wmWindowTypeToolbar())
92 return WindowTypeToolbar;
93 if (atom == ewmh.wmWindowTypeUtility())
94 return WindowTypeUtility;
95 return WindowTypeNormal;
100 * Determine the appropriate decorations and functions based on the
101 * given properties and hints.
103 static void update_decorations(WindowDecorationFlags &decorations,
104 WindowFunctionFlags &functions,
105 bool transient,
106 const EWMH &ewmh,
107 const MotifHints &motifhints,
108 const WMNormalHints &wmnormal,
109 const WMProtocols &wmprotocols) {
110 decorations = AllWindowDecorations;
111 functions = AllWindowFunctions;
113 // transients should be kept on the same workspace are their parents
114 if (transient)
115 functions &= ~WindowFunctionChangeWorkspace;
117 // modify the window decorations/functions based on window type
118 switch (ewmh.window_type) {
119 case WindowTypeDialog:
120 decorations &= ~(WindowDecorationIconify |
121 WindowDecorationMaximize);
122 functions &= ~(WindowFunctionShade |
123 WindowFunctionIconify |
124 WindowFunctionMaximize |
125 WindowFunctionChangeLayer |
126 WindowFunctionFullScreen);
127 break;
129 case WindowTypeDesktop:
130 case WindowTypeDock:
131 case WindowTypeSplash:
132 decorations = NoWindowDecorations;
133 functions = NoWindowFunctions;
134 break;
136 case WindowTypeToolbar:
137 case WindowTypeMenu:
138 decorations &= ~(WindowDecorationHandle |
139 WindowDecorationGrip |
140 WindowDecorationBorder |
141 WindowDecorationIconify |
142 WindowDecorationMaximize);
143 functions &= ~(WindowFunctionResize |
144 WindowFunctionShade |
145 WindowFunctionIconify |
146 WindowFunctionMaximize |
147 WindowFunctionFullScreen);
148 break;
150 case WindowTypeUtility:
151 decorations &= ~(WindowDecorationIconify |
152 WindowDecorationMaximize);
153 functions &= ~(WindowFunctionShade |
154 WindowFunctionIconify |
155 WindowFunctionMaximize);
156 break;
158 default:
159 break;
162 // mask away stuff turned off by Motif hints
163 decorations &= motifhints.decorations;
164 functions &= motifhints.functions;
166 // disable shade if we do not have a titlebar
167 if (!(decorations & WindowDecorationTitlebar))
168 functions &= ~WindowFunctionShade;
170 // disable grips and maximize if we have a fixed size window
171 if ((wmnormal.flags & (PMinSize|PMaxSize)) == (PMinSize|PMaxSize)
172 && wmnormal.max_width <= wmnormal.min_width
173 && wmnormal.max_height <= wmnormal.min_height) {
174 decorations &= ~(WindowDecorationMaximize |
175 WindowDecorationGrip);
176 functions &= ~(WindowFunctionResize |
177 WindowFunctionMaximize);
180 // cannot close if client doesn't understand WM_DELETE_WINDOW
181 if (!wmprotocols.wm_delete_window) {
182 decorations &= ~WindowDecorationClose;
183 functions &= ~WindowFunctionClose;
189 * Calculate the frame margin based on the given decorations and
190 * style.
192 static
193 bt::EWMH::Strut update_margin(WindowDecorationFlags decorations,
194 const WindowStyle &style) {
195 bt::EWMH::Strut margin;
197 const unsigned int bw = ((decorations & WindowDecorationBorder)
198 ? style.frame_border_width
199 : 0u);
200 margin.top = margin.bottom = margin.left = margin.right = bw;
202 if (decorations & WindowDecorationTitlebar)
203 margin.top += style.title_height - bw;
205 if (decorations & WindowDecorationHandle)
206 margin.bottom += style.handle_height - bw;
208 return margin;
213 * Add specified window to the appropriate window group, creating a
214 * new group if necessary.
216 static BWindowGroup *update_window_group(Window window_group,
217 Blackbox *blackbox,
218 BlackboxWindow *win) {
219 BWindowGroup *group = win->findWindowGroup();
220 if (!group) {
221 new BWindowGroup(blackbox, window_group);
222 group = win->findWindowGroup();
223 assert(group != 0);
225 group->addWindow(win);
226 return group;
231 * Calculate the size of the frame window and constrain it to the
232 * size specified by the size hints of the client window.
234 * 'rect' refers to the geometry of the frame in pixels.
236 enum Corner {
237 TopLeft,
238 TopRight,
239 BottomLeft,
240 BottomRight
242 static bt::Rect constrain(const bt::Rect &rect,
243 const bt::EWMH::Strut &margin,
244 const WMNormalHints &wmnormal,
245 Corner corner) {
246 bt::Rect r;
248 // 'rect' represents the requested frame size, we need to strip
249 // 'margin' off and constrain the client size
250 r.setCoords(rect.left() + static_cast<signed>(margin.left),
251 rect.top() + static_cast<signed>(margin.top),
252 rect.right() - static_cast<signed>(margin.right),
253 rect.bottom() - static_cast<signed>(margin.bottom));
255 unsigned int dw = r.width(), dh = r.height();
257 const unsigned int base_width = (wmnormal.base_width
258 ? wmnormal.base_width
259 : wmnormal.min_width),
260 base_height = (wmnormal.base_height
261 ? wmnormal.base_height
262 : wmnormal.min_height);
264 // fit to min/max size
265 if (dw < wmnormal.min_width)
266 dw = wmnormal.min_width;
267 if (dh < wmnormal.min_height)
268 dh = wmnormal.min_height;
270 if (dw > wmnormal.max_width)
271 dw = wmnormal.max_width;
272 if (dh > wmnormal.max_height)
273 dh = wmnormal.max_height;
275 assert(dw >= base_width && dh >= base_height);
277 // fit to size increments
278 if (wmnormal.flags & PResizeInc) {
279 dw = (((dw - base_width) / wmnormal.width_inc)
280 * wmnormal.width_inc) + base_width;
281 dh = (((dh - base_height) / wmnormal.height_inc)
282 * wmnormal.height_inc) + base_height;
286 * honor aspect ratios (based on twm which is based on uwm)
288 * The math looks like this:
290 * minAspectX dwidth maxAspectX
291 * ---------- <= ------- <= ----------
292 * minAspectY dheight maxAspectY
294 * If that is multiplied out, then the width and height are
295 * invalid in the following situations:
297 * minAspectX * dheight > minAspectY * dwidth
298 * maxAspectX * dheight < maxAspectY * dwidth
301 if (wmnormal.flags & PAspect) {
302 unsigned int delta;
303 const unsigned int min_asp_x = wmnormal.min_aspect_x,
304 min_asp_y = wmnormal.min_aspect_y,
305 max_asp_x = wmnormal.max_aspect_x,
306 max_asp_y = wmnormal.max_aspect_y,
307 w_inc = wmnormal.width_inc,
308 h_inc = wmnormal.height_inc;
309 if (min_asp_x * dh > min_asp_y * dw) {
310 delta = ((min_asp_x * dh / min_asp_y - dw) * w_inc) / w_inc;
311 if (dw + delta <= wmnormal.max_width) {
312 dw += delta;
313 } else {
314 delta = ((dh - (dw * min_asp_y) / min_asp_x) * h_inc) / h_inc;
315 if (dh - delta >= wmnormal.min_height) dh -= delta;
318 if (max_asp_x * dh < max_asp_y * dw) {
319 delta = ((max_asp_y * dw / max_asp_x - dh) * h_inc) / h_inc;
320 if (dh + delta <= wmnormal.max_height) {
321 dh += delta;
322 } else {
323 delta = ((dw - (dh * max_asp_x) / max_asp_y) * w_inc) / w_inc;
324 if (dw - delta >= wmnormal.min_width) dw -= delta;
329 r.setSize(dw, dh);
331 // add 'margin' back onto 'r'
332 r.setCoords(r.left() - margin.left, r.top() - margin.top,
333 r.right() + margin.right, r.bottom() + margin.bottom);
335 // move 'r' to the specified corner
336 int dx = rect.right() - r.right();
337 int dy = rect.bottom() - r.bottom();
339 switch (corner) {
340 case TopLeft:
341 // nothing to do
342 break;
344 case TopRight:
345 r.setPos(r.x() + dx, r.y());
346 break;
348 case BottomLeft:
349 r.setPos(r.x(), r.y() + dy);
350 break;
352 case BottomRight:
353 r.setPos(r.x() + dx, r.y() + dy);
354 break;
357 return r;
362 * Positions 'rect' according to the client window geometry and window
363 * gravity.
365 static bt::Rect applyGravity(const bt::Rect &rect,
366 const bt::EWMH::Strut &margin,
367 int gravity) {
368 bt::Rect r;
370 // apply horizontal window gravity
371 switch (gravity) {
372 default:
373 case NorthWestGravity:
374 case SouthWestGravity:
375 case WestGravity:
376 r.setX(rect.x());
377 break;
379 case NorthGravity:
380 case SouthGravity:
381 case CenterGravity:
382 r.setX(rect.x() - (margin.left + margin.right) / 2);
383 break;
385 case NorthEastGravity:
386 case SouthEastGravity:
387 case EastGravity:
388 r.setX(rect.x() - (margin.left + margin.right) + 2);
389 break;
391 case ForgetGravity:
392 case StaticGravity:
393 r.setX(rect.x() - margin.left);
394 break;
397 // apply vertical window gravity
398 switch (gravity) {
399 default:
400 case NorthWestGravity:
401 case NorthEastGravity:
402 case NorthGravity:
403 r.setY(rect.y());
404 break;
406 case CenterGravity:
407 case EastGravity:
408 case WestGravity:
409 r.setY(rect.y() - ((margin.top + margin.bottom) / 2));
410 break;
412 case SouthWestGravity:
413 case SouthEastGravity:
414 case SouthGravity:
415 r.setY(rect.y() - (margin.bottom + margin.top) + 2);
416 break;
418 case ForgetGravity:
419 case StaticGravity:
420 r.setY(rect.y() - margin.top);
421 break;
424 r.setSize(rect.width() + margin.left + margin.right,
425 rect.height() + margin.top + margin.bottom);
426 return r;
431 * The reverse of the applyGravity function.
433 * Positions 'rect' according to the frame window geometry and window
434 * gravity.
436 static bt::Rect restoreGravity(const bt::Rect &rect,
437 const bt::EWMH::Strut &margin,
438 int gravity) {
439 bt::Rect r;
441 // restore horizontal window gravity
442 switch (gravity) {
443 default:
444 case NorthWestGravity:
445 case SouthWestGravity:
446 case WestGravity:
447 r.setX(rect.x());
448 break;
450 case NorthGravity:
451 case SouthGravity:
452 case CenterGravity:
453 r.setX(rect.x() + (margin.left + margin.right) / 2);
454 break;
456 case NorthEastGravity:
457 case SouthEastGravity:
458 case EastGravity:
459 r.setX(rect.x() + (margin.left + margin.right) - 2);
460 break;
462 case ForgetGravity:
463 case StaticGravity:
464 r.setX(rect.x() + margin.left);
465 break;
468 // restore vertical window gravity
469 switch (gravity) {
470 default:
471 case NorthWestGravity:
472 case NorthEastGravity:
473 case NorthGravity:
474 r.setY(rect.y());
475 break;
477 case CenterGravity:
478 case EastGravity:
479 case WestGravity:
480 r.setY(rect.y() + (margin.top + margin.bottom) / 2);
481 break;
483 case SouthWestGravity:
484 case SouthEastGravity:
485 case SouthGravity:
486 r.setY(rect.y() + (margin.top + margin.bottom) - 2);
487 break;
489 case ForgetGravity:
490 case StaticGravity:
491 r.setY(rect.y() + margin.top);
492 break;
495 r.setSize(rect.width() - margin.left - margin.right,
496 rect.height() - margin.top - margin.bottom);
497 return r;
501 static bt::ustring readWMName(Blackbox *blackbox, Window window) {
502 bt::ustring name;
504 if (!blackbox->ewmh().readWMName(window, name) || name.empty()) {
505 XTextProperty text_prop;
507 if (XGetWMName(blackbox->XDisplay(), window, &text_prop)) {
508 name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
509 text_prop));
510 XFree((char *) text_prop.value);
514 if (name.empty())
515 name = bt::toUnicode("Unnamed");
517 return name;
521 static bt::ustring readWMIconName(Blackbox *blackbox, Window window) {
522 bt::ustring name;
524 if (!blackbox->ewmh().readWMIconName(window, name) || name.empty()) {
525 XTextProperty text_prop;
526 if (XGetWMIconName(blackbox->XDisplay(), window, &text_prop)) {
527 name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
528 text_prop));
529 XFree((char *) text_prop.value);
533 if (name.empty())
534 return bt::ustring();
536 return name;
540 static EWMH readEWMH(const bt::EWMH &bewmh,
541 Window window,
542 int currentWorkspace) {
543 EWMH ewmh;
544 ewmh.window_type = WindowTypeNormal;
545 ewmh.workspace = 0; // initialized properly below
546 ewmh.modal = false;
547 ewmh.maxv = false;
548 ewmh.maxh = false;
549 ewmh.shaded = false;
550 ewmh.skip_taskbar = false;
551 ewmh.skip_pager = false;
552 ewmh.hidden = false;
553 ewmh.fullscreen = false;
554 ewmh.above = false;
555 ewmh.below = false;
557 // note: wm_name and wm_icon_name are read separately
559 bool ret;
561 bt::EWMH::AtomList atoms;
562 ret = bewmh.readWMWindowType(window, atoms);
563 if (ret) {
564 bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
565 for (; it != end; ++it) {
566 if (bewmh.isSupportedWMWindowType(*it)) {
567 ewmh.window_type = ::window_type_from_atom(bewmh, *it);
568 break;
573 atoms.clear();
574 ret = bewmh.readWMState(window, atoms);
575 if (ret) {
576 bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
577 for (; it != end; ++it) {
578 Atom state = *it;
579 if (state == bewmh.wmStateModal()) {
580 ewmh.modal = true;
581 } else if (state == bewmh.wmStateMaximizedVert()) {
582 ewmh.maxv = true;
583 } else if (state == bewmh.wmStateMaximizedHorz()) {
584 ewmh.maxh = true;
585 } else if (state == bewmh.wmStateShaded()) {
586 ewmh.shaded = true;
587 } else if (state == bewmh.wmStateSkipTaskbar()) {
588 ewmh.skip_taskbar = true;
589 } else if (state == bewmh.wmStateSkipPager()) {
590 ewmh.skip_pager = true;
591 } else if (state == bewmh.wmStateHidden()) {
593 ignore _NET_WM_STATE_HIDDEN if present, the wm sets this
594 state, not the application
596 } else if (state == bewmh.wmStateFullscreen()) {
597 ewmh.fullscreen = true;
598 } else if (state == bewmh.wmStateAbove()) {
599 ewmh.above = true;
600 } else if (state == bewmh.wmStateBelow()) {
601 ewmh.below = true;
606 switch (ewmh.window_type) {
607 case WindowTypeDesktop:
608 case WindowTypeDock:
609 // these types should occupy all workspaces by default
610 ewmh.workspace = bt::BSENTINEL;
611 break;
613 default:
614 if (!bewmh.readWMDesktop(window, ewmh.workspace))
615 ewmh.workspace = currentWorkspace;
616 break;
617 } //switch
619 return ewmh;
624 * Returns the MotifWM hints for the specified window.
626 static MotifHints readMotifWMHints(Blackbox *blackbox, Window window) {
627 MotifHints motif;
628 motif.decorations = AllWindowDecorations;
629 motif.functions = AllWindowFunctions;
632 this structure only contains 3 elements, even though the Motif 2.0
633 structure contains 5, because we only use the first 3
635 struct PropMotifhints {
636 unsigned long flags;
637 unsigned long functions;
638 unsigned long decorations;
640 static const unsigned int PROP_MWM_HINTS_ELEMENTS = 3u;
641 enum { // MWM flags
642 MWM_HINTS_FUNCTIONS = 1<<0,
643 MWM_HINTS_DECORATIONS = 1<<1
645 enum { // MWM functions
646 MWM_FUNC_ALL = 1<<0,
647 MWM_FUNC_RESIZE = 1<<1,
648 MWM_FUNC_MOVE = 1<<2,
649 MWM_FUNC_MINIMIZE = 1<<3,
650 MWM_FUNC_MAXIMIZE = 1<<4,
651 MWM_FUNC_CLOSE = 1<<5
653 enum { // MWM decorations
654 MWM_DECOR_ALL = 1<<0,
655 MWM_DECOR_BORDER = 1<<1,
656 MWM_DECOR_RESIZEH = 1<<2,
657 MWM_DECOR_TITLE = 1<<3,
658 MWM_DECOR_MENU = 1<<4,
659 MWM_DECOR_MINIMIZE = 1<<5,
660 MWM_DECOR_MAXIMIZE = 1<<6
663 Atom atom_return;
664 PropMotifhints *prop = 0;
665 int format;
666 unsigned long num, len;
667 int ret = XGetWindowProperty(blackbox->XDisplay(), window,
668 blackbox->motifWmHintsAtom(), 0,
669 PROP_MWM_HINTS_ELEMENTS, False,
670 blackbox->motifWmHintsAtom(), &atom_return,
671 &format, &num, &len,
672 (unsigned char **) &prop);
674 if (ret != Success || !prop || num != PROP_MWM_HINTS_ELEMENTS) {
675 if (prop) XFree(prop);
676 return motif;
679 if (prop->flags & MWM_HINTS_FUNCTIONS) {
680 if (prop->functions & MWM_FUNC_ALL) {
681 motif.functions = AllWindowFunctions;
682 } else {
683 // default to the functions that cannot be set through
684 // _MOTIF_WM_HINTS
685 motif.functions = (WindowFunctionShade
686 | WindowFunctionChangeWorkspace
687 | WindowFunctionChangeLayer
688 | WindowFunctionFullScreen);
690 if (prop->functions & MWM_FUNC_RESIZE)
691 motif.functions |= WindowFunctionResize;
692 if (prop->functions & MWM_FUNC_MOVE)
693 motif.functions |= WindowFunctionMove;
694 if (prop->functions & MWM_FUNC_MINIMIZE)
695 motif.functions |= WindowFunctionIconify;
696 if (prop->functions & MWM_FUNC_MAXIMIZE)
697 motif.functions |= WindowFunctionMaximize;
698 if (prop->functions & MWM_FUNC_CLOSE)
699 motif.functions |= WindowFunctionClose;
703 if (prop->flags & MWM_HINTS_DECORATIONS) {
704 if (prop->decorations & MWM_DECOR_ALL) {
705 motif.decorations = AllWindowDecorations;
706 } else {
707 motif.decorations = NoWindowDecorations;
709 if (prop->decorations & MWM_DECOR_BORDER)
710 motif.decorations |= WindowDecorationBorder;
711 if (prop->decorations & MWM_DECOR_RESIZEH)
712 motif.decorations |= WindowDecorationHandle;
713 if (prop->decorations & MWM_DECOR_TITLE)
714 motif.decorations |= WindowDecorationTitlebar;
715 if (prop->decorations & MWM_DECOR_MINIMIZE)
716 motif.decorations |= WindowDecorationIconify;
717 if (prop->decorations & MWM_DECOR_MAXIMIZE)
718 motif.decorations |= WindowDecorationMaximize;
722 if (motif.decorations & WindowDecorationHandle) {
723 if (motif.functions & WindowFunctionResize)
724 motif.decorations |= WindowDecorationGrip;
725 else
726 motif.decorations &= ~WindowDecorationGrip;
729 if (motif.decorations & WindowDecorationTitlebar) {
730 if (motif.functions & WindowFunctionClose)
731 motif.decorations |= WindowDecorationClose;
732 else
733 motif.decorations &= ~WindowDecorationClose;
736 XFree(prop);
738 return motif;
743 * Returns the value of the WM_HINTS property. If the property is not
744 * set, a set of default values is returned instead.
746 static WMHints readWMHints(Blackbox *blackbox, Window window) {
747 WMHints wmh;
748 wmh.accept_focus = false;
749 wmh.window_group = None;
750 wmh.initial_state = NormalState;
752 XWMHints *wmhint = XGetWMHints(blackbox->XDisplay(), window);
753 if (!wmhint) return wmh;
755 if (wmhint->flags & InputHint)
756 wmh.accept_focus = (wmhint->input == True);
757 if (wmhint->flags & StateHint)
758 wmh.initial_state = wmhint->initial_state;
759 if (wmhint->flags & WindowGroupHint)
760 wmh.window_group = wmhint->window_group;
762 XFree(wmhint);
764 return wmh;
769 * Returns the value of the WM_NORMAL_HINTS property. If the property
770 * is not set, a set of default values is returned instead.
772 static WMNormalHints readWMNormalHints(Blackbox *blackbox,
773 Window window,
774 const bt::ScreenInfo &screenInfo) {
775 WMNormalHints wmnormal;
776 wmnormal.flags = 0;
777 wmnormal.min_width = wmnormal.min_height = 1u;
778 wmnormal.width_inc = wmnormal.height_inc = 1u;
779 wmnormal.min_aspect_x = wmnormal.min_aspect_y = 1u;
780 wmnormal.max_aspect_x = wmnormal.max_aspect_y = 1u;
781 wmnormal.base_width = wmnormal.base_height = 0u;
782 wmnormal.win_gravity = NorthWestGravity;
785 use the full screen, not the strut modified size. otherwise when
786 the availableArea changes max_width/height will be incorrect and
787 lead to odd rendering bugs.
789 const bt::Rect &rect = screenInfo.rect();
790 wmnormal.max_width = rect.width();
791 wmnormal.max_height = rect.height();
793 XSizeHints sizehint;
794 long unused;
795 if (! XGetWMNormalHints(blackbox->XDisplay(), window, &sizehint, &unused))
796 return wmnormal;
798 wmnormal.flags = sizehint.flags;
800 if (sizehint.flags & PMinSize) {
801 if (sizehint.min_width > 0)
802 wmnormal.min_width = sizehint.min_width;
803 if (sizehint.min_height > 0)
804 wmnormal.min_height = sizehint.min_height;
807 if the minimum size is bigger then the screen, adjust the
808 maximum size
810 if (wmnormal.min_width > wmnormal.max_width)
811 wmnormal.max_width = wmnormal.min_width;
812 if (wmnormal.min_height > wmnormal.max_height)
813 wmnormal.max_height = wmnormal.min_height;
816 if (sizehint.flags & PMaxSize) {
817 if (sizehint.max_width >= static_cast<signed>(wmnormal.min_width))
818 wmnormal.max_width = sizehint.max_width;
819 else
820 wmnormal.max_width = wmnormal.min_width;
822 if (sizehint.max_height >= static_cast<signed>(wmnormal.min_height))
823 wmnormal.max_height = sizehint.max_height;
824 else
825 wmnormal.max_height = wmnormal.min_height;
828 if (sizehint.flags & PResizeInc) {
829 wmnormal.width_inc = sizehint.width_inc;
830 wmnormal.height_inc = sizehint.height_inc;
833 if (sizehint.flags & PAspect) {
834 wmnormal.min_aspect_x = sizehint.min_aspect.x;
835 wmnormal.min_aspect_y = sizehint.min_aspect.y;
836 wmnormal.max_aspect_x = sizehint.max_aspect.x;
837 wmnormal.max_aspect_y = sizehint.max_aspect.y;
840 if (sizehint.flags & PBaseSize) {
841 if (sizehint.base_width <= static_cast<signed>(wmnormal.min_width))
842 wmnormal.base_width = sizehint.base_width;
843 if (sizehint.base_height <= static_cast<signed>(wmnormal.min_height))
844 wmnormal.base_height = sizehint.base_height;
847 if (sizehint.flags & PWinGravity)
848 wmnormal.win_gravity = sizehint.win_gravity;
850 return wmnormal;
855 * Retrieve which Window Manager Protocols are supported by the client
856 * window.
858 static WMProtocols readWMProtocols(Blackbox *blackbox,
859 Window window) {
860 WMProtocols protocols;
861 protocols.wm_delete_window = false;
862 protocols.wm_take_focus = false;
864 Atom *proto;
865 int num_return = 0;
867 if (XGetWMProtocols(blackbox->XDisplay(), window,
868 &proto, &num_return)) {
869 for (int i = 0; i < num_return; ++i) {
870 if (proto[i] == blackbox->wmDeleteWindowAtom()) {
871 protocols.wm_delete_window = true;
872 } else if (proto[i] == blackbox->wmTakeFocusAtom()) {
873 protocols.wm_take_focus = true;
876 XFree(proto);
879 return protocols;
884 * Reads the value of the WM_TRANSIENT_FOR property and returns a
885 * pointer to the transient parent for this window. If the
886 * WM_TRANSIENT_FOR is missing or invalid, this function returns 0.
888 * 'client.wmhints' should be properly updated before calling this
889 * function.
891 * Note: a return value of ~0ul signifies a window that should be
892 * transient but has no discernible parent.
894 static Window readTransientInfo(Blackbox *blackbox,
895 Window window,
896 const bt::ScreenInfo &screenInfo,
897 const WMHints &wmhints) {
898 Window trans_for = None;
900 if (!XGetTransientForHint(blackbox->XDisplay(), window, &trans_for)) {
901 // WM_TRANSIENT_FOR hint not set
902 return 0;
905 if (trans_for == window) {
906 // wierd client... treat this window as a normal window
907 return 0;
910 if (trans_for == None || trans_for == screenInfo.rootWindow()) {
912 this is a violation of the ICCCM, yet the EWMH allows this as a
913 way to signify a group transient.
915 trans_for = wmhints.window_group;
918 return trans_for;
922 static bool readState(unsigned long &current_state,
923 Blackbox *blackbox,
924 Window window) {
925 current_state = NormalState;
927 Atom atom_return;
928 bool ret = false;
929 int foo;
930 unsigned long *state, ulfoo, nitems;
932 if ((XGetWindowProperty(blackbox->XDisplay(), window,
933 blackbox->wmStateAtom(),
934 0l, 2l, False, blackbox->wmStateAtom(),
935 &atom_return, &foo, &nitems, &ulfoo,
936 (unsigned char **) &state) != Success) ||
937 (! state)) {
938 return false;
941 if (nitems >= 1) {
942 current_state = static_cast<unsigned long>(state[0]);
943 ret = true;
946 XFree((void *) state);
948 return ret;
952 static void clearState(Blackbox *blackbox, Window window) {
953 XDeleteProperty(blackbox->XDisplay(), window, blackbox->wmStateAtom());
955 const bt::EWMH& ewmh = blackbox->ewmh();
956 ewmh.removeProperty(window, ewmh.wmDesktop());
957 ewmh.removeProperty(window, ewmh.wmState());
958 ewmh.removeProperty(window, ewmh.wmAllowedActions());
959 ewmh.removeProperty(window, ewmh.wmVisibleName());
960 ewmh.removeProperty(window, ewmh.wmVisibleIconName());
965 * Initializes the class with default values/the window's set initial values.
967 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
968 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
969 // sizeof(BlackboxWindow));
971 #ifdef DEBUG
972 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
973 #endif // DEBUG
976 set timer to zero... it is initialized properly later, so we check
977 if timer is zero in the destructor, and assume that the window is not
978 fully constructed if timer is zero...
980 timer = (bt::Timer*) 0;
981 blackbox = b;
982 client.window = w;
983 _screen = s;
984 lastButtonPressTime = 0;
987 the server needs to be grabbed here to prevent client's from sending
988 events while we are in the process of managing their window.
989 We hold the grab until after we are done moving the window around.
992 blackbox->XGrabServer();
994 // fetch client size and placement
995 XWindowAttributes wattrib;
996 if (! XGetWindowAttributes(blackbox->XDisplay(),
997 client.window, &wattrib) ||
998 ! wattrib.screen || wattrib.override_redirect) {
999 #ifdef DEBUG
1000 fprintf(stderr,
1001 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
1002 #endif // DEBUG
1004 blackbox->XUngrabServer();
1005 delete this;
1006 return;
1009 // set the eventmask early in the game so that we make sure we get
1010 // all the events we are interested in
1011 XSetWindowAttributes attrib_set;
1012 attrib_set.event_mask = ::client_window_event_mask;
1013 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
1014 ButtonMotionMask;
1015 XChangeWindowAttributes(blackbox->XDisplay(), client.window,
1016 CWEventMask|CWDontPropagate, &attrib_set);
1018 client.colormap = wattrib.colormap;
1019 window_number = bt::BSENTINEL;
1020 client.strut = 0;
1022 set the initial size and location of client window (relative to the
1023 _root window_). This position is the reference point used with the
1024 window's gravity to find the window's initial position.
1026 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1027 client.premax = client.rect;
1028 client.old_bw = wattrib.border_width;
1029 client.current_state = NormalState;
1031 frame.window = frame.plate = frame.title = frame.handle = None;
1032 frame.close_button = frame.iconify_button = frame.maximize_button = None;
1033 frame.right_grip = frame.left_grip = None;
1034 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
1035 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
1036 frame.pbutton = frame.ugrip = frame.fgrip = None;
1038 timer = new bt::Timer(blackbox, this);
1039 timer->setTimeout(blackbox->resource().autoRaiseDelay());
1041 client.title = ::readWMName(blackbox, client.window);
1042 client.icon_title = ::readWMIconName(blackbox, client.window);
1044 // get size, aspect, minimum/maximum size, ewmh and other hints set
1045 // by the client
1046 client.ewmh = ::readEWMH(blackbox->ewmh(), client.window,
1047 _screen->currentWorkspace());
1048 client.motif = ::readMotifWMHints(blackbox, client.window);
1049 client.wmhints = ::readWMHints(blackbox, client.window);
1050 client.wmnormal = ::readWMNormalHints(blackbox, client.window,
1051 _screen->screenInfo());
1052 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
1053 client.transient_for = ::readTransientInfo(blackbox, client.window,
1054 _screen->screenInfo(),
1055 client.wmhints);
1057 if (client.wmhints.window_group != None)
1058 (void) ::update_window_group(client.wmhints.window_group, blackbox, this);
1060 if (isTransient()) {
1061 // add ourselves to our transient_for
1062 BlackboxWindow *win = findTransientFor();
1063 if (win) {
1064 win->addTransient(this);
1065 client.ewmh.workspace = win->workspace();
1066 setLayer(win->layer());
1067 } else if (isGroupTransient()) {
1068 BWindowGroup *group = findWindowGroup();
1069 if (group)
1070 group->addTransient(this);
1071 } else {
1072 // broken client
1073 client.transient_for = 0;
1077 client.state.visible = false;
1078 client.state.iconic = false;
1079 client.state.moving = false;
1080 client.state.resizing = false;
1081 client.state.focused = false;
1083 switch (windowType()) {
1084 case WindowTypeDesktop:
1085 setLayer(StackingList::LayerDesktop);
1086 break;
1088 case WindowTypeDock:
1089 setLayer(StackingList::LayerAbove);
1090 // fallthrough intended
1092 default:
1093 if (client.ewmh.above)
1094 setLayer(StackingList::LayerAbove);
1095 else if (client.ewmh.below)
1096 setLayer(StackingList::LayerBelow);
1097 break;
1098 } // switch
1100 ::update_decorations(client.decorations,
1101 client.functions,
1102 isTransient(),
1103 client.ewmh,
1104 client.motif,
1105 client.wmnormal,
1106 client.wmprotocols);
1108 // sanity checks
1109 if (client.wmhints.initial_state == IconicState
1110 && !hasWindowFunction(WindowFunctionIconify))
1111 client.wmhints.initial_state = NormalState;
1112 if (isMaximized() && !hasWindowFunction(WindowFunctionMaximize))
1113 client.ewmh.maxv = client.ewmh.maxh = false;
1114 if (isFullScreen() && !hasWindowFunction(WindowFunctionFullScreen))
1115 client.ewmh.fullscreen = false;
1117 bt::EWMH::Strut strut;
1118 if (blackbox->ewmh().readWMStrut(client.window, &strut)) {
1119 client.strut = new bt::EWMH::Strut;
1120 *client.strut = strut;
1121 _screen->addStrut(client.strut);
1125 if we just managed the group leader for an existing group, move
1126 all group transients to this window
1129 BWindowGroup *group = blackbox->findWindowGroup(client.window);
1130 if (group) {
1131 BlackboxWindowList transientList = group->transients();
1132 BlackboxWindowList::const_iterator it = transientList.begin();
1133 const BlackboxWindowList::const_iterator end = transientList.end();
1134 for (; it != end; ++it) {
1135 BlackboxWindow * const w1 = *it;
1136 if (w1->client.transient_for != client.window)
1137 continue;
1138 group->removeTransient(w1);
1139 addTransient(w1);
1140 w1->changeWorkspace(workspace());
1141 w1->changeLayer(layer());
1146 frame.window = createToplevelWindow();
1147 blackbox->insertEventHandler(frame.window, this);
1149 frame.plate = createChildWindow(frame.window, NoEventMask);
1150 blackbox->insertEventHandler(frame.plate, this);
1152 if (client.decorations & WindowDecorationTitlebar)
1153 createTitlebar();
1155 if (client.decorations & WindowDecorationHandle)
1156 createHandle();
1158 // apply the size and gravity to the frame
1159 const WindowStyle &style = _screen->resource().windowStyle();
1160 frame.margin = ::update_margin(client.decorations, style);
1161 frame.rect = ::applyGravity(client.rect,
1162 frame.margin,
1163 client.wmnormal.win_gravity);
1165 associateClientWindow();
1167 blackbox->insertEventHandler(client.window, this);
1168 blackbox->insertWindow(client.window, this);
1169 blackbox->insertWindow(frame.plate, this);
1171 // preserve the window's initial state on first map, and its current
1172 // state across a restart
1173 if (!readState(client.current_state, blackbox, client.window))
1174 client.current_state = client.wmhints.initial_state;
1176 if (client.state.iconic) {
1177 // prepare the window to be iconified
1178 client.current_state = IconicState;
1179 client.state.iconic = False;
1180 } else if (workspace() != bt::BSENTINEL &&
1181 workspace() != _screen->currentWorkspace()) {
1182 client.current_state = WithdrawnState;
1185 blackbox->XUngrabServer();
1187 grabButtons();
1189 XMapSubwindows(blackbox->XDisplay(), frame.window);
1191 if (isFullScreen()) {
1192 client.ewmh.fullscreen = false; // trick setFullScreen into working
1193 setFullScreen(true);
1194 } else {
1195 if (isMaximized()) {
1196 remaximize();
1197 } else {
1198 const unsigned long save_state = client.current_state;
1200 bt::Rect r = frame.rect;
1201 // trick configure into working
1202 frame.rect = bt::Rect();
1203 configure(r);
1205 if (isShaded()) {
1206 client.ewmh.shaded = false;
1207 setShaded(true);
1210 if (isShaded() && save_state != IconicState) {
1212 At this point in the life of a window, current_state should
1213 only be set to IconicState if the window was an *icon*, not
1214 if it was shaded.
1216 client.current_state = save_state;
1223 BlackboxWindow::~BlackboxWindow(void) {
1224 #ifdef DEBUG
1225 fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
1226 client.window);
1227 #endif // DEBUG
1229 if (! timer) // window not managed...
1230 return;
1232 if (client.state.moving || client.state.resizing) {
1233 _screen->hideGeometry();
1234 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
1237 delete timer;
1239 if (client.strut) {
1240 _screen->removeStrut(client.strut);
1241 delete client.strut;
1244 BWindowGroup *group = findWindowGroup();
1246 if (isTransient()) {
1247 // remove ourselves from our transient_for
1248 BlackboxWindow *win = findTransientFor();
1249 if (win) {
1250 win->removeTransient(this);
1251 } else if (isGroupTransient()) {
1252 if (group)
1253 group->removeTransient(this);
1255 client.transient_for = 0;
1258 if (group)
1259 group->removeWindow(this);
1261 if (frame.title)
1262 destroyTitlebar();
1264 if (frame.handle)
1265 destroyHandle();
1267 blackbox->removeEventHandler(client.window);
1268 blackbox->removeWindow(client.window);
1270 blackbox->removeEventHandler(frame.plate);
1271 blackbox->removeWindow(frame.plate);
1272 XDestroyWindow(blackbox->XDisplay(), frame.plate);
1274 blackbox->removeEventHandler(frame.window);
1275 XDestroyWindow(blackbox->XDisplay(), frame.window);
1280 * Creates a new top level window, with a given location, size, and border
1281 * width.
1282 * Returns: the newly created window
1284 Window BlackboxWindow::createToplevelWindow(void) {
1285 XSetWindowAttributes attrib_create;
1286 unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
1288 attrib_create.colormap = _screen->screenInfo().colormap();
1289 attrib_create.override_redirect = True;
1290 attrib_create.event_mask = EnterWindowMask | LeaveWindowMask;
1292 return XCreateWindow(blackbox->XDisplay(),
1293 _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
1294 _screen->screenInfo().depth(), InputOutput,
1295 _screen->screenInfo().visual(),
1296 create_mask, &attrib_create);
1301 * Creates a child window, and optionally associates a given cursor with
1302 * the new window.
1304 Window BlackboxWindow::createChildWindow(Window parent,
1305 unsigned long event_mask,
1306 Cursor cursor) {
1307 XSetWindowAttributes attrib_create;
1308 unsigned long create_mask = CWEventMask;
1310 attrib_create.event_mask = event_mask;
1312 if (cursor) {
1313 create_mask |= CWCursor;
1314 attrib_create.cursor = cursor;
1317 return XCreateWindow(blackbox->XDisplay(), parent, 0, 0, 1, 1, 0,
1318 _screen->screenInfo().depth(), InputOutput,
1319 _screen->screenInfo().visual(),
1320 create_mask, &attrib_create);
1325 * Reparents the client window into the newly created frame.
1327 * Note: the server must be grabbed before calling this function.
1329 void BlackboxWindow::associateClientWindow(void) {
1330 XSetWindowBorderWidth(blackbox->XDisplay(), client.window, 0);
1331 XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeInsert);
1333 XSelectInput(blackbox->XDisplay(), frame.plate,
1334 FocusChangeMask | SubstructureRedirectMask);
1336 XSelectInput(blackbox->XDisplay(), client.window,
1337 client_window_event_mask & ~StructureNotifyMask);
1338 XReparentWindow(blackbox->XDisplay(), client.window, frame.plate, 0, 0);
1339 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
1341 #ifdef SHAPE
1342 if (blackbox->hasShapeExtensions()) {
1343 XShapeSelectInput(blackbox->XDisplay(), client.window,
1344 ShapeNotifyMask);
1346 Bool shaped = False;
1347 int foo;
1348 unsigned int ufoo;
1350 XShapeQueryExtents(blackbox->XDisplay(), client.window, &shaped,
1351 &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
1352 &ufoo, &ufoo);
1353 client.state.shaped = shaped;
1355 #endif // SHAPE
1359 void BlackboxWindow::decorate(void) {
1360 const WindowStyle &style = _screen->resource().windowStyle();
1361 if (client.decorations & WindowDecorationTitlebar) {
1362 // render focused button texture
1363 frame.fbutton =
1364 bt::PixmapCache::find(_screen->screenNumber(),
1365 style.focus.button,
1366 style.button_width,
1367 style.button_width,
1368 frame.fbutton);
1370 // render unfocused button texture
1371 frame.ubutton =
1372 bt::PixmapCache::find(_screen->screenNumber(),
1373 style.unfocus.button,
1374 style.button_width,
1375 style.button_width,
1376 frame.ubutton);
1378 // render pressed button texture
1379 frame.pbutton =
1380 bt::PixmapCache::find(_screen->screenNumber(),
1381 style.pressed,
1382 style.button_width,
1383 style.button_width,
1384 frame.pbutton);
1386 // render focused titlebar texture
1387 frame.ftitle =
1388 bt::PixmapCache::find(_screen->screenNumber(),
1389 style.focus.title,
1390 frame.rect.width(),
1391 style.title_height,
1392 frame.ftitle);
1394 // render unfocused titlebar texture
1395 frame.utitle =
1396 bt::PixmapCache::find(_screen->screenNumber(),
1397 style.unfocus.title,
1398 frame.rect.width(),
1399 style.title_height,
1400 frame.utitle);
1402 // render focused label texture
1403 frame.flabel =
1404 bt::PixmapCache::find(_screen->screenNumber(),
1405 style.focus.label,
1406 frame.label_w,
1407 style.label_height,
1408 frame.flabel);
1410 // render unfocused label texture
1411 frame.ulabel =
1412 bt::PixmapCache::find(_screen->screenNumber(),
1413 style.unfocus.label,
1414 frame.label_w,
1415 style.label_height,
1416 frame.ulabel);
1419 if (client.decorations & WindowDecorationHandle) {
1420 frame.fhandle =
1421 bt::PixmapCache::find(_screen->screenNumber(),
1422 style.focus.handle,
1423 frame.rect.width(),
1424 style.handle_height,
1425 frame.fhandle);
1427 frame.uhandle =
1428 bt::PixmapCache::find(_screen->screenNumber(),
1429 style.unfocus.handle,
1430 frame.rect.width(),
1431 style.handle_height,
1432 frame.uhandle);
1435 if (client.decorations & WindowDecorationGrip) {
1436 frame.fgrip =
1437 bt::PixmapCache::find(_screen->screenNumber(),
1438 style.focus.grip,
1439 style.grip_width,
1440 style.handle_height,
1441 frame.fgrip);
1443 frame.ugrip =
1444 bt::PixmapCache::find(_screen->screenNumber(),
1445 style.unfocus.grip,
1446 style.grip_width,
1447 style.handle_height,
1448 frame.ugrip);
1453 void BlackboxWindow::createHandle(void) {
1454 frame.handle = createChildWindow(frame.window,
1455 ButtonPressMask | ButtonReleaseMask |
1456 ButtonMotionMask | ExposureMask);
1457 blackbox->insertEventHandler(frame.handle, this);
1459 if (client.decorations & WindowDecorationGrip)
1460 createGrips();
1464 void BlackboxWindow::destroyHandle(void) {
1465 if (frame.left_grip || frame.right_grip)
1466 destroyGrips();
1468 if (frame.fhandle) bt::PixmapCache::release(frame.fhandle);
1469 if (frame.uhandle) bt::PixmapCache::release(frame.uhandle);
1471 frame.fhandle = frame.uhandle = None;
1473 blackbox->removeEventHandler(frame.handle);
1474 XDestroyWindow(blackbox->XDisplay(), frame.handle);
1475 frame.handle = None;
1479 void BlackboxWindow::createGrips(void) {
1480 frame.left_grip =
1481 createChildWindow(frame.handle,
1482 ButtonPressMask | ButtonReleaseMask |
1483 ButtonMotionMask | ExposureMask,
1484 blackbox->resource().cursors().resize_bottom_left);
1485 blackbox->insertEventHandler(frame.left_grip, this);
1487 frame.right_grip =
1488 createChildWindow(frame.handle,
1489 ButtonPressMask | ButtonReleaseMask |
1490 ButtonMotionMask | ExposureMask,
1491 blackbox->resource().cursors().resize_bottom_right);
1492 blackbox->insertEventHandler(frame.right_grip, this);
1496 void BlackboxWindow::destroyGrips(void) {
1497 if (frame.fgrip) bt::PixmapCache::release(frame.fgrip);
1498 if (frame.ugrip) bt::PixmapCache::release(frame.ugrip);
1500 frame.fgrip = frame.ugrip = None;
1502 blackbox->removeEventHandler(frame.left_grip);
1503 blackbox->removeEventHandler(frame.right_grip);
1505 XDestroyWindow(blackbox->XDisplay(), frame.left_grip);
1506 XDestroyWindow(blackbox->XDisplay(), frame.right_grip);
1507 frame.left_grip = frame.right_grip = None;
1511 void BlackboxWindow::createTitlebar(void) {
1512 frame.title = createChildWindow(frame.window,
1513 ButtonPressMask | ButtonReleaseMask |
1514 ButtonMotionMask | ExposureMask);
1515 frame.label = createChildWindow(frame.title,
1516 ButtonPressMask | ButtonReleaseMask |
1517 ButtonMotionMask | ExposureMask);
1518 blackbox->insertEventHandler(frame.title, this);
1519 blackbox->insertEventHandler(frame.label, this);
1521 if (client.decorations & WindowDecorationIconify) createIconifyButton();
1522 if (client.decorations & WindowDecorationMaximize) createMaximizeButton();
1523 if (client.decorations & WindowDecorationClose) createCloseButton();
1527 void BlackboxWindow::destroyTitlebar(void) {
1528 if (frame.close_button)
1529 destroyCloseButton();
1531 if (frame.iconify_button)
1532 destroyIconifyButton();
1534 if (frame.maximize_button)
1535 destroyMaximizeButton();
1537 if (frame.fbutton) bt::PixmapCache::release(frame.fbutton);
1538 if (frame.ubutton) bt::PixmapCache::release(frame.ubutton);
1539 if (frame.pbutton) bt::PixmapCache::release(frame.pbutton);
1540 if (frame.ftitle) bt::PixmapCache::release(frame.ftitle);
1541 if (frame.utitle) bt::PixmapCache::release(frame.utitle);
1542 if (frame.flabel) bt::PixmapCache::release(frame.flabel);
1543 if (frame.ulabel) bt::PixmapCache::release(frame.ulabel);
1545 frame.fbutton = frame.ubutton = frame.pbutton =
1546 frame.ftitle = frame.utitle =
1547 frame.flabel = frame.ulabel = None;
1549 blackbox->removeEventHandler(frame.title);
1550 blackbox->removeEventHandler(frame.label);
1552 XDestroyWindow(blackbox->XDisplay(), frame.label);
1553 XDestroyWindow(blackbox->XDisplay(), frame.title);
1554 frame.title = frame.label = None;
1558 void BlackboxWindow::createCloseButton(void) {
1559 if (frame.title != None) {
1560 frame.close_button = createChildWindow(frame.title,
1561 ButtonPressMask |
1562 ButtonReleaseMask |
1563 ButtonMotionMask | ExposureMask);
1564 blackbox->insertEventHandler(frame.close_button, this);
1569 void BlackboxWindow::destroyCloseButton(void) {
1570 blackbox->removeEventHandler(frame.close_button);
1571 XDestroyWindow(blackbox->XDisplay(), frame.close_button);
1572 frame.close_button = None;
1576 void BlackboxWindow::createIconifyButton(void) {
1577 if (frame.title != None) {
1578 frame.iconify_button = createChildWindow(frame.title,
1579 ButtonPressMask |
1580 ButtonReleaseMask |
1581 ButtonMotionMask | ExposureMask);
1582 blackbox->insertEventHandler(frame.iconify_button, this);
1587 void BlackboxWindow::destroyIconifyButton(void) {
1588 blackbox->removeEventHandler(frame.iconify_button);
1589 XDestroyWindow(blackbox->XDisplay(), frame.iconify_button);
1590 frame.iconify_button = None;
1594 void BlackboxWindow::createMaximizeButton(void) {
1595 if (frame.title != None) {
1596 frame.maximize_button = createChildWindow(frame.title,
1597 ButtonPressMask |
1598 ButtonReleaseMask |
1599 ButtonMotionMask | ExposureMask);
1600 blackbox->insertEventHandler(frame.maximize_button, this);
1605 void BlackboxWindow::destroyMaximizeButton(void) {
1606 blackbox->removeEventHandler(frame.maximize_button);
1607 XDestroyWindow(blackbox->XDisplay(), frame.maximize_button);
1608 frame.maximize_button = None;
1612 void BlackboxWindow::positionButtons(bool redecorate_label) {
1613 // we need to use signed ints here to detect windows that are too small
1614 const WindowStyle &style = _screen->resource().windowStyle();
1615 const int extra = style.title_margin == 0 ?
1616 style.focus.button.borderWidth() : 0,
1617 bw = style.button_width + style.title_margin
1618 - extra,
1619 by = style.title_margin +
1620 style.focus.title.borderWidth();
1621 int lx = by, lw = frame.rect.width() - by;
1623 if (client.decorations & WindowDecorationIconify) {
1624 if (frame.iconify_button == None) createIconifyButton();
1626 XMoveResizeWindow(blackbox->XDisplay(), frame.iconify_button, by, by,
1627 style.button_width, style.button_width);
1628 XMapWindow(blackbox->XDisplay(), frame.iconify_button);
1630 lx += bw;
1631 lw -= bw;
1632 } else if (frame.iconify_button) {
1633 destroyIconifyButton();
1636 int bx = frame.rect.width() - bw
1637 - style.focus.title.borderWidth() - extra;
1639 if (client.decorations & WindowDecorationClose) {
1640 if (frame.close_button == None) createCloseButton();
1642 XMoveResizeWindow(blackbox->XDisplay(), frame.close_button, bx, by,
1643 style.button_width, style.button_width);
1644 XMapWindow(blackbox->XDisplay(), frame.close_button);
1646 bx -= bw;
1647 lw -= bw;
1648 } else if (frame.close_button) {
1649 destroyCloseButton();
1652 if (client.decorations & WindowDecorationMaximize) {
1653 if (frame.maximize_button == None) createMaximizeButton();
1655 XMoveResizeWindow(blackbox->XDisplay(), frame.maximize_button, bx, by,
1656 style.button_width, style.button_width);
1657 XMapWindow(blackbox->XDisplay(), frame.maximize_button);
1659 bx -= bw;
1660 lw -= bw;
1661 } else if (frame.maximize_button) {
1662 destroyMaximizeButton();
1665 if (lw > by) {
1666 frame.label_w = lw - by;
1667 XMoveResizeWindow(blackbox->XDisplay(), frame.label, lx, by,
1668 frame.label_w, style.label_height);
1669 XMapWindow(blackbox->XDisplay(), frame.label);
1671 if (redecorate_label) {
1672 frame.flabel =
1673 bt::PixmapCache::find(_screen->screenNumber(),
1674 style.focus.label,
1675 frame.label_w, style.label_height,
1676 frame.flabel);
1677 frame.ulabel =
1678 bt::PixmapCache::find(_screen->screenNumber(),
1679 style.unfocus.label,
1680 frame.label_w, style.label_height,
1681 frame.ulabel);
1684 const bt::ustring ellided =
1685 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
1686 _screen->screenNumber(), style.font);
1688 if (ellided != client.visible_title) {
1689 client.visible_title = ellided;
1690 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
1692 } else {
1693 frame.label_w = 1;
1694 XUnmapWindow(blackbox->XDisplay(), frame.label);
1697 redrawLabel();
1698 redrawAllButtons();
1702 void BlackboxWindow::reconfigure(void) {
1703 const WindowStyle &style = _screen->resource().windowStyle();
1704 if (isMaximized()) {
1705 // update the frame margin in case the style has changed
1706 frame.margin = ::update_margin(client.decorations, style);
1708 // make sure maximized windows have the correct size after a style
1709 // change
1710 remaximize();
1711 } else {
1712 // get the client window geometry as if it was unmanaged
1713 bt::Rect r = frame.rect;
1714 if (client.ewmh.shaded) {
1715 r.setHeight(client.rect.height() + frame.margin.top
1716 + frame.margin.bottom);
1718 r = ::restoreGravity(r, frame.margin, client.wmnormal.win_gravity);
1720 // update the frame margin in case the style has changed
1721 frame.margin = ::update_margin(client.decorations, style);
1723 // get the frame window geometry from the client window geometry
1724 // calculated above
1725 r = ::applyGravity(r, frame.margin, client.wmnormal.win_gravity);
1726 if (client.ewmh.shaded) {
1727 frame.rect = r;
1729 positionWindows();
1730 decorate();
1732 // keep the window shaded
1733 frame.rect.setHeight(style.title_height);
1734 XResizeWindow(blackbox->XDisplay(), frame.window,
1735 frame.rect.width(), frame.rect.height());
1736 } else {
1737 // trick configure into working
1738 frame.rect = bt::Rect();
1739 configure(r);
1743 ungrabButtons();
1744 grabButtons();
1748 void BlackboxWindow::grabButtons(void) {
1749 if (blackbox->resource().focusModel() == ClickToFocusModel
1750 || blackbox->resource().clickRaise())
1751 // grab button 1 for changing focus/raising
1752 blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
1753 GrabModeSync, GrabModeSync, frame.plate, None,
1754 blackbox->resource().allowScrollLock());
1756 if (hasWindowFunction(WindowFunctionMove))
1757 blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
1758 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1759 GrabModeAsync, frame.window,
1760 blackbox->resource().cursors().move,
1761 blackbox->resource().allowScrollLock());
1762 if (hasWindowFunction(WindowFunctionResize))
1763 blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
1764 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1765 GrabModeAsync, frame.window,
1766 None, blackbox->resource().allowScrollLock());
1767 // alt+middle lowers the window
1768 blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
1769 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1770 frame.window, None,
1771 blackbox->resource().allowScrollLock());
1773 blackbox->grabButton(Button3, Mod4Mask, frame.window, True,
1774 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1775 frame.window, None,
1776 blackbox->resource().allowScrollLock());
1780 void BlackboxWindow::ungrabButtons(void) {
1781 blackbox->ungrabButton(Button1, 0, frame.plate);
1782 blackbox->ungrabButton(Button1, Mod1Mask, frame.window);
1783 blackbox->ungrabButton(Button2, Mod1Mask, frame.window);
1784 blackbox->ungrabButton(Button3, Mod1Mask, frame.window);
1785 blackbox->ungrabButton(Button3, Mod4Mask, frame.window);
1789 void BlackboxWindow::positionWindows(void) {
1790 const WindowStyle &style = _screen->resource().windowStyle();
1791 const unsigned int bw = (hasWindowDecoration(WindowDecorationBorder)
1792 ? style.frame_border_width
1793 : 0);
1795 XMoveResizeWindow(blackbox->XDisplay(), frame.plate,
1796 frame.margin.left - bw,
1797 frame.margin.top - bw,
1798 client.rect.width(), client.rect.height());
1799 XSetWindowBorderWidth(blackbox->XDisplay(), frame.plate, bw);
1800 XMoveResizeWindow(blackbox->XDisplay(), client.window,
1801 0, 0, client.rect.width(), client.rect.height());
1802 // ensure client.rect contains the real location
1803 client.rect.setPos(frame.rect.left() + frame.margin.left,
1804 frame.rect.top() + frame.margin.top);
1806 if (client.decorations & WindowDecorationTitlebar) {
1807 if (frame.title == None) createTitlebar();
1809 XMoveResizeWindow(blackbox->XDisplay(), frame.title,
1810 0, 0, frame.rect.width(), style.title_height);
1812 positionButtons();
1813 XMapSubwindows(blackbox->XDisplay(), frame.title);
1814 XMapWindow(blackbox->XDisplay(), frame.title);
1815 } else if (frame.title) {
1816 destroyTitlebar();
1819 if (client.decorations & WindowDecorationHandle) {
1820 if (frame.handle == None) createHandle();
1822 // use client.rect here so the value is correct even if shaded
1823 XMoveResizeWindow(blackbox->XDisplay(), frame.handle,
1824 0, client.rect.height() + frame.margin.top,
1825 frame.rect.width(), style.handle_height);
1827 if (client.decorations & WindowDecorationGrip) {
1828 if (frame.left_grip == None || frame.right_grip == None) createGrips();
1830 XMoveResizeWindow(blackbox->XDisplay(), frame.left_grip, 0, 0,
1831 style.grip_width, style.handle_height);
1833 const int nx = frame.rect.width() - style.grip_width;
1834 XMoveResizeWindow(blackbox->XDisplay(), frame.right_grip, nx, 0,
1835 style.grip_width, style.handle_height);
1837 XMapSubwindows(blackbox->XDisplay(), frame.handle);
1838 } else {
1839 destroyGrips();
1842 XMapWindow(blackbox->XDisplay(), frame.handle);
1843 } else if (frame.handle) {
1844 destroyHandle();
1850 * This function is responsible for updating both the client and the
1851 * frame rectangles. According to the ICCCM a client message is not
1852 * sent for a resize, only a move.
1854 void BlackboxWindow::configure(int dx, int dy,
1855 unsigned int dw, unsigned int dh) {
1856 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1857 ! client.state.moving);
1859 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1860 frame.rect.setRect(dx, dy, dw, dh);
1862 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1863 frame.rect.setPos(0, 0);
1865 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1866 frame.rect.top() + frame.margin.top,
1867 frame.rect.right() - frame.margin.right,
1868 frame.rect.bottom() - frame.margin.bottom);
1870 #ifdef SHAPE
1871 if (client.state.shaped)
1872 configureShape();
1873 #endif // SHAPE
1875 XMoveResizeWindow(blackbox->XDisplay(), frame.window,
1876 frame.rect.x(), frame.rect.y(),
1877 frame.rect.width(), frame.rect.height());
1879 positionWindows();
1880 decorate();
1881 redrawWindowFrame();
1882 } else {
1883 frame.rect.setPos(dx, dy);
1885 XMoveWindow(blackbox->XDisplay(), frame.window,
1886 frame.rect.x(), frame.rect.y());
1888 we may have been called just after an opaque window move, so
1889 even though the old coords match the new ones no ConfigureNotify
1890 has been sent yet. There are likely other times when this will
1891 be relevant as well.
1893 if (! client.state.moving) send_event = True;
1896 if (send_event) {
1897 // if moving, the update and event will occur when the move finishes
1898 client.rect.setPos(frame.rect.left() + frame.margin.left,
1899 frame.rect.top() + frame.margin.top);
1901 XEvent event;
1902 event.type = ConfigureNotify;
1904 event.xconfigure.display = blackbox->XDisplay();
1905 event.xconfigure.event = client.window;
1906 event.xconfigure.window = client.window;
1907 event.xconfigure.x = client.rect.x();
1908 event.xconfigure.y = client.rect.y();
1909 event.xconfigure.width = client.rect.width();
1910 event.xconfigure.height = client.rect.height();
1911 event.xconfigure.border_width = client.old_bw;
1912 event.xconfigure.above = frame.window;
1913 event.xconfigure.override_redirect = False;
1915 XSendEvent(blackbox->XDisplay(), client.window, False,
1916 StructureNotifyMask, &event);
1921 #ifdef SHAPE
1922 void BlackboxWindow::configureShape(void) {
1923 XShapeCombineShape(blackbox->XDisplay(), frame.window, ShapeBounding,
1924 frame.margin.left, frame.margin.top,
1925 client.window, ShapeBounding, ShapeSet);
1927 int num = 0;
1928 XRectangle xrect[2];
1930 const WindowStyle &style = _screen->resource().windowStyle();
1931 if (client.decorations & WindowDecorationTitlebar) {
1932 xrect[0].x = xrect[0].y = 0;
1933 xrect[0].width = frame.rect.width();
1934 xrect[0].height = style.title_height;
1935 ++num;
1938 if (client.decorations & WindowDecorationHandle) {
1939 xrect[1].x = 0;
1940 xrect[1].y = client.rect.height() + frame.margin.top;
1941 xrect[1].width = frame.rect.width();
1942 xrect[1].height = style.handle_height;
1943 ++num;
1946 XShapeCombineRectangles(blackbox->XDisplay(), frame.window,
1947 ShapeBounding, 0, 0, xrect, num,
1948 ShapeUnion, Unsorted);
1950 #endif // SHAPE
1953 void BlackboxWindow::addTransient(BlackboxWindow *win)
1954 { client.transientList.push_front(win); }
1957 void BlackboxWindow::removeTransient(BlackboxWindow *win)
1958 { client.transientList.remove(win); }
1961 BlackboxWindow *BlackboxWindow::findTransientFor(void) const {
1962 BlackboxWindow *win = 0;
1963 if (isTransient()) {
1964 win = blackbox->findWindow(client.transient_for);
1965 if (win && win->_screen != _screen)
1966 win = 0;
1968 return win;
1973 walk up to either 1) a non-transient window 2) a group transient,
1974 watching out for a circular chain
1976 this function returns zero for non-transient windows
1978 BlackboxWindow *BlackboxWindow::findNonTransientParent(void) const {
1979 BlackboxWindowList seen;
1980 seen.push_back(const_cast<BlackboxWindow *>(this));
1982 BlackboxWindow *w = findTransientFor();
1983 if (!w)
1984 return 0;
1986 while (w->isTransient() && !w->isGroupTransient()) {
1987 seen.push_back(w);
1988 BlackboxWindow * const tmp = w->findTransientFor();
1989 if (!tmp)
1990 break;
1991 if (std::find(seen.begin(), seen.end(), tmp) != seen.end()) {
1992 // circular transient chain
1993 break;
1995 w = tmp;
1997 return w;
2002 Returns a list of all transients. This is recursive, so it returns
2003 all transients of transients as well.
2005 BlackboxWindowList BlackboxWindow::buildFullTransientList(void) const {
2006 BlackboxWindowList all = client.transientList;
2007 BlackboxWindowList::const_iterator it = client.transientList.begin(),
2008 end = client.transientList.end();
2009 for (; it != end; ++it) {
2010 BlackboxWindowList x = (*it)->buildFullTransientList();
2011 all.splice(all.end(), x);
2013 return all;
2017 BWindowGroup *BlackboxWindow::findWindowGroup(void) const {
2018 BWindowGroup *group = 0;
2019 if (client.wmhints.window_group)
2020 group = blackbox->findWindowGroup(client.wmhints.window_group);
2021 return group;
2025 void BlackboxWindow::setWorkspace(unsigned int new_workspace) {
2026 client.ewmh.workspace = new_workspace;
2027 blackbox->ewmh().setWMDesktop(client.window, client.ewmh.workspace);
2031 void BlackboxWindow::changeWorkspace(unsigned int new_workspace,
2032 ChangeWorkspaceOption how) {
2033 if (client.ewmh.workspace == new_workspace)
2034 return;
2036 if (isTransient()) {
2037 BlackboxWindow *win = findTransientFor();
2038 if (win) {
2039 if (win->workspace() != new_workspace) {
2040 win->changeWorkspace(new_workspace, how);
2041 return;
2044 } else {
2045 assert(hasWindowFunction(WindowFunctionChangeWorkspace));
2048 Workspace *ws;
2049 if (workspace() != bt::BSENTINEL) {
2050 ws = _screen->findWorkspace(workspace());
2051 assert(ws != 0);
2052 ws->removeWindow(this);
2055 if (new_workspace != bt::BSENTINEL) {
2056 ws = _screen->findWorkspace(new_workspace);
2057 assert(ws != 0);
2058 ws->addWindow(this);
2061 switch (how) {
2062 case StayOnCurrentWorkspace:
2063 if (isVisible() && workspace() != bt::BSENTINEL
2064 && workspace() != _screen->currentWorkspace()) {
2065 hide();
2066 } else if (!isVisible()
2067 && (workspace() == bt::BSENTINEL
2068 || workspace() == _screen->currentWorkspace())) {
2069 show();
2071 break;
2073 case SwitchToNewWorkspace:
2075 we will change to the new workspace soon, so force this window
2076 to be visible
2078 show();
2079 break;
2082 // change workspace on all transients
2083 if (!client.transientList.empty()) {
2084 BlackboxWindowList::iterator it = client.transientList.begin(),
2085 end = client.transientList.end();
2086 for (; it != end; ++it)
2087 (*it)->changeWorkspace(new_workspace, how);
2092 void BlackboxWindow::changeLayer(StackingList::Layer new_layer) {
2093 if (layer() == new_layer)
2094 return;
2096 bool restack = false;
2097 if (isTransient()) {
2098 BlackboxWindow *win = findTransientFor();
2099 if (win) {
2100 if (win->layer() != new_layer) {
2101 win->changeLayer(new_layer);
2102 return;
2103 } else {
2104 restack = true;
2107 } else {
2108 assert(hasWindowFunction(WindowFunctionChangeLayer));
2109 restack = true;
2112 _screen->stackingList().changeLayer(this, new_layer);
2114 if (!client.transientList.empty()) {
2115 BlackboxWindowList::iterator it = client.transientList.begin();
2116 const BlackboxWindowList::iterator end = client.transientList.end();
2117 for (; it != end; ++it)
2118 (*it)->changeLayer(new_layer);
2121 if (restack)
2122 _screen->restackWindows();
2126 bool BlackboxWindow::setInputFocus(void) {
2127 if (!isVisible())
2128 return false;
2129 if (client.state.focused)
2130 return true;
2132 switch (windowType()) {
2133 case WindowTypeDock:
2135 Many docks have auto-hide features similar to the toolbar and
2136 slit... we don't want these things to be moved to the center of
2137 the screen when switching to an empty workspace.
2139 break;
2141 default:
2142 { const bt::Rect &scr = _screen->screenInfo().rect();
2143 if (!frame.rect.intersects(scr)) {
2144 // client is outside the screen, move it to the center
2145 configure(scr.x() + (scr.width() - frame.rect.width()) / 2,
2146 scr.y() + (scr.height() - frame.rect.height()) / 2,
2147 frame.rect.width(), frame.rect.height());
2149 break;
2154 pass focus to any modal transients, giving modal group transients
2155 higher priority
2157 BWindowGroup *group = findWindowGroup();
2158 if (group && !group->transients().empty()) {
2159 BlackboxWindowList::const_iterator it = group->transients().begin(),
2160 end = group->transients().end();
2161 for (; it != end; ++it) {
2162 BlackboxWindow * const tmp = *it;
2163 if (!tmp->isVisible() || !tmp->isModal())
2164 continue;
2165 if (tmp == this) {
2166 // we are the newest modal group transient
2167 break;
2169 if (isTransient()) {
2170 if (tmp == findNonTransientParent()) {
2171 // we are a transient of the modal group transient
2172 break;
2175 return tmp->setInputFocus();
2179 if (!client.transientList.empty()) {
2180 BlackboxWindowList::const_iterator it = client.transientList.begin(),
2181 end = client.transientList.end();
2182 for (; it != end; ++it) {
2183 BlackboxWindow * const tmp = *it;
2184 if (tmp->isVisible() && tmp->isModal())
2185 return tmp->setInputFocus();
2189 switch (windowType()) {
2190 case WindowTypeDock:
2191 return false;
2192 default:
2193 break;
2196 XSetInputFocus(blackbox->XDisplay(), client.window,
2197 RevertToPointerRoot, blackbox->XTime());
2199 if (client.wmprotocols.wm_take_focus) {
2200 XEvent ce;
2201 ce.xclient.type = ClientMessage;
2202 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2203 ce.xclient.display = blackbox->XDisplay();
2204 ce.xclient.window = client.window;
2205 ce.xclient.format = 32;
2206 ce.xclient.data.l[0] = blackbox->wmTakeFocusAtom();
2207 ce.xclient.data.l[1] = blackbox->XTime();
2208 ce.xclient.data.l[2] = 0l;
2209 ce.xclient.data.l[3] = 0l;
2210 ce.xclient.data.l[4] = 0l;
2211 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2214 return true;
2218 void BlackboxWindow::show(void) {
2219 if (client.state.visible)
2220 return;
2222 if (client.state.iconic)
2223 _screen->removeIcon(this);
2225 client.state.iconic = false;
2226 client.state.visible = true;
2227 setState(isShaded() ? IconicState : NormalState);
2229 XMapWindow(blackbox->XDisplay(), client.window);
2230 XMapSubwindows(blackbox->XDisplay(), frame.window);
2231 XMapWindow(blackbox->XDisplay(), frame.window);
2233 if (!client.transientList.empty()) {
2234 BlackboxWindowList::iterator it = client.transientList.begin(),
2235 end = client.transientList.end();
2236 for (; it != end; ++it)
2237 (*it)->show();
2240 #ifdef DEBUG
2241 int real_x, real_y;
2242 Window child;
2243 XTranslateCoordinates(blackbox->XDisplay(), client.window,
2244 _screen->screenInfo().rootWindow(),
2245 0, 0, &real_x, &real_y, &child);
2246 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
2247 client.rect.left(), client.rect.top(), real_x, real_y);
2248 assert(client.rect.left() == real_x && client.rect.top() == real_y);
2249 #endif
2253 void BlackboxWindow::hide(void) {
2254 if (!client.state.visible)
2255 return;
2257 client.state.visible = false;
2258 setState(client.state.iconic ? IconicState : client.current_state);
2260 XUnmapWindow(blackbox->XDisplay(), frame.window);
2263 * we don't want this XUnmapWindow call to generate an UnmapNotify
2264 * event, so we need to clear the event mask on client.window for a
2265 * split second. HOWEVER, since X11 is asynchronous, the window
2266 * could be destroyed in that split second, leaving us with a ghost
2267 * window... so, we need to do this while the X server is grabbed
2269 blackbox->XGrabServer();
2270 XSelectInput(blackbox->XDisplay(), client.window,
2271 client_window_event_mask & ~StructureNotifyMask);
2272 XUnmapWindow(blackbox->XDisplay(), client.window);
2273 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
2274 blackbox->XUngrabServer();
2278 void BlackboxWindow::close(void) {
2279 assert(hasWindowFunction(WindowFunctionClose));
2281 XEvent ce;
2282 ce.xclient.type = ClientMessage;
2283 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2284 ce.xclient.display = blackbox->XDisplay();
2285 ce.xclient.window = client.window;
2286 ce.xclient.format = 32;
2287 ce.xclient.data.l[0] = blackbox->wmDeleteWindowAtom();
2288 ce.xclient.data.l[1] = blackbox->XTime();
2289 ce.xclient.data.l[2] = 0l;
2290 ce.xclient.data.l[3] = 0l;
2291 ce.xclient.data.l[4] = 0l;
2292 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2296 void BlackboxWindow::activate(void) {
2297 if (workspace() != bt::BSENTINEL
2298 && workspace() != _screen->currentWorkspace())
2299 _screen->setCurrentWorkspace(workspace());
2300 if (client.state.iconic)
2301 show();
2302 if (client.ewmh.shaded)
2303 setShaded(false);
2304 if (setInputFocus())
2305 _screen->raiseWindow(this);
2309 void BlackboxWindow::iconify(void) {
2310 if (client.state.iconic)
2311 return;
2313 if (isTransient()) {
2314 BlackboxWindow *win = findTransientFor();
2315 if (win) {
2316 if (!win->isIconic()) {
2317 win->iconify();
2318 return;
2321 } else {
2322 assert(hasWindowFunction(WindowFunctionIconify));
2325 _screen->addIcon(this);
2327 client.state.iconic = true;
2328 hide();
2330 // iconify all transients
2331 if (!client.transientList.empty()) {
2332 BlackboxWindowList::iterator it = client.transientList.begin(),
2333 end = client.transientList.end();
2334 for (; it != end; ++it)
2335 (*it)->iconify();
2340 void BlackboxWindow::maximize(unsigned int button) {
2341 assert(hasWindowFunction(WindowFunctionMaximize));
2343 // any maximize operation always unshades
2344 client.ewmh.shaded = false;
2345 frame.rect.setHeight(client.rect.height() + frame.margin.top
2346 + frame.margin.bottom);
2348 if (isMaximized()) {
2349 // restore from maximized
2350 client.ewmh.maxh = client.ewmh.maxv = false;
2352 if (!isFullScreen()) {
2354 when a resize is begun, maximize(0) is called to clear any
2355 maximization flags currently set. Otherwise it still thinks
2356 it is maximized. so we do not need to call configure()
2357 because resizing will handle it
2359 if (! client.state.resizing) {
2360 bt::Rect r = ::applyGravity(client.premax,
2361 frame.margin,
2362 client.wmnormal.win_gravity);
2363 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2364 // trick configure into working
2365 frame.rect = bt::Rect();
2366 configure(r);
2369 redrawAllButtons(); // in case it is not called in configure()
2372 updateEWMHState();
2373 updateEWMHAllowedActions();
2374 return;
2377 switch (button) {
2378 case 1:
2379 client.ewmh.maxh = true;
2380 client.ewmh.maxv = true;
2381 break;
2383 case 2:
2384 client.ewmh.maxh = false;
2385 client.ewmh.maxv = true;
2386 break;
2388 case 3:
2389 client.ewmh.maxh = true;
2390 client.ewmh.maxv = false;
2391 break;
2393 default:
2394 assert(0);
2395 break;
2398 if (!isFullScreen()) {
2399 // go go gadget-maximize!
2400 bt::Rect r = _screen->availableArea();
2402 if (!client.ewmh.maxh) {
2403 r.setX(frame.rect.x());
2404 r.setWidth(frame.rect.width());
2406 if (!client.ewmh.maxv) {
2407 r.setY(frame.rect.y());
2408 r.setHeight(frame.rect.height());
2411 // store the current frame geometry, so that we can restore it later
2412 client.premax = ::restoreGravity(frame.rect,
2413 frame.margin,
2414 client.wmnormal.win_gravity);
2416 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2417 // trick configure into working
2418 frame.rect = bt::Rect();
2419 configure(r);
2422 updateEWMHState();
2423 updateEWMHAllowedActions();
2428 re-maximizes the window to take into account availableArea changes.
2430 note that unlike maximize(), the shaded state is preserved.
2432 void BlackboxWindow::remaximize(void) {
2433 if (isShaded()) {
2434 bt::Rect r = _screen->availableArea();
2436 if (!client.ewmh.maxh) {
2437 r.setX(frame.rect.x());
2438 r.setWidth(frame.rect.width());
2440 if (!client.ewmh.maxv) {
2441 r.setY(frame.rect.y());
2442 r.setHeight(frame.rect.height());
2445 frame.rect = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2447 positionWindows();
2448 decorate();
2450 // set the frame rect to the shaded size
2451 const WindowStyle &style = _screen->resource().windowStyle();
2452 frame.rect.setHeight(style.title_height);
2453 XResizeWindow(blackbox->XDisplay(), frame.window,
2454 frame.rect.width(), frame.rect.height());
2455 return;
2458 unsigned int button = 0u;
2459 if (client.ewmh.maxv) {
2460 button = (client.ewmh.maxh) ? 1u : 2u;
2461 } else if (client.ewmh.maxh) {
2462 button = (client.ewmh.maxv) ? 1u : 3u;
2465 // trick maximize() into working
2466 client.ewmh.maxh = client.ewmh.maxv = false;
2467 const bt::Rect tmp = client.premax;
2468 maximize(button);
2469 client.premax = tmp;
2473 void BlackboxWindow::setShaded(bool shaded) {
2474 assert(hasWindowFunction(WindowFunctionShade));
2476 if (client.ewmh.shaded == shaded)
2477 return;
2479 client.ewmh.shaded = shaded;
2480 if (!isShaded()) {
2481 if (isMaximized()) {
2482 remaximize();
2483 } else {
2484 // set the frame rect to the normal size
2485 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2486 frame.margin.bottom);
2488 XResizeWindow(blackbox->XDisplay(), frame.window,
2489 frame.rect.width(), frame.rect.height());
2492 setState(NormalState);
2493 } else {
2494 // set the frame rect to the shaded size
2495 const WindowStyle &style = _screen->resource().windowStyle();
2496 frame.rect.setHeight(style.title_height);
2498 XResizeWindow(blackbox->XDisplay(), frame.window,
2499 frame.rect.width(), frame.rect.height());
2501 setState(IconicState);
2506 void BlackboxWindow::setFullScreen(bool b) {
2507 assert(hasWindowFunction(WindowFunctionFullScreen));
2509 if (client.ewmh.fullscreen == b)
2510 return;
2512 // any fullscreen operation always unshades
2513 client.ewmh.shaded = false;
2514 frame.rect.setHeight(client.rect.height() + frame.margin.top
2515 + frame.margin.bottom);
2517 bool refocus = isFocused();
2518 client.ewmh.fullscreen = b;
2519 if (isFullScreen()) {
2520 // go go gadget-fullscreen!
2521 if (!isMaximized())
2522 client.premax = ::restoreGravity(frame.rect,
2523 frame.margin,
2524 client.wmnormal.win_gravity);
2526 // modify decorations, functions and frame margin
2527 client.decorations = NoWindowDecorations;
2528 client.functions &= ~(WindowFunctionMove |
2529 WindowFunctionResize |
2530 WindowFunctionShade);
2531 const WindowStyle &style = _screen->resource().windowStyle();
2532 frame.margin = ::update_margin(client.decorations, style);
2535 * Note: we don't call ::constrain() here, because many
2536 * applications are broken in this respect. Some specify a
2537 * max-size or aspect-ratio that simply doesn't cover the entire
2538 * screen. Let's try to be smarter than such applications and
2539 * simply cover the entire screen.
2542 // trick configure() into working
2543 frame.rect = bt::Rect();
2544 configure(_screen->screenInfo().rect());
2546 if (isVisible())
2547 changeLayer(StackingList::LayerFullScreen);
2549 updateEWMHState();
2550 updateEWMHAllowedActions();
2551 } else {
2552 // restore from fullscreen
2553 ::update_decorations(client.decorations,
2554 client.functions,
2555 isTransient(),
2556 client.ewmh,
2557 client.motif,
2558 client.wmnormal,
2559 client.wmprotocols);
2560 const WindowStyle &style = _screen->resource().windowStyle();
2561 frame.margin = ::update_margin(client.decorations, style);
2563 if (isVisible())
2564 changeLayer(StackingList::LayerNormal);
2566 if (isMaximized()) {
2567 remaximize();
2568 } else {
2569 bt::Rect r = ::applyGravity(client.premax,
2570 frame.margin,
2571 client.wmnormal.win_gravity);
2572 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2574 // trick configure into working
2575 frame.rect = bt::Rect();
2576 configure(r);
2578 updateEWMHState();
2579 updateEWMHAllowedActions();
2583 ungrabButtons();
2584 grabButtons();
2586 if (refocus)
2587 (void) setInputFocus();
2591 void BlackboxWindow::redrawWindowFrame(void) const {
2592 if (client.decorations & WindowDecorationTitlebar) {
2593 redrawTitle();
2594 redrawLabel();
2595 redrawAllButtons();
2598 if (client.decorations & WindowDecorationBorder) {
2599 const WindowStyle &style = _screen->resource().windowStyle();
2600 const bt::Color &c = (isFocused()
2601 ? style.focus.frame_border
2602 : style.unfocus.frame_border);
2603 XSetWindowBorder(blackbox->XDisplay(), frame.plate,
2604 c.pixel(_screen->screenNumber()));
2607 if (client.decorations & WindowDecorationHandle) {
2608 redrawHandle();
2610 if (client.decorations & WindowDecorationGrip)
2611 redrawGrips();
2616 void BlackboxWindow::setFocused(bool focused) {
2617 if (focused == client.state.focused)
2618 return;
2620 client.state.focused = isVisible() ? focused : false;
2622 if (isVisible()) {
2623 redrawWindowFrame();
2625 if (client.state.focused) {
2626 XInstallColormap(blackbox->XDisplay(), client.colormap);
2627 } else {
2628 if (client.ewmh.fullscreen && layer() != StackingList::LayerBelow)
2629 changeLayer(StackingList::LayerBelow);
2635 void BlackboxWindow::setState(unsigned long new_state) {
2636 client.current_state = new_state;
2638 unsigned long state[2];
2639 state[0] = client.current_state;
2640 state[1] = None;
2641 XChangeProperty(blackbox->XDisplay(), client.window,
2642 blackbox->wmStateAtom(), blackbox->wmStateAtom(), 32,
2643 PropModeReplace, (unsigned char *) state, 2);
2645 updateEWMHState();
2646 updateEWMHAllowedActions();
2650 void BlackboxWindow::updateEWMHState() {
2651 const bt::EWMH& ewmh = blackbox->ewmh();
2653 // set _NET_WM_STATE
2654 bt::EWMH::AtomList atoms;
2655 if (isModal())
2656 atoms.push_back(ewmh.wmStateModal());
2657 if (isShaded())
2658 atoms.push_back(ewmh.wmStateShaded());
2659 if (isIconic())
2660 atoms.push_back(ewmh.wmStateHidden());
2661 if (isFullScreen())
2662 atoms.push_back(ewmh.wmStateFullscreen());
2663 if (client.ewmh.maxh)
2664 atoms.push_back(ewmh.wmStateMaximizedHorz());
2665 if (client.ewmh.maxv)
2666 atoms.push_back(ewmh.wmStateMaximizedVert());
2667 if (client.ewmh.skip_taskbar)
2668 atoms.push_back(ewmh.wmStateSkipTaskbar());
2669 if (client.ewmh.skip_pager)
2670 atoms.push_back(ewmh.wmStateSkipPager());
2672 switch (layer()) {
2673 case StackingList::LayerAbove:
2674 atoms.push_back(ewmh.wmStateAbove());
2675 break;
2676 case StackingList::LayerBelow:
2677 atoms.push_back(ewmh.wmStateBelow());
2678 break;
2679 default:
2680 break;
2683 if (atoms.empty())
2684 ewmh.removeProperty(client.window, ewmh.wmState());
2685 else
2686 ewmh.setWMState(client.window, atoms);
2690 void BlackboxWindow::updateEWMHAllowedActions() {
2691 const bt::EWMH& ewmh = blackbox->ewmh();
2693 // set _NET_WM_ALLOWED_ACTIONS
2694 bt::EWMH::AtomList atoms;
2695 if (! client.state.iconic) {
2696 if (hasWindowFunction(WindowFunctionChangeWorkspace))
2697 atoms.push_back(ewmh.wmActionChangeDesktop());
2699 if (hasWindowFunction(WindowFunctionIconify))
2700 atoms.push_back(ewmh.wmActionMinimize());
2702 if (hasWindowFunction(WindowFunctionShade))
2703 atoms.push_back(ewmh.wmActionShade());
2705 if (hasWindowFunction(WindowFunctionMove))
2706 atoms.push_back(ewmh.wmActionMove());
2708 if (hasWindowFunction(WindowFunctionResize))
2709 atoms.push_back(ewmh.wmActionResize());
2711 if (hasWindowFunction(WindowFunctionMaximize)) {
2712 atoms.push_back(ewmh.wmActionMaximizeHorz());
2713 atoms.push_back(ewmh.wmActionMaximizeVert());
2716 atoms.push_back(ewmh.wmActionFullscreen());
2719 if (hasWindowFunction(WindowFunctionClose))
2720 atoms.push_back(ewmh.wmActionClose());
2722 if (atoms.empty())
2723 ewmh.removeProperty(client.window, ewmh.wmAllowedActions());
2724 else
2725 ewmh.setWMAllowedActions(client.window, atoms);
2729 void BlackboxWindow::redrawTitle(void) const {
2730 const WindowStyle &style = _screen->resource().windowStyle();
2731 const bt::Rect u(0, 0, frame.rect.width(), style.title_height);
2732 bt::drawTexture(_screen->screenNumber(),
2733 (client.state.focused
2734 ? style.focus.title
2735 : style.unfocus.title),
2736 frame.title, u, u,
2737 (client.state.focused
2738 ? frame.ftitle
2739 : frame.utitle));
2743 void BlackboxWindow::redrawLabel(void) const {
2744 const WindowStyle &style = _screen->resource().windowStyle();
2745 bt::Rect u(0, 0, frame.label_w, style.label_height);
2746 Pixmap p = (client.state.focused ? frame.flabel : frame.ulabel);
2747 if (p == ParentRelative) {
2748 const bt::Texture &texture =
2749 (isFocused() ? style.focus.title : style.unfocus.title);
2750 int offset = texture.borderWidth();
2751 if (client.decorations & WindowDecorationIconify)
2752 offset += style.button_width + style.title_margin;
2754 const bt::Rect t(-(style.title_margin + offset),
2755 -(style.title_margin + texture.borderWidth()),
2756 frame.rect.width(), style.title_height);
2757 bt::drawTexture(_screen->screenNumber(), texture, frame.label, t, u,
2758 (client.state.focused ? frame.ftitle : frame.utitle));
2759 } else {
2760 bt::drawTexture(_screen->screenNumber(),
2761 (client.state.focused
2762 ? style.focus.label
2763 : style.unfocus.label),
2764 frame.label, u, u, p);
2767 const bt::Pen pen(_screen->screenNumber(),
2768 ((client.state.focused)
2769 ? style.focus.text
2770 : style.unfocus.text));
2771 u.setCoords(u.left() + style.label_margin,
2772 u.top() + style.label_margin,
2773 u.right() - style.label_margin,
2774 u.bottom() - style.label_margin);
2775 bt::drawText(style.font, pen, frame.label, u,
2776 style.alignment, client.visible_title);
2780 void BlackboxWindow::redrawAllButtons(void) const {
2781 if (frame.iconify_button) redrawIconifyButton();
2782 if (frame.maximize_button) redrawMaximizeButton();
2783 if (frame.close_button) redrawCloseButton();
2787 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2788 const WindowStyle &style = _screen->resource().windowStyle();
2789 const bt::Rect u(0, 0, style.button_width, style.button_width);
2790 Pixmap p = (pressed ? frame.pbutton :
2791 (client.state.focused ? frame.fbutton : frame.ubutton));
2792 if (p == ParentRelative) {
2793 const bt::Texture &texture =
2794 (isFocused() ? style.focus.title : style.unfocus.title);
2795 const bt::Rect t(-(style.title_margin + texture.borderWidth()),
2796 -(style.title_margin + texture.borderWidth()),
2797 frame.rect.width(), style.title_height);
2798 bt::drawTexture(_screen->screenNumber(), texture, frame.iconify_button,
2799 t, u, (client.state.focused
2800 ? frame.ftitle
2801 : frame.utitle));
2802 } else {
2803 bt::drawTexture(_screen->screenNumber(),
2804 (pressed ? style.pressed :
2805 (client.state.focused ? style.focus.button :
2806 style.unfocus.button)),
2807 frame.iconify_button, u, u, p);
2810 const bt::Pen pen(_screen->screenNumber(),
2811 (client.state.focused
2812 ? style.focus.foreground
2813 : style.unfocus.foreground));
2814 bt::drawBitmap(style.iconify, pen, frame.iconify_button, u);
2818 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2819 const WindowStyle &style = _screen->resource().windowStyle();
2820 const bt::Rect u(0, 0, style.button_width, style.button_width);
2821 Pixmap p = (pressed ? frame.pbutton :
2822 (client.state.focused ? frame.fbutton : frame.ubutton));
2823 if (p == ParentRelative) {
2824 const bt::Texture &texture =
2825 (isFocused() ? style.focus.title : style.unfocus.title);
2826 int button_w = style.button_width
2827 + style.title_margin + texture.borderWidth();
2828 if (client.decorations & WindowDecorationClose)
2829 button_w *= 2;
2830 const bt::Rect t(-(frame.rect.width() - button_w),
2831 -(style.title_margin + texture.borderWidth()),
2832 frame.rect.width(), style.title_height);
2833 bt::drawTexture(_screen->screenNumber(), texture, frame.maximize_button,
2834 t, u, (client.state.focused
2835 ? frame.ftitle
2836 : frame.utitle));
2837 } else {
2838 bt::drawTexture(_screen->screenNumber(),
2839 (pressed ? style.pressed :
2840 (client.state.focused ? style.focus.button :
2841 style.unfocus.button)),
2842 frame.maximize_button, u, u, p);
2845 const bt::Pen pen(_screen->screenNumber(),
2846 (client.state.focused
2847 ? style.focus.foreground
2848 : style.unfocus.foreground));
2849 bt::drawBitmap(isMaximized() ? style.restore : style.maximize,
2850 pen, frame.maximize_button, u);
2854 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2855 const WindowStyle &style = _screen->resource().windowStyle();
2856 const bt::Rect u(0, 0, style.button_width, style.button_width);
2857 Pixmap p = (pressed ? frame.pbutton :
2858 (client.state.focused ? frame.fbutton : frame.ubutton));
2859 if (p == ParentRelative) {
2860 const bt::Texture &texture =
2861 (isFocused() ? style.focus.title : style.unfocus.title);
2862 const int button_w = style.button_width +
2863 style.title_margin +
2864 texture.borderWidth();
2865 const bt::Rect t(-(frame.rect.width() - button_w),
2866 -(style.title_margin + texture.borderWidth()),
2867 frame.rect.width(), style.title_height);
2868 bt::drawTexture(_screen->screenNumber(),texture, frame.close_button, t, u,
2869 (client.state.focused ? frame.ftitle : frame.utitle));
2870 } else {
2871 bt::drawTexture(_screen->screenNumber(),
2872 (pressed ? style.pressed :
2873 (client.state.focused ? style.focus.button :
2874 style.unfocus.button)),
2875 frame.close_button, u, u, p);
2878 const bt::Pen pen(_screen->screenNumber(),
2879 (client.state.focused
2880 ? style.focus.foreground
2881 : style.unfocus.foreground));
2882 bt::drawBitmap(style.close, pen, frame.close_button, u);
2886 void BlackboxWindow::redrawHandle(void) const {
2887 const WindowStyle &style = _screen->resource().windowStyle();
2888 const bt::Rect u(0, 0, frame.rect.width(), style.handle_height);
2889 bt::drawTexture(_screen->screenNumber(),
2890 (client.state.focused ? style.focus.handle :
2891 style.unfocus.handle),
2892 frame.handle, u, u,
2893 (client.state.focused ? frame.fhandle : frame.uhandle));
2897 void BlackboxWindow::redrawGrips(void) const {
2898 const WindowStyle &style = _screen->resource().windowStyle();
2899 const bt::Rect u(0, 0, style.grip_width, style.handle_height);
2900 Pixmap p = (client.state.focused ? frame.fgrip : frame.ugrip);
2901 if (p == ParentRelative) {
2902 bt::Rect t(0, 0, frame.rect.width(), style.handle_height);
2903 bt::drawTexture(_screen->screenNumber(),
2904 (client.state.focused ? style.focus.handle :
2905 style.unfocus.handle),
2906 frame.right_grip, t, u, p);
2908 t.setPos(-(frame.rect.width() - style.grip_width), 0);
2909 bt::drawTexture(_screen->screenNumber(),
2910 (client.state.focused ? style.focus.handle :
2911 style.unfocus.handle),
2912 frame.right_grip, t, u, p);
2913 } else {
2914 bt::drawTexture(_screen->screenNumber(),
2915 (client.state.focused ? style.focus.grip :
2916 style.unfocus.grip),
2917 frame.left_grip, u, u, p);
2919 bt::drawTexture(_screen->screenNumber(),
2920 (client.state.focused ? style.focus.grip :
2921 style.unfocus.grip),
2922 frame.right_grip, u, u, p);
2927 void
2928 BlackboxWindow::clientMessageEvent(const XClientMessageEvent * const event) {
2929 if (event->format != 32)
2930 return;
2932 const bt::EWMH& ewmh = blackbox->ewmh();
2934 if (event->message_type == blackbox->wmChangeStateAtom()) {
2935 if (event->data.l[0] == IconicState) {
2936 if (hasWindowFunction(WindowFunctionIconify))
2937 iconify();
2938 } else if (event->data.l[0] == NormalState) {
2939 activate();
2941 } else if (event->message_type == ewmh.activeWindow()) {
2942 activate();
2943 } else if (event->message_type == ewmh.closeWindow()) {
2944 if (hasWindowFunction(WindowFunctionClose))
2945 close();
2946 } else if (event->message_type == ewmh.moveresizeWindow()) {
2947 XConfigureRequestEvent request;
2948 request.window = event->window;
2949 request.value_mask =
2950 (event->data.l[0] >> 8) & (CWX | CWY | CWWidth | CWHeight);
2951 request.x = event->data.l[1];
2952 request.y = event->data.l[2];
2953 request.width = event->data.l[3];
2954 request.height = event->data.l[4];
2956 const int gravity = (event->data.l[0] & 0xff);
2957 const int old_gravity = client.wmnormal.win_gravity;
2958 if (event->data.l[0] != 0)
2959 client.wmnormal.win_gravity = gravity;
2961 configureRequestEvent(&request);
2963 client.wmnormal.win_gravity = old_gravity;
2964 } else if (event->message_type == ewmh.wmDesktop()) {
2965 if (hasWindowFunction(WindowFunctionChangeWorkspace)) {
2966 const unsigned int new_workspace = event->data.l[0];
2967 changeWorkspace(new_workspace);
2969 } else if (event->message_type == ewmh.wmState()) {
2970 Atom action = event->data.l[0],
2971 first = event->data.l[1],
2972 second = event->data.l[2];
2974 if (first == ewmh.wmStateModal() || second == ewmh.wmStateModal()) {
2975 if ((action == ewmh.wmStateAdd() ||
2976 (action == ewmh.wmStateToggle() && ! client.ewmh.modal)) &&
2977 isTransient())
2978 client.ewmh.modal = true;
2979 else
2980 client.ewmh.modal = false;
2983 if (hasWindowFunction(WindowFunctionMaximize)) {
2984 int max_horz = 0, max_vert = 0;
2986 if (first == ewmh.wmStateMaximizedHorz() ||
2987 second == ewmh.wmStateMaximizedHorz()) {
2988 max_horz = ((action == ewmh.wmStateAdd()
2989 || (action == ewmh.wmStateToggle()
2990 && !client.ewmh.maxh))
2991 ? 1 : -1);
2994 if (first == ewmh.wmStateMaximizedVert() ||
2995 second == ewmh.wmStateMaximizedVert()) {
2996 max_vert = ((action == ewmh.wmStateAdd()
2997 || (action == ewmh.wmStateToggle()
2998 && !client.ewmh.maxv))
2999 ? 1 : -1);
3002 if (max_horz != 0 || max_vert != 0) {
3003 if (isMaximized())
3004 maximize(0);
3005 unsigned int button = 0u;
3006 if (max_horz == 1 && max_vert != 1)
3007 button = 3u;
3008 else if (max_vert == 1 && max_horz != 1)
3009 button = 2u;
3010 else if (max_vert == 1 && max_horz == 1)
3011 button = 1u;
3012 if (button)
3013 maximize(button);
3017 if (hasWindowFunction(WindowFunctionShade)) {
3018 if (first == ewmh.wmStateShaded() ||
3019 second == ewmh.wmStateShaded()) {
3020 if (action == ewmh.wmStateRemove())
3021 setShaded(false);
3022 else if (action == ewmh.wmStateAdd())
3023 setShaded(true);
3024 else if (action == ewmh.wmStateToggle())
3025 setShaded(!isShaded());
3029 if (first == ewmh.wmStateSkipTaskbar()
3030 || second == ewmh.wmStateSkipTaskbar()
3031 || first == ewmh.wmStateSkipPager()
3032 || second == ewmh.wmStateSkipPager()) {
3033 if (first == ewmh.wmStateSkipTaskbar()
3034 || second == ewmh.wmStateSkipTaskbar()) {
3035 client.ewmh.skip_taskbar = (action == ewmh.wmStateAdd()
3036 || (action == ewmh.wmStateToggle()
3037 && !client.ewmh.skip_taskbar));
3039 if (first == ewmh.wmStateSkipPager()
3040 || second == ewmh.wmStateSkipPager()) {
3041 client.ewmh.skip_pager = (action == ewmh.wmStateAdd()
3042 || (action == ewmh.wmStateToggle()
3043 && !client.ewmh.skip_pager));
3045 // we do nothing with skip_*, but others might... we should at
3046 // least make sure these are present in _NET_WM_STATE
3047 updateEWMHState();
3050 if (first == ewmh.wmStateHidden() ||
3051 second == ewmh.wmStateHidden()) {
3053 ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
3054 application
3058 if (hasWindowFunction(WindowFunctionFullScreen)) {
3059 if (first == ewmh.wmStateFullscreen() ||
3060 second == ewmh.wmStateFullscreen()) {
3061 if (action == ewmh.wmStateAdd() ||
3062 (action == ewmh.wmStateToggle() &&
3063 ! client.ewmh.fullscreen)) {
3064 setFullScreen(true);
3065 } else if (action == ewmh.wmStateToggle() ||
3066 action == ewmh.wmStateRemove()) {
3067 setFullScreen(false);
3072 if (hasWindowFunction(WindowFunctionChangeLayer)) {
3073 if (first == ewmh.wmStateAbove() ||
3074 second == ewmh.wmStateAbove()) {
3075 if (action == ewmh.wmStateAdd() ||
3076 (action == ewmh.wmStateToggle() &&
3077 layer() != StackingList::LayerAbove)) {
3078 changeLayer(StackingList::LayerAbove);
3079 } else if (action == ewmh.wmStateToggle() ||
3080 action == ewmh.wmStateRemove()) {
3081 changeLayer(StackingList::LayerNormal);
3085 if (first == ewmh.wmStateBelow() ||
3086 second == ewmh.wmStateBelow()) {
3087 if (action == ewmh.wmStateAdd() ||
3088 (action == ewmh.wmStateToggle() &&
3089 layer() != StackingList::LayerBelow)) {
3090 changeLayer(StackingList::LayerBelow);
3091 } else if (action == ewmh.wmStateToggle() ||
3092 action == ewmh.wmStateRemove()) {
3093 changeLayer(StackingList::LayerNormal);
3101 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent * const event) {
3102 if (event->window != client.window)
3103 return;
3105 #ifdef DEBUG
3106 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
3107 client.window);
3108 #endif // DEBUG
3110 _screen->releaseWindow(this);
3114 void
3115 BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent * const event) {
3116 if (event->window != client.window)
3117 return;
3119 #ifdef DEBUG
3120 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
3121 client.window);
3122 #endif // DEBUG
3124 _screen->releaseWindow(this);
3128 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent * const event) {
3129 if (event->window != client.window || event->parent == frame.plate)
3130 return;
3132 #ifdef DEBUG
3133 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
3134 "0x%lx.\n", client.window, event->parent);
3135 #endif // DEBUG
3138 put the ReparentNotify event back into the queue so that
3139 BlackboxWindow::restore(void) can do the right thing
3141 XEvent replay;
3142 replay.xreparent = *event;
3143 XPutBackEvent(blackbox->XDisplay(), &replay);
3145 _screen->releaseWindow(this);
3149 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent * const event) {
3150 #ifdef DEBUG
3151 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
3152 client.window);
3153 #endif
3155 switch(event->atom) {
3156 case XA_WM_TRANSIENT_FOR: {
3157 if (isTransient()) {
3158 // remove ourselves from our transient_for
3159 BlackboxWindow *win = findTransientFor();
3160 if (win) {
3161 win->removeTransient(this);
3162 } else if (isGroupTransient()) {
3163 BWindowGroup *group = findWindowGroup();
3164 if (group)
3165 group->removeTransient(this);
3169 // determine if this is a transient window
3170 client.transient_for = ::readTransientInfo(blackbox,
3171 client.window,
3172 _screen->screenInfo(),
3173 client.wmhints);
3175 if (isTransient()) {
3176 BlackboxWindow *win = findTransientFor();
3177 if (win) {
3178 // add ourselves to our new transient_for
3179 win->addTransient(this);
3180 changeWorkspace(win->workspace());
3181 changeLayer(win->layer());
3182 } else if (isGroupTransient()) {
3183 BWindowGroup *group = findWindowGroup();
3184 if (group)
3185 group->addTransient(this);
3186 } else {
3187 // broken client
3188 client.transient_for = 0;
3192 ::update_decorations(client.decorations,
3193 client.functions,
3194 isTransient(),
3195 client.ewmh,
3196 client.motif,
3197 client.wmnormal,
3198 client.wmprotocols);
3200 reconfigure();
3201 break;
3204 case XA_WM_HINTS: {
3205 // remove from current window group
3206 BWindowGroup *group = findWindowGroup();
3207 if (group) {
3208 if (isTransient() && !findTransientFor() && isGroupTransient())
3209 group->removeTransient(this);
3210 group->removeWindow(this);
3211 group = 0;
3214 client.wmhints = ::readWMHints(blackbox, client.window);
3216 if (client.wmhints.window_group != None) {
3217 // add to new window group
3218 group = ::update_window_group(client.wmhints.window_group,
3219 blackbox,
3220 this);
3221 if (isTransient() && !findTransientFor() && isGroupTransient()) {
3222 if (group)
3223 group->addTransient(this);
3226 break;
3229 case XA_WM_ICON_NAME: {
3230 client.icon_title = ::readWMIconName(blackbox, client.window);
3231 if (client.state.iconic)
3232 _screen->propagateWindowName(this);
3233 break;
3236 case XA_WM_NAME: {
3237 client.title = ::readWMName(blackbox, client.window);
3239 client.visible_title =
3240 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
3241 _screen->screenNumber(),
3242 _screen->resource().windowStyle().font);
3243 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
3245 if (client.decorations & WindowDecorationTitlebar)
3246 redrawLabel();
3248 _screen->propagateWindowName(this);
3249 break;
3252 case XA_WM_NORMAL_HINTS: {
3253 WMNormalHints wmnormal = ::readWMNormalHints(blackbox, client.window,
3254 _screen->screenInfo());
3255 if (wmnormal == client.wmnormal) {
3256 // apps like xv and GNU emacs seem to like to repeatedly set
3257 // this property over and over
3258 break;
3261 client.wmnormal = wmnormal;
3263 ::update_decorations(client.decorations,
3264 client.functions,
3265 isTransient(),
3266 client.ewmh,
3267 client.motif,
3268 client.wmnormal,
3269 client.wmprotocols);
3271 reconfigure();
3272 break;
3275 default: {
3276 if (event->atom == blackbox->wmProtocolsAtom()) {
3277 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
3279 ::update_decorations(client.decorations,
3280 client.functions,
3281 isTransient(),
3282 client.ewmh,
3283 client.motif,
3284 client.wmnormal,
3285 client.wmprotocols);
3287 reconfigure();
3288 } else if (event->atom == blackbox->motifWmHintsAtom()) {
3289 client.motif = ::readMotifWMHints(blackbox, client.window);
3291 ::update_decorations(client.decorations,
3292 client.functions,
3293 isTransient(),
3294 client.ewmh,
3295 client.motif,
3296 client.wmnormal,
3297 client.wmprotocols);
3299 reconfigure();
3300 } else if (event->atom == blackbox->ewmh().wmStrut()) {
3301 if (! client.strut) {
3302 client.strut = new bt::EWMH::Strut;
3303 _screen->addStrut(client.strut);
3306 blackbox->ewmh().readWMStrut(client.window, client.strut);
3307 if (client.strut->left || client.strut->right ||
3308 client.strut->top || client.strut->bottom) {
3309 _screen->updateStrut();
3310 } else {
3311 _screen->removeStrut(client.strut);
3312 delete client.strut;
3316 break;
3318 } // switch
3322 void BlackboxWindow::exposeEvent(const XExposeEvent * const event) {
3323 #ifdef DEBUG
3324 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
3325 #endif
3327 if (frame.title == event->window)
3328 redrawTitle();
3329 else if (frame.label == event->window)
3330 redrawLabel();
3331 else if (frame.close_button == event->window)
3332 redrawCloseButton();
3333 else if (frame.maximize_button == event->window)
3334 redrawMaximizeButton();
3335 else if (frame.iconify_button == event->window)
3336 redrawIconifyButton();
3337 else if (frame.handle == event->window)
3338 redrawHandle();
3339 else if (frame.left_grip == event->window ||
3340 frame.right_grip == event->window)
3341 redrawGrips();
3345 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *
3346 const event) {
3347 if (event->window != client.window || client.state.iconic)
3348 return;
3350 if (event->value_mask & CWBorderWidth)
3351 client.old_bw = event->border_width;
3353 if (event->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
3354 bt::Rect req = frame.rect;
3356 if (event->value_mask & (CWX | CWY)) {
3357 req = ::restoreGravity(req, frame.margin, client.wmnormal.win_gravity);
3359 if (event->value_mask & CWX)
3360 req.setX(event->x);
3361 if (event->value_mask & CWY)
3362 req.setY(event->y);
3364 req = ::applyGravity(req, frame.margin, client.wmnormal.win_gravity);
3367 if (event->value_mask & (CWWidth | CWHeight)) {
3368 if (event->value_mask & CWWidth)
3369 req.setWidth(event->width + frame.margin.left + frame.margin.right);
3370 if (event->value_mask & CWHeight)
3371 req.setHeight(event->height + frame.margin.top + frame.margin.bottom);
3374 configure(req);
3377 if (event->value_mask & CWStackMode) {
3378 switch (event->detail) {
3379 case Below:
3380 case BottomIf:
3381 _screen->lowerWindow(this);
3382 break;
3384 case Above:
3385 case TopIf:
3386 default:
3387 _screen->raiseWindow(this);
3388 break;
3394 void BlackboxWindow::buttonPressEvent(const XButtonEvent * const event) {
3395 #ifdef DEBUG
3396 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3397 client.window);
3398 #endif
3400 if (frame.maximize_button == event->window) {
3401 if (event->button < 4)
3402 redrawMaximizeButton(true);
3403 } else if (frame.iconify_button == event->window) {
3404 if (event->button == 1)
3405 redrawIconifyButton(true);
3406 } else if (frame.close_button == event->window) {
3407 if (event->button == 1)
3408 redrawCloseButton(true);
3409 } else {
3410 if (event->button == 1
3411 || (event->button == 3 && event->state == Mod1Mask)) {
3412 frame.grab_x = event->x_root - frame.rect.x();
3413 frame.grab_y = event->y_root - frame.rect.y();
3415 _screen->raiseWindow(this);
3417 if (! client.state.focused)
3418 (void) setInputFocus();
3419 else
3420 XInstallColormap(blackbox->XDisplay(), client.colormap);
3422 if (frame.plate == event->window) {
3423 XAllowEvents(blackbox->XDisplay(), ReplayPointer, event->time);
3424 } else if ((frame.title == event->window
3425 || frame.label == event->window)
3426 && hasWindowFunction(WindowFunctionShade)) {
3427 if ((event->time - lastButtonPressTime <=
3428 blackbox->resource().doubleClickInterval()) ||
3429 event->state == ControlMask) {
3430 lastButtonPressTime = 0;
3431 setShaded(!isShaded());
3432 } else {
3433 lastButtonPressTime = event->time;
3436 } else if (event->button == 2) {
3437 _screen->lowerWindow(this);
3438 } else if (event->button == 3
3439 || (event->button == 3 && event->state == Mod4Mask)) {
3440 const int extra = _screen->resource().windowStyle().frame_border_width;
3441 const bt::Rect rect(client.rect.x() - extra,
3442 client.rect.y() - extra,
3443 client.rect.width() + (extra * 2),
3444 client.rect.height() + (extra * 2));
3446 Windowmenu *windowmenu = _screen->windowmenu(this);
3447 windowmenu->popup(event->x_root, event->y_root, rect);
3448 } else if (blackbox->resource().shadeWindowWithMouseWheel()) {
3449 if (event->button == 4
3450 && hasWindowFunction(WindowFunctionShade)
3451 && !isShaded()) {
3452 setShaded(true);
3453 } else if (event->button == 5
3454 && hasWindowFunction(WindowFunctionShade)
3455 && isShaded()) {
3456 setShaded(false);
3463 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent * const event) {
3464 #ifdef DEBUG
3465 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3466 client.window);
3467 #endif
3469 const WindowStyle &style = _screen->resource().windowStyle();
3470 if (event->window == frame.maximize_button) {
3471 if (event->button < 4) {
3472 if (bt::within(event->x, event->y,
3473 style.button_width, style.button_width)) {
3474 maximize(event->button);
3475 _screen->raiseWindow(this);
3476 } else {
3477 redrawMaximizeButton();
3480 } else if (event->window == frame.iconify_button) {
3481 if (event->button == 1) {
3482 if (bt::within(event->x, event->y,
3483 style.button_width, style.button_width))
3484 iconify();
3485 else
3486 redrawIconifyButton();
3488 } else if (event->window == frame.close_button) {
3489 if (event->button == 1) {
3490 if (bt::within(event->x, event->y,
3491 style.button_width, style.button_width))
3492 close();
3493 redrawCloseButton();
3495 } else if (client.state.moving) {
3496 finishMove();
3497 } else if (client.state.resizing) {
3498 finishResize();
3499 } else if (event->window == frame.window) {
3500 if (event->button == 2 && event->state == Mod1Mask)
3501 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3506 void BlackboxWindow::motionNotifyEvent(const XMotionEvent * const event) {
3507 #ifdef DEBUG
3508 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3509 client.window);
3510 #endif
3512 if (hasWindowFunction(WindowFunctionMove)
3513 && !client.state.resizing
3514 && event->state & Button1Mask
3515 && (frame.title == event->window || frame.label == event->window
3516 || frame.handle == event->window || frame.window == event->window)) {
3517 if (! client.state.moving)
3518 startMove();
3519 else
3520 continueMove(event->x_root, event->y_root);
3521 } else if (hasWindowFunction(WindowFunctionResize)
3522 && (event->state & Button1Mask
3523 && (event->window == frame.right_grip
3524 || event->window == frame.left_grip))
3525 || (event->state & Button3Mask
3526 && event->state & Mod1Mask
3527 && event->window == frame.window)) {
3528 if (!client.state.resizing)
3529 startResize(event->window);
3530 else
3531 continueResize(event->x_root, event->y_root);
3536 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent * const event) {
3537 if (event->window != frame.window || event->mode != NotifyNormal)
3538 return;
3540 if (blackbox->resource().focusModel() == ClickToFocusModel || !isVisible())
3541 return;
3543 switch (windowType()) {
3544 case WindowTypeDesktop:
3545 case WindowTypeDock:
3546 // these types cannot be focused w/ sloppy focus
3547 return;
3549 default:
3550 break;
3553 XEvent next;
3554 bool leave = False, inferior = False;
3556 while (XCheckTypedWindowEvent(blackbox->XDisplay(), event->window,
3557 LeaveNotify, &next)) {
3558 if (next.type == LeaveNotify && next.xcrossing.mode == NotifyNormal) {
3559 leave = True;
3560 inferior = (next.xcrossing.detail == NotifyInferior);
3564 if ((! leave || inferior) && ! isFocused())
3565 (void) setInputFocus();
3567 if (blackbox->resource().autoRaise())
3568 timer->start();
3572 void
3573 BlackboxWindow::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
3574 if (!(blackbox->resource().focusModel() == SloppyFocusModel
3575 && blackbox->resource().autoRaise()))
3576 return;
3578 if (timer->isTiming())
3579 timer->stop();
3583 #ifdef SHAPE
3584 void BlackboxWindow::shapeEvent(const XEvent * const /*unused*/)
3585 { if (client.state.shaped) configureShape(); }
3586 #endif // SHAPE
3592 void BlackboxWindow::restore(void) {
3593 XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeDelete);
3594 XSelectInput(blackbox->XDisplay(), client.window, NoEventMask);
3595 XSelectInput(blackbox->XDisplay(), frame.plate, NoEventMask);
3597 client.state.visible = false;
3600 remove WM_STATE unless the we are shutting down (in which case we
3601 want to make sure we preserve the state across restarts).
3603 if (!blackbox->shuttingDown()) {
3604 clearState(blackbox, client.window);
3605 } else if (isShaded() && !isIconic()) {
3606 // do not leave a shaded window as an icon unless it was an icon
3607 setState(NormalState);
3610 client.rect = ::restoreGravity(frame.rect, frame.margin,
3611 client.wmnormal.win_gravity);
3613 blackbox->XGrabServer();
3615 XUnmapWindow(blackbox->XDisplay(), frame.window);
3616 XUnmapWindow(blackbox->XDisplay(), client.window);
3618 XSetWindowBorderWidth(blackbox->XDisplay(), client.window, client.old_bw);
3619 if (isMaximized()) {
3620 // preserve the original size
3621 client.rect = client.premax;
3622 XMoveResizeWindow(blackbox->XDisplay(), client.window,
3623 client.premax.x(),
3624 client.premax.y(),
3625 client.premax.width(),
3626 client.premax.height());
3627 } else {
3628 XMoveWindow(blackbox->XDisplay(), client.window,
3629 client.rect.x() - frame.rect.x(),
3630 client.rect.y() - frame.rect.y());
3633 blackbox->XUngrabServer();
3635 XEvent unused;
3636 if (!XCheckTypedWindowEvent(blackbox->XDisplay(), client.window,
3637 ReparentNotify, &unused)) {
3639 according to the ICCCM, the window manager is responsible for
3640 reparenting the window back to root... however, we don't want to
3641 do this if the window has been reparented by someone else
3642 (i.e. not us).
3644 XReparentWindow(blackbox->XDisplay(), client.window,
3645 _screen->screenInfo().rootWindow(),
3646 client.rect.x(), client.rect.y());
3649 if (blackbox->shuttingDown())
3650 XMapWindow(blackbox->XDisplay(), client.window);
3654 // timer for autoraise
3655 void BlackboxWindow::timeout(bt::Timer *)
3656 { _screen->raiseWindow(this); }
3659 void BlackboxWindow::startMove() {
3660 // begin a move
3661 XGrabPointer(blackbox->XDisplay(), frame.window, false,
3662 Button1MotionMask | ButtonReleaseMask,
3663 GrabModeAsync, GrabModeAsync, None,
3664 blackbox->resource().cursors().move, blackbox->XTime());
3666 client.state.moving = true;
3668 if (! blackbox->resource().opaqueMove()) {
3669 blackbox->XGrabServer();
3671 frame.changing = frame.rect;
3672 _screen->showGeometry(BScreen::Position, frame.changing);
3674 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3675 const int bw = _screen->resource().windowStyle().frame_border_width,
3676 hw = bw / 2;
3677 pen.setGCFunction(GXxor);
3678 pen.setLineWidth(bw);
3679 pen.setSubWindowMode(IncludeInferiors);
3680 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3681 pen.gc(),
3682 frame.changing.x() + hw,
3683 frame.changing.y() + hw,
3684 frame.changing.width() - bw,
3685 frame.changing.height() - bw);
3690 static
3691 void collisionAdjust(int *dx, int *dy, int x, int y,
3692 unsigned int width, unsigned int height,
3693 const bt::Rect& rect, int snap_distance,
3694 bool snapCenter = false)
3696 // window corners
3697 const int wleft = x,
3698 wright = x + width - 1,
3699 wtop = y,
3700 wbottom = y + height - 1,
3701 // left, right, top + bottom are for rect, douterleft = left border of rect
3702 dinnerleft = abs(wleft - rect.left()),
3703 dinnerright = abs(wright - rect.right()),
3704 dinnertop = abs(wtop - rect.top()),
3705 dinnerbottom = abs(wbottom - rect.bottom()),
3706 douterleft = abs(wright - rect.left()),
3707 douterright = abs(wleft - rect.right()),
3708 doutertop = abs(wbottom - rect.top()),
3709 douterbottom = abs(wtop - rect.bottom());
3711 if ((wtop <= rect.bottom() && wbottom >= rect.top())
3712 || doutertop <= snap_distance
3713 || douterbottom <= snap_distance) {
3714 // snap left or right
3715 if (douterleft <= dinnerleft && douterleft <= snap_distance)
3716 // snap outer left
3717 *dx = (x - (rect.left() - width));
3718 else if (douterright <= dinnerright && douterright <= snap_distance)
3719 // snap outer right
3720 *dx = (x - rect.right() - 1);
3721 else if (dinnerleft <= dinnerright && dinnerleft < snap_distance)
3722 // snap inner left
3723 *dx = (x - rect.left());
3724 else if (dinnerright < snap_distance)
3725 // snap inner right
3726 *dx = (x - (rect.right() - width + 1));
3729 if ((wleft <= rect.right() && wright >= rect.left())
3730 || douterleft <= snap_distance
3731 || douterright <= snap_distance) {
3732 // snap top or bottom
3733 if (doutertop <= dinnertop && doutertop <= snap_distance)
3734 // snap outer top
3735 *dy = (y - (rect.top() - height));
3736 else if (douterbottom <= dinnerbottom && douterbottom <= snap_distance)
3737 // snap outer bottom
3738 *dy = (y - rect.bottom() - 1);
3739 else if (dinnertop <= dinnerbottom && dinnertop < snap_distance)
3740 // snap inner top
3741 *dy = (y - rect.top());
3742 else if (dinnerbottom < snap_distance)
3743 // snap inner bottom
3744 *dy = (y - (rect.bottom() - height + 1));
3747 if (snapCenter) {
3748 const int cwx = x + width / 2;
3749 const int cwy = y + height / 2;
3750 const int crx = rect.x() + rect.width() / 2;
3751 const int cry = rect.y() + rect.height() / 2;
3752 const int cdx = abs(cwx - crx);
3753 const int cdy = abs(cwy - cry);
3754 if (cdx <= snap_distance)
3755 // snap to horizontal center
3756 *dx = x - (rect.x() + ((rect.width() - width) / 2));
3757 if (cdy <= snap_distance)
3758 // snap to vertical center
3759 *dy = y - (rect.y() + ((rect.height() - height) / 2));
3764 void BlackboxWindow::snapAdjust(int *x, int *y) {
3765 int nx, ny, dx, dy, init_dx, init_dy;
3766 const int edge_distance = blackbox->resource().edgeSnapThreshold();
3767 const int win_distance = blackbox->resource().windowSnapThreshold();
3769 nx = (win_distance > edge_distance) ? win_distance : edge_distance;
3770 ny = (win_distance > edge_distance) ? win_distance : edge_distance;
3771 dx = init_dx = ++nx; dy = init_dy = ++ny;
3773 if (edge_distance) {
3774 collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(), frame.rect.height(),
3775 _screen->availableArea(), edge_distance, true);
3776 nx = (dx != init_dx && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
3777 ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
3778 if (!blackbox->resource().fullMaximization()) {
3779 collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(), frame.rect.height(),
3780 _screen->screenInfo().rect(), edge_distance);
3781 nx = (dx != init_dx && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
3782 ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
3785 if (win_distance) {
3786 StackingList::const_iterator it = _screen->stackingList().begin(),
3787 end = _screen->stackingList().end();
3788 for (; it != end; ++it) {
3789 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
3790 if (win && win != this &&
3791 win->workspace() == _screen->currentWorkspace()) {
3792 collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(),
3793 frame.rect.height(), win->frame.rect, win_distance);
3794 nx = (dx != init_dx && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
3795 ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
3800 *x = (nx != init_dx) ? (*x - nx) : *x;
3801 *y = (ny != init_dy) ? (*y - ny) : *y;
3805 void BlackboxWindow::continueMove(int x_root, int y_root) {
3806 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3808 snapAdjust(&dx, &dy);
3810 if (blackbox->resource().opaqueMove()) {
3811 configure(dx, dy, frame.rect.width(), frame.rect.height());
3812 } else {
3813 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3814 const int bw = _screen->resource().windowStyle().frame_border_width,
3815 hw = bw / 2;
3816 pen.setGCFunction(GXxor);
3817 pen.setLineWidth(bw);
3818 pen.setSubWindowMode(IncludeInferiors);
3819 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3820 pen.gc(),
3821 frame.changing.x() + hw,
3822 frame.changing.y() + hw,
3823 frame.changing.width() - bw,
3824 frame.changing.height() - bw);
3826 frame.changing.setPos(dx, dy);
3828 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3829 pen.gc(),
3830 frame.changing.x() + hw,
3831 frame.changing.y() + hw,
3832 frame.changing.width() - bw,
3833 frame.changing.height() - bw);
3836 _screen->showGeometry(BScreen::Position, bt::Rect(dx, dy, 0, 0));
3840 void BlackboxWindow::finishMove() {
3841 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3843 client.state.moving = false;
3845 if (!blackbox->resource().opaqueMove()) {
3846 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3847 const int bw = _screen->resource().windowStyle().frame_border_width,
3848 hw = bw / 2;
3849 pen.setGCFunction(GXxor);
3850 pen.setLineWidth(bw);
3851 pen.setSubWindowMode(IncludeInferiors);
3852 XDrawRectangle(blackbox->XDisplay(),
3853 _screen->screenInfo().rootWindow(),
3854 pen.gc(),
3855 frame.changing.x() + hw,
3856 frame.changing.y() + hw,
3857 frame.changing.width() - bw,
3858 frame.changing.height() - bw);
3859 blackbox->XUngrabServer();
3861 configure(frame.changing);
3862 } else {
3863 configure(frame.rect);
3866 _screen->hideGeometry();
3870 void BlackboxWindow::startResize(Window window) {
3871 if (frame.grab_x < (signed) frame.rect.width() / 2) {
3872 if (frame.grab_y < (signed) frame.rect.height() / 2)
3873 frame.corner = BottomRight;
3874 else
3875 frame.corner = TopRight;
3876 } else {
3877 if (frame.grab_y < (signed) frame.rect.height() / 2)
3878 frame.corner = BottomLeft;
3879 else
3880 frame.corner = TopLeft;
3883 Cursor cursor = None;
3884 switch (frame.corner) {
3885 case TopLeft:
3886 cursor = blackbox->resource().cursors().resize_bottom_right;
3887 frame.grab_x = frame.rect.width() - frame.grab_x;
3888 frame.grab_y = frame.rect.height() - frame.grab_y;
3889 break;
3890 case BottomLeft:
3891 cursor = blackbox->resource().cursors().resize_top_right;
3892 frame.grab_x = frame.rect.width() - frame.grab_x;
3893 break;
3894 case TopRight:
3895 cursor = blackbox->resource().cursors().resize_bottom_left;
3896 frame.grab_y = frame.rect.height() - frame.grab_y;
3897 break;
3898 case BottomRight:
3899 cursor = blackbox->resource().cursors().resize_top_left;
3900 break;
3903 // begin a resize
3904 XGrabPointer(blackbox->XDisplay(), window, False,
3905 ButtonMotionMask | ButtonReleaseMask,
3906 GrabModeAsync, GrabModeAsync, None, cursor, blackbox->XTime());
3908 client.state.resizing = true;
3910 frame.changing = constrain(frame.rect, frame.margin, client.wmnormal,
3911 Corner(frame.corner));
3913 if (!blackbox->resource().opaqueResize()) {
3914 blackbox->XGrabServer();
3916 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3917 const int bw = _screen->resource().windowStyle().frame_border_width,
3918 hw = bw / 2;
3919 pen.setGCFunction(GXxor);
3920 pen.setLineWidth(bw);
3921 pen.setSubWindowMode(IncludeInferiors);
3922 XDrawRectangle(blackbox->XDisplay(),
3923 _screen->screenInfo().rootWindow(),
3924 pen.gc(),
3925 frame.changing.x() + hw,
3926 frame.changing.y() + hw,
3927 frame.changing.width() - bw,
3928 frame.changing.height() - bw);
3929 } else {
3930 // unset maximized state when resized
3931 if (isMaximized())
3932 maximize(0);
3935 showGeometry(frame.changing);
3939 void BlackboxWindow::continueResize(int x_root, int y_root) {
3940 // continue a resize
3941 const bt::Rect curr = frame.changing;
3943 switch (frame.corner) {
3944 case TopLeft:
3945 case BottomLeft:
3946 frame.changing.setCoords(frame.changing.left(),
3947 frame.changing.top(),
3948 std::max<signed>(x_root + frame.grab_x,
3949 frame.changing.left()
3950 + (frame.margin.left
3951 + frame.margin.right + 1)),
3952 frame.changing.bottom());
3953 break;
3954 case TopRight:
3955 case BottomRight:
3956 frame.changing.setCoords(std::min<signed>(x_root - frame.grab_x,
3957 frame.changing.right()
3958 - (frame.margin.left
3959 + frame.margin.right + 1)),
3960 frame.changing.top(),
3961 frame.changing.right(),
3962 frame.changing.bottom());
3963 break;
3966 switch (frame.corner) {
3967 case TopLeft:
3968 case TopRight:
3969 frame.changing.setCoords(frame.changing.left(),
3970 frame.changing.top(),
3971 frame.changing.right(),
3972 std::max<signed>(y_root + frame.grab_y,
3973 frame.changing.top()
3974 + (frame.margin.top
3975 + frame.margin.bottom + 1)));
3976 break;
3977 case BottomLeft:
3978 case BottomRight:
3979 frame.changing.setCoords(frame.changing.left(),
3980 std::min<signed>(y_root - frame.grab_y,
3981 frame.rect.bottom()
3982 - (frame.margin.top
3983 + frame.margin.bottom + 1)),
3984 frame.changing.right(),
3985 frame.changing.bottom());
3986 break;
3989 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
3990 Corner(frame.corner));
3992 if (curr != frame.changing) {
3993 if (blackbox->resource().opaqueResize()) {
3994 configure(frame.changing);
3995 } else {
3996 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3997 const int bw = _screen->resource().windowStyle().frame_border_width,
3998 hw = bw / 2;
3999 pen.setGCFunction(GXxor);
4000 pen.setLineWidth(bw);
4001 pen.setSubWindowMode(IncludeInferiors);
4002 XDrawRectangle(blackbox->XDisplay(),
4003 _screen->screenInfo().rootWindow(),
4004 pen.gc(),
4005 curr.x() + hw,
4006 curr.y() + hw,
4007 curr.width() - bw,
4008 curr.height() - bw);
4010 XDrawRectangle(blackbox->XDisplay(),
4011 _screen->screenInfo().rootWindow(),
4012 pen.gc(),
4013 frame.changing.x() + hw,
4014 frame.changing.y() + hw,
4015 frame.changing.width() - bw,
4016 frame.changing.height() - bw);
4019 showGeometry(frame.changing);
4024 void BlackboxWindow::finishResize() {
4026 if (!blackbox->resource().opaqueResize()) {
4027 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
4028 const int bw = _screen->resource().windowStyle().frame_border_width,
4029 hw = bw / 2;
4030 pen.setGCFunction(GXxor);
4031 pen.setLineWidth(bw);
4032 pen.setSubWindowMode(IncludeInferiors);
4033 XDrawRectangle(blackbox->XDisplay(),
4034 _screen->screenInfo().rootWindow(),
4035 pen.gc(),
4036 frame.changing.x() + hw,
4037 frame.changing.y() + hw,
4038 frame.changing.width() - bw,
4039 frame.changing.height() - bw);
4041 blackbox->XUngrabServer();
4043 // unset maximized state when resized
4044 if (isMaximized())
4045 maximize(0);
4048 client.state.resizing = false;
4050 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
4052 _screen->hideGeometry();
4054 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
4055 Corner(frame.corner));
4056 configure(frame.changing);
4061 * show the geometry of the window based on rectangle r.
4062 * The logical width and height are used here. This refers to the user's
4063 * perception of the window size (for example an xterm resizes in cells,
4064 * not in pixels). No extra work is needed if there is no difference between
4065 * the logical and actual dimensions.
4067 void BlackboxWindow::showGeometry(const bt::Rect &r) const {
4068 unsigned int w = r.width(), h = r.height();
4070 // remove the window frame
4071 w -= frame.margin.left + frame.margin.right;
4072 h -= frame.margin.top + frame.margin.bottom;
4074 if (client.wmnormal.flags & PResizeInc) {
4075 if (client.wmnormal.flags & (PMinSize|PBaseSize)) {
4076 w -= ((client.wmnormal.base_width)
4077 ? client.wmnormal.base_width
4078 : client.wmnormal.min_width);
4079 h -= ((client.wmnormal.base_height)
4080 ? client.wmnormal.base_height
4081 : client.wmnormal.min_height);
4084 w /= client.wmnormal.width_inc;
4085 h /= client.wmnormal.height_inc;
4088 _screen->showGeometry(BScreen::Size, bt::Rect(0, 0, w, h));
4092 // see my rant above for an explanation of this operator
4093 bool operator==(const WMNormalHints &x, const WMNormalHints &y) {
4094 return (x.flags == y.flags
4095 && x.min_width == y.min_width
4096 && x.min_height == y.min_height
4097 && x.max_width == y.max_width
4098 && x.max_height == y.max_height
4099 && x.width_inc == y.width_inc
4100 && x.height_inc == y.height_inc
4101 && x.min_aspect_x == y.min_aspect_x
4102 && x.min_aspect_y == y.min_aspect_y
4103 && x.max_aspect_x == y.max_aspect_x
4104 && x.max_aspect_y == y.max_aspect_y
4105 && x.base_width == y.base_width
4106 && x.base_height == y.base_height
4107 && x.win_gravity == y.win_gravity);