diff --git a/extra/bash.completion b/extra/bash.completion index 0bfadd6..826be8d 100644 --- a/extra/bash.completion +++ b/extra/bash.completion @@ -10,8 +10,9 @@ _swayimg() -f --fullscreen \ -s --scale \ -b --background \ - -w --window \ - -g --geometry \ + -w --wndbkg \ + -p --wndpos \ + -g --wndsize \ -i --info \ -e --exec \ -c --class \ diff --git a/extra/swayimg.1 b/extra/swayimg.1 index 1a0fe37..0829760 100644 --- a/extra/swayimg.1 +++ b/extra/swayimg.1 @@ -39,22 +39,28 @@ as a path to some file (not a directory), \fBswayimg\fR will add all files in that directory to the image list. .IP "\fB\-l\fR, \fB\-\-slideshow\fR" Activate slideshow mode on startup. -.IP "\fB\-g\fR, \fB\-\-geometry\fR\fB=\fR\fIX,Y,WIDTH,HEIGHT\fR" -Set the absolute position and size of the window. For example, \fI10,20,100,200\fR -creates a window at position 10x20 (top left corner) with a size of -100x200. The comma can be replaced with any non-numeric character. If this -parameter is not specified, the geometry of the currently focused window -will be used. .IP "\fB\-b\fR, \fB\-\-background\fR\fB=\fR\fIXXXXXX\fR" Set background for transparent images: .nf \fInone\fR: transparent; \fIgrid\fR: show grid as background (default); -.IP "\fB\-w\fR, \fB\-\-window\fR\fB=\fR\fIXXXXXX\fR" +\fIXXXXXX\fR: hexadecimal RGB color. +.IP "\fB\-w\fR, \fB\-\-wndbkg\fR\fB=\fR\fICOLOR\fR" Set window background: .nf \fInone\fR: transparent (default); -\fI#XXXXXX\fR: hexadecimal RGB color. +\fIXXXXXX\fR: hexadecimal RGB color. +.IP "\fB\-p\fR, \fB\-\-wndpos\fR\fB=\fR\fIPOS\fR" +Set the absolute position of the window: +.nf +\fIparent\fR: set position from parent (currently active) window (default); +\fIX,Y\fR: absolute coordinates of the top left corner. +.IP "\fB\-g\fR, \fB\-\-wndsize\fR\fB=\fR\fISIZE\fR" +Set window size: +.nf +\fIparent\fR: set size from parent (currently active) window (default); +\fIimage\fR: set size from the first image; +\fIWIDTH,HEIGHT\fR: absolute size of the window. .IP "\fB\-f\fR, \fB\-\-fullscreen\fR" Start in full screen mode. .IP "\fB\-s\fR, \fB\-\-scale\fR\fB=\fR\fISCALE\fR" diff --git a/extra/swayimgrc b/extra/swayimgrc index da449d4..8edddf0 100644 --- a/extra/swayimgrc +++ b/extra/swayimgrc @@ -18,7 +18,13 @@ fullscreen = no background = grid # Window background mode/color (none or RGB, e.g. #112233) -window = none +wndbkg = none + +# Window position (parent or absolute coordiantes, e.g. 100,200) +wndpos = parent + +# Window size (parent, image, or absolute size, e.g. 800,600) +wndsize = parent # Show image info: format, size, EXIF, and current scale (yes/no) info = no diff --git a/extra/swayimgrc.5 b/extra/swayimgrc.5 index 9d7ed43..562187b 100644 --- a/extra/swayimgrc.5 +++ b/extra/swayimgrc.5 @@ -34,11 +34,18 @@ Empty lines and comments are ignored. .nf \fInone\fR: transparent; \fIgrid\fR: show grid as background (default); -\fI#XXXXXX\fR: hexadecimal RGB color. -.IP "\fBwindow\fR: window background:" +\fIXXXXXX\fR: hexadecimal RGB color. +.IP "\fBwndbkg\fR: window background:" .nf \fInone\fR: transparent (default); -\fI#XXXXXX\fR: hexadecimal RGB color. +\fIXXXXXX\fR: hexadecimal RGB color. +.IP "\fBwndpos\fR: window position:" +\fIparent\fR: set position from parent (currently active) window (default); +\fIX,Y\fR: absolute coordinates of the top left corner. +.IP "\fBwndsize\fR: window size:" +\fIparent\fR: set size from parent (currently active) window (default); +\fIimage\fR: set size from the first image; +\fIWIDTH,HEIGHT\fR: absolute size of the window. .IP "\fBantialiasing\fR: enable/disable anti-aliasing, \fIyes\fR or [\fIno\fR];" .IP "\fBinfo\fR: show image meta information: format, size, EXIF, and current scale, \fIyes\fR or [\fIno\fR];" .IP "\fBfont\fR: font name used for printing image meta info, default is \fImonospace\fR;" diff --git a/extra/zsh.completion b/extra/zsh.completion index 9bbff84..b2e74a7 100644 --- a/extra/zsh.completion +++ b/extra/zsh.completion @@ -11,8 +11,9 @@ _arguments \ '(-f --fullscreen)'{-f,--fullscreen}'[show image in full screen mode]' \ '(-s --scale=SCALE)'{-s,--scale=}'[set initial image scale]:scale:(optimal fit fill real)' \ '(-b --background)'{-b,--background=}'[set image background color]:bkg:(none grid)' \ - '(-w --window)'{-w,--window=}'[set window background color]:wnd:(none)' \ - '(-g --geometry)'{-g,--geometry=}'[set window geometry]:geo' \ + '(-w --wndbkg)'{-w,--wndbkg=}'[set window background color]:wndbkg:(none)' \ + '(-p --wndpos)'{-p,--wndpos=}'[set window position]:wndpos:(parent)' \ + '(-g --wndsize)'{-g,--wndsize=}'[set window size]:wndsize:(parent image)' \ '(-i --info)'{-i,--info}'[show image meta information (name, EXIF, etc)]' \ '(-e --exec)'{-e,--exec=}'[set execution command]' \ '(-c --class)'{-c,--class=}'[set window class/app_id]:class' \ diff --git a/src/config.c b/src/config.c index bf580a0..115e289 100644 --- a/src/config.c +++ b/src/config.c @@ -211,6 +211,36 @@ static bool set_color(const char* text, uint32_t* value) return true; } +/** + * Parse pair of numbers. + * @param text text to parse + * @param n1,n2 output values + * @return false if values have invalid format + */ +static bool parse_numpair(const char* text, long* n1, long* n2) +{ + char* endptr; + + errno = 0; + + // first number + *n1 = strtol(text, &endptr, 0); + if (errno) { + return false; + } + // skip delimeter + while (*endptr && *endptr == ',') { + ++endptr; + } + // second number + *n2 = strtol(endptr, &endptr, 0); + if (errno) { + return false; + } + + return *endptr == 0; +} + /** * Set (replace) string in config parameter. * @param src source string @@ -248,8 +278,12 @@ static bool apply_conf(struct config* ctx, const char* key, const char* value) return set_boolean(value, &ctx->fullscreen); } else if (strcmp(key, "background") == 0) { return config_set_background(ctx, value); - } else if (strcmp(key, "window") == 0) { - return config_set_window(ctx, value); + } else if (strcmp(key, "wndbkg") == 0) { + return config_set_wndbkg(ctx, value); + } else if (strcmp(key, "wndpos") == 0) { + return config_set_wndpos(ctx, value); + } else if (strcmp(key, "wndsize") == 0) { + return config_set_wndsize(ctx, value); } else if (strcmp(key, "info") == 0) { return set_boolean(value, &ctx->show_info); } else if (strcmp(key, "font") == 0) { @@ -356,6 +390,10 @@ static struct config* default_config(void) ctx->background = BACKGROUND_GRID; ctx->window = COLOR_TRANSPARENT; ctx->sway_wm = true; + ctx->geometry.x = SAME_AS_PARENT; + ctx->geometry.y = SAME_AS_PARENT; + ctx->geometry.width = SAME_AS_PARENT; + ctx->geometry.height = SAME_AS_PARENT; config_set_font_name(ctx, "monospace"); ctx->font_color = 0xcccccc; ctx->font_size = 14; @@ -549,7 +587,7 @@ bool config_set_background(struct config* ctx, const char* val) return true; } -bool config_set_window(struct config* ctx, const char* val) +bool config_set_wndbkg(struct config* ctx, const char* val) { if (strcmp(val, "none") == 0) { ctx->window = COLOR_TRANSPARENT; @@ -561,35 +599,46 @@ bool config_set_window(struct config* ctx, const char* val) return true; } -bool config_set_geometry(struct config* ctx, const char* val) +bool config_set_wndpos(struct config* ctx, const char* val) { - long nums[4]; // x,y,width,height - const char* ptr = val; - size_t idx; - - for (idx = 0; *ptr && idx < sizeof(nums) / sizeof(nums[0]); ++idx) { - char* endptr; - nums[idx] = strtol(ptr, &endptr, 0); - if (ptr == endptr) { - break; - } - ptr = endptr; - while (*ptr && *ptr == ',') { - ++ptr; + if (strcmp(val, "parent") == 0) { + ctx->geometry.x = SAME_AS_PARENT; + ctx->geometry.y = SAME_AS_PARENT; + return true; + } else { + long x, y; + if (parse_numpair(val, &x, &y)) { + ctx->geometry.x = (ssize_t)x; + ctx->geometry.y = (ssize_t)y; + return true; } } + fprintf(stderr, "Invalid window position: %s\n", val); + fprintf(stderr, "Expected 'parent' or X,Y.\n"); + return false; +} - if (idx == sizeof(nums) / sizeof(nums[0]) && !*ptr && - nums[2 /*width*/] > 0 && nums[3 /*height*/] > 0) { - ctx->geometry.x = (ssize_t)nums[0]; - ctx->geometry.y = (ssize_t)nums[1]; - ctx->geometry.width = (size_t)nums[2]; - ctx->geometry.height = (size_t)nums[3]; +bool config_set_wndsize(struct config* ctx, const char* val) +{ + if (strcmp(val, "parent") == 0) { + ctx->geometry.width = SAME_AS_PARENT; + ctx->geometry.height = SAME_AS_PARENT; + return true; + } else if (strcmp(val, "image") == 0) { + ctx->geometry.width = SAME_AS_IMAGE; + ctx->geometry.height = SAME_AS_IMAGE; return true; + } else { + long width, height; + if (parse_numpair(val, &width, &height) && width > 0 && height > 0) { + ctx->geometry.width = (size_t)width; + ctx->geometry.height = (size_t)height; + return true; + } } - fprintf(stderr, "Invalid window geometry: %s\n", val); - fprintf(stderr, "Expected X,Y,W,H format.\n"); + fprintf(stderr, "Invalid window size: %s\n", val); + fprintf(stderr, "Expected 'parent', 'image', or WIDTH,HEIGHT.\n"); return false; } diff --git a/src/config.h b/src/config.h index 3ae7c07..e70ccf1 100644 --- a/src/config.h +++ b/src/config.h @@ -12,6 +12,11 @@ #define COLOR_TRANSPARENT 0xff000000 #define BACKGROUND_GRID 0xfe000000 +// Copy position or size from parent window +#define SAME_AS_PARENT 0xffffffff +// Copy size from image +#define SAME_AS_IMAGE 0 + // Max number of key bindings #define MAX_KEYBINDINGS 128 @@ -111,8 +116,9 @@ void config_free(struct config* ctx); // Configuration setters bool config_set_scale(struct config* ctx, const char* val); bool config_set_background(struct config* ctx, const char* val); -bool config_set_window(struct config* ctx, const char* val); -bool config_set_geometry(struct config* ctx, const char* val); +bool config_set_wndbkg(struct config* ctx, const char* val); +bool config_set_wndpos(struct config* ctx, const char* val); +bool config_set_wndsize(struct config* ctx, const char* val); bool config_set_font_name(struct config* ctx, const char* val); bool config_set_font_size(struct config* ctx, const char* val); bool config_set_order(struct config* ctx, const char* val); diff --git a/src/main.c b/src/main.c index cc02ad2..8327cd8 100644 --- a/src/main.c +++ b/src/main.c @@ -29,9 +29,10 @@ static const struct cmdarg arguments[] = { { 'l', "slideshow", NULL, "activate slideshow mode on startup" }, { 'f', "fullscreen", NULL, "show image in full screen mode" }, { 's', "scale", "SCALE", "set initial image scale: [optimal]/fit/fill/real" }, - { 'b', "background", "XXXXXX", "set image background color: none/[grid]/RGB" }, - { 'w', "window", "XXXXXX", "set window background color: [none]/RGB" }, - { 'g', "geometry", "X,Y,W,H", "set window geometry" }, + { 'b', "background", "COLOR", "set image background color: none/[grid]/RGB" }, + { 'w', "wndbkg", "COLOR", "set window background color: [none]/RGB" }, + { 'p', "wndpos", "POS", "set window position [parent]/X,Y" }, + { 'g', "wndsize", "SIZE", "set window size: [parent]/image/W,H" }, { 'i', "info", NULL, "show image meta information (name, EXIF, etc)" }, { 'e', "exec", "CMD", "set execution command" }, { 'c', "class", "NAME", "set window class/app_id" }, @@ -47,12 +48,14 @@ static const struct cmdarg arguments[] = { static void print_help(void) { char buf_lopt[32]; + puts("Usage: " APP_NAME " [OPTION]... [FILE]..."); puts("Show images from FILE(s)."); puts("If FILE is -, read standard input."); puts("If no FILE specified - read all files from the current directory.\n"); puts("Mandatory arguments to long options are mandatory for short options " "too."); + for (size_t i = 0; i < sizeof(arguments) / sizeof(arguments[0]); ++i) { const struct cmdarg* arg = &arguments[i]; strcpy(buf_lopt, arg->long_opt); @@ -127,12 +130,17 @@ static int parse_cmdargs(int argc, char* argv[], struct config* cfg) } break; case 'w': - if (!config_set_window(cfg, optarg)) { + if (!config_set_wndbkg(cfg, optarg)) { + return -1; + } + break; + case 'p': + if (!config_set_wndpos(cfg, optarg)) { return -1; } break; case 'g': - if (!config_set_geometry(cfg, optarg)) { + if (!config_set_wndsize(cfg, optarg)) { return -1; } break; @@ -174,21 +182,29 @@ static int parse_cmdargs(int argc, char* argv[], struct config* cfg) */ static void sway_setup(struct config* cfg) { - const bool absolute = cfg->geometry.width; - const int ipc = sway_connect(); + int ipc; + struct rect wnd_parent; + bool wnd_fullscreen = false; + const bool absolute = + cfg->geometry.x != SAME_AS_PARENT && cfg->geometry.y != SAME_AS_PARENT; - if (ipc == -1) { + ipc = sway_connect(); + if (ipc == INVALID_SWAY_IPC) { return; } - if (!absolute) { - bool fullscreen = false; - // get coordinates and size of the currently focused window - if (!sway_current(ipc, &cfg->geometry, &fullscreen)) { - sway_disconnect(ipc); - return; + if (sway_current(ipc, &wnd_parent, &wnd_fullscreen)) { + cfg->fullscreen |= wnd_fullscreen; + if (cfg->geometry.x == SAME_AS_PARENT && + cfg->geometry.y == SAME_AS_PARENT) { + cfg->geometry.x = wnd_parent.x; + cfg->geometry.y = wnd_parent.y; + } + if (cfg->geometry.width == SAME_AS_PARENT && + cfg->geometry.height == SAME_AS_PARENT) { + cfg->geometry.width = wnd_parent.width; + cfg->geometry.height = wnd_parent.height; } - cfg->fullscreen |= fullscreen; } if (!cfg->fullscreen) { @@ -227,24 +243,20 @@ int main(int argc, char* argv[]) goto done; } - // check configuration - if (cfg->geometry.width) { - if (!cfg->sway_wm) { - fprintf(stderr, - "Warning: unable to set window geometry without sway\n"); - } - if (cfg->fullscreen) { - fprintf(stderr, - "Warning: window geometry used in fullscreen mode\n"); - } - } - // compose file list list = image_list_init((const char**)&argv[index], argc - index, cfg); if (!list) { goto done; } + // set window size form the first image + if (cfg->geometry.width == SAME_AS_IMAGE || + cfg->geometry.height == SAME_AS_IMAGE) { + struct image_entry first = image_list_current(list); + cfg->geometry.width = first.image->frames[0].width; + cfg->geometry.height = first.image->frames[0].height; + } + if (cfg->sway_wm && !cfg->fullscreen) { sway_setup(cfg); }