add scale_filter output config option

This commit is contained in:
Ronan Pigott 2019-11-13 11:23:36 -07:00 committed by Simon Ser
parent 4b57953628
commit 6968fb3123
13 changed files with 131 additions and 1 deletions

View file

@ -276,6 +276,7 @@ sway_cmd output_cmd_max_render_time;
sway_cmd output_cmd_mode; sway_cmd output_cmd_mode;
sway_cmd output_cmd_position; sway_cmd output_cmd_position;
sway_cmd output_cmd_scale; sway_cmd output_cmd_scale;
sway_cmd output_cmd_scale_filter;
sway_cmd output_cmd_subpixel; sway_cmd output_cmd_subpixel;
sway_cmd output_cmd_toggle; sway_cmd output_cmd_toggle;
sway_cmd output_cmd_transform; sway_cmd output_cmd_transform;

View file

@ -204,6 +204,13 @@ enum config_dpms {
DPMS_OFF DPMS_OFF
}; };
enum scale_filter_mode {
SCALE_FILTER_DEFAULT, // the default is currently smart
SCALE_FILTER_LINEAR,
SCALE_FILTER_NEAREST,
SCALE_FILTER_SMART
};
/** /**
* Size and position configuration for a particular output. * Size and position configuration for a particular output.
* *
@ -217,6 +224,7 @@ struct output_config {
int custom_mode; int custom_mode;
int x, y; int x, y;
float scale; float scale;
enum scale_filter_mode scale_filter;
int32_t transform; int32_t transform;
enum wl_output_subpixel subpixel; enum wl_output_subpixel subpixel;
int max_render_time; // In milliseconds int max_render_time; // In milliseconds
@ -655,6 +663,8 @@ int output_name_cmp(const void *item, const void *data);
void output_get_identifier(char *identifier, size_t len, void output_get_identifier(char *identifier, size_t len,
struct sway_output *output); struct sway_output *output);
const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter);
struct output_config *new_output_config(const char *name); struct output_config *new_output_config(const char *name);
void merge_output_config(struct output_config *dst, struct output_config *src); void merge_output_config(struct output_config *dst, struct output_config *src);

View file

@ -32,6 +32,7 @@ struct sway_output {
int lx, ly; // layout coords int lx, ly; // layout coords
int width, height; // transformed buffer size int width, height; // transformed buffer size
enum wl_output_subpixel detected_subpixel; enum wl_output_subpixel detected_subpixel;
enum scale_filter_mode scale_filter;
// last applied mode when the output is DPMS'ed // last applied mode when the output is DPMS'ed
struct wlr_output_mode *current_mode; struct wlr_output_mode *current_mode;

View file

@ -48,6 +48,7 @@ pango = dependency('pango')
pangocairo = dependency('pangocairo') pangocairo = dependency('pangocairo')
gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
pixman = dependency('pixman-1') pixman = dependency('pixman-1')
glesv2 = dependency('glesv2')
libevdev = dependency('libevdev') libevdev = dependency('libevdev')
libinput = dependency('libinput', version: '>=1.6.0') libinput = dependency('libinput', version: '>=1.6.0')
systemd = dependency('libsystemd', version: '>=239', required: false) systemd = dependency('libsystemd', version: '>=239', required: false)

View file

@ -19,6 +19,7 @@ static struct cmd_handler output_handlers[] = {
{ "res", output_cmd_mode }, { "res", output_cmd_mode },
{ "resolution", output_cmd_mode }, { "resolution", output_cmd_mode },
{ "scale", output_cmd_scale }, { "scale", output_cmd_scale },
{ "scale_filter", output_cmd_scale_filter },
{ "subpixel", output_cmd_subpixel }, { "subpixel", output_cmd_subpixel },
{ "toggle", output_cmd_toggle }, { "toggle", output_cmd_toggle },
{ "transform", output_cmd_transform }, { "transform", output_cmd_transform },

View file

@ -0,0 +1,34 @@
#include <string.h>
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
struct cmd_results *output_cmd_scale_filter(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
if (!argc) {
return cmd_results_new(CMD_INVALID, "Missing scale_filter argument.");
}
enum scale_filter_mode scale_filter;
if (strcmp(*argv, "linear") == 0) {
scale_filter = SCALE_FILTER_LINEAR;
} else if (strcmp(*argv, "nearest") == 0) {
scale_filter = SCALE_FILTER_NEAREST;
} else if (strcmp(*argv, "smart") == 0) {
scale_filter = SCALE_FILTER_SMART;
} else {
return cmd_results_new(CMD_INVALID, "Invalid output scale_filter.");
}
struct output_config *oc = config->handler_context.output_config;
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
oc->scale_filter = scale_filter;
return NULL;
}

View file

@ -29,6 +29,21 @@ void output_get_identifier(char *identifier, size_t len,
wlr_output->serial); wlr_output->serial);
} }
const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
switch (scale_filter) {
case SCALE_FILTER_DEFAULT:
return "smart";
case SCALE_FILTER_LINEAR:
return "linear";
case SCALE_FILTER_NEAREST:
return "nearest";
case SCALE_FILTER_SMART:
return "smart";
}
sway_assert(false, "Unknown value for scale_filter.");
return NULL;
}
struct output_config *new_output_config(const char *name) { struct output_config *new_output_config(const char *name) {
struct output_config *oc = calloc(1, sizeof(struct output_config)); struct output_config *oc = calloc(1, sizeof(struct output_config));
if (oc == NULL) { if (oc == NULL) {
@ -45,6 +60,7 @@ struct output_config *new_output_config(const char *name) {
oc->custom_mode = -1; oc->custom_mode = -1;
oc->x = oc->y = -1; oc->x = oc->y = -1;
oc->scale = -1; oc->scale = -1;
oc->scale_filter = SCALE_FILTER_DEFAULT;
oc->transform = -1; oc->transform = -1;
oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
oc->max_render_time = -1; oc->max_render_time = -1;
@ -70,6 +86,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
if (src->scale != -1) { if (src->scale != -1) {
dst->scale = src->scale; dst->scale = src->scale;
} }
if (src->scale_filter != SCALE_FILTER_DEFAULT) {
dst->scale_filter = src->scale_filter;
}
if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
dst->subpixel = src->subpixel; dst->subpixel = src->subpixel;
} }
@ -297,6 +316,24 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
if (oc && oc->scale > 0) { if (oc && oc->scale > 0) {
sway_log(SWAY_DEBUG, "Set %s scale to %f", oc->name, oc->scale); sway_log(SWAY_DEBUG, "Set %s scale to %f", oc->name, oc->scale);
wlr_output_set_scale(wlr_output, oc->scale); wlr_output_set_scale(wlr_output, oc->scale);
enum scale_filter_mode scale_filter_old = output->scale_filter;
switch (oc->scale_filter) {
case SCALE_FILTER_DEFAULT:
case SCALE_FILTER_SMART:
output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ?
SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR;
break;
case SCALE_FILTER_LINEAR:
case SCALE_FILTER_NEAREST:
output->scale_filter = oc->scale_filter;
break;
}
if (scale_filter_old != output->scale_filter) {
sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
sway_output_scale_filter_to_string(output->scale_filter));
output_damage_whole(output);
}
} }
if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@ -352,6 +389,7 @@ static void default_output_config(struct output_config *oc,
} }
oc->x = oc->y = -1; oc->x = oc->y = -1;
oc->scale = 1; oc->scale = 1;
oc->scale_filter = SCALE_FILTER_DEFAULT;
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
oc->subpixel = output->detected_subpixel; oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;

View file

@ -1,9 +1,11 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <assert.h> #include <assert.h>
#include <GLES2/gl2.h>
#include <stdlib.h> #include <stdlib.h>
#include <strings.h> #include <strings.h>
#include <time.h> #include <time.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_buffer.h>
@ -70,6 +72,28 @@ static void scissor_output(struct wlr_output *wlr_output,
wlr_renderer_scissor(renderer, &box); wlr_renderer_scissor(renderer, &box);
} }
static void set_scale_filter(struct wlr_output *wlr_output,
struct wlr_texture *texture, enum scale_filter_mode scale_filter) {
if (!wlr_texture_is_gles2(texture)) {
return;
}
struct wlr_gles2_texture_attribs attribs;
wlr_gles2_texture_get_attribs(texture, &attribs);
switch (scale_filter) {
case SCALE_FILTER_LINEAR:
glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
break;
case SCALE_FILTER_NEAREST:
glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
break;
case SCALE_FILTER_DEFAULT:
case SCALE_FILTER_SMART:
assert(false); // unreachable
}
}
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_box *box, const float matrix[static 9], float alpha) { const struct wlr_box *box, const float matrix[static 9], float alpha) {
@ -119,6 +143,7 @@ static void render_surface_iterator(struct sway_output *output, struct sway_view
wlr_matrix_project_box(matrix, &box, transform, rotation, wlr_matrix_project_box(matrix, &box, transform, rotation,
wlr_output->transform_matrix); wlr_output->transform_matrix);
set_scale_filter(wlr_output, texture, output->scale_filter);
render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); render_texture(wlr_output, output_damage, texture, &box, matrix, alpha);
wlr_presentation_surface_sampled_on_output(server.presentation, surface, wlr_presentation_surface_sampled_on_output(server.presentation, surface,
@ -268,6 +293,7 @@ static void render_saved_view(struct sway_view *view,
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
wlr_output->transform_matrix); wlr_output->transform_matrix);
set_scale_filter(wlr_output, view->saved_buffer->texture, output->scale_filter);
render_texture(wlr_output, damage, view->saved_buffer->texture, render_texture(wlr_output, damage, view->saved_buffer->texture,
&box, matrix, alpha); &box, matrix, alpha);

View file

@ -177,6 +177,9 @@ static void ipc_json_describe_output(struct sway_output *output,
json_object_new_string(wlr_output->serial)); json_object_new_string(wlr_output->serial));
json_object_object_add(object, "scale", json_object_object_add(object, "scale",
json_object_new_double(wlr_output->scale)); json_object_new_double(wlr_output->scale));
json_object_object_add(object, "scale_filter",
json_object_new_string(
sway_output_scale_filter_to_string(output->scale_filter)));
json_object_object_add(object, "transform", json_object_object_add(object, "transform",
json_object_new_string( json_object_new_string(
ipc_json_output_transform_description(wlr_output->transform))); ipc_json_output_transform_description(wlr_output->transform)));

View file

@ -182,6 +182,7 @@ sway_sources = files(
'commands/output/mode.c', 'commands/output/mode.c',
'commands/output/position.c', 'commands/output/position.c',
'commands/output/scale.c', 'commands/output/scale.c',
'commands/output/scale_filter.c',
'commands/output/subpixel.c', 'commands/output/subpixel.c',
'commands/output/toggle.c', 'commands/output/toggle.c',
'commands/output/transform.c', 'commands/output/transform.c',
@ -203,6 +204,7 @@ sway_deps = [
math, math,
pango, pango,
pcre, pcre,
glesv2,
pixman, pixman,
server_protos, server_protos,
wayland_server, wayland_server,

View file

@ -70,6 +70,14 @@ must be separated by one space. For example:
applications to taste. HiDPI isn't supported with Xwayland clients (windows applications to taste. HiDPI isn't supported with Xwayland clients (windows
will blur). will blur).
*output* <name> scale_filter linear|nearest|smart
Indicates how to scale application buffers that are rendered at a scale
lower than the output's configured scale, such as lo-dpi applications on
hi-dpi screens. Linear is smoother and blurrier, nearest (also known as
nearest neighbor) is sharper and blockier. Setting "smart" will apply
nearest scaling when the output has an integer scale factor, otherwise
linear. The default is "smart".
*output* <name> subpixel rgb|bgr|vrgb|vbgr|none *output* <name> subpixel rgb|bgr|vrgb|vbgr|none
Manually sets the subpixel hinting for the specified output. This value is Manually sets the subpixel hinting for the specified output. This value is
usually auto-detected, but some displays may misreport their subpixel usually auto-detected, but some displays may misreport their subpixel

View file

@ -93,6 +93,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
output->wlr_output = wlr_output; output->wlr_output = wlr_output;
wlr_output->data = output; wlr_output->data = output;
output->detected_subpixel = wlr_output->subpixel; output->detected_subpixel = wlr_output->subpixel;
output->scale_filter = SCALE_FILTER_NEAREST;
wl_signal_init(&output->events.destroy); wl_signal_init(&output->events.destroy);

View file

@ -189,11 +189,13 @@ static void pretty_print_output(json_object *o) {
json_object_object_get_ex(o, "focused", &focused); json_object_object_get_ex(o, "focused", &focused);
json_object_object_get_ex(o, "active", &active); json_object_object_get_ex(o, "active", &active);
json_object_object_get_ex(o, "current_workspace", &ws); json_object_object_get_ex(o, "current_workspace", &ws);
json_object *make, *model, *serial, *scale, *subpixel, *transform, *max_render_time; json_object *make, *model, *serial, *scale, *scale_filter, *subpixel,
*transform, *max_render_time;
json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "make", &make);
json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "model", &model);
json_object_object_get_ex(o, "serial", &serial); json_object_object_get_ex(o, "serial", &serial);
json_object_object_get_ex(o, "scale", &scale); json_object_object_get_ex(o, "scale", &scale);
json_object_object_get_ex(o, "scale_filter", &scale_filter);
json_object_object_get_ex(o, "subpixel_hinting", &subpixel); json_object_object_get_ex(o, "subpixel_hinting", &subpixel);
json_object_object_get_ex(o, "transform", &transform); json_object_object_get_ex(o, "transform", &transform);
json_object_object_get_ex(o, "max_render_time", &max_render_time); json_object_object_get_ex(o, "max_render_time", &max_render_time);
@ -214,6 +216,7 @@ static void pretty_print_output(json_object *o) {
" Current mode: %dx%d @ %f Hz\n" " Current mode: %dx%d @ %f Hz\n"
" Position: %d,%d\n" " Position: %d,%d\n"
" Scale factor: %f\n" " Scale factor: %f\n"
" Scale filter: %s\n"
" Subpixel hinting: %s\n" " Subpixel hinting: %s\n"
" Transform: %s\n" " Transform: %s\n"
" Workspace: %s\n" " Workspace: %s\n"
@ -228,6 +231,7 @@ static void pretty_print_output(json_object *o) {
(float)json_object_get_int(refresh) / 1000, (float)json_object_get_int(refresh) / 1000,
json_object_get_int(x), json_object_get_int(y), json_object_get_int(x), json_object_get_int(y),
json_object_get_double(scale), json_object_get_double(scale),
json_object_get_string(scale_filter),
json_object_get_string(subpixel), json_object_get_string(subpixel),
json_object_get_string(transform), json_object_get_string(transform),
json_object_get_string(ws) json_object_get_string(ws)