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