implement solid color rendering for swaybg

This commit is contained in:
Zandr Martin 2016-07-30 18:50:13 -05:00
parent 2e4ece65da
commit 98aa59fdda
No known key found for this signature in database
GPG Key ID: AA2BB8EF77F7BBDC
10 changed files with 197 additions and 157 deletions

View File

@ -12,6 +12,8 @@ add_library(sway-common STATIC
stringop.c stringop.c
) )
target_link_libraries(sway-common m)
if(Backtrace_FOUND) if(Backtrace_FOUND)
set_target_properties(sway-common set_target_properties(sway-common
PROPERTIES PROPERTIES

View File

@ -97,3 +97,16 @@ pid_t get_parent_pid(pid_t child) {
return -1; return -1;
} }
uint32_t parse_color(const char *color) {
int len = strlen(color);
if (color[0] != '#' || (len != 7 && len != 9)) {
sway_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);
return 0xFFFFFFFF;
}
uint32_t res = (uint32_t)strtol(color + 1, NULL, 16);
if (strlen(color) == 7) {
res = (res << 8) | 0xFF;
}
return res;
}

View File

@ -5,6 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "list.h" #include "list.h"
#include "util.h"
/** /**
* Colors for a box with background, border and text colors. * Colors for a box with background, border and text colors.
@ -47,11 +48,6 @@ struct config {
} colors; } colors;
}; };
/**
* Parse colors defined as hex string to uint32_t.
*/
uint32_t parse_color(const char *color);
/** /**
* Parse position top|bottom|left|right. * Parse position top|bottom|left|right.
*/ */

View File

@ -81,7 +81,7 @@ struct output_config {
int enabled; int enabled;
int width, height; int width, height;
int x, y; int x, y;
int scale; int scale;
char *background; char *background;
char *background_option; char *background_option;
}; };

View File

@ -44,4 +44,10 @@ int get_modifier_names(const char **names, uint32_t modifier_masks);
*/ */
pid_t get_parent_pid(pid_t pid); pid_t get_parent_pid(pid_t pid);
/**
* Given a string that represents an RGB(A) color, return a uint32_t
* version of the color.
*/
uint32_t parse_color(const char *color);
#endif #endif

View File

@ -1651,47 +1651,52 @@ static struct cmd_results *cmd_output(int argc, char **argv) {
} else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) { } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
wordexp_t p; wordexp_t p;
if (++i >= argc) { if (++i >= argc) {
return cmd_results_new(CMD_INVALID, "output", "Missing background file."); return cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
} }
if (i + 1 >= argc) { if (i + 1 >= argc) {
return cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode."); return cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
} }
char *src = join_args(argv + i, argc - i - 1); if (strcasecmp(argv[argc - 1], "solid_color") == 0) {
char *mode = argv[argc - 1]; output->background = strdup(argv[argc - 2]);
if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { output->background_option = strdup("solid_color");
return cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); } else {
} char *src = join_args(argv + i, argc - i - 1);
free(src); char *mode = argv[argc - 1];
src = p.we_wordv[0]; if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
if (config->reading && *src != '/') { return cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src);
char *conf = strdup(config->current_config);
char *conf_path = dirname(conf);
src = malloc(strlen(conf_path) + strlen(src) + 2);
sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
free(conf);
}
if (access(src, F_OK) == -1) {
return cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
}
for (char *m = mode; *m; ++m) *m = tolower(*m);
// Check mode
bool valid = false;
size_t j;
for (j = 0; j < sizeof(bg_options) / sizeof(char *); ++j) {
if (strcasecmp(mode, bg_options[j]) == 0) {
valid = true;
break;
} }
}
if (!valid) {
return cmd_results_new(CMD_INVALID, "output", "Invalid background scaling mode.");
}
output->background = strdup(src);
output->background_option = strdup(mode);
if (src != p.we_wordv[0]) {
free(src); free(src);
src = p.we_wordv[0];
if (config->reading && *src != '/') {
char *conf = strdup(config->current_config);
char *conf_path = dirname(conf);
src = malloc(strlen(conf_path) + strlen(src) + 2);
sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
free(conf);
}
if (access(src, F_OK) == -1) {
return cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
}
for (char *m = mode; *m; ++m) *m = tolower(*m);
// Check mode
bool valid = false;
size_t j;
for (j = 0; j < sizeof(bg_options) / sizeof(char *); ++j) {
if (strcasecmp(mode, bg_options[j]) == 0) {
valid = true;
break;
}
}
if (!valid) {
return cmd_results_new(CMD_INVALID, "output", "Invalid background scaling mode.");
}
output->background = strdup(src);
output->background_option = strdup(mode);
if (src != p.we_wordv[0]) {
free(src);
}
wordfree(&p);
} }
wordfree(&p);
} }
} }

View File

@ -308,6 +308,10 @@ The default colors are:
Sets the wallpaper for the given output to the specified file, using the given Sets the wallpaper for the given output to the specified file, using the given
scaling mode (one of "stretch", "fill", "fit", "center", "tile"). scaling mode (one of "stretch", "fill", "fit", "center", "tile").
**output** <name> <background|bg> <color> solid_color::
Sets the background of the given output to the specified color. _color_ should
be specified as an _#rrggbb_ (no alpha) color.
**output** <name> disable:: **output** <name> disable::
Disables the specified output. Disables the specified output.

View File

@ -5,19 +5,6 @@
#include "log.h" #include "log.h"
#include "bar/config.h" #include "bar/config.h"
uint32_t parse_color(const char *color) {
if (color[0] != '#') {
sway_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);
return 0xFFFFFFFF;
}
char *end;
uint32_t res = (uint32_t)strtol(color + 1, &end, 16);
if (strlen(color) == 7) {
res = (res << 8) | 0xFF;
}
return res;
}
uint32_t parse_position(const char *position) { uint32_t parse_position(const char *position) {
if (strcmp("top", position) == 0) { if (strcmp("top", position) == 0) {
return DESKTOP_SHELL_PANEL_POSITION_TOP; return DESKTOP_SHELL_PANEL_POSITION_TOP;

View File

@ -6,6 +6,7 @@
#include "log.h" #include "log.h"
#include "bar/config.h" #include "bar/config.h"
#include "bar/status_line.h" #include "bar/status_line.h"
#include "util.h"
#define I3JSON_MAXDEPTH 4 #define I3JSON_MAXDEPTH 4
#define I3JSON_UNKNOWN 0 #define I3JSON_UNKNOWN 0

View File

@ -1,6 +1,8 @@
#include "wayland-desktop-shell-client-protocol.h" #include "wayland-desktop-shell-client-protocol.h"
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
@ -9,6 +11,7 @@
#include "client/cairo.h" #include "client/cairo.h"
#include "log.h" #include "log.h"
#include "list.h" #include "list.h"
#include "util.h"
list_t *surfaces; list_t *surfaces;
struct registry *registry; struct registry *registry;
@ -32,6 +35,23 @@ void sway_terminate(int exit_code) {
exit(exit_code); exit(exit_code);
} }
bool is_valid_color(const char *color) {
int len = strlen(color);
if (len != 7 || color[0] != '#') {
sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color);
return false;
}
int i;
for (i = 1; i < len; ++i) {
if (!isxdigit(color[i])) {
return false;
}
}
return true;
}
int main(int argc, const char **argv) { int main(int argc, const char **argv) {
init_log(L_INFO); init_log(L_INFO);
surfaces = create_list(); surfaces = create_list();
@ -57,112 +77,118 @@ int main(int argc, const char **argv) {
window_make_shell(window); window_make_shell(window);
list_add(surfaces, window); list_add(surfaces, window);
#ifdef WITH_GDK_PIXBUF if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) {
GError *err = NULL; cairo_set_source_u32(window->cairo, parse_color(argv[2]));
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err); cairo_paint(window->cairo);
if (!pixbuf) { window_render(window);
sway_abort("Failed to load background image.");
}
cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
#else
cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
#endif //WITH_GDK_PIXBUF
if (!image) {
sway_abort("Failed to read background image.");
}
double width = cairo_image_surface_get_width(image);
double height = cairo_image_surface_get_height(image);
const char *scaling_mode_str = argv[3];
enum scaling_mode scaling_mode = SCALING_MODE_STRETCH;
if (strcmp(scaling_mode_str, "stretch") == 0) {
scaling_mode = SCALING_MODE_STRETCH;
} else if (strcmp(scaling_mode_str, "fill") == 0) {
scaling_mode = SCALING_MODE_FILL;
} else if (strcmp(scaling_mode_str, "fit") == 0) {
scaling_mode = SCALING_MODE_FIT;
} else if (strcmp(scaling_mode_str, "center") == 0) {
scaling_mode = SCALING_MODE_CENTER;
} else if (strcmp(scaling_mode_str, "tile") == 0) {
scaling_mode = SCALING_MODE_TILE;
} else { } else {
sway_abort("Unsupported scaling mode: %s", scaling_mode_str); #ifdef WITH_GDK_PIXBUF
} GError *err = NULL;
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err);
for (i = 0; i < surfaces->length; ++i) { if (!pixbuf) {
struct window *window = surfaces->items[i]; sway_abort("Failed to load background image.");
if (window_prerender(window) && window->cairo) {
switch (scaling_mode) {
case SCALING_MODE_STRETCH:
cairo_scale(window->cairo,
(double) window->width / width,
(double) window->height / height);
cairo_set_source_surface(window->cairo, image, 0, 0);
break;
case SCALING_MODE_FILL:
{
double window_ratio = (double) window->width / window->height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double) window->width / width;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
0,
(double) window->height/2 / scale - height/2);
} else {
double scale = (double) window->height / height;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 / scale - width/2,
0);
}
break;
}
case SCALING_MODE_FIT:
{
double window_ratio = (double) window->width / window->height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double) window->height / height;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 / scale - width/2,
0);
} else {
double scale = (double) window->width / width;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
0,
(double) window->height/2 / scale - height/2);
}
break;
}
case SCALING_MODE_CENTER:
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 - width/2,
(double) window->height/2 - height/2);
break;
case SCALING_MODE_TILE:
{
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
cairo_set_source(window->cairo, pattern);
break;
}
default:
sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str);
}
cairo_paint(window->cairo);
window_render(window);
} }
} cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
#else
cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
#endif //WITH_GDK_PIXBUF
if (!image) {
sway_abort("Failed to read background image.");
}
double width = cairo_image_surface_get_width(image);
double height = cairo_image_surface_get_height(image);
cairo_surface_destroy(image); const char *scaling_mode_str = argv[3];
enum scaling_mode scaling_mode = SCALING_MODE_STRETCH;
if (strcmp(scaling_mode_str, "stretch") == 0) {
scaling_mode = SCALING_MODE_STRETCH;
} else if (strcmp(scaling_mode_str, "fill") == 0) {
scaling_mode = SCALING_MODE_FILL;
} else if (strcmp(scaling_mode_str, "fit") == 0) {
scaling_mode = SCALING_MODE_FIT;
} else if (strcmp(scaling_mode_str, "center") == 0) {
scaling_mode = SCALING_MODE_CENTER;
} else if (strcmp(scaling_mode_str, "tile") == 0) {
scaling_mode = SCALING_MODE_TILE;
} else {
sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
}
for (i = 0; i < surfaces->length; ++i) {
struct window *window = surfaces->items[i];
if (window_prerender(window) && window->cairo) {
switch (scaling_mode) {
case SCALING_MODE_STRETCH:
cairo_scale(window->cairo,
(double) window->width / width,
(double) window->height / height);
cairo_set_source_surface(window->cairo, image, 0, 0);
break;
case SCALING_MODE_FILL:
{
double window_ratio = (double) window->width / window->height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double) window->width / width;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
0,
(double) window->height/2 / scale - height/2);
} else {
double scale = (double) window->height / height;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 / scale - width/2,
0);
}
break;
}
case SCALING_MODE_FIT:
{
double window_ratio = (double) window->width / window->height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double) window->height / height;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 / scale - width/2,
0);
} else {
double scale = (double) window->width / width;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
0,
(double) window->height/2 / scale - height/2);
}
break;
}
case SCALING_MODE_CENTER:
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 - width/2,
(double) window->height/2 - height/2);
break;
case SCALING_MODE_TILE:
{
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
cairo_set_source(window->cairo, pattern);
break;
}
default:
sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str);
}
cairo_paint(window->cairo);
window_render(window);
}
}
cairo_surface_destroy(image);
}
while (wl_display_dispatch(registry->display) != -1); while (wl_display_dispatch(registry->display) != -1);