mirror of
https://github.com/swaywm/sway.git
synced 2024-11-29 19:31:29 +00:00
add scale_filter output config option
This commit is contained in:
parent
4b57953628
commit
6968fb3123
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
34
sway/commands/output/scale_filter.c
Normal file
34
sway/commands/output/scale_filter.c
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)));
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue