Add support for manually setting subpixel hinting on outputs.

Many laptop screens report unknown subpixel order. Allow users to manually set subpixel hinting to work around this.

Addresses https://github.com/swaywm/sway/issues/3163
This commit is contained in:
Geoff Greer 2019-02-10 16:56:57 -08:00 committed by emersion
parent 200833caae
commit 6e3046878d
14 changed files with 102 additions and 5 deletions

View file

@ -4,6 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <wayland-server-protocol.h>
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
@ -54,3 +55,23 @@ float parse_float(const char *value) {
} }
return flt; return flt;
} }
const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) {
switch (subpixel) {
case WL_OUTPUT_SUBPIXEL_UNKNOWN:
return "unknown";
case WL_OUTPUT_SUBPIXEL_NONE:
return "none";
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
return "rgb";
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
return "bgr";
case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
return "vrgb";
case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
return "vbgr";
}
sway_assert(false, "Unknown value for wl_output_subpixel.");
return NULL;
}

View file

@ -261,6 +261,7 @@ sway_cmd output_cmd_enable;
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_subpixel;
sway_cmd output_cmd_transform; sway_cmd output_cmd_transform;
sway_cmd seat_cmd_attach; sway_cmd seat_cmd_attach;

View file

@ -184,6 +184,7 @@ struct output_config {
int x, y; int x, y;
float scale; float scale;
int32_t transform; int32_t transform;
enum wl_output_subpixel subpixel;
char *background; char *background;
char *background_option; char *background_option;

View file

@ -31,6 +31,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;
bool enabled, configured; bool enabled, configured;
list_t *workspaces; list_t *workspaces;

View file

@ -3,6 +3,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <wayland-server-protocol.h>
/** /**
* Wrap i into the range [0, max[ * Wrap i into the range [0, max[
@ -29,4 +30,6 @@ bool parse_boolean(const char *boolean, bool current);
*/ */
float parse_float(const char *value); float parse_float(const char *value);
const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel);
#endif #endif

View file

@ -18,6 +18,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 },
{ "subpixel", output_cmd_subpixel },
{ "transform", output_cmd_transform }, { "transform", output_cmd_transform },
}; };

View file

@ -0,0 +1,36 @@
#include <string.h>
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
struct cmd_results *output_cmd_subpixel(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 subpixel argument.");
}
enum wl_output_subpixel subpixel;
if (strcmp(*argv, "rgb") == 0) {
subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
} else if (strcmp(*argv, "bgr") == 0) {
subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
} else if (strcmp(*argv, "vrgb") == 0) {
subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
} else if (strcmp(*argv, "vbgr") == 0) {
subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
} else if (strcmp(*argv, "none") == 0) {
subpixel = WL_OUTPUT_SUBPIXEL_NONE;
} else {
return cmd_results_new(CMD_INVALID, "Invalid output subpixel.");
}
struct output_config *oc = config->handler_context.output_config;
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
oc->subpixel = subpixel;
return NULL;
}

View file

@ -8,10 +8,11 @@
#include <unistd.h> #include <unistd.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include "log.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "log.h"
#include "util.h"
int output_name_cmp(const void *item, const void *data) { int output_name_cmp(const void *item, const void *data) {
const struct output_config *output = item; const struct output_config *output = item;
@ -43,6 +44,7 @@ struct output_config *new_output_config(const char *name) {
oc->x = oc->y = -1; oc->x = oc->y = -1;
oc->scale = -1; oc->scale = -1;
oc->transform = -1; oc->transform = -1;
oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
return oc; return oc;
} }
@ -65,6 +67,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->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
dst->subpixel = src->subpixel;
}
if (src->refresh_rate != -1) { if (src->refresh_rate != -1) {
dst->refresh_rate = src->refresh_rate; dst->refresh_rate = src->refresh_rate;
} }
@ -187,10 +192,10 @@ struct output_config *store_output_config(struct output_config *oc) {
} }
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
oc->x, oc->y, oc->scale, oc->transform, oc->background, oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->background_option, oc->dpms_state); oc->transform, oc->background, oc->background_option, oc->dpms_state);
return oc; return oc;
} }
@ -363,6 +368,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
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);
} }
if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
sway_wl_output_subpixel_to_string(oc->subpixel));
wlr_output_set_subpixel(wlr_output, oc->subpixel);
output_damage_whole(output);
}
if (oc && oc->transform >= 0) { if (oc && oc->transform >= 0) {
sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform);
wlr_output_set_transform(wlr_output, oc->transform); wlr_output_set_transform(wlr_output, oc->transform);
@ -424,6 +437,8 @@ 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;
struct sway_output *output = wlr_output->data;
oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
oc->dpms_state = DPMS_ON; oc->dpms_state = DPMS_ON;
} }

View file

@ -644,6 +644,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
json_object_object_add(output_json, "focused", json_object_object_add(output_json, "focused",
json_object_new_boolean(focused)); json_object_new_boolean(focused));
const char *subpixel = sway_wl_output_subpixel_to_string(output->wlr_output->subpixel);
json_object_object_add(output_json, "subpixel_hinting", json_object_new_string(subpixel));
json_object_array_add(outputs, output_json); json_object_array_add(outputs, output_json);
} }
struct sway_output *output; struct sway_output *output;

View file

@ -169,6 +169,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/subpixel.c',
'commands/output/transform.c', 'commands/output/transform.c',
'tree/arrange.c', 'tree/arrange.c',

View file

@ -211,6 +211,9 @@ following properties:
|- scale |- scale
: float : float
: The scale currently in use on the output or _-1_ for disabled outputs : The scale currently in use on the output or _-1_ for disabled outputs
|- subpixel_hinting
: string
: The subpixel hinting current in use on the output. This can be _rgb_, _bgr_, _vrgb_, _vbgr_, or _none_
|- transform |- transform
: string : string
: The transform currently in use for the output. This can be _normal_, _90_, : The transform currently in use for the output. This can be _normal_, _90_,
@ -242,6 +245,7 @@ following properties:
"active": true, "active": true,
"primary": false, "primary": false,
"scale": 1.0, "scale": 1.0,
"subpixel_hinting": "rgb",
"transform": "normal", "transform": "normal",
"current_workspace": "1", "current_workspace": "1",
"modes": [ "modes": [

View file

@ -64,6 +64,13 @@ 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> subpixel rgb|bgr|vrgb|vbgr|none
Manually sets the subpixel hinting for the specified output. This value is
usually auto-detected, but some displays may misreport their subpixel
geometry. Using the correct subpixel hinting allows for sharper text.
Incorrect values will result in blurrier text. When changing this via
*swaymsg*, some applications may need to be restarted to use the new value.
*output* <name> background|bg <file> <mode> [<fallback_color>] *output* <name> background|bg <file> <mode> [<fallback_color>]
Sets the wallpaper for the given output to the specified file, using the Sets the wallpaper for the given output to the specified file, using the
given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If

View file

@ -92,6 +92,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
node_init(&output->node, N_OUTPUT, output); node_init(&output->node, N_OUTPUT, 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;
wl_signal_init(&output->events.destroy); wl_signal_init(&output->events.destroy);

View file

@ -186,11 +186,12 @@ 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, *transform; json_object *make, *model, *serial, *scale, *subpixel, *transform;
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, "subpixel_hinting", &subpixel);
json_object_object_get_ex(o, "transform", &transform); json_object_object_get_ex(o, "transform", &transform);
json_object *x, *y; json_object *x, *y;
json_object_object_get_ex(rect, "x", &x); json_object_object_get_ex(rect, "x", &x);
@ -209,6 +210,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"
" Subpixel hinting: %s\n"
" Transform: %s\n" " Transform: %s\n"
" Workspace: %s\n", " Workspace: %s\n",
json_object_get_string(name), json_object_get_string(name),
@ -221,6 +223,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(subpixel),
json_object_get_string(transform), json_object_get_string(transform),
json_object_get_string(ws) json_object_get_string(ws)
); );