Per application color saturation support (#21)

* Initial implementation without fullscreen support

* Limit saturation to 2

* Fixed saturation not working for fullscreen applications like CSGO

* Fixed saturation ignoring border radius

* Updated README and sway.5 man page

* Rebased from Master

* Added command to README

* Fixed nitpicks
This commit is contained in:
Erik Reider 2022-11-12 01:38:09 +01:00 committed by GitHub
parent 8c907a0bcb
commit 1881b01d3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 137 additions and 25 deletions

View file

@ -5,13 +5,17 @@
Sway is an incredible window manager, and certainly one of the most well established wayland window managers. However, it is restricted to only include the functionality that existed in i3. This fork ditches the simple wlr_renderer, and replaces it with our fx_renderer, capable of rendering with fancy GLES2 effects. This, along with a couple of minor changes, expands sway's featureset to include the following: Sway is an incredible window manager, and certainly one of the most well established wayland window managers. However, it is restricted to only include the functionality that existed in i3. This fork ditches the simple wlr_renderer, and replaces it with our fx_renderer, capable of rendering with fancy GLES2 effects. This, along with a couple of minor changes, expands sway's featureset to include the following:
+ **Anti-aliased rounded corners, borders, and titlebars** + **Anti-aliased rounded corners, borders, and titlebars**
+ **Per application saturation control**: Allows the user to set the saturation (Digital Vibrance) for specific applications. Great for some FPS games!
+ **Scratchpad treated as minimize**: Allows docks, or panels with a taskbar, to correctly interpret minimize / unminimize requests ([thanks to LCBCrion](https://github.com/swaywm/sway/issues/6457)) + **Scratchpad treated as minimize**: Allows docks, or panels with a taskbar, to correctly interpret minimize / unminimize requests ([thanks to LCBCrion](https://github.com/swaywm/sway/issues/6457))
+ **Add a nix flake to the repo**: Allows nixos users to easily contribute to and test this project + **Add a nix flake to the repo**: Allows nixos users to easily contribute to and test this project
## New Configuration Options ## New Configuration Options
+ Corner radius: `corner_radius <val>` + Corner radius: `corner_radius <val>`
+ Application saturation: `for_window [CRITERIA HERE] saturation <set|plus|minus> <val 0.0 <-> 2.0>`
## Roadmap ## Roadmap
+ fade in / out animations + fade in / out animations
+ window movement animations + window movement animations
+ drop shadows + drop shadows

View file

@ -159,6 +159,7 @@ sway_cmd cmd_new_float;
sway_cmd cmd_new_window; sway_cmd cmd_new_window;
sway_cmd cmd_nop; sway_cmd cmd_nop;
sway_cmd cmd_opacity; sway_cmd cmd_opacity;
sway_cmd cmd_saturation;
sway_cmd cmd_new_float; sway_cmd cmd_new_float;
sway_cmd cmd_new_window; sway_cmd cmd_new_window;
sway_cmd cmd_no_focus; sway_cmd cmd_no_focus;

View file

@ -16,6 +16,7 @@ struct gles2_tex_shader {
GLint size; GLint size;
GLint position; GLint position;
GLint radius; GLint radius;
GLint saturation;
GLint has_titlebar; GLint has_titlebar;
}; };
@ -79,10 +80,10 @@ void fx_renderer_scissor(struct wlr_box *box);
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9], const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],
float alpha, int radius, const bool has_titlebar); float alpha, int radius, float saturation, const bool has_titlebar);
bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
const struct wlr_box *dst_box, const float matrix[static 9], float alpha, int radius, const struct wlr_box *dst_box, const float matrix[static 9], float alpha, int radius, float saturation,
const bool has_titlebar); const bool has_titlebar);
void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box, void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box,

View file

@ -113,6 +113,8 @@ struct sway_container {
// Hidden scratchpad containers have a NULL parent. // Hidden scratchpad containers have a NULL parent.
bool scratchpad; bool scratchpad;
float saturation;
float alpha; float alpha;
int corner_radius; int corner_radius;

View file

@ -127,6 +127,7 @@ static const struct cmd_handler command_handlers[] = {
{ "reload", cmd_reload }, { "reload", cmd_reload },
{ "rename", cmd_rename }, { "rename", cmd_rename },
{ "resize", cmd_resize }, { "resize", cmd_resize },
{ "saturation", cmd_saturation },
{ "scratchpad", cmd_scratchpad }, { "scratchpad", cmd_scratchpad },
{ "shortcuts_inhibitor", cmd_shortcuts_inhibitor }, { "shortcuts_inhibitor", cmd_shortcuts_inhibitor },
{ "split", cmd_split }, { "split", cmd_split },

View file

@ -0,0 +1,43 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include "sway/commands.h"
#include "sway/tree/view.h"
#include "log.h"
struct cmd_results *cmd_saturation(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "saturation", EXPECTED_AT_LEAST, 1))) {
return error;
}
struct sway_container *con = config->handler_context.container;
if (con == NULL) {
return cmd_results_new(CMD_FAILURE, "No current container");
}
char *err;
float val = strtof(argc == 1 ? argv[0] : argv[1], &err);
if (*err) {
return cmd_results_new(CMD_INVALID, "saturation float invalid");
}
if (!strcasecmp(argv[0], "plus")) {
val = con->saturation + val;
} else if (!strcasecmp(argv[0], "minus")) {
val = con->saturation - val;
} else if (argc > 1 && strcasecmp(argv[0], "set")) {
return cmd_results_new(CMD_INVALID,
"Expected: set|plus|minus <0..2>: %s", argv[0]);
}
if (val < 0 || val > 2) {
return cmd_results_new(CMD_FAILURE, "saturation value out of bounds");
}
con->saturation = val;
container_damage_whole(con);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -99,6 +99,7 @@ bool init_frag_shader(struct gles2_tex_shader *shader, GLuint prog) {
shader->size = glGetUniformLocation(prog, "size"); shader->size = glGetUniformLocation(prog, "size");
shader->position = glGetUniformLocation(prog, "position"); shader->position = glGetUniformLocation(prog, "position");
shader->radius = glGetUniformLocation(prog, "radius"); shader->radius = glGetUniformLocation(prog, "radius");
shader->saturation = glGetUniformLocation(prog, "saturation");
shader->has_titlebar = glGetUniformLocation(prog, "has_titlebar"); shader->has_titlebar = glGetUniformLocation(prog, "has_titlebar");
return true; return true;
} }
@ -254,8 +255,7 @@ void fx_renderer_scissor(struct wlr_box *box) {
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9], const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],
float alpha, int radius, const bool has_titlebar) { float alpha, int radius, float saturation, const bool has_titlebar) {
assert(wlr_texture_is_gles2(wlr_texture)); assert(wlr_texture_is_gles2(wlr_texture));
struct wlr_gles2_texture_attribs texture_attrs; struct wlr_gles2_texture_attribs texture_attrs;
wlr_gles2_texture_get_attribs(wlr_texture, &texture_attrs); wlr_gles2_texture_get_attribs(wlr_texture, &texture_attrs);
@ -312,6 +312,7 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t
glUniform1i(shader->tex, 0); glUniform1i(shader->tex, 0);
glUniform1f(shader->alpha, alpha); glUniform1f(shader->alpha, alpha);
glUniform1f(shader->has_titlebar, has_titlebar); glUniform1f(shader->has_titlebar, has_titlebar);
glUniform1f(shader->saturation, saturation);
// rounded corners // rounded corners
glUniform2f(shader->size, dst_box->width, dst_box->height); glUniform2f(shader->size, dst_box->width, dst_box->height);
@ -347,14 +348,14 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t
bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture,
const struct wlr_box *dst_box, const float matrix[static 9], float alpha, int radius, const struct wlr_box *dst_box, const float matrix[static 9], float alpha, int radius,
const bool has_titlebar) { float saturation, const bool has_titlebar) {
struct wlr_fbox src_box = { struct wlr_fbox src_box = {
.x = 0, .x = 0,
.y = 0, .y = 0,
.width = wlr_texture->width, .width = wlr_texture->width,
.height = wlr_texture->height, .height = wlr_texture->height,
}; };
return fx_render_subtexture_with_matrix(renderer, wlr_texture, &src_box, dst_box, matrix, alpha, radius, has_titlebar); return fx_render_subtexture_with_matrix(renderer, wlr_texture, &src_box, dst_box, matrix, alpha, radius, saturation, has_titlebar);
} }
void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box, void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box,

View file

@ -509,7 +509,9 @@ static int output_repaint_timer_handler(void *data) {
fullscreen_con = workspace->current.fullscreen; fullscreen_con = workspace->current.fullscreen;
} }
if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { if (fullscreen_con && fullscreen_con->view && !debug.noscanout
// Only output to monitor without compositing when saturation is changed
&& fullscreen_con->saturation == 1.0f) {
// Try to scan-out the fullscreen view // Try to scan-out the fullscreen view
static bool last_scanned_out = false; static bool last_scanned_out = false;
bool scanned_out = bool scanned_out =

View file

@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <assert.h> #include <assert.h>
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <stdlib.h> #include <stdlib.h>
@ -32,6 +33,7 @@
struct render_data { struct render_data {
pixman_region32_t *damage; pixman_region32_t *damage;
float alpha; float alpha;
float saturation;
int corner_radius; int corner_radius;
bool has_titlebar; bool has_titlebar;
struct wlr_box *clip_box; struct wlr_box *clip_box;
@ -103,7 +105,7 @@ static void set_scale_filter(struct wlr_output *wlr_output,
static void render_texture(struct wlr_output *wlr_output, static void render_texture(struct wlr_output *wlr_output,
pixman_region32_t *output_damage, struct wlr_texture *texture, pixman_region32_t *output_damage, struct wlr_texture *texture,
const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
const float matrix[static 9], float alpha, int corner_radius, bool has_titlebar) { const float matrix[static 9], float alpha, int corner_radius, float saturation, bool has_titlebar) {
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
struct fx_renderer *renderer = output->server->renderer; struct fx_renderer *renderer = output->server->renderer;
@ -123,9 +125,11 @@ static void render_texture(struct wlr_output *wlr_output,
scissor_output(wlr_output, &rects[i]); scissor_output(wlr_output, &rects[i]);
set_scale_filter(wlr_output, texture, output->scale_filter); set_scale_filter(wlr_output, texture, output->scale_filter);
if (src_box != NULL) { if (src_box != NULL) {
fx_render_subtexture_with_matrix(renderer, texture, src_box, dst_box, matrix, alpha, corner_radius, has_titlebar); fx_render_subtexture_with_matrix(renderer, texture, src_box, dst_box, matrix,
alpha, corner_radius, saturation, has_titlebar);
} else { } else {
fx_render_texture_with_matrix(renderer, texture, dst_box, matrix, alpha, corner_radius, has_titlebar); fx_render_texture_with_matrix(renderer, texture, dst_box, matrix,
alpha, corner_radius, saturation, has_titlebar);
} }
} }
@ -140,6 +144,7 @@ static void render_surface_iterator(struct sway_output *output,
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
pixman_region32_t *output_damage = data->damage; pixman_region32_t *output_damage = data->damage;
float alpha = data->alpha; float alpha = data->alpha;
float saturation = data->saturation;
int corner_radius = data->corner_radius; int corner_radius = data->corner_radius;
bool has_titlebar = data->has_titlebar; bool has_titlebar = data->has_titlebar;
@ -169,7 +174,7 @@ static void render_surface_iterator(struct sway_output *output,
scale_box(&dst_box, wlr_output->scale); scale_box(&dst_box, wlr_output->scale);
render_texture(wlr_output, output_damage, texture, &src_box, &dst_box, render_texture(wlr_output, output_damage, texture, &src_box, &dst_box,
matrix, alpha, corner_radius * wlr_output->scale, has_titlebar); matrix, alpha, corner_radius * wlr_output->scale, saturation, has_titlebar);
wlr_presentation_surface_sampled_on_output(server.presentation, surface, wlr_presentation_surface_sampled_on_output(server.presentation, surface,
wlr_output); wlr_output);
@ -180,6 +185,7 @@ static void render_layer_toplevel(struct sway_output *output,
struct render_data data = { struct render_data data = {
.damage = damage, .damage = damage,
.alpha = 1.0f, .alpha = 1.0f,
.saturation = 1.0f,
.corner_radius = 0, .corner_radius = 0,
.has_titlebar = false, .has_titlebar = false,
}; };
@ -192,6 +198,7 @@ static void render_layer_popups(struct sway_output *output,
struct render_data data = { struct render_data data = {
.damage = damage, .damage = damage,
.alpha = 1.0f, .alpha = 1.0f,
.saturation = 1.0f,
.corner_radius = 0, .corner_radius = 0,
.has_titlebar = false, .has_titlebar = false,
}; };
@ -205,6 +212,7 @@ static void render_unmanaged(struct sway_output *output,
struct render_data data = { struct render_data data = {
.damage = damage, .damage = damage,
.alpha = 1.0f, .alpha = 1.0f,
.saturation = 1.0f,
.corner_radius = 0, .corner_radius = 0,
.has_titlebar = false, .has_titlebar = false,
}; };
@ -218,6 +226,7 @@ static void render_drag_icons(struct sway_output *output,
struct render_data data = { struct render_data data = {
.damage = damage, .damage = damage,
.alpha = 1.0f, .alpha = 1.0f,
.saturation = 1.0f,
.corner_radius = 0, .corner_radius = 0,
.has_titlebar = false, .has_titlebar = false,
}; };
@ -335,10 +344,12 @@ void premultiply_alpha(float color[4], float opacity) {
} }
static void render_view_toplevels(struct sway_view *view, struct sway_output *output, static void render_view_toplevels(struct sway_view *view, struct sway_output *output,
pixman_region32_t *damage, float alpha, int corner_radius, bool has_titlebar) { pixman_region32_t *damage, float alpha, int corner_radius,
float saturation, bool has_titlebar) {
struct render_data data = { struct render_data data = {
.damage = damage, .damage = damage,
.alpha = alpha, .alpha = alpha,
.saturation = saturation,
.corner_radius = corner_radius, .corner_radius = corner_radius,
.has_titlebar = has_titlebar, .has_titlebar = has_titlebar,
}; };
@ -360,7 +371,8 @@ static void render_view_toplevels(struct sway_view *view, struct sway_output *ou
} }
static void render_view_popups(struct sway_view *view, struct sway_output *output, static void render_view_popups(struct sway_view *view, struct sway_output *output,
pixman_region32_t *damage, float alpha, int corner_radius, bool has_titlebar) { pixman_region32_t *damage, float alpha, int corner_radius,
float saturation, bool has_titlebar) {
struct render_data data = { struct render_data data = {
.damage = damage, .damage = damage,
.alpha = alpha, .alpha = alpha,
@ -372,7 +384,8 @@ static void render_view_popups(struct sway_view *view, struct sway_output *outpu
} }
static void render_saved_view(struct sway_view *view, struct sway_output *output, static void render_saved_view(struct sway_view *view, struct sway_output *output,
pixman_region32_t *damage, float alpha, int corner_radius, bool has_titlebar) { pixman_region32_t *damage, float alpha, int corner_radius,
float saturation, bool has_titlebar) {
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
if (wl_list_empty(&view->saved_buffers)) { if (wl_list_empty(&view->saved_buffers)) {
@ -424,7 +437,8 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output
scale_box(&dst_box, wlr_output->scale); scale_box(&dst_box, wlr_output->scale);
render_texture(wlr_output, damage, saved_buf->buffer->texture, render_texture(wlr_output, damage, saved_buf->buffer->texture,
&saved_buf->source_box, &dst_box, matrix, alpha, corner_radius * wlr_output->scale, has_titlebar); &saved_buf->source_box, &dst_box, matrix, alpha, corner_radius * wlr_output->scale,
saturation, has_titlebar);
} }
// FIXME: we should set the surface that this saved buffer originates from // FIXME: we should set the surface that this saved buffer originates from
@ -439,9 +453,11 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
struct sway_container *con, struct border_colors *colors, bool has_titlebar) { struct sway_container *con, struct border_colors *colors, bool has_titlebar) {
struct sway_view *view = con->view; struct sway_view *view = con->view;
if (!wl_list_empty(&view->saved_buffers)) { if (!wl_list_empty(&view->saved_buffers)) {
render_saved_view(view, output, damage, con->alpha, con->corner_radius, has_titlebar); render_saved_view(view, output, damage, con->alpha, con->corner_radius,
con->saturation, has_titlebar);
} else if (view->surface) { } else if (view->surface) {
render_view_toplevels(view, output, damage, con->alpha, con->corner_radius, has_titlebar); render_view_toplevels(view, output, damage, con->alpha, con->corner_radius,
con->saturation, has_titlebar);
} }
if (con->current.border == B_NONE || con->current.border == B_CSD) { if (con->current.border == B_NONE || con->current.border == B_CSD) {
@ -679,7 +695,7 @@ static void render_titlebar(struct sway_output *output,
texture_box.width = ob_inner_width; texture_box.width = ob_inner_width;
} }
render_texture(output->wlr_output, output_damage, marks_texture, render_texture(output->wlr_output, output_damage, marks_texture,
NULL, &texture_box, matrix, con->alpha, 0, false); NULL, &texture_box, matrix, con->alpha, 0, 1.0f, false);
// Padding above // Padding above
memcpy(&color, colors->background, sizeof(float) * 4); memcpy(&color, colors->background, sizeof(float) * 4);
@ -755,7 +771,7 @@ static void render_titlebar(struct sway_output *output,
} }
render_texture(output->wlr_output, output_damage, title_texture, render_texture(output->wlr_output, output_damage, title_texture,
NULL, &texture_box, matrix, con->alpha, 0, false); NULL, &texture_box, matrix, con->alpha, 0, 1.0f, false);
// Padding above // Padding above
memcpy(&color, colors->background, sizeof(float) * 4); memcpy(&color, colors->background, sizeof(float) * 4);
@ -1274,9 +1290,11 @@ void output_render(struct sway_output *output, struct timespec *when,
if (fullscreen_con->view) { if (fullscreen_con->view) {
if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
render_saved_view(fullscreen_con->view, output, damage, 1.0f, 0, false); render_saved_view(fullscreen_con->view, output, damage, 1.0f, 0,
fullscreen_con->saturation, false);
} else if (fullscreen_con->view->surface) { } else if (fullscreen_con->view->surface) {
render_view_toplevels(fullscreen_con->view, output, damage, 1.0f, 0, false); render_view_toplevels(fullscreen_con->view, output, damage, 1.0f, 0,
fullscreen_con->saturation, false);
} }
} else { } else {
render_container(output, damage, fullscreen_con, render_container(output, damage, fullscreen_con,
@ -1330,7 +1348,7 @@ void output_render(struct sway_output *output, struct timespec *when,
struct sway_container *focus = seat_get_focused_container(seat); struct sway_container *focus = seat_get_focused_container(seat);
if (focus && focus->view) { if (focus && focus->view) {
render_view_popups(focus->view, output, damage, focus->alpha, render_view_popups(focus->view, output, damage, focus->alpha,
focus->corner_radius, focus->current.border == B_NORMAL); focus->corner_radius, focus->saturation, focus->current.border == B_NORMAL);
} }
render_overlay: render_overlay:

View file

@ -9,9 +9,20 @@ uniform vec2 size;
uniform vec2 position; uniform vec2 position;
uniform float radius; uniform float radius;
uniform bool has_titlebar; uniform bool has_titlebar;
uniform float saturation;
const vec3 saturation_weight = vec3(0.2125, 0.7154, 0.0721);
void main() { void main() {
// Saturation
if (saturation != 1.0) {
vec4 pixColor = texture2D(texture0, v_texcoord);
vec3 irgb = pixColor.rgb;
vec3 target = vec3(dot(irgb, saturation_weight));
gl_FragColor = vec4(mix(target, irgb, saturation), pixColor.a) * alpha;
} else {
gl_FragColor = texture2D(texture0, v_texcoord) * alpha; gl_FragColor = texture2D(texture0, v_texcoord) * alpha;
}
if (!has_titlebar || gl_FragCoord.y - position.y > radius) { if (!has_titlebar || gl_FragCoord.y - position.y > radius) {
vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy); vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy);
if (max(corner_distance.x, corner_distance.y) < radius) { if (max(corner_distance.x, corner_distance.y) < radius) {

View file

@ -7,9 +7,20 @@ uniform vec2 size;
uniform vec2 position; uniform vec2 position;
uniform float radius; uniform float radius;
uniform bool has_titlebar; uniform bool has_titlebar;
uniform float saturation;
const vec3 saturation_weight = vec3(0.2125, 0.7154, 0.0721);
void main() { void main() {
// Saturation
if (saturation != 1.0) {
vec4 pixColor = texture2D(tex, v_texcoord);
vec3 irgb = pixColor.rgb;
vec3 target = vec3(dot(irgb, saturation_weight));
gl_FragColor = vec4(mix(target, irgb, saturation), pixColor.a) * alpha;
} else {
gl_FragColor = texture2D(tex, v_texcoord) * alpha; gl_FragColor = texture2D(tex, v_texcoord) * alpha;
}
if (!has_titlebar || gl_FragCoord.y - position.y > radius) { if (!has_titlebar || gl_FragCoord.y - position.y > radius) {
vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy); vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy);
if (max(corner_distance.x, corner_distance.y) < radius) { if (max(corner_distance.x, corner_distance.y) < radius) {

View file

@ -7,9 +7,19 @@ uniform vec2 size;
uniform vec2 position; uniform vec2 position;
uniform float radius; uniform float radius;
uniform bool has_titlebar; uniform bool has_titlebar;
uniform float saturation;
const vec3 saturation_weight = vec3(0.2125, 0.7154, 0.0721);
void main() { void main() {
// Saturation
if (saturation != 1.0) {
vec3 irgb = texture2D(tex, v_texcoord).rgb;
vec3 target = vec3(dot(irgb, saturation_weight));
gl_FragColor = vec4(mix(target, irgb, saturation), 1.0) * alpha;
} else {
gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha; gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha;
}
if (!has_titlebar || gl_FragCoord.y - position.y > radius) { if (!has_titlebar || gl_FragCoord.y - position.y > radius) {
vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy); vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy);
if (max(corner_distance.x, corner_distance.y) < radius) { if (max(corner_distance.x, corner_distance.y) < radius) {

View file

@ -73,6 +73,7 @@ sway_sources = files(
'commands/mark.c', 'commands/mark.c',
'commands/max_render_time.c', 'commands/max_render_time.c',
'commands/opacity.c', 'commands/opacity.c',
'commands/saturation.c',
'commands/include.c', 'commands/include.c',
'commands/input.c', 'commands/input.c',
'commands/layout.c', 'commands/layout.c',

View file

@ -767,6 +767,11 @@ The default colors are:
Adjusts the opacity of the window between 0 (completely transparent) and Adjusts the opacity of the window between 0 (completely transparent) and
1 (completely opaque). If the operation is omitted, _set_ will be used. 1 (completely opaque). If the operation is omitted, _set_ will be used.
*saturation* [set|plus|minus] <value>
Adjusts the saturation (Digital Vibrance) of the window between 0 (black and
white) and 2 (over saturated which is suited for some FPS games) while 1 is
the default saturation. If the operation is omitted, _set_ will be used.
*tiling_drag* enable|disable|toggle *tiling_drag* enable|disable|toggle
Sets whether or not tiling containers can be dragged with the mouse. If Sets whether or not tiling containers can be dragged with the mouse. If
_enabled_ (default), the _floating_mod_ can be used to drag tiling, as well _enabled_ (default), the _floating_mod_ can be used to drag tiling, as well

View file

@ -40,6 +40,7 @@ struct sway_container *container_create(struct sway_view *view) {
c->pending.layout = L_NONE; c->pending.layout = L_NONE;
c->view = view; c->view = view;
c->alpha = 1.0f; c->alpha = 1.0f;
c->saturation = 1.0f;
c->corner_radius = config->corner_radius; c->corner_radius = config->corner_radius;
if (!view) { if (!view) {