sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | 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