don't use uninitialized variables... the label width needs to be set
[blackbox.git] / src / Window.cc
blob26370de93a7f0816bae8f8df760df87620d4cb29
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 WindowDecorationGrip);
715 if (prop->decorations & MWM_DECOR_TITLE) {
716 motif.decorations |= (WindowDecorationTitlebar |
717 WindowDecorationClose);
719 if (prop->decorations & MWM_DECOR_MINIMIZE)
720 motif.decorations |= WindowDecorationIconify;
721 if (prop->decorations & MWM_DECOR_MAXIMIZE)
722 motif.decorations |= WindowDecorationMaximize;
726 XFree(prop);
728 return motif;
733 * Returns the value of the WM_HINTS property. If the property is not
734 * set, a set of default values is returned instead.
736 static WMHints readWMHints(Blackbox *blackbox, Window window) {
737 WMHints wmh;
738 wmh.accept_focus = false;
739 wmh.window_group = None;
740 wmh.initial_state = NormalState;
742 XWMHints *wmhint = XGetWMHints(blackbox->XDisplay(), window);
743 if (!wmhint) return wmh;
745 if (wmhint->flags & InputHint)
746 wmh.accept_focus = (wmhint->input == True);
747 if (wmhint->flags & StateHint)
748 wmh.initial_state = wmhint->initial_state;
749 if (wmhint->flags & WindowGroupHint)
750 wmh.window_group = wmhint->window_group;
752 XFree(wmhint);
754 return wmh;
759 * Returns the value of the WM_NORMAL_HINTS property. If the property
760 * is not set, a set of default values is returned instead.
762 static WMNormalHints readWMNormalHints(Blackbox *blackbox,
763 Window window,
764 const bt::ScreenInfo &screenInfo) {
765 WMNormalHints wmnormal;
766 wmnormal.flags = 0;
767 wmnormal.min_width = wmnormal.min_height = 1u;
768 wmnormal.width_inc = wmnormal.height_inc = 1u;
769 wmnormal.min_aspect_x = wmnormal.min_aspect_y = 1u;
770 wmnormal.max_aspect_x = wmnormal.max_aspect_y = 1u;
771 wmnormal.base_width = wmnormal.base_height = 0u;
772 wmnormal.win_gravity = NorthWestGravity;
775 use the full screen, not the strut modified size. otherwise when
776 the availableArea changes max_width/height will be incorrect and
777 lead to odd rendering bugs.
779 const bt::Rect &rect = screenInfo.rect();
780 wmnormal.max_width = rect.width();
781 wmnormal.max_height = rect.height();
783 XSizeHints sizehint;
784 long unused;
785 if (! XGetWMNormalHints(blackbox->XDisplay(), window, &sizehint, &unused))
786 return wmnormal;
788 wmnormal.flags = sizehint.flags;
790 if (sizehint.flags & PMinSize) {
791 if (sizehint.min_width > 0)
792 wmnormal.min_width = sizehint.min_width;
793 if (sizehint.min_height > 0)
794 wmnormal.min_height = sizehint.min_height;
797 if (sizehint.flags & PMaxSize) {
798 if (sizehint.max_width > static_cast<signed>(wmnormal.min_width))
799 wmnormal.max_width = sizehint.max_width;
800 else
801 wmnormal.max_width = wmnormal.min_width;
803 if (sizehint.max_height > static_cast<signed>(wmnormal.min_height))
804 wmnormal.max_height = sizehint.max_height;
805 else
806 wmnormal.max_height = wmnormal.min_height;
809 if (sizehint.flags & PResizeInc) {
810 wmnormal.width_inc = sizehint.width_inc;
811 wmnormal.height_inc = sizehint.height_inc;
814 if (sizehint.flags & PAspect) {
815 wmnormal.min_aspect_x = sizehint.min_aspect.x;
816 wmnormal.min_aspect_y = sizehint.min_aspect.y;
817 wmnormal.max_aspect_x = sizehint.max_aspect.x;
818 wmnormal.max_aspect_y = sizehint.max_aspect.y;
821 if (sizehint.flags & PBaseSize) {
822 wmnormal.base_width = sizehint.base_width;
823 wmnormal.base_height = sizehint.base_height;
825 // sanity checks
826 wmnormal.min_width = std::max(wmnormal.min_width, wmnormal.base_width);
827 wmnormal.min_height = std::max(wmnormal.min_height, wmnormal.base_height);
830 if (sizehint.flags & PWinGravity)
831 wmnormal.win_gravity = sizehint.win_gravity;
833 return wmnormal;
838 * Retrieve which Window Manager Protocols are supported by the client
839 * window.
841 static WMProtocols readWMProtocols(Blackbox *blackbox,
842 Window window) {
843 WMProtocols protocols;
844 protocols.wm_delete_window = false;
845 protocols.wm_take_focus = false;
847 Atom *proto;
848 int num_return = 0;
850 if (XGetWMProtocols(blackbox->XDisplay(), window,
851 &proto, &num_return)) {
852 for (int i = 0; i < num_return; ++i) {
853 if (proto[i] == blackbox->wmDeleteWindowAtom()) {
854 protocols.wm_delete_window = true;
855 } else if (proto[i] == blackbox->wmTakeFocusAtom()) {
856 protocols.wm_take_focus = true;
859 XFree(proto);
862 return protocols;
867 * Reads the value of the WM_TRANSIENT_FOR property and returns a
868 * pointer to the transient parent for this window. If the
869 * WM_TRANSIENT_FOR is missing or invalid, this function returns 0.
871 * 'client.wmhints' should be properly updated before calling this
872 * function.
874 * Note: a return value of ~0ul signifies a window that should be
875 * transient but has no discernible parent.
877 static Window readTransientInfo(Blackbox *blackbox,
878 Window window,
879 const bt::ScreenInfo &screenInfo,
880 const WMHints &wmhints) {
881 Window trans_for = None;
883 if (!XGetTransientForHint(blackbox->XDisplay(), window, &trans_for)) {
884 // WM_TRANSIENT_FOR hint not set
885 return 0;
888 if (trans_for == window) {
889 // wierd client... treat this window as a normal window
890 return 0;
893 if (trans_for == None || trans_for == screenInfo.rootWindow()) {
895 this is a violation of the ICCCM, yet the EWMH allows this as a
896 way to signify a group transient.
898 trans_for = wmhints.window_group;
901 return trans_for;
905 static bool readState(unsigned long &current_state,
906 Blackbox *blackbox,
907 Window window) {
908 current_state = NormalState;
910 Atom atom_return;
911 bool ret = false;
912 int foo;
913 unsigned long *state, ulfoo, nitems;
915 if ((XGetWindowProperty(blackbox->XDisplay(), window,
916 blackbox->wmStateAtom(),
917 0l, 2l, False, blackbox->wmStateAtom(),
918 &atom_return, &foo, &nitems, &ulfoo,
919 (unsigned char **) &state) != Success) ||
920 (! state)) {
921 return false;
924 if (nitems >= 1) {
925 current_state = static_cast<unsigned long>(state[0]);
926 ret = true;
929 XFree((void *) state);
931 return ret;
935 static void clearState(Blackbox *blackbox, Window window) {
936 XDeleteProperty(blackbox->XDisplay(), window, blackbox->wmStateAtom());
938 const bt::EWMH& ewmh = blackbox->ewmh();
939 ewmh.removeProperty(window, ewmh.wmDesktop());
940 ewmh.removeProperty(window, ewmh.wmState());
941 ewmh.removeProperty(window, ewmh.wmAllowedActions());
942 ewmh.removeProperty(window, ewmh.wmVisibleName());
943 ewmh.removeProperty(window, ewmh.wmVisibleIconName());
948 * Initializes the class with default values/the window's set initial values.
950 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
951 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
952 // sizeof(BlackboxWindow));
954 #ifdef DEBUG
955 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
956 #endif // DEBUG
959 set timer to zero... it is initialized properly later, so we check
960 if timer is zero in the destructor, and assume that the window is not
961 fully constructed if timer is zero...
963 timer = (bt::Timer*) 0;
964 blackbox = b;
965 client.window = w;
966 _screen = s;
967 lastButtonPressTime = 0;
970 the server needs to be grabbed here to prevent client's from sending
971 events while we are in the process of managing their window.
972 We hold the grab until after we are done moving the window around.
975 blackbox->XGrabServer();
977 // fetch client size and placement
978 XWindowAttributes wattrib;
979 if (! XGetWindowAttributes(blackbox->XDisplay(),
980 client.window, &wattrib) ||
981 ! wattrib.screen || wattrib.override_redirect) {
982 #ifdef DEBUG
983 fprintf(stderr,
984 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
985 #endif // DEBUG
987 blackbox->XUngrabServer();
988 delete this;
989 return;
992 // set the eventmask early in the game so that we make sure we get
993 // all the events we are interested in
994 XSetWindowAttributes attrib_set;
995 attrib_set.event_mask = ::client_window_event_mask;
996 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
997 ButtonMotionMask;
998 XChangeWindowAttributes(blackbox->XDisplay(), client.window,
999 CWEventMask|CWDontPropagate, &attrib_set);
1001 client.colormap = wattrib.colormap;
1002 window_number = bt::BSENTINEL;
1003 client.strut = 0;
1005 set the initial size and location of client window (relative to the
1006 _root window_). This position is the reference point used with the
1007 window's gravity to find the window's initial position.
1009 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1010 client.premax = client.rect;
1011 client.old_bw = wattrib.border_width;
1012 client.current_state = NormalState;
1014 frame.window = frame.plate = frame.title = frame.handle = None;
1015 frame.close_button = frame.iconify_button = frame.maximize_button = None;
1016 frame.right_grip = frame.left_grip = None;
1017 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
1018 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
1019 frame.pbutton = frame.ugrip = frame.fgrip = None;
1021 timer = new bt::Timer(blackbox, this);
1022 timer->setTimeout(blackbox->resource().autoRaiseDelay());
1024 client.title = ::readWMName(blackbox, client.window);
1025 client.icon_title = ::readWMIconName(blackbox, client.window);
1027 // get size, aspect, minimum/maximum size, ewmh and other hints set
1028 // by the client
1029 client.ewmh = ::readEWMH(blackbox->ewmh(), client.window,
1030 _screen->currentWorkspace());
1031 client.motif = ::readMotifWMHints(blackbox, client.window);
1032 client.wmhints = ::readWMHints(blackbox, client.window);
1033 client.wmnormal = ::readWMNormalHints(blackbox, client.window,
1034 _screen->screenInfo());
1035 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
1036 client.transient_for = ::readTransientInfo(blackbox, client.window,
1037 _screen->screenInfo(),
1038 client.wmhints);
1040 if (client.wmhints.window_group != None)
1041 (void) ::update_window_group(client.wmhints.window_group, blackbox, this);
1043 if (isTransient()) {
1044 // add ourselves to our transient_for
1045 BlackboxWindow *win = findTransientFor();
1046 if (win) {
1047 win->addTransient(this);
1048 client.ewmh.workspace = win->workspace();
1049 setLayer(win->layer());
1050 } else if (isGroupTransient()) {
1051 BWindowGroup *group = findWindowGroup();
1052 if (group)
1053 group->addTransient(this);
1054 } else {
1055 // broken client
1056 client.transient_for = 0;
1060 client.state.visible = false;
1061 client.state.iconic = false;
1062 client.state.moving = false;
1063 client.state.resizing = false;
1064 client.state.focused = false;
1066 switch (windowType()) {
1067 case WindowTypeDesktop:
1068 setLayer(StackingList::LayerDesktop);
1069 break;
1071 case WindowTypeDock:
1072 setLayer(StackingList::LayerAbove);
1073 // fallthrough intended
1075 default:
1076 if (client.ewmh.above)
1077 setLayer(StackingList::LayerAbove);
1078 else if (client.ewmh.below)
1079 setLayer(StackingList::LayerBelow);
1080 break;
1081 } // switch
1083 ::update_decorations(client.decorations,
1084 client.functions,
1085 isTransient(),
1086 client.ewmh,
1087 client.motif,
1088 client.wmnormal,
1089 client.wmprotocols);
1091 // sanity checks
1092 if (client.wmhints.initial_state == IconicState
1093 && !hasWindowFunction(WindowFunctionIconify))
1094 client.wmhints.initial_state = NormalState;
1095 if (isMaximized() && !hasWindowFunction(WindowFunctionMaximize))
1096 client.ewmh.maxv = client.ewmh.maxh = false;
1097 if (isFullScreen() && !hasWindowFunction(WindowFunctionFullScreen))
1098 client.ewmh.fullscreen = false;
1100 bt::EWMH::Strut strut;
1101 if (blackbox->ewmh().readWMStrut(client.window, &strut)) {
1102 client.strut = new bt::EWMH::Strut;
1103 *client.strut = strut;
1104 _screen->addStrut(client.strut);
1107 frame.window = createToplevelWindow();
1108 blackbox->insertEventHandler(frame.window, this);
1110 frame.plate = createChildWindow(frame.window, NoEventMask);
1111 blackbox->insertEventHandler(frame.plate, this);
1113 if (client.decorations & WindowDecorationTitlebar)
1114 createTitlebar();
1116 if (client.decorations & WindowDecorationHandle)
1117 createHandle();
1119 // apply the size and gravity to the frame
1120 const WindowStyle &style = _screen->resource().windowStyle();
1121 frame.margin = ::update_margin(client.decorations, style);
1122 frame.rect = ::applyGravity(client.rect,
1123 frame.margin,
1124 client.wmnormal.win_gravity);
1126 associateClientWindow();
1128 blackbox->insertEventHandler(client.window, this);
1129 blackbox->insertWindow(client.window, this);
1130 blackbox->insertWindow(frame.plate, this);
1132 // preserve the window's initial state on first map, and its current
1133 // state across a restart
1134 if (!readState(client.current_state, blackbox, client.window))
1135 client.current_state = client.wmhints.initial_state;
1137 if (client.state.iconic) {
1138 // prepare the window to be iconified
1139 client.current_state = IconicState;
1140 client.state.iconic = False;
1141 } else if (workspace() != bt::BSENTINEL &&
1142 workspace() != _screen->currentWorkspace()) {
1143 client.current_state = WithdrawnState;
1146 blackbox->XUngrabServer();
1148 grabButtons();
1150 XMapSubwindows(blackbox->XDisplay(), frame.window);
1152 if (isFullScreen()) {
1153 client.ewmh.fullscreen = false; // trick setFullScreen into working
1154 setFullScreen(true);
1155 } else {
1156 if (isMaximized()) {
1157 remaximize();
1158 } else {
1159 bt::Rect r = frame.rect;
1160 // trick configure into working
1161 frame.rect = bt::Rect();
1162 configure(r);
1165 if (isShaded()) {
1166 client.ewmh.shaded = false;
1167 unsigned long save_state = client.current_state;
1168 setShaded(true);
1171 At this point in the life of a window, current_state should only be set
1172 to IconicState if the window was an *icon*, not if it was shaded.
1174 if (save_state != IconicState)
1175 client.current_state = save_state;
1181 BlackboxWindow::~BlackboxWindow(void) {
1182 #ifdef DEBUG
1183 fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
1184 client.window);
1185 #endif // DEBUG
1187 if (! timer) // window not managed...
1188 return;
1190 if (client.state.moving || client.state.resizing) {
1191 _screen->hideGeometry();
1192 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
1195 delete timer;
1197 if (client.strut) {
1198 _screen->removeStrut(client.strut);
1199 delete client.strut;
1202 BWindowGroup *group = findWindowGroup();
1204 if (isTransient()) {
1205 // remove ourselves from our transient_for
1206 BlackboxWindow *win = findTransientFor();
1207 if (win) {
1208 win->removeTransient(this);
1209 } else if (isGroupTransient()) {
1210 if (group)
1211 group->removeTransient(this);
1213 client.transient_for = 0;
1216 if (group)
1217 group->removeWindow(this);
1219 if (frame.title)
1220 destroyTitlebar();
1222 if (frame.handle)
1223 destroyHandle();
1225 blackbox->removeEventHandler(client.window);
1226 blackbox->removeWindow(client.window);
1228 blackbox->removeEventHandler(frame.plate);
1229 blackbox->removeWindow(frame.plate);
1230 XDestroyWindow(blackbox->XDisplay(), frame.plate);
1232 blackbox->removeEventHandler(frame.window);
1233 XDestroyWindow(blackbox->XDisplay(), frame.window);
1238 * Creates a new top level window, with a given location, size, and border
1239 * width.
1240 * Returns: the newly created window
1242 Window BlackboxWindow::createToplevelWindow(void) {
1243 XSetWindowAttributes attrib_create;
1244 unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
1246 attrib_create.colormap = _screen->screenInfo().colormap();
1247 attrib_create.override_redirect = True;
1248 attrib_create.event_mask = EnterWindowMask | LeaveWindowMask;
1250 return XCreateWindow(blackbox->XDisplay(),
1251 _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
1252 _screen->screenInfo().depth(), InputOutput,
1253 _screen->screenInfo().visual(),
1254 create_mask, &attrib_create);
1259 * Creates a child window, and optionally associates a given cursor with
1260 * the new window.
1262 Window BlackboxWindow::createChildWindow(Window parent,
1263 unsigned long event_mask,
1264 Cursor cursor) {
1265 XSetWindowAttributes attrib_create;
1266 unsigned long create_mask = CWEventMask;
1268 attrib_create.event_mask = event_mask;
1270 if (cursor) {
1271 create_mask |= CWCursor;
1272 attrib_create.cursor = cursor;
1275 return XCreateWindow(blackbox->XDisplay(), parent, 0, 0, 1, 1, 0,
1276 _screen->screenInfo().depth(), InputOutput,
1277 _screen->screenInfo().visual(),
1278 create_mask, &attrib_create);
1283 * Reparents the client window into the newly created frame.
1285 * Note: the server must be grabbed before calling this function.
1287 void BlackboxWindow::associateClientWindow(void) {
1288 XSetWindowBorderWidth(blackbox->XDisplay(), client.window, 0);
1289 XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeInsert);
1291 XSelectInput(blackbox->XDisplay(), frame.plate,
1292 FocusChangeMask | SubstructureRedirectMask);
1294 XSelectInput(blackbox->XDisplay(), client.window,
1295 client_window_event_mask & ~StructureNotifyMask);
1296 XReparentWindow(blackbox->XDisplay(), client.window, frame.plate, 0, 0);
1297 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
1299 #ifdef SHAPE
1300 if (blackbox->hasShapeExtensions()) {
1301 XShapeSelectInput(blackbox->XDisplay(), client.window,
1302 ShapeNotifyMask);
1304 Bool shaped = False;
1305 int foo;
1306 unsigned int ufoo;
1308 XShapeQueryExtents(blackbox->XDisplay(), client.window, &shaped,
1309 &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
1310 &ufoo, &ufoo);
1311 client.state.shaped = shaped;
1313 #endif // SHAPE
1317 void BlackboxWindow::decorate(void) {
1318 const WindowStyle &style = _screen->resource().windowStyle();
1319 if (client.decorations & WindowDecorationTitlebar) {
1320 // render focused button texture
1321 frame.fbutton =
1322 bt::PixmapCache::find(_screen->screenNumber(),
1323 style.focus.button,
1324 style.button_width,
1325 style.button_width,
1326 frame.fbutton);
1328 // render unfocused button texture
1329 frame.ubutton =
1330 bt::PixmapCache::find(_screen->screenNumber(),
1331 style.unfocus.button,
1332 style.button_width,
1333 style.button_width,
1334 frame.ubutton);
1336 // render pressed button texture
1337 frame.pbutton =
1338 bt::PixmapCache::find(_screen->screenNumber(),
1339 style.pressed,
1340 style.button_width,
1341 style.button_width,
1342 frame.pbutton);
1344 // render focused titlebar texture
1345 frame.ftitle =
1346 bt::PixmapCache::find(_screen->screenNumber(),
1347 style.focus.title,
1348 frame.rect.width(),
1349 style.title_height,
1350 frame.ftitle);
1352 // render unfocused titlebar texture
1353 frame.utitle =
1354 bt::PixmapCache::find(_screen->screenNumber(),
1355 style.unfocus.title,
1356 frame.rect.width(),
1357 style.title_height,
1358 frame.utitle);
1360 // render focused label texture
1361 frame.flabel =
1362 bt::PixmapCache::find(_screen->screenNumber(),
1363 style.focus.label,
1364 frame.label_w,
1365 style.label_height,
1366 frame.flabel);
1368 // render unfocused label texture
1369 frame.ulabel =
1370 bt::PixmapCache::find(_screen->screenNumber(),
1371 style.unfocus.label,
1372 frame.label_w,
1373 style.label_height,
1374 frame.ulabel);
1377 XSetWindowBorder(blackbox->XDisplay(), frame.plate,
1378 style.frame_border.pixel(_screen->screenNumber()));
1380 if (client.decorations & WindowDecorationHandle) {
1381 frame.fhandle =
1382 bt::PixmapCache::find(_screen->screenNumber(),
1383 style.focus.handle,
1384 frame.rect.width(),
1385 style.handle_height,
1386 frame.fhandle);
1388 frame.uhandle =
1389 bt::PixmapCache::find(_screen->screenNumber(),
1390 style.unfocus.handle,
1391 frame.rect.width(),
1392 style.handle_height,
1393 frame.uhandle);
1396 if (client.decorations & WindowDecorationGrip) {
1397 frame.fgrip =
1398 bt::PixmapCache::find(_screen->screenNumber(),
1399 style.focus.grip,
1400 style.grip_width,
1401 style.handle_height,
1402 frame.fgrip);
1404 frame.ugrip =
1405 bt::PixmapCache::find(_screen->screenNumber(),
1406 style.unfocus.grip,
1407 style.grip_width,
1408 style.handle_height,
1409 frame.ugrip);
1414 void BlackboxWindow::createHandle(void) {
1415 frame.handle = createChildWindow(frame.window,
1416 ButtonPressMask | ButtonReleaseMask |
1417 ButtonMotionMask | ExposureMask);
1418 blackbox->insertEventHandler(frame.handle, this);
1420 if (client.decorations & WindowDecorationGrip)
1421 createGrips();
1425 void BlackboxWindow::destroyHandle(void) {
1426 if (frame.left_grip || frame.right_grip)
1427 destroyGrips();
1429 if (frame.fhandle) bt::PixmapCache::release(frame.fhandle);
1430 if (frame.uhandle) bt::PixmapCache::release(frame.uhandle);
1432 frame.fhandle = frame.uhandle = None;
1434 blackbox->removeEventHandler(frame.handle);
1435 XDestroyWindow(blackbox->XDisplay(), frame.handle);
1436 frame.handle = None;
1440 void BlackboxWindow::createGrips(void) {
1441 frame.left_grip =
1442 createChildWindow(frame.handle,
1443 ButtonPressMask | ButtonReleaseMask |
1444 ButtonMotionMask | ExposureMask,
1445 blackbox->resource().cursors().resize_bottom_left);
1446 blackbox->insertEventHandler(frame.left_grip, this);
1448 frame.right_grip =
1449 createChildWindow(frame.handle,
1450 ButtonPressMask | ButtonReleaseMask |
1451 ButtonMotionMask | ExposureMask,
1452 blackbox->resource().cursors().resize_bottom_right);
1453 blackbox->insertEventHandler(frame.right_grip, this);
1457 void BlackboxWindow::destroyGrips(void) {
1458 if (frame.fgrip) bt::PixmapCache::release(frame.fgrip);
1459 if (frame.ugrip) bt::PixmapCache::release(frame.ugrip);
1461 frame.fgrip = frame.ugrip = None;
1463 blackbox->removeEventHandler(frame.left_grip);
1464 blackbox->removeEventHandler(frame.right_grip);
1466 XDestroyWindow(blackbox->XDisplay(), frame.left_grip);
1467 XDestroyWindow(blackbox->XDisplay(), frame.right_grip);
1468 frame.left_grip = frame.right_grip = None;
1472 void BlackboxWindow::createTitlebar(void) {
1473 frame.title = createChildWindow(frame.window,
1474 ButtonPressMask | ButtonReleaseMask |
1475 ButtonMotionMask | ExposureMask);
1476 frame.label = createChildWindow(frame.title,
1477 ButtonPressMask | ButtonReleaseMask |
1478 ButtonMotionMask | ExposureMask);
1479 blackbox->insertEventHandler(frame.title, this);
1480 blackbox->insertEventHandler(frame.label, this);
1482 if (client.decorations & WindowDecorationIconify) createIconifyButton();
1483 if (client.decorations & WindowDecorationMaximize) createMaximizeButton();
1484 if (client.decorations & WindowDecorationClose) createCloseButton();
1488 void BlackboxWindow::destroyTitlebar(void) {
1489 if (frame.close_button)
1490 destroyCloseButton();
1492 if (frame.iconify_button)
1493 destroyIconifyButton();
1495 if (frame.maximize_button)
1496 destroyMaximizeButton();
1498 if (frame.fbutton) bt::PixmapCache::release(frame.fbutton);
1499 if (frame.ubutton) bt::PixmapCache::release(frame.ubutton);
1500 if (frame.pbutton) bt::PixmapCache::release(frame.pbutton);
1501 if (frame.ftitle) bt::PixmapCache::release(frame.ftitle);
1502 if (frame.utitle) bt::PixmapCache::release(frame.utitle);
1503 if (frame.flabel) bt::PixmapCache::release(frame.flabel);
1504 if (frame.ulabel) bt::PixmapCache::release(frame.ulabel);
1506 frame.fbutton = frame.ubutton = frame.pbutton =
1507 frame.ftitle = frame.utitle =
1508 frame.flabel = frame.ulabel = None;
1510 blackbox->removeEventHandler(frame.title);
1511 blackbox->removeEventHandler(frame.label);
1513 XDestroyWindow(blackbox->XDisplay(), frame.label);
1514 XDestroyWindow(blackbox->XDisplay(), frame.title);
1515 frame.title = frame.label = None;
1519 void BlackboxWindow::createCloseButton(void) {
1520 if (frame.title != None) {
1521 frame.close_button = createChildWindow(frame.title,
1522 ButtonPressMask |
1523 ButtonReleaseMask |
1524 ButtonMotionMask | ExposureMask);
1525 blackbox->insertEventHandler(frame.close_button, this);
1530 void BlackboxWindow::destroyCloseButton(void) {
1531 blackbox->removeEventHandler(frame.close_button);
1532 XDestroyWindow(blackbox->XDisplay(), frame.close_button);
1533 frame.close_button = None;
1537 void BlackboxWindow::createIconifyButton(void) {
1538 if (frame.title != None) {
1539 frame.iconify_button = createChildWindow(frame.title,
1540 ButtonPressMask |
1541 ButtonReleaseMask |
1542 ButtonMotionMask | ExposureMask);
1543 blackbox->insertEventHandler(frame.iconify_button, this);
1548 void BlackboxWindow::destroyIconifyButton(void) {
1549 blackbox->removeEventHandler(frame.iconify_button);
1550 XDestroyWindow(blackbox->XDisplay(), frame.iconify_button);
1551 frame.iconify_button = None;
1555 void BlackboxWindow::createMaximizeButton(void) {
1556 if (frame.title != None) {
1557 frame.maximize_button = createChildWindow(frame.title,
1558 ButtonPressMask |
1559 ButtonReleaseMask |
1560 ButtonMotionMask | ExposureMask);
1561 blackbox->insertEventHandler(frame.maximize_button, this);
1566 void BlackboxWindow::destroyMaximizeButton(void) {
1567 blackbox->removeEventHandler(frame.maximize_button);
1568 XDestroyWindow(blackbox->XDisplay(), frame.maximize_button);
1569 frame.maximize_button = None;
1573 void BlackboxWindow::positionButtons(bool redecorate_label) {
1574 // we need to use signed ints here to detect windows that are too small
1575 const WindowStyle &style = _screen->resource().windowStyle();
1576 const int extra = style.title_margin == 0 ?
1577 style.focus.button.borderWidth() : 0,
1578 bw = style.button_width + style.title_margin
1579 - extra,
1580 by = style.title_margin +
1581 style.focus.title.borderWidth();
1582 int lx = by, lw = frame.rect.width() - by;
1584 if (client.decorations & WindowDecorationIconify) {
1585 if (frame.iconify_button == None) createIconifyButton();
1587 XMoveResizeWindow(blackbox->XDisplay(), frame.iconify_button, by, by,
1588 style.button_width, style.button_width);
1589 XMapWindow(blackbox->XDisplay(), frame.iconify_button);
1591 lx += bw;
1592 lw -= bw;
1593 } else if (frame.iconify_button) {
1594 destroyIconifyButton();
1597 int bx = frame.rect.width() - bw
1598 - style.focus.title.borderWidth() - extra;
1600 if (client.decorations & WindowDecorationClose) {
1601 if (frame.close_button == None) createCloseButton();
1603 XMoveResizeWindow(blackbox->XDisplay(), frame.close_button, bx, by,
1604 style.button_width, style.button_width);
1605 XMapWindow(blackbox->XDisplay(), frame.close_button);
1607 bx -= bw;
1608 lw -= bw;
1609 } else if (frame.close_button) {
1610 destroyCloseButton();
1613 if (client.decorations & WindowDecorationMaximize) {
1614 if (frame.maximize_button == None) createMaximizeButton();
1616 XMoveResizeWindow(blackbox->XDisplay(), frame.maximize_button, bx, by,
1617 style.button_width, style.button_width);
1618 XMapWindow(blackbox->XDisplay(), frame.maximize_button);
1620 bx -= bw;
1621 lw -= bw;
1622 } else if (frame.maximize_button) {
1623 destroyMaximizeButton();
1626 if (lw > by) {
1627 frame.label_w = lw - by;
1628 XMoveResizeWindow(blackbox->XDisplay(), frame.label, lx, by,
1629 frame.label_w, style.label_height);
1630 XMapWindow(blackbox->XDisplay(), frame.label);
1632 if (redecorate_label) {
1633 frame.flabel =
1634 bt::PixmapCache::find(_screen->screenNumber(),
1635 style.focus.label,
1636 frame.label_w, style.label_height,
1637 frame.flabel);
1638 frame.ulabel =
1639 bt::PixmapCache::find(_screen->screenNumber(),
1640 style.unfocus.label,
1641 frame.label_w, style.label_height,
1642 frame.ulabel);
1645 const bt::ustring ellided =
1646 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
1647 _screen->screenNumber(), style.font);
1649 if (ellided != client.visible_title) {
1650 client.visible_title = ellided;
1651 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
1653 } else {
1654 frame.label_w = 1;
1655 XUnmapWindow(blackbox->XDisplay(), frame.label);
1658 redrawLabel();
1659 redrawAllButtons();
1663 void BlackboxWindow::reconfigure(void) {
1664 const WindowStyle &style = _screen->resource().windowStyle();
1665 if (isMaximized()) {
1666 // update the frame margin in case the style has changed
1667 frame.margin = ::update_margin(client.decorations, style);
1669 // make sure maximized windows have the correct size after a style
1670 // change
1671 remaximize();
1672 } else {
1673 // get the client window geometry as if it was unmanaged
1674 bt::Rect r = frame.rect;
1675 if (client.ewmh.shaded) {
1676 r.setHeight(client.rect.height() + frame.margin.top
1677 + frame.margin.bottom);
1679 r = ::restoreGravity(r, frame.margin, client.wmnormal.win_gravity);
1681 // update the frame margin in case the style has changed
1682 frame.margin = ::update_margin(client.decorations, style);
1684 // get the frame window geometry from the client window geometry
1685 // calculated above
1686 r = ::applyGravity(r, frame.margin, client.wmnormal.win_gravity);
1687 if (client.ewmh.shaded) {
1688 frame.rect = r;
1690 positionWindows();
1691 decorate();
1693 // keep the window shaded
1694 frame.rect.setHeight(style.title_height);
1695 XResizeWindow(blackbox->XDisplay(), frame.window,
1696 frame.rect.width(), frame.rect.height());
1697 } else {
1698 // trick configure into working
1699 frame.rect = bt::Rect();
1700 configure(r);
1704 ungrabButtons();
1705 grabButtons();
1709 void BlackboxWindow::grabButtons(void) {
1710 if (blackbox->resource().focusModel() == ClickToFocusModel
1711 || blackbox->resource().clickRaise())
1712 // grab button 1 for changing focus/raising
1713 blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
1714 GrabModeSync, GrabModeSync, frame.plate, None,
1715 blackbox->resource().allowScrollLock());
1717 if (hasWindowFunction(WindowFunctionMove))
1718 blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
1719 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1720 GrabModeAsync, frame.window,
1721 blackbox->resource().cursors().move,
1722 blackbox->resource().allowScrollLock());
1723 if (hasWindowFunction(WindowFunctionResize))
1724 blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
1725 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1726 GrabModeAsync, frame.window,
1727 None, blackbox->resource().allowScrollLock());
1728 // alt+middle lowers the window
1729 blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
1730 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1731 frame.window, None,
1732 blackbox->resource().allowScrollLock());
1736 void BlackboxWindow::ungrabButtons(void) {
1737 blackbox->ungrabButton(Button1, 0, frame.plate);
1738 blackbox->ungrabButton(Button1, Mod1Mask, frame.window);
1739 blackbox->ungrabButton(Button2, Mod1Mask, frame.window);
1740 blackbox->ungrabButton(Button3, Mod1Mask, frame.window);
1744 void BlackboxWindow::positionWindows(void) {
1745 const WindowStyle &style = _screen->resource().windowStyle();
1746 const unsigned int bw = (hasWindowDecoration(WindowDecorationBorder)
1747 ? style.frame_border_width
1748 : 0);
1750 XMoveResizeWindow(blackbox->XDisplay(), frame.plate,
1751 frame.margin.left - bw,
1752 frame.margin.top - bw,
1753 client.rect.width(), client.rect.height());
1754 XSetWindowBorderWidth(blackbox->XDisplay(), frame.plate, bw);
1755 XMoveResizeWindow(blackbox->XDisplay(), client.window,
1756 0, 0, client.rect.width(), client.rect.height());
1757 // ensure client.rect contains the real location
1758 client.rect.setPos(frame.rect.left() + frame.margin.left,
1759 frame.rect.top() + frame.margin.top);
1761 if (client.decorations & WindowDecorationTitlebar) {
1762 if (frame.title == None) createTitlebar();
1764 XMoveResizeWindow(blackbox->XDisplay(), frame.title,
1765 0, 0, frame.rect.width(), style.title_height);
1767 positionButtons();
1768 XMapSubwindows(blackbox->XDisplay(), frame.title);
1769 XMapWindow(blackbox->XDisplay(), frame.title);
1770 } else if (frame.title) {
1771 destroyTitlebar();
1774 if (client.decorations & WindowDecorationHandle) {
1775 if (frame.handle == None) createHandle();
1777 // use client.rect here so the value is correct even if shaded
1778 XMoveResizeWindow(blackbox->XDisplay(), frame.handle,
1779 0, client.rect.height() + frame.margin.top,
1780 frame.rect.width(), style.handle_height);
1782 if (client.decorations & WindowDecorationGrip) {
1783 if (frame.left_grip == None || frame.right_grip == None) createGrips();
1785 XMoveResizeWindow(blackbox->XDisplay(), frame.left_grip, 0, 0,
1786 style.grip_width, style.handle_height);
1788 const int nx = frame.rect.width() - style.grip_width;
1789 XMoveResizeWindow(blackbox->XDisplay(), frame.right_grip, nx, 0,
1790 style.grip_width, style.handle_height);
1792 XMapSubwindows(blackbox->XDisplay(), frame.handle);
1793 } else {
1794 destroyGrips();
1797 XMapWindow(blackbox->XDisplay(), frame.handle);
1798 } else if (frame.handle) {
1799 destroyHandle();
1805 * This function is responsible for updating both the client and the
1806 * frame rectangles. According to the ICCCM a client message is not
1807 * sent for a resize, only a move.
1809 void BlackboxWindow::configure(int dx, int dy,
1810 unsigned int dw, unsigned int dh) {
1811 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1812 ! client.state.moving);
1814 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1815 frame.rect.setRect(dx, dy, dw, dh);
1817 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1818 frame.rect.setPos(0, 0);
1820 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1821 frame.rect.top() + frame.margin.top,
1822 frame.rect.right() - frame.margin.right,
1823 frame.rect.bottom() - frame.margin.bottom);
1825 #ifdef SHAPE
1826 if (client.state.shaped)
1827 configureShape();
1828 #endif // SHAPE
1830 XMoveResizeWindow(blackbox->XDisplay(), frame.window,
1831 frame.rect.x(), frame.rect.y(),
1832 frame.rect.width(), frame.rect.height());
1834 positionWindows();
1835 decorate();
1836 redrawWindowFrame();
1837 } else {
1838 frame.rect.setPos(dx, dy);
1840 XMoveWindow(blackbox->XDisplay(), frame.window,
1841 frame.rect.x(), frame.rect.y());
1843 we may have been called just after an opaque window move, so
1844 even though the old coords match the new ones no ConfigureNotify
1845 has been sent yet. There are likely other times when this will
1846 be relevant as well.
1848 if (! client.state.moving) send_event = True;
1851 if (send_event) {
1852 // if moving, the update and event will occur when the move finishes
1853 client.rect.setPos(frame.rect.left() + frame.margin.left,
1854 frame.rect.top() + frame.margin.top);
1856 XEvent event;
1857 event.type = ConfigureNotify;
1859 event.xconfigure.display = blackbox->XDisplay();
1860 event.xconfigure.event = client.window;
1861 event.xconfigure.window = client.window;
1862 event.xconfigure.x = client.rect.x();
1863 event.xconfigure.y = client.rect.y();
1864 event.xconfigure.width = client.rect.width();
1865 event.xconfigure.height = client.rect.height();
1866 event.xconfigure.border_width = client.old_bw;
1867 event.xconfigure.above = frame.window;
1868 event.xconfigure.override_redirect = False;
1870 XSendEvent(blackbox->XDisplay(), client.window, False,
1871 StructureNotifyMask, &event);
1876 #ifdef SHAPE
1877 void BlackboxWindow::configureShape(void) {
1878 XShapeCombineShape(blackbox->XDisplay(), frame.window, ShapeBounding,
1879 frame.margin.left, frame.margin.top,
1880 client.window, ShapeBounding, ShapeSet);
1882 int num = 0;
1883 XRectangle xrect[2];
1885 const WindowStyle &style = _screen->resource().windowStyle();
1886 if (client.decorations & WindowDecorationTitlebar) {
1887 xrect[0].x = xrect[0].y = 0;
1888 xrect[0].width = frame.rect.width();
1889 xrect[0].height = style.title_height;
1890 ++num;
1893 if (client.decorations & WindowDecorationHandle) {
1894 xrect[1].x = 0;
1895 xrect[1].y = client.rect.height() + frame.margin.top;
1896 xrect[1].width = frame.rect.width();
1897 xrect[1].height = style.handle_height;
1898 ++num;
1901 XShapeCombineRectangles(blackbox->XDisplay(), frame.window,
1902 ShapeBounding, 0, 0, xrect, num,
1903 ShapeUnion, Unsorted);
1905 #endif // SHAPE
1908 void BlackboxWindow::addTransient(BlackboxWindow *win)
1909 { client.transientList.push_front(win); }
1912 void BlackboxWindow::removeTransient(BlackboxWindow *win)
1913 { client.transientList.remove(win); }
1916 BlackboxWindow *BlackboxWindow::findTransientFor(void) const {
1917 BlackboxWindow *win = 0;
1918 if (isTransient()) {
1919 win = blackbox->findWindow(client.transient_for);
1920 if (win && win->_screen != _screen)
1921 win = 0;
1923 return win;
1928 walk up to either 1) a non-transient window 2) a group transient,
1929 watching out for a circular chain
1931 this function returns zero for non-transient windows
1933 BlackboxWindow *BlackboxWindow::findNonTransientParent(void) const {
1934 BlackboxWindowList seen;
1935 seen.push_back(const_cast<BlackboxWindow *>(this));
1937 BlackboxWindow *w = findTransientFor();
1938 if (!w)
1939 return 0;
1941 while (w->isTransient() && !w->isGroupTransient()) {
1942 seen.push_back(w);
1943 BlackboxWindow * const tmp = w->findTransientFor();
1944 if (!tmp)
1945 break;
1946 if (std::find(seen.begin(), seen.end(), tmp) != seen.end()) {
1947 // circular transient chain
1948 break;
1950 w = tmp;
1952 return w;
1957 Returns a list of all transients. This is recursive, so it returns
1958 all transients of transients as well.
1960 BlackboxWindowList BlackboxWindow::buildFullTransientList(void) const {
1961 BlackboxWindowList all = client.transientList;
1962 BlackboxWindowList::const_iterator it = client.transientList.begin(),
1963 end = client.transientList.end();
1964 for (; it != end; ++it) {
1965 BlackboxWindowList x = (*it)->buildFullTransientList();
1966 all.splice(all.end(), x);
1968 return all;
1972 BWindowGroup *BlackboxWindow::findWindowGroup(void) const {
1973 BWindowGroup *group = 0;
1974 if (client.wmhints.window_group)
1975 group = blackbox->findWindowGroup(client.wmhints.window_group);
1976 return group;
1980 void BlackboxWindow::setWorkspace(unsigned int new_workspace) {
1981 client.ewmh.workspace = new_workspace;
1982 blackbox->ewmh().setWMDesktop(client.window, client.ewmh.workspace);
1986 void BlackboxWindow::changeWorkspace(unsigned int new_workspace,
1987 ChangeWorkspaceOption how) {
1988 if (client.ewmh.workspace == new_workspace)
1989 return;
1991 if (isTransient()) {
1992 BlackboxWindow *win = findTransientFor();
1993 if (win) {
1994 if (win->workspace() != new_workspace) {
1995 win->changeWorkspace(new_workspace, how);
1996 return;
1999 } else {
2000 assert(hasWindowFunction(WindowFunctionChangeWorkspace));
2003 Workspace *ws;
2004 if (workspace() != bt::BSENTINEL) {
2005 ws = _screen->findWorkspace(workspace());
2006 assert(ws != 0);
2007 ws->removeWindow(this);
2010 if (new_workspace != bt::BSENTINEL) {
2011 ws = _screen->findWorkspace(new_workspace);
2012 assert(ws != 0);
2013 ws->addWindow(this);
2016 switch (how) {
2017 case StayOnCurrentWorkspace:
2018 if (isVisible() && workspace() != bt::BSENTINEL
2019 && workspace() != _screen->currentWorkspace()) {
2020 hide();
2021 } else if (!isVisible()
2022 && (workspace() == bt::BSENTINEL
2023 || workspace() == _screen->currentWorkspace())) {
2024 show();
2026 break;
2028 case SwitchToNewWorkspace:
2030 we will change to the new workspace soon, so force this window
2031 to be visible
2033 show();
2034 break;
2037 // change workspace on all transients
2038 if (!client.transientList.empty()) {
2039 BlackboxWindowList::iterator it = client.transientList.begin(),
2040 end = client.transientList.end();
2041 for (; it != end; ++it)
2042 (*it)->changeWorkspace(new_workspace, how);
2047 void BlackboxWindow::changeLayer(StackingList::Layer new_layer) {
2048 if (layer() == new_layer)
2049 return;
2051 bool restack = false;
2052 if (isTransient()) {
2053 BlackboxWindow *win = findTransientFor();
2054 if (win) {
2055 if (win->layer() != new_layer) {
2056 win->changeLayer(new_layer);
2057 return;
2058 } else {
2059 restack = true;
2062 } else {
2063 assert(hasWindowFunction(WindowFunctionChangeLayer));
2064 restack = true;
2067 _screen->stackingList().changeLayer(this, new_layer);
2069 if (!client.transientList.empty()) {
2070 BlackboxWindowList::iterator it = client.transientList.begin();
2071 const BlackboxWindowList::iterator end = client.transientList.end();
2072 for (; it != end; ++it)
2073 (*it)->changeLayer(new_layer);
2076 if (restack)
2077 _screen->restackWindows();
2081 bool BlackboxWindow::setInputFocus(void) {
2082 if (!isVisible())
2083 return false;
2084 if (client.state.focused)
2085 return true;
2087 switch (windowType()) {
2088 case WindowTypeDock:
2090 Many docks have auto-hide features similar to the toolbar and
2091 slit... we don't want these things to be moved to the center of
2092 the screen when switching to an empty workspace.
2094 break;
2096 default:
2097 { const bt::Rect &scr = _screen->screenInfo().rect();
2098 if (!frame.rect.intersects(scr)) {
2099 // client is outside the screen, move it to the center
2100 configure(scr.x() + (scr.width() - frame.rect.width()) / 2,
2101 scr.y() + (scr.height() - frame.rect.height()) / 2,
2102 frame.rect.width(), frame.rect.height());
2104 break;
2109 pass focus to any modal transients, giving modal group transients
2110 higher priority
2112 BWindowGroup *group = findWindowGroup();
2113 if (group && !group->transients().empty()) {
2114 BlackboxWindowList::const_iterator it = group->transients().begin(),
2115 end = group->transients().end();
2116 for (; it != end; ++it) {
2117 BlackboxWindow * const tmp = *it;
2118 if (!tmp->isVisible() || !tmp->isModal())
2119 continue;
2120 if (tmp == this) {
2121 // we are the newest modal group transient
2122 break;
2124 if (isTransient()) {
2125 if (tmp == findNonTransientParent()) {
2126 // we are a transient of the modal group transient
2127 break;
2130 return tmp->setInputFocus();
2134 if (!client.transientList.empty()) {
2135 BlackboxWindowList::const_iterator it = client.transientList.begin(),
2136 end = client.transientList.end();
2137 for (; it != end; ++it) {
2138 BlackboxWindow * const tmp = *it;
2139 if (tmp->isVisible() && tmp->isModal())
2140 return tmp->setInputFocus();
2144 switch (windowType()) {
2145 case WindowTypeDock:
2146 return false;
2147 default:
2148 break;
2151 XSetInputFocus(blackbox->XDisplay(), client.window,
2152 RevertToPointerRoot, blackbox->XTime());
2154 if (client.wmprotocols.wm_take_focus) {
2155 XEvent ce;
2156 ce.xclient.type = ClientMessage;
2157 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2158 ce.xclient.display = blackbox->XDisplay();
2159 ce.xclient.window = client.window;
2160 ce.xclient.format = 32;
2161 ce.xclient.data.l[0] = blackbox->wmTakeFocusAtom();
2162 ce.xclient.data.l[1] = blackbox->XTime();
2163 ce.xclient.data.l[2] = 0l;
2164 ce.xclient.data.l[3] = 0l;
2165 ce.xclient.data.l[4] = 0l;
2166 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2169 return true;
2173 void BlackboxWindow::show(void) {
2174 if (client.state.visible)
2175 return;
2177 if (client.state.iconic)
2178 _screen->removeIcon(this);
2180 client.state.iconic = false;
2181 client.state.visible = true;
2182 setState(isShaded() ? IconicState : NormalState);
2184 XMapWindow(blackbox->XDisplay(), client.window);
2185 XMapSubwindows(blackbox->XDisplay(), frame.window);
2186 XMapWindow(blackbox->XDisplay(), frame.window);
2188 if (!client.transientList.empty()) {
2189 BlackboxWindowList::iterator it = client.transientList.begin(),
2190 end = client.transientList.end();
2191 for (; it != end; ++it)
2192 (*it)->show();
2195 #ifdef DEBUG
2196 int real_x, real_y;
2197 Window child;
2198 XTranslateCoordinates(blackbox->XDisplay(), client.window,
2199 _screen->screenInfo().rootWindow(),
2200 0, 0, &real_x, &real_y, &child);
2201 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
2202 client.rect.left(), client.rect.top(), real_x, real_y);
2203 assert(client.rect.left() == real_x && client.rect.top() == real_y);
2204 #endif
2208 void BlackboxWindow::hide(void) {
2209 if (!client.state.visible)
2210 return;
2212 client.state.visible = false;
2213 setState(client.state.iconic ? IconicState : client.current_state);
2215 XUnmapWindow(blackbox->XDisplay(), frame.window);
2218 * we don't want this XUnmapWindow call to generate an UnmapNotify
2219 * event, so we need to clear the event mask on client.window for a
2220 * split second. HOWEVER, since X11 is asynchronous, the window
2221 * could be destroyed in that split second, leaving us with a ghost
2222 * window... so, we need to do this while the X server is grabbed
2224 blackbox->XGrabServer();
2225 XSelectInput(blackbox->XDisplay(), client.window,
2226 client_window_event_mask & ~StructureNotifyMask);
2227 XUnmapWindow(blackbox->XDisplay(), client.window);
2228 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
2229 blackbox->XUngrabServer();
2233 void BlackboxWindow::close(void) {
2234 assert(hasWindowFunction(WindowFunctionClose));
2236 XEvent ce;
2237 ce.xclient.type = ClientMessage;
2238 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2239 ce.xclient.display = blackbox->XDisplay();
2240 ce.xclient.window = client.window;
2241 ce.xclient.format = 32;
2242 ce.xclient.data.l[0] = blackbox->wmDeleteWindowAtom();
2243 ce.xclient.data.l[1] = blackbox->XTime();
2244 ce.xclient.data.l[2] = 0l;
2245 ce.xclient.data.l[3] = 0l;
2246 ce.xclient.data.l[4] = 0l;
2247 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2251 void BlackboxWindow::activate(void) {
2252 if (workspace() != bt::BSENTINEL
2253 && workspace() != _screen->currentWorkspace())
2254 _screen->setCurrentWorkspace(workspace());
2255 if (client.state.iconic)
2256 show();
2257 if (client.ewmh.shaded)
2258 setShaded(false);
2259 if (setInputFocus())
2260 _screen->raiseWindow(this);
2264 void BlackboxWindow::iconify(void) {
2265 if (client.state.iconic)
2266 return;
2268 if (isTransient()) {
2269 BlackboxWindow *win = findTransientFor();
2270 if (win) {
2271 if (!win->isIconic()) {
2272 win->iconify();
2273 return;
2276 } else {
2277 assert(hasWindowFunction(WindowFunctionIconify));
2280 _screen->addIcon(this);
2282 client.state.iconic = true;
2283 hide();
2285 // iconify all transients
2286 if (!client.transientList.empty()) {
2287 BlackboxWindowList::iterator it = client.transientList.begin(),
2288 end = client.transientList.end();
2289 for (; it != end; ++it)
2290 (*it)->iconify();
2295 void BlackboxWindow::maximize(unsigned int button) {
2296 assert(hasWindowFunction(WindowFunctionMaximize));
2298 // any maximize operation always unshades
2299 client.ewmh.shaded = false;
2300 frame.rect.setHeight(client.rect.height() + frame.margin.top
2301 + frame.margin.bottom);
2303 if (isMaximized()) {
2304 // restore from maximized
2305 client.ewmh.maxh = client.ewmh.maxv = false;
2307 if (!isFullScreen()) {
2309 when a resize is begun, maximize(0) is called to clear any
2310 maximization flags currently set. Otherwise it still thinks
2311 it is maximized. so we do not need to call configure()
2312 because resizing will handle it
2314 if (! client.state.resizing) {
2315 bt::Rect r = ::applyGravity(client.premax,
2316 frame.margin,
2317 client.wmnormal.win_gravity);
2318 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2319 // trick configure into working
2320 frame.rect = bt::Rect();
2321 configure(r);
2324 redrawAllButtons(); // in case it is not called in configure()
2327 updateEWMHState();
2328 updateEWMHAllowedActions();
2329 return;
2332 switch (button) {
2333 case 1:
2334 client.ewmh.maxh = true;
2335 client.ewmh.maxv = true;
2336 break;
2338 case 2:
2339 client.ewmh.maxh = false;
2340 client.ewmh.maxv = true;
2341 break;
2343 case 3:
2344 client.ewmh.maxh = true;
2345 client.ewmh.maxv = false;
2346 break;
2348 default:
2349 assert(0);
2350 break;
2353 if (!isFullScreen()) {
2354 // go go gadget-maximize!
2355 bt::Rect r = _screen->availableArea();
2357 if (!client.ewmh.maxh) {
2358 r.setX(frame.rect.x());
2359 r.setWidth(frame.rect.width());
2361 if (!client.ewmh.maxv) {
2362 r.setY(frame.rect.y());
2363 r.setHeight(frame.rect.height());
2366 // store the current frame geometry, so that we can restore it later
2367 client.premax = ::restoreGravity(frame.rect,
2368 frame.margin,
2369 client.wmnormal.win_gravity);
2371 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2372 // trick configure into working
2373 frame.rect = bt::Rect();
2374 configure(r);
2377 updateEWMHState();
2378 updateEWMHAllowedActions();
2382 // re-maximizes the window to take into account availableArea changes
2383 void BlackboxWindow::remaximize(void) {
2384 if (isShaded()) return;
2386 unsigned int button = 0u;
2387 if (client.ewmh.maxv) {
2388 button = (client.ewmh.maxh) ? 1u : 2u;
2389 } else if (client.ewmh.maxh) {
2390 button = (client.ewmh.maxv) ? 1u : 3u;
2393 // trick maximize() into working
2394 client.ewmh.maxh = client.ewmh.maxv = false;
2396 const bt::Rect tmp = client.premax;
2397 maximize(button);
2398 client.premax = tmp;
2402 void BlackboxWindow::setShaded(bool shaded) {
2403 assert(hasWindowFunction(WindowFunctionShade));
2405 if (client.ewmh.shaded == shaded)
2406 return;
2408 client.ewmh.shaded = shaded;
2409 if (!isShaded()) {
2410 if (isMaximized()) {
2411 remaximize();
2412 } else {
2413 // set the frame rect to the normal size
2414 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2415 frame.margin.bottom);
2417 XResizeWindow(blackbox->XDisplay(), frame.window,
2418 frame.rect.width(), frame.rect.height());
2421 setState(NormalState);
2422 } else {
2423 // set the frame rect to the shaded size
2424 const WindowStyle &style = _screen->resource().windowStyle();
2425 frame.rect.setHeight(style.title_height);
2427 XResizeWindow(blackbox->XDisplay(), frame.window,
2428 frame.rect.width(), frame.rect.height());
2430 setState(IconicState);
2435 void BlackboxWindow::setFullScreen(bool b) {
2436 assert(hasWindowFunction(WindowFunctionFullScreen));
2438 if (client.ewmh.fullscreen == b)
2439 return;
2441 // any fullscreen operation always unshades
2442 client.ewmh.shaded = false;
2443 frame.rect.setHeight(client.rect.height() + frame.margin.top
2444 + frame.margin.bottom);
2446 bool refocus = isFocused();
2447 client.ewmh.fullscreen = b;
2448 if (isFullScreen()) {
2449 // go go gadget-fullscreen!
2450 if (!isMaximized())
2451 client.premax = ::restoreGravity(frame.rect,
2452 frame.margin,
2453 client.wmnormal.win_gravity);
2455 // modify decorations, functions and frame margin
2456 client.decorations = NoWindowDecorations;
2457 client.functions &= ~(WindowFunctionMove |
2458 WindowFunctionResize |
2459 WindowFunctionShade);
2460 const WindowStyle &style = _screen->resource().windowStyle();
2461 frame.margin = ::update_margin(client.decorations, style);
2463 bt::Rect r = ::constrain(_screen->screenInfo().rect(),
2464 frame.margin,
2465 client.wmnormal,
2466 TopLeft);
2467 // trick configure() into working
2468 frame.rect = bt::Rect();
2469 configure(r);
2471 if (isVisible())
2472 changeLayer(StackingList::LayerFullScreen);
2474 updateEWMHState();
2475 updateEWMHAllowedActions();
2476 } else {
2477 // restore from fullscreen
2478 ::update_decorations(client.decorations,
2479 client.functions,
2480 isTransient(),
2481 client.ewmh,
2482 client.motif,
2483 client.wmnormal,
2484 client.wmprotocols);
2485 const WindowStyle &style = _screen->resource().windowStyle();
2486 frame.margin = ::update_margin(client.decorations, style);
2488 if (isVisible())
2489 changeLayer(StackingList::LayerNormal);
2491 if (isMaximized()) {
2492 remaximize();
2493 } else {
2494 bt::Rect r = ::applyGravity(client.premax,
2495 frame.margin,
2496 client.wmnormal.win_gravity);
2497 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2499 // trick configure into working
2500 frame.rect = bt::Rect();
2501 configure(r);
2503 updateEWMHState();
2504 updateEWMHAllowedActions();
2508 ungrabButtons();
2509 grabButtons();
2511 if (refocus)
2512 (void) setInputFocus();
2516 void BlackboxWindow::redrawWindowFrame(void) const {
2517 if (client.decorations & WindowDecorationTitlebar) {
2518 redrawTitle();
2519 redrawLabel();
2520 redrawAllButtons();
2523 if (client.decorations & WindowDecorationHandle) {
2524 redrawHandle();
2526 if (client.decorations & WindowDecorationGrip)
2527 redrawGrips();
2532 void BlackboxWindow::setFocused(bool focused) {
2533 if (focused == client.state.focused)
2534 return;
2536 client.state.focused = isVisible() ? focused : false;
2538 if (isVisible()) {
2539 redrawWindowFrame();
2541 if (client.state.focused) {
2542 XInstallColormap(blackbox->XDisplay(), client.colormap);
2543 } else {
2544 if (client.ewmh.fullscreen && layer() != StackingList::LayerBelow)
2545 changeLayer(StackingList::LayerBelow);
2551 void BlackboxWindow::setState(unsigned long new_state) {
2552 client.current_state = new_state;
2554 unsigned long state[2];
2555 state[0] = client.current_state;
2556 state[1] = None;
2557 XChangeProperty(blackbox->XDisplay(), client.window,
2558 blackbox->wmStateAtom(), blackbox->wmStateAtom(), 32,
2559 PropModeReplace, (unsigned char *) state, 2);
2561 updateEWMHState();
2562 updateEWMHAllowedActions();
2566 void BlackboxWindow::updateEWMHState() {
2567 const bt::EWMH& ewmh = blackbox->ewmh();
2569 // set _NET_WM_STATE
2570 bt::EWMH::AtomList atoms;
2571 if (isModal())
2572 atoms.push_back(ewmh.wmStateModal());
2573 if (isShaded())
2574 atoms.push_back(ewmh.wmStateShaded());
2575 if (isIconic())
2576 atoms.push_back(ewmh.wmStateHidden());
2577 if (isFullScreen())
2578 atoms.push_back(ewmh.wmStateFullscreen());
2579 if (client.ewmh.maxh)
2580 atoms.push_back(ewmh.wmStateMaximizedHorz());
2581 if (client.ewmh.maxv)
2582 atoms.push_back(ewmh.wmStateMaximizedVert());
2583 if (client.ewmh.skip_taskbar)
2584 atoms.push_back(ewmh.wmStateSkipTaskbar());
2585 if (client.ewmh.skip_pager)
2586 atoms.push_back(ewmh.wmStateSkipPager());
2588 switch (layer()) {
2589 case StackingList::LayerAbove:
2590 atoms.push_back(ewmh.wmStateAbove());
2591 break;
2592 case StackingList::LayerBelow:
2593 atoms.push_back(ewmh.wmStateBelow());
2594 break;
2595 default:
2596 break;
2599 if (atoms.empty())
2600 ewmh.removeProperty(client.window, ewmh.wmState());
2601 else
2602 ewmh.setWMState(client.window, atoms);
2606 void BlackboxWindow::updateEWMHAllowedActions() {
2607 const bt::EWMH& ewmh = blackbox->ewmh();
2609 // set _NET_WM_ALLOWED_ACTIONS
2610 bt::EWMH::AtomList atoms;
2611 if (! client.state.iconic) {
2612 if (hasWindowFunction(WindowFunctionChangeWorkspace))
2613 atoms.push_back(ewmh.wmActionChangeDesktop());
2615 if (hasWindowFunction(WindowFunctionIconify))
2616 atoms.push_back(ewmh.wmActionMinimize());
2618 if (hasWindowFunction(WindowFunctionShade))
2619 atoms.push_back(ewmh.wmActionShade());
2621 if (hasWindowFunction(WindowFunctionMove))
2622 atoms.push_back(ewmh.wmActionMove());
2624 if (hasWindowFunction(WindowFunctionResize))
2625 atoms.push_back(ewmh.wmActionResize());
2627 if (hasWindowFunction(WindowFunctionMaximize)) {
2628 atoms.push_back(ewmh.wmActionMaximizeHorz());
2629 atoms.push_back(ewmh.wmActionMaximizeVert());
2632 atoms.push_back(ewmh.wmActionFullscreen());
2635 if (hasWindowFunction(WindowFunctionClose))
2636 atoms.push_back(ewmh.wmActionClose());
2638 if (atoms.empty())
2639 ewmh.removeProperty(client.window, ewmh.wmAllowedActions());
2640 else
2641 ewmh.setWMAllowedActions(client.window, atoms);
2645 void BlackboxWindow::redrawTitle(void) const {
2646 const WindowStyle &style = _screen->resource().windowStyle();
2647 const bt::Rect u(0, 0, frame.rect.width(), style.title_height);
2648 bt::drawTexture(_screen->screenNumber(),
2649 (client.state.focused
2650 ? style.focus.title
2651 : style.unfocus.title),
2652 frame.title, u, u,
2653 (client.state.focused
2654 ? frame.ftitle
2655 : frame.utitle));
2659 void BlackboxWindow::redrawLabel(void) const {
2660 const WindowStyle &style = _screen->resource().windowStyle();
2661 bt::Rect u(0, 0, frame.label_w, style.label_height);
2662 Pixmap p = (client.state.focused ? frame.flabel : frame.ulabel);
2663 if (p == ParentRelative) {
2664 const bt::Texture &texture =
2665 (isFocused() ? style.focus.title : style.unfocus.title);
2666 int offset = texture.borderWidth();
2667 if (client.decorations & WindowDecorationIconify)
2668 offset += style.button_width + style.title_margin;
2670 const bt::Rect t(-(style.title_margin + offset),
2671 -(style.title_margin + texture.borderWidth()),
2672 frame.rect.width(), style.title_height);
2673 bt::drawTexture(_screen->screenNumber(), texture, frame.label, t, u,
2674 (client.state.focused ? frame.ftitle : frame.utitle));
2675 } else {
2676 bt::drawTexture(_screen->screenNumber(),
2677 (client.state.focused
2678 ? style.focus.label
2679 : style.unfocus.label),
2680 frame.label, u, u, p);
2683 const bt::Pen pen(_screen->screenNumber(),
2684 ((client.state.focused)
2685 ? style.focus.text
2686 : style.unfocus.text));
2687 u.setCoords(u.left() + style.label_margin,
2688 u.top() + style.label_margin,
2689 u.right() - style.label_margin,
2690 u.bottom() - style.label_margin);
2691 bt::drawText(style.font, pen, frame.label, u,
2692 style.alignment, client.visible_title);
2696 void BlackboxWindow::redrawAllButtons(void) const {
2697 if (frame.iconify_button) redrawIconifyButton();
2698 if (frame.maximize_button) redrawMaximizeButton();
2699 if (frame.close_button) redrawCloseButton();
2703 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2704 const WindowStyle &style = _screen->resource().windowStyle();
2705 const bt::Rect u(0, 0, style.button_width, style.button_width);
2706 Pixmap p = (pressed ? frame.pbutton :
2707 (client.state.focused ? frame.fbutton : frame.ubutton));
2708 if (p == ParentRelative) {
2709 const bt::Texture &texture =
2710 (isFocused() ? style.focus.title : style.unfocus.title);
2711 const bt::Rect t(-(style.title_margin + texture.borderWidth()),
2712 -(style.title_margin + texture.borderWidth()),
2713 frame.rect.width(), style.title_height);
2714 bt::drawTexture(_screen->screenNumber(), texture, frame.iconify_button,
2715 t, u, (client.state.focused
2716 ? frame.ftitle
2717 : frame.utitle));
2718 } else {
2719 bt::drawTexture(_screen->screenNumber(),
2720 (pressed ? style.pressed :
2721 (client.state.focused ? style.focus.button :
2722 style.unfocus.button)),
2723 frame.iconify_button, u, u, p);
2726 const bt::Pen pen(_screen->screenNumber(),
2727 (client.state.focused
2728 ? style.focus.foreground
2729 : style.unfocus.foreground));
2730 bt::drawBitmap(style.iconify, pen, frame.iconify_button, u);
2734 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2735 const WindowStyle &style = _screen->resource().windowStyle();
2736 const bt::Rect u(0, 0, style.button_width, style.button_width);
2737 Pixmap p = (pressed ? frame.pbutton :
2738 (client.state.focused ? frame.fbutton : frame.ubutton));
2739 if (p == ParentRelative) {
2740 const bt::Texture &texture =
2741 (isFocused() ? style.focus.title : style.unfocus.title);
2742 int button_w = style.button_width
2743 + style.title_margin + texture.borderWidth();
2744 if (client.decorations & WindowDecorationClose)
2745 button_w *= 2;
2746 const bt::Rect t(-(frame.rect.width() - button_w),
2747 -(style.title_margin + texture.borderWidth()),
2748 frame.rect.width(), style.title_height);
2749 bt::drawTexture(_screen->screenNumber(), texture, frame.maximize_button,
2750 t, u, (client.state.focused
2751 ? frame.ftitle
2752 : frame.utitle));
2753 } else {
2754 bt::drawTexture(_screen->screenNumber(),
2755 (pressed ? style.pressed :
2756 (client.state.focused ? style.focus.button :
2757 style.unfocus.button)),
2758 frame.maximize_button, u, u, p);
2761 const bt::Pen pen(_screen->screenNumber(),
2762 (client.state.focused
2763 ? style.focus.foreground
2764 : style.unfocus.foreground));
2765 bt::drawBitmap(isMaximized() ? style.restore : style.maximize,
2766 pen, frame.maximize_button, u);
2770 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2771 const WindowStyle &style = _screen->resource().windowStyle();
2772 const bt::Rect u(0, 0, style.button_width, style.button_width);
2773 Pixmap p = (pressed ? frame.pbutton :
2774 (client.state.focused ? frame.fbutton : frame.ubutton));
2775 if (p == ParentRelative) {
2776 const bt::Texture &texture =
2777 (isFocused() ? style.focus.title : style.unfocus.title);
2778 const int button_w = style.button_width +
2779 style.title_margin +
2780 texture.borderWidth();
2781 const bt::Rect t(-(frame.rect.width() - button_w),
2782 -(style.title_margin + texture.borderWidth()),
2783 frame.rect.width(), style.title_height);
2784 bt::drawTexture(_screen->screenNumber(),texture, frame.close_button, t, u,
2785 (client.state.focused ? frame.ftitle : frame.utitle));
2786 } else {
2787 bt::drawTexture(_screen->screenNumber(),
2788 (pressed ? style.pressed :
2789 (client.state.focused ? style.focus.button :
2790 style.unfocus.button)),
2791 frame.close_button, u, u, p);
2794 const bt::Pen pen(_screen->screenNumber(),
2795 (client.state.focused
2796 ? style.focus.foreground
2797 : style.unfocus.foreground));
2798 bt::drawBitmap(style.close, pen, frame.close_button, u);
2802 void BlackboxWindow::redrawHandle(void) const {
2803 const WindowStyle &style = _screen->resource().windowStyle();
2804 const bt::Rect u(0, 0, frame.rect.width(), style.handle_height);
2805 bt::drawTexture(_screen->screenNumber(),
2806 (client.state.focused ? style.focus.handle :
2807 style.unfocus.handle),
2808 frame.handle, u, u,
2809 (client.state.focused ? frame.fhandle : frame.uhandle));
2813 void BlackboxWindow::redrawGrips(void) const {
2814 const WindowStyle &style = _screen->resource().windowStyle();
2815 const bt::Rect u(0, 0, style.grip_width, style.handle_height);
2816 Pixmap p = (client.state.focused ? frame.fgrip : frame.ugrip);
2817 if (p == ParentRelative) {
2818 bt::Rect t(0, 0, frame.rect.width(), style.handle_height);
2819 bt::drawTexture(_screen->screenNumber(),
2820 (client.state.focused ? style.focus.handle :
2821 style.unfocus.handle),
2822 frame.right_grip, t, u, p);
2824 t.setPos(-(frame.rect.width() - style.grip_width), 0);
2825 bt::drawTexture(_screen->screenNumber(),
2826 (client.state.focused ? style.focus.handle :
2827 style.unfocus.handle),
2828 frame.right_grip, t, u, p);
2829 } else {
2830 bt::drawTexture(_screen->screenNumber(),
2831 (client.state.focused ? style.focus.grip :
2832 style.unfocus.grip),
2833 frame.left_grip, u, u, p);
2835 bt::drawTexture(_screen->screenNumber(),
2836 (client.state.focused ? style.focus.grip :
2837 style.unfocus.grip),
2838 frame.right_grip, u, u, p);
2843 void
2844 BlackboxWindow::clientMessageEvent(const XClientMessageEvent * const event) {
2845 if (event->format != 32)
2846 return;
2848 const bt::EWMH& ewmh = blackbox->ewmh();
2850 if (event->message_type == blackbox->wmChangeStateAtom()) {
2851 if (event->data.l[0] == IconicState) {
2852 if (hasWindowFunction(WindowFunctionIconify))
2853 iconify();
2854 } else if (event->data.l[0] == NormalState) {
2855 activate();
2857 } else if (event->message_type == ewmh.activeWindow()) {
2858 activate();
2859 } else if (event->message_type == ewmh.closeWindow()) {
2860 if (hasWindowFunction(WindowFunctionClose))
2861 close();
2862 } else if (event->message_type == ewmh.moveresizeWindow()) {
2863 XConfigureRequestEvent request;
2864 request.window = event->window;
2865 request.x = event->data.l[1];
2866 request.y = event->data.l[2];
2867 request.width = event->data.l[3];
2868 request.height = event->data.l[4];
2869 request.value_mask = CWX | CWY | CWWidth | CWHeight;
2871 const int old_gravity = client.wmnormal.win_gravity;
2872 if (event->data.l[0] != 0)
2873 client.wmnormal.win_gravity = event->data.l[0];
2875 configureRequestEvent(&request);
2877 client.wmnormal.win_gravity = old_gravity;
2878 } else if (event->message_type == ewmh.wmDesktop()) {
2879 if (hasWindowFunction(WindowFunctionChangeWorkspace)) {
2880 const unsigned int new_workspace = event->data.l[0];
2881 changeWorkspace(new_workspace);
2883 } else if (event->message_type == ewmh.wmState()) {
2884 Atom action = event->data.l[0],
2885 first = event->data.l[1],
2886 second = event->data.l[2];
2888 if (first == ewmh.wmStateModal() || second == ewmh.wmStateModal()) {
2889 if ((action == ewmh.wmStateAdd() ||
2890 (action == ewmh.wmStateToggle() && ! client.ewmh.modal)) &&
2891 isTransient())
2892 client.ewmh.modal = true;
2893 else
2894 client.ewmh.modal = false;
2897 if (hasWindowFunction(WindowFunctionMaximize)) {
2898 int max_horz = 0, max_vert = 0;
2900 if (first == ewmh.wmStateMaximizedHorz() ||
2901 second == ewmh.wmStateMaximizedHorz()) {
2902 max_horz = ((action == ewmh.wmStateAdd()
2903 || (action == ewmh.wmStateToggle()
2904 && !client.ewmh.maxh))
2905 ? 1 : -1);
2908 if (first == ewmh.wmStateMaximizedVert() ||
2909 second == ewmh.wmStateMaximizedVert()) {
2910 max_vert = ((action == ewmh.wmStateAdd()
2911 || (action == ewmh.wmStateToggle()
2912 && !client.ewmh.maxv))
2913 ? 1 : -1);
2916 if (max_horz != 0 || max_vert != 0) {
2917 if (isMaximized())
2918 maximize(0);
2919 unsigned int button = 0u;
2920 if (max_horz == 1 && max_vert != 1)
2921 button = 3u;
2922 else if (max_vert == 1 && max_horz != 1)
2923 button = 2u;
2924 else if (max_vert == 1 && max_horz == 1)
2925 button = 1u;
2926 if (button)
2927 maximize(button);
2931 if (hasWindowFunction(WindowFunctionShade)) {
2932 if (first == ewmh.wmStateShaded() ||
2933 second == ewmh.wmStateShaded()) {
2934 if (action == ewmh.wmStateRemove())
2935 setShaded(false);
2936 else if (action == ewmh.wmStateAdd())
2937 setShaded(true);
2938 else if (action == ewmh.wmStateToggle())
2939 setShaded(!isShaded());
2943 if (first == ewmh.wmStateSkipTaskbar()
2944 || second == ewmh.wmStateSkipTaskbar()
2945 || first == ewmh.wmStateSkipPager()
2946 || second == ewmh.wmStateSkipPager()) {
2947 if (first == ewmh.wmStateSkipTaskbar()
2948 || second == ewmh.wmStateSkipTaskbar()) {
2949 client.ewmh.skip_taskbar = (action == ewmh.wmStateAdd()
2950 || (action == ewmh.wmStateToggle()
2951 && !client.ewmh.skip_taskbar));
2953 if (first == ewmh.wmStateSkipPager()
2954 || second == ewmh.wmStateSkipPager()) {
2955 client.ewmh.skip_pager = (action == ewmh.wmStateAdd()
2956 || (action == ewmh.wmStateToggle()
2957 && !client.ewmh.skip_pager));
2959 // we do nothing with skip_*, but others might... we should at
2960 // least make sure these are present in _NET_WM_STATE
2961 updateEWMHState();
2964 if (first == ewmh.wmStateHidden() ||
2965 second == ewmh.wmStateHidden()) {
2967 ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
2968 application
2972 if (hasWindowFunction(WindowFunctionFullScreen)) {
2973 if (first == ewmh.wmStateFullscreen() ||
2974 second == ewmh.wmStateFullscreen()) {
2975 if (action == ewmh.wmStateAdd() ||
2976 (action == ewmh.wmStateToggle() &&
2977 ! client.ewmh.fullscreen)) {
2978 setFullScreen(true);
2979 } else if (action == ewmh.wmStateToggle() ||
2980 action == ewmh.wmStateRemove()) {
2981 setFullScreen(false);
2986 if (hasWindowFunction(WindowFunctionChangeLayer)) {
2987 if (first == ewmh.wmStateAbove() ||
2988 second == ewmh.wmStateAbove()) {
2989 if (action == ewmh.wmStateAdd() ||
2990 (action == ewmh.wmStateToggle() &&
2991 layer() != StackingList::LayerAbove)) {
2992 changeLayer(StackingList::LayerAbove);
2993 } else if (action == ewmh.wmStateToggle() ||
2994 action == ewmh.wmStateRemove()) {
2995 changeLayer(StackingList::LayerNormal);
2999 if (first == ewmh.wmStateBelow() ||
3000 second == ewmh.wmStateBelow()) {
3001 if (action == ewmh.wmStateAdd() ||
3002 (action == ewmh.wmStateToggle() &&
3003 layer() != StackingList::LayerBelow)) {
3004 changeLayer(StackingList::LayerBelow);
3005 } else if (action == ewmh.wmStateToggle() ||
3006 action == ewmh.wmStateRemove()) {
3007 changeLayer(StackingList::LayerNormal);
3015 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent * const event) {
3016 if (event->window != client.window)
3017 return;
3019 #ifdef DEBUG
3020 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
3021 client.window);
3022 #endif // DEBUG
3024 _screen->releaseWindow(this);
3028 void
3029 BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent * const event) {
3030 if (event->window != client.window)
3031 return;
3033 #ifdef DEBUG
3034 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
3035 client.window);
3036 #endif // DEBUG
3038 _screen->releaseWindow(this);
3042 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent * const event) {
3043 if (event->window != client.window || event->parent == frame.plate)
3044 return;
3046 #ifdef DEBUG
3047 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
3048 "0x%lx.\n", client.window, event->parent);
3049 #endif // DEBUG
3052 put the ReparentNotify event back into the queue so that
3053 BlackboxWindow::restore(void) can do the right thing
3055 XEvent replay;
3056 replay.xreparent = *event;
3057 XPutBackEvent(blackbox->XDisplay(), &replay);
3059 _screen->releaseWindow(this);
3063 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent * const event) {
3064 #ifdef DEBUG
3065 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
3066 client.window);
3067 #endif
3069 switch(event->atom) {
3070 case XA_WM_TRANSIENT_FOR: {
3071 if (isTransient()) {
3072 // remove ourselves from our transient_for
3073 BlackboxWindow *win = findTransientFor();
3074 if (win) {
3075 win->removeTransient(this);
3076 } else if (isGroupTransient()) {
3077 BWindowGroup *group = findWindowGroup();
3078 if (group)
3079 group->removeTransient(this);
3083 // determine if this is a transient window
3084 client.transient_for = ::readTransientInfo(blackbox,
3085 client.window,
3086 _screen->screenInfo(),
3087 client.wmhints);
3089 if (isTransient()) {
3090 BlackboxWindow *win = findTransientFor();
3091 if (win) {
3092 // add ourselves to our new transient_for
3093 win->addTransient(this);
3094 changeWorkspace(win->workspace());
3095 changeLayer(win->layer());
3096 } else if (isGroupTransient()) {
3097 BWindowGroup *group = findWindowGroup();
3098 if (group)
3099 group->addTransient(this);
3103 ::update_decorations(client.decorations,
3104 client.functions,
3105 isTransient(),
3106 client.ewmh,
3107 client.motif,
3108 client.wmnormal,
3109 client.wmprotocols);
3111 reconfigure();
3112 break;
3115 case XA_WM_HINTS: {
3116 // remove from current window group
3117 BWindowGroup *group = findWindowGroup();
3118 if (group) {
3119 if (isTransient() && isGroupTransient())
3120 group->removeTransient(this);
3121 group->removeWindow(this);
3122 group = 0;
3125 client.wmhints = ::readWMHints(blackbox, client.window);
3127 if (client.wmhints.window_group != None) {
3128 // add to new window group
3129 group = ::update_window_group(client.wmhints.window_group,
3130 blackbox,
3131 this);
3132 if (isTransient() && isGroupTransient()) {
3133 if (group)
3134 group->addTransient(this);
3137 break;
3140 case XA_WM_ICON_NAME: {
3141 client.icon_title = ::readWMIconName(blackbox, client.window);
3142 if (client.state.iconic)
3143 _screen->propagateWindowName(this);
3144 break;
3147 case XA_WM_NAME: {
3148 client.title = ::readWMName(blackbox, client.window);
3150 client.visible_title =
3151 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
3152 _screen->screenNumber(),
3153 _screen->resource().windowStyle().font);
3154 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
3156 if (client.decorations & WindowDecorationTitlebar)
3157 redrawLabel();
3159 _screen->propagateWindowName(this);
3160 break;
3163 case XA_WM_NORMAL_HINTS: {
3164 WMNormalHints wmnormal = ::readWMNormalHints(blackbox, client.window,
3165 _screen->screenInfo());
3166 if (wmnormal == client.wmnormal) {
3167 // apps like xv and GNU emacs seem to like to repeatedly set
3168 // this property over and over
3169 break;
3172 client.wmnormal = wmnormal;
3174 ::update_decorations(client.decorations,
3175 client.functions,
3176 isTransient(),
3177 client.ewmh,
3178 client.motif,
3179 client.wmnormal,
3180 client.wmprotocols);
3182 reconfigure();
3183 break;
3186 default: {
3187 if (event->atom == blackbox->wmProtocolsAtom()) {
3188 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
3190 ::update_decorations(client.decorations,
3191 client.functions,
3192 isTransient(),
3193 client.ewmh,
3194 client.motif,
3195 client.wmnormal,
3196 client.wmprotocols);
3198 reconfigure();
3199 } else if (event->atom == blackbox->motifWmHintsAtom()) {
3200 client.motif = ::readMotifWMHints(blackbox, client.window);
3202 ::update_decorations(client.decorations,
3203 client.functions,
3204 isTransient(),
3205 client.ewmh,
3206 client.motif,
3207 client.wmnormal,
3208 client.wmprotocols);
3210 reconfigure();
3211 } else if (event->atom == blackbox->ewmh().wmStrut()) {
3212 if (! client.strut) {
3213 client.strut = new bt::EWMH::Strut;
3214 _screen->addStrut(client.strut);
3217 blackbox->ewmh().readWMStrut(client.window, client.strut);
3218 if (client.strut->left || client.strut->right ||
3219 client.strut->top || client.strut->bottom) {
3220 _screen->updateStrut();
3221 } else {
3222 _screen->removeStrut(client.strut);
3223 delete client.strut;
3227 break;
3229 } // switch
3233 void BlackboxWindow::exposeEvent(const XExposeEvent * const event) {
3234 #ifdef DEBUG
3235 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
3236 #endif
3238 if (frame.title == event->window)
3239 redrawTitle();
3240 else if (frame.label == event->window)
3241 redrawLabel();
3242 else if (frame.close_button == event->window)
3243 redrawCloseButton();
3244 else if (frame.maximize_button == event->window)
3245 redrawMaximizeButton();
3246 else if (frame.iconify_button == event->window)
3247 redrawIconifyButton();
3248 else if (frame.handle == event->window)
3249 redrawHandle();
3250 else if (frame.left_grip == event->window ||
3251 frame.right_grip == event->window)
3252 redrawGrips();
3256 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *
3257 const event) {
3258 if (event->window != client.window || client.state.iconic)
3259 return;
3261 if (event->value_mask & CWBorderWidth)
3262 client.old_bw = event->border_width;
3264 if (event->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
3265 bt::Rect req = frame.rect;
3267 if (event->value_mask & (CWX | CWY)) {
3268 req = ::restoreGravity(req, frame.margin, client.wmnormal.win_gravity);
3270 if (event->value_mask & CWX)
3271 req.setX(event->x);
3272 if (event->value_mask & CWY)
3273 req.setY(event->y);
3275 req = ::applyGravity(req, frame.margin, client.wmnormal.win_gravity);
3278 if (event->value_mask & (CWWidth | CWHeight)) {
3279 if (event->value_mask & CWWidth)
3280 req.setWidth(event->width + frame.margin.left + frame.margin.right);
3281 if (event->value_mask & CWHeight)
3282 req.setHeight(event->height + frame.margin.top + frame.margin.bottom);
3285 configure(req);
3288 if (event->value_mask & CWStackMode) {
3289 switch (event->detail) {
3290 case Below:
3291 case BottomIf:
3292 _screen->lowerWindow(this);
3293 break;
3295 case Above:
3296 case TopIf:
3297 default:
3298 _screen->raiseWindow(this);
3299 break;
3305 void BlackboxWindow::buttonPressEvent(const XButtonEvent * const event) {
3306 #ifdef DEBUG
3307 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3308 client.window);
3309 #endif
3311 if (frame.maximize_button == event->window) {
3312 if (event->button < 4)
3313 redrawMaximizeButton(true);
3314 } else if (frame.iconify_button == event->window) {
3315 if (event->button == 1)
3316 redrawIconifyButton(true);
3317 } else if (frame.close_button == event->window) {
3318 if (event->button == 1)
3319 redrawCloseButton(true);
3320 } else {
3321 if (event->button == 1
3322 || (event->button == 3 && event->state == Mod1Mask)) {
3323 frame.grab_x = event->x_root - frame.rect.x();
3324 frame.grab_y = event->y_root - frame.rect.y();
3326 _screen->raiseWindow(this);
3328 if (! client.state.focused)
3329 (void) setInputFocus();
3330 else
3331 XInstallColormap(blackbox->XDisplay(), client.colormap);
3333 if (frame.plate == event->window) {
3334 XAllowEvents(blackbox->XDisplay(), ReplayPointer, event->time);
3335 } else if ((frame.title == event->window
3336 || frame.label == event->window)
3337 && hasWindowFunction(WindowFunctionShade)) {
3338 if ((event->time - lastButtonPressTime <=
3339 blackbox->resource().doubleClickInterval()) ||
3340 event->state == ControlMask) {
3341 lastButtonPressTime = 0;
3342 setShaded(!isShaded());
3343 } else {
3344 lastButtonPressTime = event->time;
3347 } else if (event->button == 2) {
3348 _screen->lowerWindow(this);
3349 } else if (event->button == 3) {
3350 const int extra = _screen->resource().windowStyle().frame_border_width;
3351 const bt::Rect rect(client.rect.x() - extra,
3352 client.rect.y() - extra,
3353 client.rect.width() + (extra * 2),
3354 client.rect.height() + (extra * 2));
3356 Windowmenu *windowmenu = _screen->windowmenu(this);
3357 windowmenu->popup(event->x_root, event->y_root, rect);
3363 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent * const event) {
3364 #ifdef DEBUG
3365 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3366 client.window);
3367 #endif
3369 const WindowStyle &style = _screen->resource().windowStyle();
3370 if (event->window == frame.maximize_button) {
3371 if (event->button < 4) {
3372 if (bt::within(event->x, event->y,
3373 style.button_width, style.button_width)) {
3374 maximize(event->button);
3375 _screen->raiseWindow(this);
3376 } else {
3377 redrawMaximizeButton();
3380 } else if (event->window == frame.iconify_button) {
3381 if (event->button == 1) {
3382 if (bt::within(event->x, event->y,
3383 style.button_width, style.button_width))
3384 iconify();
3385 else
3386 redrawIconifyButton();
3388 } else if (event->window == frame.close_button) {
3389 if (event->button == 1) {
3390 if (bt::within(event->x, event->y,
3391 style.button_width, style.button_width))
3392 close();
3393 redrawCloseButton();
3395 } else if (client.state.moving) {
3396 finishMove();
3397 } else if (client.state.resizing) {
3398 finishResize();
3399 } else if (event->window == frame.window) {
3400 if (event->button == 2 && event->state == Mod1Mask)
3401 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3406 void BlackboxWindow::motionNotifyEvent(const XMotionEvent * const event) {
3407 #ifdef DEBUG
3408 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3409 client.window);
3410 #endif
3412 if (hasWindowFunction(WindowFunctionMove)
3413 && !client.state.resizing
3414 && event->state & Button1Mask
3415 && (frame.title == event->window || frame.label == event->window
3416 || frame.handle == event->window || frame.window == event->window)) {
3417 if (! client.state.moving)
3418 startMove();
3419 else
3420 continueMove(event->x_root, event->y_root);
3421 } else if (hasWindowFunction(WindowFunctionResize)
3422 && (event->state & Button1Mask
3423 && (event->window == frame.right_grip
3424 || event->window == frame.left_grip))
3425 || (event->state & Button3Mask
3426 && event->state & Mod1Mask
3427 && event->window == frame.window)) {
3428 if (!client.state.resizing)
3429 startResize(event->window);
3430 else
3431 continueResize(event->x_root, event->y_root);
3436 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent * const event) {
3437 if (event->window != frame.window || event->mode != NotifyNormal)
3438 return;
3440 if (blackbox->resource().focusModel() == ClickToFocusModel || !isVisible())
3441 return;
3443 switch (windowType()) {
3444 case WindowTypeDesktop:
3445 case WindowTypeDock:
3446 // these types cannot be focused w/ sloppy focus
3447 return;
3449 default:
3450 break;
3453 XEvent next;
3454 bool leave = False, inferior = False;
3456 while (XCheckTypedWindowEvent(blackbox->XDisplay(), event->window,
3457 LeaveNotify, &next)) {
3458 if (next.type == LeaveNotify && next.xcrossing.mode == NotifyNormal) {
3459 leave = True;
3460 inferior = (next.xcrossing.detail == NotifyInferior);
3464 if ((! leave || inferior) && ! isFocused())
3465 (void) setInputFocus();
3467 if (blackbox->resource().autoRaise())
3468 timer->start();
3472 void
3473 BlackboxWindow::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
3474 if (!(blackbox->resource().focusModel() == SloppyFocusModel
3475 && blackbox->resource().autoRaise()))
3476 return;
3478 if (timer->isTiming())
3479 timer->stop();
3483 #ifdef SHAPE
3484 void BlackboxWindow::shapeEvent(const XEvent * const /*unused*/)
3485 { if (client.state.shaped) configureShape(); }
3486 #endif // SHAPE
3492 void BlackboxWindow::restore(void) {
3493 XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeDelete);
3494 XSelectInput(blackbox->XDisplay(), client.window, NoEventMask);
3495 XSelectInput(blackbox->XDisplay(), frame.plate, NoEventMask);
3497 client.state.visible = false;
3500 remove WM_STATE unless the we are shutting down (in which case we
3501 want to make sure we preserve the state across restarts).
3503 if (!blackbox->shuttingDown()) {
3504 clearState(blackbox, client.window);
3505 } else if (isShaded() && !isIconic()) {
3506 // do not leave a shaded window as an icon unless it was an icon
3507 setState(NormalState);
3510 client.rect = ::restoreGravity(frame.rect, frame.margin,
3511 client.wmnormal.win_gravity);
3513 blackbox->XGrabServer();
3515 XUnmapWindow(blackbox->XDisplay(), frame.window);
3516 XUnmapWindow(blackbox->XDisplay(), client.window);
3518 XSetWindowBorderWidth(blackbox->XDisplay(), client.window, client.old_bw);
3519 if (isMaximized()) {
3520 // preserve the original size
3521 client.rect = client.premax;
3522 XMoveResizeWindow(blackbox->XDisplay(), client.window,
3523 client.premax.x(),
3524 client.premax.y(),
3525 client.premax.width(),
3526 client.premax.height());
3527 } else {
3528 XMoveWindow(blackbox->XDisplay(), client.window,
3529 client.rect.x() - frame.rect.x(),
3530 client.rect.y() - frame.rect.y());
3533 blackbox->XUngrabServer();
3535 XEvent unused;
3536 if (!XCheckTypedWindowEvent(blackbox->XDisplay(), client.window,
3537 ReparentNotify, &unused)) {
3539 according to the ICCCM, the window manager is responsible for
3540 reparenting the window back to root... however, we don't want to
3541 do this if the window has been reparented by someone else
3542 (i.e. not us).
3544 XReparentWindow(blackbox->XDisplay(), client.window,
3545 _screen->screenInfo().rootWindow(),
3546 client.rect.x(), client.rect.y());
3549 if (blackbox->shuttingDown())
3550 XMapWindow(blackbox->XDisplay(), client.window);
3554 // timer for autoraise
3555 void BlackboxWindow::timeout(bt::Timer *)
3556 { _screen->raiseWindow(this); }
3559 void BlackboxWindow::startMove() {
3560 // begin a move
3561 XGrabPointer(blackbox->XDisplay(), frame.window, false,
3562 Button1MotionMask | ButtonReleaseMask,
3563 GrabModeAsync, GrabModeAsync, None,
3564 blackbox->resource().cursors().move, blackbox->XTime());
3566 client.state.moving = true;
3568 if (! blackbox->resource().opaqueMove()) {
3569 blackbox->XGrabServer();
3571 frame.changing = frame.rect;
3572 _screen->showGeometry(BScreen::Position, frame.changing);
3574 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3575 const int bw = _screen->resource().windowStyle().frame_border_width,
3576 hw = bw / 2;
3577 pen.setGCFunction(GXxor);
3578 pen.setLineWidth(bw);
3579 pen.setSubWindowMode(IncludeInferiors);
3580 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3581 pen.gc(),
3582 frame.changing.x() + hw,
3583 frame.changing.y() + hw,
3584 frame.changing.width() - bw,
3585 frame.changing.height() - bw);
3590 static
3591 void collisionAdjust(int* x, int* y, unsigned int width, unsigned int height,
3592 const bt::Rect& rect, int snap_distance) {
3593 // window corners
3594 const int wleft = *x,
3595 wright = *x + width - 1,
3596 wtop = *y,
3597 wbottom = *y + height - 1,
3599 dleft = abs(wleft - rect.left()),
3600 dright = abs(wright - rect.right()),
3601 dtop = abs(wtop - rect.top()),
3602 dbottom = abs(wbottom - rect.bottom());
3604 // snap left?
3605 if (dleft < snap_distance && dleft <= dright)
3606 *x = rect.left();
3607 // snap right?
3608 else if (dright < snap_distance)
3609 *x = rect.right() - width + 1;
3611 // snap top?
3612 if (dtop < snap_distance && dtop <= dbottom)
3613 *y = rect.top();
3614 // snap bottom?
3615 else if (dbottom < snap_distance)
3616 *y = rect.bottom() - height + 1;
3620 void BlackboxWindow::continueMove(int x_root, int y_root) {
3621 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3622 const int snap_distance = blackbox->resource().edgeSnapThreshold();
3624 if (snap_distance) {
3625 collisionAdjust(&dx, &dy, frame.rect.width(), frame.rect.height(),
3626 _screen->availableArea(), snap_distance);
3627 if (!blackbox->resource().fullMaximization())
3628 collisionAdjust(&dx, &dy, frame.rect.width(), frame.rect.height(),
3629 _screen->screenInfo().rect(), snap_distance);
3632 if (blackbox->resource().opaqueMove()) {
3633 configure(dx, dy, frame.rect.width(), frame.rect.height());
3634 } else {
3635 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3636 const int bw = _screen->resource().windowStyle().frame_border_width,
3637 hw = bw / 2;
3638 pen.setGCFunction(GXxor);
3639 pen.setLineWidth(bw);
3640 pen.setSubWindowMode(IncludeInferiors);
3641 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3642 pen.gc(),
3643 frame.changing.x() + hw,
3644 frame.changing.y() + hw,
3645 frame.changing.width() - bw,
3646 frame.changing.height() - bw);
3648 frame.changing.setPos(dx, dy);
3650 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3651 pen.gc(),
3652 frame.changing.x() + hw,
3653 frame.changing.y() + hw,
3654 frame.changing.width() - bw,
3655 frame.changing.height() - bw);
3658 _screen->showGeometry(BScreen::Position, bt::Rect(dx, dy, 0, 0));
3662 void BlackboxWindow::finishMove() {
3663 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3665 client.state.moving = false;
3667 if (!blackbox->resource().opaqueMove()) {
3668 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3669 const int bw = _screen->resource().windowStyle().frame_border_width,
3670 hw = bw / 2;
3671 pen.setGCFunction(GXxor);
3672 pen.setLineWidth(bw);
3673 pen.setSubWindowMode(IncludeInferiors);
3674 XDrawRectangle(blackbox->XDisplay(),
3675 _screen->screenInfo().rootWindow(),
3676 pen.gc(),
3677 frame.changing.x() + hw,
3678 frame.changing.y() + hw,
3679 frame.changing.width() - bw,
3680 frame.changing.height() - bw);
3681 blackbox->XUngrabServer();
3683 configure(frame.changing);
3684 } else {
3685 configure(frame.rect);
3688 _screen->hideGeometry();
3692 void BlackboxWindow::startResize(Window window) {
3693 if (frame.grab_x < (signed) frame.rect.width() / 2) {
3694 if (frame.grab_y < (signed) frame.rect.height() / 2)
3695 frame.corner = BottomRight;
3696 else
3697 frame.corner = TopRight;
3698 } else {
3699 if (frame.grab_y < (signed) frame.rect.height() / 2)
3700 frame.corner = BottomLeft;
3701 else
3702 frame.corner = TopLeft;
3705 Cursor cursor = None;
3706 switch (frame.corner) {
3707 case TopLeft:
3708 cursor = blackbox->resource().cursors().resize_bottom_right;
3709 frame.grab_x = frame.rect.width() - frame.grab_x;
3710 frame.grab_y = frame.rect.height() - frame.grab_y;
3711 break;
3712 case BottomLeft:
3713 cursor = blackbox->resource().cursors().resize_top_right;
3714 frame.grab_x = frame.rect.width() - frame.grab_x;
3715 break;
3716 case TopRight:
3717 cursor = blackbox->resource().cursors().resize_bottom_left;
3718 frame.grab_y = frame.rect.height() - frame.grab_y;
3719 break;
3720 case BottomRight:
3721 cursor = blackbox->resource().cursors().resize_top_left;
3722 break;
3725 // begin a resize
3726 XGrabPointer(blackbox->XDisplay(), window, False,
3727 ButtonMotionMask | ButtonReleaseMask,
3728 GrabModeAsync, GrabModeAsync, None, cursor, blackbox->XTime());
3730 client.state.resizing = true;
3732 frame.changing = constrain(frame.rect, frame.margin, client.wmnormal,
3733 Corner(frame.corner));
3735 if (!blackbox->resource().opaqueResize()) {
3736 blackbox->XGrabServer();
3738 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3739 const int bw = _screen->resource().windowStyle().frame_border_width,
3740 hw = bw / 2;
3741 pen.setGCFunction(GXxor);
3742 pen.setLineWidth(bw);
3743 pen.setSubWindowMode(IncludeInferiors);
3744 XDrawRectangle(blackbox->XDisplay(),
3745 _screen->screenInfo().rootWindow(),
3746 pen.gc(),
3747 frame.changing.x() + hw,
3748 frame.changing.y() + hw,
3749 frame.changing.width() - bw,
3750 frame.changing.height() - bw);
3751 } else {
3752 // unset maximized state when resized
3753 if (isMaximized())
3754 maximize(0);
3757 showGeometry(frame.changing);
3761 void BlackboxWindow::continueResize(int x_root, int y_root) {
3762 // continue a resize
3763 const bt::Rect curr = frame.changing;
3765 switch (frame.corner) {
3766 case TopLeft:
3767 case BottomLeft:
3768 frame.changing.setCoords(frame.changing.left(),
3769 frame.changing.top(),
3770 std::max<signed>(x_root + frame.grab_x,
3771 frame.changing.left()
3772 + (frame.margin.left
3773 + frame.margin.right + 1)),
3774 frame.changing.bottom());
3775 break;
3776 case TopRight:
3777 case BottomRight:
3778 frame.changing.setCoords(std::min<signed>(x_root - frame.grab_x,
3779 frame.changing.right()
3780 - (frame.margin.left
3781 + frame.margin.right + 1)),
3782 frame.changing.top(),
3783 frame.changing.right(),
3784 frame.changing.bottom());
3785 break;
3788 switch (frame.corner) {
3789 case TopLeft:
3790 case TopRight:
3791 frame.changing.setCoords(frame.changing.left(),
3792 frame.changing.top(),
3793 frame.changing.right(),
3794 std::max<signed>(y_root + frame.grab_y,
3795 frame.changing.top()
3796 + (frame.margin.top
3797 + frame.margin.bottom + 1)));
3798 break;
3799 case BottomLeft:
3800 case BottomRight:
3801 frame.changing.setCoords(frame.changing.left(),
3802 std::min<signed>(y_root - frame.grab_y,
3803 frame.rect.bottom()
3804 - (frame.margin.top
3805 + frame.margin.bottom + 1)),
3806 frame.changing.right(),
3807 frame.changing.bottom());
3808 break;
3811 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
3812 Corner(frame.corner));
3814 if (curr != frame.changing) {
3815 if (blackbox->resource().opaqueResize()) {
3816 configure(frame.changing);
3817 } else {
3818 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3819 const int bw = _screen->resource().windowStyle().frame_border_width,
3820 hw = bw / 2;
3821 pen.setGCFunction(GXxor);
3822 pen.setLineWidth(bw);
3823 pen.setSubWindowMode(IncludeInferiors);
3824 XDrawRectangle(blackbox->XDisplay(),
3825 _screen->screenInfo().rootWindow(),
3826 pen.gc(),
3827 curr.x() + hw,
3828 curr.y() + hw,
3829 curr.width() - bw,
3830 curr.height() - bw);
3832 XDrawRectangle(blackbox->XDisplay(),
3833 _screen->screenInfo().rootWindow(),
3834 pen.gc(),
3835 frame.changing.x() + hw,
3836 frame.changing.y() + hw,
3837 frame.changing.width() - bw,
3838 frame.changing.height() - bw);
3841 showGeometry(frame.changing);
3846 void BlackboxWindow::finishResize() {
3848 if (!blackbox->resource().opaqueResize()) {
3849 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3850 const int bw = _screen->resource().windowStyle().frame_border_width,
3851 hw = bw / 2;
3852 pen.setGCFunction(GXxor);
3853 pen.setLineWidth(bw);
3854 pen.setSubWindowMode(IncludeInferiors);
3855 XDrawRectangle(blackbox->XDisplay(),
3856 _screen->screenInfo().rootWindow(),
3857 pen.gc(),
3858 frame.changing.x() + hw,
3859 frame.changing.y() + hw,
3860 frame.changing.width() - bw,
3861 frame.changing.height() - bw);
3863 blackbox->XUngrabServer();
3865 // unset maximized state when resized
3866 if (isMaximized())
3867 maximize(0);
3870 client.state.resizing = false;
3872 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3874 _screen->hideGeometry();
3876 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
3877 Corner(frame.corner));
3878 configure(frame.changing);
3883 * show the geometry of the window based on rectangle r.
3884 * The logical width and height are used here. This refers to the user's
3885 * perception of the window size (for example an xterm resizes in cells,
3886 * not in pixels). No extra work is needed if there is no difference between
3887 * the logical and actual dimensions.
3889 void BlackboxWindow::showGeometry(const bt::Rect &r) const {
3890 unsigned int w = r.width(), h = r.height();
3892 // remove the window frame
3893 w -= frame.margin.left + frame.margin.right;
3894 h -= frame.margin.top + frame.margin.bottom;
3896 if (client.wmnormal.flags & PResizeInc) {
3897 if (client.wmnormal.flags & (PMinSize|PBaseSize)) {
3898 w -= ((client.wmnormal.base_width)
3899 ? client.wmnormal.base_width
3900 : client.wmnormal.min_width);
3901 h -= ((client.wmnormal.base_height)
3902 ? client.wmnormal.base_height
3903 : client.wmnormal.min_height);
3906 w /= client.wmnormal.width_inc;
3907 h /= client.wmnormal.height_inc;
3910 _screen->showGeometry(BScreen::Size, bt::Rect(0, 0, w, h));
3914 // see my rant above for an explanation of this operator
3915 bool operator==(const WMNormalHints &x, const WMNormalHints &y) {
3916 return (x.flags == y.flags
3917 && x.min_width == y.min_width
3918 && x.min_height == y.min_height
3919 && x.max_width == y.max_width
3920 && x.max_height == y.max_height
3921 && x.width_inc == y.width_inc
3922 && x.height_inc == y.height_inc
3923 && x.min_aspect_x == y.min_aspect_x
3924 && x.min_aspect_y == y.min_aspect_y
3925 && x.max_aspect_x == y.max_aspect_x
3926 && x.max_aspect_y == y.max_aspect_y
3927 && x.base_width == y.base_width
3928 && x.base_height == y.base_height
3929 && x.win_gravity == y.win_gravity);