0 /*
1 * calmwm - the calm window manager
2 *
3 * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * $OpenBSD$
18 */
19
20 #include <sys/types.h>
21 #include <sys/queue.h>
22
23 #include <err.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "calmwm.h"
33
34 static struct geom screen_apply_gap(struct screen_ctx *, struct geom);
35 static void screen_scan(struct screen_ctx *);
36
37 void
38 screen_init(int which)
39 {
40 struct screen_ctx *sc;
41 XSetWindowAttributes attr;
42
43 sc = xmalloc(sizeof(*sc));
44
45 TAILQ_INIT(&sc->clientq);
46 TAILQ_INIT(&sc->regionq);
47 TAILQ_INIT(&sc->groupq);
48
49 sc->which = which;
50 sc->rootwin = RootWindow(X_Dpy, sc->which);
51 sc->colormap = DefaultColormap(X_Dpy, sc->which);
52 sc->visual = DefaultVisual(X_Dpy, sc->which);
53 sc->cycling = 0;
54 sc->hideall = 0;
55
56 conf_screen(sc);
57
58 xu_ewmh_net_supported(sc);
59 xu_ewmh_net_supported_wm_check(sc);
60
61 conf_group(sc);
62 sc->group_last = sc->group_active;
63 screen_update_geometry(sc);
64
65 xu_ewmh_net_desktop_names(sc);
66 xu_ewmh_net_number_of_desktops(sc);
67 xu_ewmh_net_showing_desktop(sc);
68 xu_ewmh_net_virtual_roots(sc);
69
70 attr.cursor = Conf.cursor[CF_NORMAL];
71 attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
72 EnterWindowMask | PropertyChangeMask | ButtonPressMask;
73 XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr);
74
75 if (Conf.xrandr)
76 XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
77
78 screen_scan(sc);
79 screen_updatestackingorder(sc);
80
81 TAILQ_INSERT_TAIL(&Screenq, sc, entry);
82
83 XSync(X_Dpy, False);
84 }
85
86 static void
87 screen_scan(struct screen_ctx *sc)
88 {
89 struct client_ctx *cc, *active = NULL;
90 Window *wins, w0, w1, rwin, cwin;
91 unsigned int nwins, i, mask;
92 int rx, ry, wx, wy;
93
94 XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin,
95 &rx, &ry, &wx, &wy, &mask);
96
97 if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
98 for (i = 0; i < nwins; i++) {
99 if ((cc = client_init(wins[i], sc)) != NULL)
100 if (cc->win == cwin)
101 active = cc;
102 }
103 XFree(wins);
104 }
105 if (active)
106 client_set_active(active);
107 }
108
109 struct screen_ctx *
110 screen_find(Window win)
111 {
112 struct screen_ctx *sc;
113
114 TAILQ_FOREACH(sc, &Screenq, entry) {
115 if (sc->rootwin == win)
116 return sc;
117 }
118 warnx("%s: failure win 0x%lx", __func__, win);
119 return NULL;
120 }
121
122 void
123 screen_updatestackingorder(struct screen_ctx *sc)
124 {
125 Window *wins, w0, w1;
126 struct client_ctx *cc;
127 unsigned int nwins, i, s;
128
129 if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
130 for (s = 0, i = 0; i < nwins; i++) {
131 /* Skip hidden windows */
132 if ((cc = client_find(wins[i])) == NULL ||
133 cc->flags & CLIENT_HIDDEN)
134 continue;
135
136 cc->stackingorder = s++;
137 }
138 XFree(wins);
139 }
140 }
141
142 struct region_ctx *
143 region_find(struct screen_ctx *sc, int x, int y)
144 {
145 struct region_ctx *rc;
146
147 TAILQ_FOREACH(rc, &sc->regionq, entry) {
148 if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) &&
149 (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) {
150 break;
151 }
152 }
153 return rc;
154 }
155
156 struct geom
157 screen_area(struct screen_ctx *sc, int x, int y, int apply_gap)
158 {
159 struct region_ctx *rc;
160 struct geom area = sc->view;
161
162 TAILQ_FOREACH(rc, &sc->regionq, entry) {
163 if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) &&
164 (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) {
165 area = rc->view;
166 break;
167 }
168 }
169 if (apply_gap)
170 area = screen_apply_gap(sc, area);
171 return area;
172 }
173
174 void
175 screen_update_geometry(struct screen_ctx *sc)
176 {
177 struct region_ctx *rc;
178
179 sc->view.x = 0;
180 sc->view.y = 0;
181 sc->view.w = DisplayWidth(X_Dpy, sc->which);
182 sc->view.h = DisplayHeight(X_Dpy, sc->which);
183 sc->work = screen_apply_gap(sc, sc->view);
184
185 while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) {
186 TAILQ_REMOVE(&sc->regionq, rc, entry);
187 free(rc);
188 }
189
190 if (Conf.xrandr) {
191 XRRScreenResources *sr;
192 XRRCrtcInfo *ci;
193 int i;
194
195 sr = XRRGetScreenResources(X_Dpy, sc->rootwin);
196 for (i = 0, ci = NULL; i < sr->ncrtc; i++) {
197 ci = XRRGetCrtcInfo(X_Dpy, sr, sr->crtcs[i]);
198 if (ci == NULL)
199 continue;
200 if (ci->noutput == 0) {
201 XRRFreeCrtcInfo(ci);
202 continue;
203 }
204
205 rc = xmalloc(sizeof(*rc));
206 rc->num = i;
207 rc->view.x = ci->x;
208 rc->view.y = ci->y;
209 rc->view.w = ci->width;
210 rc->view.h = ci->height;
211 rc->work = screen_apply_gap(sc, rc->view);
212 TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
213
214 XRRFreeCrtcInfo(ci);
215 }
216 XRRFreeScreenResources(sr);
217 } else {
218 rc = xmalloc(sizeof(*rc));
219 rc->num = 0;
220 rc->view.x = 0;
221 rc->view.y = 0;
222 rc->view.w = DisplayWidth(X_Dpy, sc->which);
223 rc->view.h = DisplayHeight(X_Dpy, sc->which);
224 rc->work = screen_apply_gap(sc, rc->view);
225 TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
226 }
227
228 xu_ewmh_net_desktop_geometry(sc);
229 xu_ewmh_net_desktop_viewport(sc);
230 xu_ewmh_net_workarea(sc);
231 }
232
233 static struct geom
234 screen_apply_gap(struct screen_ctx *sc, struct geom geom)
235 {
236 geom.x += sc->gap.left;
237 geom.y += sc->gap.top;
238 geom.w -= (sc->gap.left + sc->gap.right);
239 geom.h -= (sc->gap.top + sc->gap.bottom);
240
241 return geom;
242 }
243
244 /* Bring back clients which are beyond the screen. */
245 void
246 screen_assert_clients_within(struct screen_ctx *sc)
247 {
248 struct client_ctx *cc;
249 int top, left, right, bottom;
250
251 TAILQ_FOREACH(cc, &sc->clientq, entry) {
252 if (cc->sc != sc)
253 continue;
254 top = cc->geom.y;
255 left = cc->geom.x;
256 right = cc->geom.x + cc->geom.w + (cc->bwidth * 2) - 1;
257 bottom = cc->geom.y + cc->geom.h + (cc->bwidth * 2) - 1;
258 if ((top > sc->view.h || left > sc->view.w) ||
259 (bottom < 0 || right < 0)) {
260 cc->geom.x = sc->gap.left;
261 cc->geom.y = sc->gap.top;
262 client_move(cc);
263 }
264 }
265 }
266
267 void
268 screen_prop_win_create(struct screen_ctx *sc, Window win)
269 {
270 sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0,
271 sc->xftcolor[CWM_COLOR_MENU_BG].pixel,
272 sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
273 sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win,
274 sc->visual, sc->colormap);
275
276 XMapWindow(X_Dpy, sc->prop.win);
277 }
278
279 void
280 screen_prop_win_destroy(struct screen_ctx *sc)
281 {
282 XftDrawDestroy(sc->prop.xftdraw);
283 XDestroyWindow(X_Dpy, sc->prop.win);
284 }
285
286 void
287 screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...)
288 {
289 va_list ap;
290 char *text;
291 XGlyphInfo extents;
292
293 va_start(ap, fmt);
294 xvasprintf(&text, fmt, ap);
295 va_end(ap);
296
297 XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
298 strlen(text), &extents);
299 XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height);
300 XClearWindow(X_Dpy, sc->prop.win);
301 XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
302 sc->xftfont, 0, sc->xftfont->ascent + 1,
303 (const FcChar8*)text, strlen(text));
304
305 free(text);
306 }
307