From 3f456f3a379f1f6c6a41fc9cae33de531b061e66 Mon Sep 17 00:00:00 2001 From: speedie Date: Fri, 30 Jun 2023 02:44:29 +0200 Subject: [PATCH] Feature: Add the ability to take "screenshots" of spmenu. It doesn't actually capture your screen, but rather saves the Cairo surface to an image. The path to the image and some other options can also be configured in the config file. By default, Print Screen can be pressed in Normal mode with no modifier to take a screenshot. The default location is the user's home directory, and the file has a date attached to it. Of course, this can be changed as well. --- docs/docs.md | 1 + docs/spmenu.conf | 4 ++++ libs/arg.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ libs/arg.h | 1 + libs/conf/config.c | 12 ++++++++++++ libs/conf/config.h | 1 + libs/draw/draw.c | 10 ++++++++++ libs/draw/draw.h | 3 +++ libs/keybinds.h | 2 ++ libs/options.h | 5 +++++ spmenu.1 | 11 +++++++++++ 11 files changed, 98 insertions(+) diff --git a/docs/docs.md b/docs/docs.md index a1fd8ef..d9bbddf 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -792,6 +792,7 @@ These are the default keybinds. You can generate these yourself from a | 0 | 0 | Prior | moveprev | 0 | | 0 | Ctrl | p | navhistory | -1 | | 0 | Ctrl | n | navhistory | +1 | +| 0 | 0 | Print | screenshot | 0 | | 1 | 0 | Escape | switchmode | 0 | ## .Xresources diff --git a/docs/spmenu.conf b/docs/spmenu.conf index 8450cdc..d9c3056 100644 --- a/docs/spmenu.conf +++ b/docs/spmenu.conf @@ -213,6 +213,9 @@ spmenu = { binds = 1; // Load binds (~/.config/spmenu/binds.conf) on runtime themefile = "NULL"; // Path to theme file to load on runtime. NULL means default. bindsfile = "NULL"; // Path to binds file to load on runtime. NULL means default. + screenshotfile = "NULL"; // Screenshot file path. NULL means default. + screenshotname = "NULL"; // Screenshot file name. NULL means default. + screenshotdir = "NULL"; // Screenshot file directory. NULL means default. } ); /* Input options */ @@ -333,6 +336,7 @@ spmenu = { { mode = 0; modifier = "None"; key = "Prior"; function = "moveprev"; argument = "0"; }, // Prior: Move to the previous entry (undo) { mode = 0; modifier = "Ctrl"; key = "p"; function = "navhistory"; argument = "-1"; }, // Alt+p: Navigate to the previous entry in the history buffer { mode = 0; modifier = "Ctrl"; key = "n"; function = "navhistory"; argument = "+1"; }, // Alt+n: Navigate to the next entry in the history buffer + { mode = 0; modifier = "None"; key = "PrintScr"; function = "screenshot"; argument = "0"; }, // Print Screen: Screenshot spmenu { ignoreglobalkeys = 1; } ), // Ignore hardcoded keybinds (0/1) }; diff --git a/libs/arg.c b/libs/arg.c index 3d6529e..9df9da9 100644 --- a/libs/arg.c +++ b/libs/arg.c @@ -507,3 +507,51 @@ void switchmode(Arg *arg) { strncpy(tx.modetext, sp.mode ? instext : normtext, 15); drawmenu(); } + +void screenshot(Arg *arg) { + char *file = NULL; + char *home = NULL; + time_t time_ = time(NULL); + struct tm t = *localtime(&time_); + + if (!screenshotfile) { + if (!(home = getenv("HOME"))) { + fprintf(stderr, "spmenu: failed to determine home directory\n"); + return; + } + + if (!screenshotdir && !screenshotname) { // default + if (!(file = malloc(snprintf(NULL, 0, "%s/%s-%02d-%02d-%02d%s", home, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png") + 1))) { + die("spmenu: failed to malloc screenshot file"); + } + + sprintf(file, "%s/%s-%02d-%02d-%02d%s", home, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png"); + } else if (!screenshotdir && screenshotname) { // no dir but name + if (!(file = malloc(snprintf(NULL, 0, "%s/%s", home, screenshotname) + 1))) { + die("spmenu: failed to malloc screenshot file"); + } + + sprintf(file, "%s/%s", home, screenshotname); + } else if (screenshotdir && !screenshotname) { // dir but no name + if (!(file = malloc(snprintf(NULL, 0, "%s/%s-%02d-%02d-%02d%s", screenshotdir, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png") + 1))) { + die("spmenu: failed to malloc screenshot file"); + } + + sprintf(file, "%s/%s-%02d-%02d-%02d%s", screenshotdir, "spmenu-screenshot", t.tm_hour, t.tm_min, t.tm_sec, ".png"); + } else { // dir and name + if (!(file = malloc(snprintf(NULL, 0, "%s/%s", screenshotdir, screenshotname) + 1))) { + die("spmenu: failed to malloc screenshot file"); + } + + sprintf(file, "%s/%s", screenshotdir, screenshotname); + } + } else { // custom file + if (!(file = malloc(snprintf(NULL, 0, "%s", screenshotfile) + 1))) { + die("spmenu: failed to malloc screenshot file"); + } + + sprintf(file, "%s", screenshotfile); + } + + draw_save_screen(draw, file); +} diff --git a/libs/arg.h b/libs/arg.h index 520706d..f2260af 100644 --- a/libs/arg.h +++ b/libs/arg.h @@ -43,3 +43,4 @@ static void spawn(Arg *arg); static void togglehighlight(Arg *arg); static void setprofile(Arg *arg); static void switchmode(Arg *arg); +static void screenshot(Arg *arg); diff --git a/libs/conf/config.c b/libs/conf/config.c index a5a12d1..95ed777 100644 --- a/libs/conf/config.c +++ b/libs/conf/config.c @@ -441,6 +441,18 @@ void conf_init(void) { if (config_setting_lookup_string(conf, "bindsfile", &dest)) { bindsfile = strdup(dest); } + + if (config_setting_lookup_string(conf, "screenshotfile", &dest) && strcmp(dest, "NULL")) { + screenshotfile = strdup(dest); + } + + if (config_setting_lookup_string(conf, "screenshotname", &dest) && strcmp(dest, "NULL")) { + screenshotname = strdup(dest); + } + + if (config_setting_lookup_string(conf, "screenshotdir", &dest) && strcmp(dest, "NULL")) { + screenshotdir = strdup(dest); + } } } diff --git a/libs/conf/config.h b/libs/conf/config.h index 62bd6ca..ec4fada 100644 --- a/libs/conf/config.h +++ b/libs/conf/config.h @@ -384,6 +384,7 @@ static FuncList fl[] = { { "setimggaps", setimggaps }, { "setlines", setlines }, { "setlines", setlines }, + { "screenshot", screenshot }, { "setcolumns", setcolumns }, { "togglehighlight",togglehighlight }, { "setprofile", setprofile }, diff --git a/libs/draw/draw.c b/libs/draw/draw.c index 6a513be..4d2f139 100644 --- a/libs/draw/draw.c +++ b/libs/draw/draw.c @@ -417,6 +417,16 @@ void draw_img(Draw_t *draw, int x, int y) { cairo_set_source_surface(draw->d, draw->surface, draw->w, draw->h); } +void draw_save_screen(Draw_t *draw, const char *file) { + if (!draw || !draw->surface) { + return; + } + + if (cairo_surface_write_to_png(draw->surface, file)) { + fprintf(stderr, "spmenu: failed to write file %s\n", file); + } +} + unsigned int draw_fontset_getwidth_clamp(Draw_t *draw, const char *text, unsigned int n, Bool markup) { unsigned int tmp = 0; if (draw && draw->font && text && n) diff --git a/libs/draw/draw.h b/libs/draw/draw.h index c1d953a..95b085f 100644 --- a/libs/draw/draw.h +++ b/libs/draw/draw.h @@ -64,3 +64,6 @@ void draw_map(Draw_t *draw, Window win, int x, int y, unsigned int w, unsigned i /* Powerline functions */ void draw_arrow(Draw_t *draw, int x, int y, unsigned int w, unsigned int h, int direction, int slash, char *prevcol, char *nextcol, int prevalpha, int nextalpha); void draw_circle(Draw_t *draw, int x, int y, unsigned int w, unsigned int h, int direction, char *prevcol, char *nextcol, int prevalpha, int nextalpha); + +/* Screenshot functions */ +void draw_save_screen(Draw_t *draw, const char *file); diff --git a/libs/keybinds.h b/libs/keybinds.h index 667c576..6afb4fa 100644 --- a/libs/keybinds.h +++ b/libs/keybinds.h @@ -71,6 +71,7 @@ static Key keys[] = { { 0, 0, XK_Prior, moveprev, {0} }, { 0, Ctrl, XK_p, navhistory, {.i = -1 } }, { 0, Ctrl, XK_n, navhistory, {.i = +1 } }, + { 0, 0, XK_Print, screenshot, {0} }, /* insert mode */ { 1, 0, XK_Escape, switchmode, {0} }, @@ -146,6 +147,7 @@ static WlKey wl_keys[] = { { 0, WL_None, XKB_KEY_Prior, moveprev, {0} }, { 0, WL_Ctrl, XKB_KEY_p, navhistory, {.i = -1 } }, { 0, WL_Ctrl, XKB_KEY_n, navhistory, {.i = +1 } }, + { 0, WL_None, XKB_KEY_Print, screenshot, {0} }, /* insert mode */ { 1, WL_None, XKB_KEY_Escape, switchmode, {0} }, diff --git a/libs/options.h b/libs/options.h index 4a8967c..cb7f4b6 100644 --- a/libs/options.h +++ b/libs/options.h @@ -56,6 +56,11 @@ static int generatecache = 1; /* Generate image cache by default * static int maxcache = 512; /* Max image size to cache */ static char *cachedir = "default"; /* Cache directory. Default means spmenu will determine automatically */ +/* Screenshot options */ +static char *screenshotfile = NULL; /* Screenshot file path. If set to NULL, the default path will be used. */ +static char *screenshotname = NULL; /* Screenshot file name. If set to NULL, the default name will be used. */ +static char *screenshotdir = NULL; /* Screenshot file directory. If set to NULL, the default directory will be used. */ + /* Mode options */ static int mode = 0; /* Mode to start speedwm in (0: Normal mode, 1: Insert mode) */ static char *normtext = "Normal"; /* Text to display for normal mode */ diff --git a/spmenu.1 b/spmenu.1 index 1f9c636..09a063f 100644 --- a/spmenu.1 +++ b/spmenu.1 @@ -1458,6 +1458,17 @@ T}@T{ +1 T} T{ +0 +T}@T{ +0 +T}@T{ +Print +T}@T{ +screenshot +T}@T{ +0 +T} +T{ 1 T}@T{ 0