dwm-preview-all-windows-20250407-e381933.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
dwm-preview-all-windows-20250407-e381933.diff (12466B)
---
1 From e381933255718c6ed30e1b930d8fd76d62ccda75 Mon Sep 17 00:00:00 2001
2 From: elbachir-one <bachiralfa@gmail.com>
3 Date: Mon, 7 Apr 2025 06:02:14 +0100
4 Subject: [PATCH] Added some keys to move/select around windows in the preview
5
6 ---
7 config.def.h | 4 +
8 config.mk | 2 +-
9 dwm.c | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++
10 3 files changed, 312 insertions(+), 1 deletion(-)
11
12 diff --git a/config.def.h b/config.def.h
13 index 4412cb1..37feaf7 100644
14 --- a/config.def.h
15 +++ b/config.def.h
16 @@ -1,5 +1,8 @@
17 /* See LICENSE file for copyright and license details. */
18
19 +//#define ACTUALFULLSCREEN /* Uncomment if the actualfullscreen patch is added */
20 +//#define AWESOMEBAR /* Uncommnet if the awesomebar patch is used */
21 +
22 /* appearance */
23 static const unsigned int borderpx = 1; /* border pixel of windows */
24 static const unsigned int snap = 32; /* snap pixel */
25 @@ -95,6 +98,7 @@ static const Key keys[] = {
26 TAGKEYS( XK_8, 7)
27 TAGKEYS( XK_9, 8)
28 { MODKEY|ShiftMask, XK_q, quit, {0} },
29 + { MODKEY, XK_r, togglepreviewallwin, {0} },
30 };
31
32 /* button definitions */
33 diff --git a/config.mk b/config.mk
34 index 8efca9a..8df2978 100644
35 --- a/config.mk
36 +++ b/config.mk
37 @@ -23,7 +23,7 @@ FREETYPEINC = /usr/include/freetype2
38
39 # includes and libs
40 INCS = -I${X11INC} -I${FREETYPEINC}
41 -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
42 +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender
43
44 # flags
45 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
46 diff --git a/dwm.c b/dwm.c
47 index 1443802..6d38874 100644
48 --- a/dwm.c
49 +++ b/dwm.c
50 @@ -40,6 +40,7 @@
51 #include <X11/extensions/Xinerama.h>
52 #endif /* XINERAMA */
53 #include <X11/Xft/Xft.h>
54 +#include <X11/extensions/Xrender.h>
55
56 #include "drw.h"
57 #include "util.h"
58 @@ -83,6 +84,16 @@ typedef struct {
59
60 typedef struct Monitor Monitor;
61 typedef struct Client Client;
62 +
63 +typedef struct Preview Preview;
64 +struct Preview {
65 + XImage *orig_image;
66 + XImage *scaled_image;
67 + Window win;
68 + unsigned int x, y;
69 + Preview *next;
70 +};
71 +
72 struct Client {
73 char name[256];
74 float mina, maxa;
75 @@ -96,6 +107,7 @@ struct Client {
76 Client *snext;
77 Monitor *mon;
78 Window win;
79 + Preview pre;
80 };
81
82 typedef struct {
83 @@ -232,8 +244,14 @@ static int xerror(Display *dpy, XErrorEvent *ee);
84 static int xerrordummy(Display *dpy, XErrorEvent *ee);
85 static int xerrorstart(Display *dpy, XErrorEvent *ee);
86 static void zoom(const Arg *arg);
87 +static void togglepreviewallwin();
88 +static void highlightwindow(int idx, Monitor *m);
89 +static void setpreviewwindowsizepositions(unsigned int n, Monitor *m, unsigned int gappo, unsigned int gappi);
90 +static XImage *getwindowximage(Client *c);
91 +static XImage *scaledownimage(XImage *orig_image, unsigned int cw, unsigned int ch);
92
93 /* variables */
94 +
95 static const char broken[] = "broken";
96 static char stext[256];
97 static int screen;
98 @@ -266,6 +284,7 @@ static Display *dpy;
99 static Drw *drw;
100 static Monitor *mons, *selmon;
101 static Window root, wmcheckwin;
102 +static int previewallwin = 0;
103
104 /* configuration, allows nested code to access above variables */
105 #include "config.h"
106 @@ -2139,6 +2158,294 @@ zoom(const Arg *arg)
107 pop(c);
108 }
109
110 +void
111 +togglepreviewallwin() {
112 + if (previewallwin) { /* If already active, disable preview */
113 + previewallwin = 0;
114 + for (Client *c = selmon->clients; c; c = c->next) {
115 + XUnmapWindow(dpy, c->pre.win);
116 + XMapWindow(dpy, c->win);
117 + if (c->pre.orig_image)
118 + XDestroyImage(c->pre.orig_image);
119 + if (c->pre.scaled_image)
120 + XDestroyImage(c->pre.scaled_image);
121 + }
122 + arrange(selmon);
123 + focus(NULL);
124 + return; /* Exit function early to prevent running again */
125 + }
126 +
127 + previewallwin = 1; /* Enable preview mode */
128 + Monitor *m = selmon;
129 + Client *c, *focus_c = NULL;
130 + unsigned int n = 0;
131 +
132 + for (c = m->clients; c; c = c->next, n++) {
133 +#ifdef ACTUALFULLSCREEN
134 + if (c->isfullscreen)
135 + togglefullscr(&(Arg){0});
136 +#endif
137 +#ifdef AWESOMEBAR
138 + if (HIDDEN(c))
139 + continue;
140 +#endif
141 + c->pre.orig_image = getwindowximage(c);
142 + }
143 +
144 + if (n == 0) return;
145 +
146 + setpreviewwindowsizepositions(n, m, 60, 15);
147 + XEvent event;
148 +
149 + for (c = m->clients; c; c = c->next) {
150 + if (!c->pre.win)
151 + c->pre.win = XCreateSimpleWindow(dpy, root, c->pre.x, c->pre.y,
152 + c->pre.scaled_image->width, c->pre.scaled_image->height,
153 + 1, BlackPixel(dpy, screen), WhitePixel(dpy, screen));
154 + else
155 + XMoveResizeWindow(dpy, c->pre.win, c->pre.x, c->pre.y,
156 + c->pre.scaled_image->width, c->pre.scaled_image->height);
157 +
158 + XSetWindowBorder(dpy, c->pre.win, scheme[SchemeNorm][ColBorder].pixel);
159 + XUnmapWindow(dpy, c->win);
160 +
161 + if (c->pre.win) {
162 + XSelectInput(dpy, c->pre.win, ButtonPress | EnterWindowMask | LeaveWindowMask);
163 + XMapWindow(dpy, c->pre.win);
164 + GC gc = XCreateGC(dpy, c->pre.win, 0, NULL);
165 + XPutImage(dpy, c->pre.win, gc, c->pre.scaled_image, 0, 0, 0, 0,
166 + c->pre.scaled_image->width, c->pre.scaled_image->height);
167 + }
168 + }
169 +
170 + int selected_idx = 0;
171 +
172 + while (previewallwin) {
173 + XNextEvent(dpy, &event);
174 + if (event.type == ButtonPress) {
175 + if (event.xbutton.button == Button1) { /* Left-click to select a window */
176 + for (c = selmon->clients; c; c = c->next) {
177 + if (event.xbutton.window == c->pre.win) {
178 + previewallwin = 0;
179 + selmon->tagset[selmon->seltags] = c->tags;
180 + focus(c);
181 + arrange(selmon); /* Ensure layout updates correctly */
182 + break;
183 + }
184 + }
185 + }
186 + }
187 + if (event.type == KeyPress) {
188 + if (event.xkey.keycode == XKeysymToKeycode(dpy, XK_j)) {
189 + if (selected_idx < n - 1) {
190 + selected_idx++;
191 + }
192 + highlightwindow(selected_idx, m);
193 + }
194 + if (event.xkey.keycode == XKeysymToKeycode(dpy, XK_k)) {
195 + if (selected_idx > 0) {
196 + selected_idx--;
197 + }
198 + highlightwindow(selected_idx, m);
199 + }
200 + if (event.xkey.keycode == XKeysymToKeycode(dpy, XK_Return)) {
201 + previewallwin = 0;
202 + Client *selected_client = NULL;
203 + int idx = 0;
204 +
205 + for (c = m->clients; c; c = c->next, idx++) {
206 + if (idx == selected_idx) {
207 + selected_client = c;
208 + break;
209 + }
210 + }
211 + if (selected_client) {
212 + selmon->tagset[selmon->seltags] = selected_client->tags;
213 + focus(selected_client);
214 + arrange(selmon);
215 + }
216 + }
217 + if (event.xkey.keycode == XKeysymToKeycode(dpy, XK_r)) {
218 + previewallwin = 0;
219 + Client *selected_client = NULL;
220 + int idx = 0;
221 +
222 + for (c = m->clients; c; c = c->next, idx++) {
223 + if (idx == selected_idx) {
224 + selected_client = c;
225 + break;
226 + }
227 + }
228 + if (selected_client) {
229 + selmon->tagset[selmon->seltags] = selected_client->tags;
230 + focus(selected_client);
231 + arrange(selmon);
232 + }
233 + }
234 + }
235 +
236 + if (event.type == EnterNotify) {
237 + for (c = m->clients; c; c = c->next) {
238 + if (event.xcrossing.window == c->pre.win) {
239 + XSetWindowBorder(dpy, c->pre.win, scheme[SchemeSel][ColBorder].pixel);
240 + break;
241 + }
242 + }
243 + }
244 + if (event.type == LeaveNotify) {
245 + for (c = m->clients; c; c = c->next) {
246 + if (event.xcrossing.window == c->pre.win) {
247 + XSetWindowBorder(dpy, c->pre.win, scheme[SchemeNorm][ColBorder].pixel);
248 + break;
249 + }
250 + }
251 + }
252 + }
253 + for (c = selmon->clients; c; c = c->next) { /* Restore all windows when exiting */
254 + XUnmapWindow(dpy, c->pre.win);
255 + XMapWindow(dpy, c->win);
256 + if (c->pre.orig_image)
257 + XDestroyImage(c->pre.orig_image);
258 + if (c->pre.scaled_image)
259 + XDestroyImage(c->pre.scaled_image);
260 + }
261 + arrange(m);
262 + focus(focus_c);
263 +}
264 +
265 +void
266 +highlightwindow(int idx, Monitor *m) {
267 + int i = 0;
268 + Client *c;
269 + for (c = m->clients; c; c = c->next, i++) {
270 + if (i == idx) {
271 + XSetWindowBorder(dpy, c->pre.win, scheme[SchemeSel][ColBorder].pixel); /* Highlight selected window */
272 + } else {
273 + XSetWindowBorder(dpy, c->pre.win, scheme[SchemeNorm][ColBorder].pixel); /* Reset border for other windows */
274 + }
275 + }
276 +}
277 +
278 +void
279 +setpreviewwindowsizepositions(unsigned int n, Monitor *m, unsigned int gappo, unsigned int gappi){
280 + unsigned int i, j;
281 + unsigned int cx, cy, cw, ch, cmaxh;
282 + unsigned int cols, rows;
283 + Client *c, *tmpc;
284 +
285 + if (n == 1) {
286 + c = m->clients;
287 + cw = (m->ww - 2 * gappo) * 0.8;
288 + ch = (m->wh - 2 * gappo) * 0.9;
289 + c->pre.scaled_image = scaledownimage(c->pre.orig_image, cw, ch);
290 + c->pre.x = m->mx + (m->mw - c->pre.scaled_image->width) / 2;
291 + c->pre.y = m->my + (m->mh - c->pre.scaled_image->height) / 2;
292 + return;
293 + }
294 + if (n == 2) {
295 + c = m->clients;
296 + cw = (m->ww - 2 * gappo - gappi) / 2;
297 + ch = (m->wh - 2 * gappo) * 0.7;
298 + c->pre.scaled_image = scaledownimage(c->pre.orig_image, cw, ch);
299 + c->next->pre.scaled_image = scaledownimage(c->next->pre.orig_image, cw, ch);
300 + c->pre.x = m->mx + (m->mw - c->pre.scaled_image->width - gappi - c->next->pre.scaled_image->width) / 2;
301 + c->pre.y = m->my + (m->mh - c->pre.scaled_image->height) / 2;
302 + c->next->pre.x = c->pre.x + c->pre.scaled_image->width + gappi;
303 + c->next->pre.y = m->my + (m->mh - c->next->pre.scaled_image->height) / 2;
304 + return;
305 + }
306 + for (cols = 0; cols <= n / 2; cols++)
307 + if (cols * cols >= n)
308 + break;
309 + rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols;
310 + ch = (m->wh - 2 * gappo) / rows;
311 + cw = (m->ww - 2 * gappo) / cols;
312 + c = m->clients;
313 + cy = 0;
314 + for (i = 0; i < rows; i++) {
315 + cx = 0;
316 + cmaxh = 0;
317 + tmpc = c;
318 + for (int j = 0; j < cols; j++) {
319 + if (!c)
320 + break;
321 + c->pre.scaled_image = scaledownimage(c->pre.orig_image, cw, ch);
322 + c->pre.x = cx;
323 + cmaxh = c->pre.scaled_image->height > cmaxh ? c->pre.scaled_image->height : cmaxh;
324 + cx += c->pre.scaled_image->width + gappi;
325 + c = c->next;
326 + }
327 + c = tmpc;
328 + cx = m->wx + (m->ww - cx) / 2;
329 + for (j = 0; j < cols; j++) {
330 + if (!c)
331 + break;
332 + c->pre.x += cx;
333 + c->pre.y = cy + (cmaxh - c->pre.scaled_image->height) / 2;
334 + c = c->next;
335 + }
336 + cy += cmaxh + gappi;
337 + }
338 + cy = m->wy + (m->wh - cy) / 2;
339 + for (c = m->clients; c; c = c->next)
340 + c->pre.y += cy;
341 +}
342 +
343 +XImage*
344 +getwindowximage(Client *c) {
345 + XWindowAttributes attr;
346 + XGetWindowAttributes( dpy, c->win, &attr );
347 + XRenderPictFormat *format = XRenderFindVisualFormat( dpy, attr.visual );
348 + int hasAlpha = ( format->type == PictTypeDirect && format->direct.alphaMask );
349 + XRenderPictureAttributes pa;
350 + pa.subwindow_mode = IncludeInferiors;
351 + Picture picture = XRenderCreatePicture( dpy, c->win, format, CPSubwindowMode, &pa );
352 + Pixmap pixmap = XCreatePixmap(dpy, root, c->w, c->h, 32);
353 + XRenderPictureAttributes pa2;
354 + XRenderPictFormat *format2 = XRenderFindStandardFormat(dpy, PictStandardARGB32);
355 + Picture pixmapPicture = XRenderCreatePicture( dpy, pixmap, format2, 0, &pa2 );
356 + XRenderColor color;
357 + color.red = 0x0000;
358 + color.green = 0x0000;
359 + color.blue = 0x0000;
360 + color.alpha = 0x0000;
361 + XRenderFillRectangle (dpy, PictOpSrc, pixmapPicture, &color, 0, 0, c->w, c->h);
362 + XRenderComposite(dpy, hasAlpha ? PictOpOver : PictOpSrc, picture, 0,
363 + pixmapPicture, 0, 0, 0, 0, 0, 0,
364 + c->w, c->h);
365 + XImage* temp = XGetImage( dpy, pixmap, 0, 0, c->w, c->h, AllPlanes, ZPixmap );
366 + temp->red_mask = format2->direct.redMask << format2->direct.red;
367 + temp->green_mask = format2->direct.greenMask << format2->direct.green;
368 + temp->blue_mask = format2->direct.blueMask << format2->direct.blue;
369 + temp->depth = DefaultDepth(dpy, screen);
370 + return temp;
371 +}
372 +
373 +XImage*
374 +scaledownimage(XImage *orig_image, unsigned int cw, unsigned int ch) {
375 + int factor_w = orig_image->width / cw + 1;
376 + int factor_h = orig_image->height / ch + 1;
377 + int scale_factor = factor_w > factor_h ? factor_w : factor_h;
378 + int scaled_width = orig_image->width / scale_factor;
379 + int scaled_height = orig_image->height / scale_factor;
380 + XImage *scaled_image = XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
381 + orig_image->depth,
382 + ZPixmap, 0, NULL,
383 + scaled_width, scaled_height,
384 + 32, 0);
385 + scaled_image->data = malloc(scaled_image->height * scaled_image->bytes_per_line);
386 + for (int y = 0; y < scaled_height; y++) {
387 + for (int x = 0; x < scaled_width; x++) {
388 + int orig_x = x * scale_factor;
389 + int orig_y = y * scale_factor;
390 + unsigned long pixel = XGetPixel(orig_image, orig_x, orig_y);
391 + XPutPixel(scaled_image, x, y, pixel);
392 + }
393 + }
394 + scaled_image->depth = orig_image->depth;
395 + return scaled_image;
396 +}
397 +
398 int
399 main(int argc, char *argv[])
400 {
401 --
402 2.48.1
403