Merge branch 'brightness' into 'master'

Add brightness support

See merge request wlroots/wlroots!5384
This commit is contained in:
Simon Ser 2026-06-13 14:43:07 +00:00
commit 5e94bd284e
11 changed files with 129 additions and 4 deletions

View file

@ -394,6 +394,11 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo
return false; return false;
} }
double brightness = (state->base->committed & WLR_OUTPUT_STATE_BRIGHTNESS) ?
state->base->brightness : output->brightness;
uint64_t luminance = conn->luminance_bounds[0] +
round(brightness * (conn->luminance_bounds[1] - conn->luminance_bounds[0]));
state->mode_id = mode_id; state->mode_id = mode_id;
state->gamma_lut = gamma_lut; state->gamma_lut = gamma_lut;
state->fb_damage_clips = fb_damage_clips; state->fb_damage_clips = fb_damage_clips;
@ -401,6 +406,7 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo
state->vrr_enabled = vrr_enabled; state->vrr_enabled = vrr_enabled;
state->colorspace = colorspace; state->colorspace = colorspace;
state->hdr_output_metadata = hdr_output_metadata; state->hdr_output_metadata = hdr_output_metadata;
state->luminance = luminance;
return true; return true;
} }
@ -573,6 +579,9 @@ static void atomic_connector_add(struct atomic *atom,
if (conn->props.hdr_output_metadata != 0) { if (conn->props.hdr_output_metadata != 0) {
atomic_add(atom, conn->id, conn->props.hdr_output_metadata, state->hdr_output_metadata); atomic_add(atom, conn->id, conn->props.hdr_output_metadata, state->hdr_output_metadata);
} }
if (conn->props.luminance != 0) {
atomic_add(atom, conn->id, conn->props.luminance, state->luminance);
}
atomic_add(atom, crtc->id, crtc->props.mode_id, state->mode_id); atomic_add(atom, crtc->id, crtc->props.mode_id, state->mode_id);
atomic_add(atom, crtc->id, crtc->props.active, active); atomic_add(atom, crtc->id, crtc->props.active, active);
if (active) { if (active) {

View file

@ -44,7 +44,8 @@ static const uint32_t COMMIT_OUTPUT_STATE =
WLR_OUTPUT_STATE_SIGNAL_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE |
WLR_OUTPUT_STATE_COLOR_TRANSFORM | WLR_OUTPUT_STATE_COLOR_TRANSFORM |
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION | WLR_OUTPUT_STATE_IMAGE_DESCRIPTION |
WLR_OUTPUT_STATE_COLOR_REPRESENTATION; WLR_OUTPUT_STATE_COLOR_REPRESENTATION |
WLR_OUTPUT_STATE_BRIGHTNESS;
static const uint32_t SUPPORTED_OUTPUT_STATE = static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE;
@ -118,6 +119,12 @@ bool check_drm_features(struct wlr_drm_backend *drm) {
wlr_log(WLR_INFO, "DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT supported"); wlr_log(WLR_INFO, "DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT supported");
} }
#endif #endif
#define DRM_CLIENT_CAP_LUMINANCE 8
#ifdef DRM_CLIENT_CAP_LUMINANCE
if (drm->iface == &atomic_iface && drmSetClientCap(drm->fd, DRM_CLIENT_CAP_LUMINANCE, 1) == 0) {
wlr_log(WLR_INFO, "DRM_CLIENT_CAP_LUMINANCE supported");
}
#endif
if (drm->iface == &legacy_iface) { if (drm->iface == &legacy_iface) {
drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1;
@ -1736,6 +1743,15 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn,
} }
output->adaptive_sync_supported = vrr_capable; output->adaptive_sync_supported = vrr_capable;
if (wlr_conn->props.luminance != 0) {
if (introspect_drm_prop_range(drm->fd, wlr_conn->props.luminance,
&wlr_conn->luminance_bounds[0], &wlr_conn->luminance_bounds[1])) {
output->brightness_supported = true;
} else {
wlr_log(WLR_ERROR, "Failed to introspect 'LUMINANCE' property");
}
}
size_t edid_len = 0; size_t edid_len = 0;
uint8_t *edid = get_drm_prop_blob(drm->fd, uint8_t *edid = get_drm_prop_blob(drm->fd,
wlr_conn->id, wlr_conn->props.edid, &edid_len); wlr_conn->id, wlr_conn->props.edid, &edid_len);

View file

@ -26,6 +26,7 @@ static const struct prop_info connector_info[] = {
{ "DPMS", INDEX(dpms) }, { "DPMS", INDEX(dpms) },
{ "EDID", INDEX(edid) }, { "EDID", INDEX(edid) },
{ "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) }, { "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) },
{ "LUMINANCE", INDEX(luminance) },
{ "PATH", INDEX(path) }, { "PATH", INDEX(path) },
{ "content type", INDEX(content_type) }, { "content type", INDEX(content_type) },
{ "link-status", INDEX(link_status) }, { "link-status", INDEX(link_status) },

View file

@ -164,6 +164,7 @@ struct wlr_drm_connector_state {
bool vrr_enabled; bool vrr_enabled;
uint32_t colorspace; uint32_t colorspace;
uint32_t hdr_output_metadata; uint32_t hdr_output_metadata;
uint64_t luminance;
}; };
/** /**
@ -199,6 +200,7 @@ struct wlr_drm_connector {
drmModeConnection status; drmModeConnection status;
uint32_t id; uint32_t id;
uint64_t max_bpc_bounds[2]; uint64_t max_bpc_bounds[2];
uint64_t luminance_bounds[2];
struct wlr_drm_lease *lease; struct wlr_drm_lease *lease;
struct wlr_drm_crtc *crtc; struct wlr_drm_crtc *crtc;

View file

@ -28,6 +28,7 @@ struct wlr_drm_connector_props {
uint32_t crtc_id; uint32_t crtc_id;
uint32_t colorspace; uint32_t colorspace;
uint32_t hdr_output_metadata; uint32_t hdr_output_metadata;
uint32_t luminance;
}; };
struct wlr_drm_crtc_props { struct wlr_drm_crtc_props {

View file

@ -78,6 +78,7 @@ enum wlr_output_state_field {
WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12,
WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13,
WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14, WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14,
WLR_OUTPUT_STATE_BRIGHTNESS = 1 << 15,
}; };
enum wlr_output_state_mode_type { enum wlr_output_state_mode_type {
@ -165,6 +166,8 @@ struct wlr_output_state {
struct wlr_color_transform *color_transform; struct wlr_color_transform *color_transform;
struct wlr_output_image_description *image_description; struct wlr_output_image_description *image_description;
float brightness;
}; };
struct wlr_output_impl; struct wlr_output_impl;
@ -213,11 +216,14 @@ struct wlr_output {
enum wlr_color_encoding color_encoding; enum wlr_color_encoding color_encoding;
enum wlr_color_range color_range; enum wlr_color_range color_range;
const struct wlr_output_image_description *image_description; const struct wlr_output_image_description *image_description;
float brightness;
// Indicates whether making changes to adaptive sync status is supported. // Indicates whether making changes to adaptive sync status is supported.
// If false, changes to adaptive sync status is guaranteed to fail. If // If false, changes to adaptive sync status is guaranteed to fail. If
// true, changes may either succeed or fail. // true, changes may either succeed or fail.
bool adaptive_sync_supported; bool adaptive_sync_supported;
// Indicates whether brightness changes are supported.
bool brightness_supported;
bool needs_frame; bool needs_frame;
// damage for cursors and fullscreen surface, in output-local coordinates // damage for cursors and fullscreen surface, in output-local coordinates
@ -632,6 +638,8 @@ void wlr_output_state_set_color_transform(struct wlr_output_state *state,
bool wlr_output_state_set_image_description(struct wlr_output_state *state, bool wlr_output_state_set_image_description(struct wlr_output_state *state,
const struct wlr_output_image_description *image_desc); const struct wlr_output_image_description *image_desc);
void wlr_output_state_set_brightness(struct wlr_output_state *state, float value);
/** /**
* Set the color encoding and range of the primary scanout buffer. * Set the color encoding and range of the primary scanout buffer.
* *

View file

@ -10,6 +10,7 @@ struct wlr_output_power_manager_v1 {
struct { struct {
struct wl_signal set_mode; // struct wlr_output_power_v1_set_mode_event struct wl_signal set_mode; // struct wlr_output_power_v1_set_mode_event
struct wl_signal set_brightness; // struct wlr_output_power_v1_set_brightness_event
struct wl_signal destroy; struct wl_signal destroy;
} events; } events;
@ -39,6 +40,11 @@ struct wlr_output_power_v1_set_mode_event {
enum zwlr_output_power_v1_mode mode; enum zwlr_output_power_v1_mode mode;
}; };
struct wlr_output_power_v1_set_brightness_event {
struct wlr_output *output;
float value;
};
struct wlr_output_power_manager_v1 *wlr_output_power_manager_v1_create( struct wlr_output_power_manager_v1 *wlr_output_power_manager_v1_create(
struct wl_display *display); struct wl_display *display);

View file

@ -42,7 +42,7 @@
interface version number is reset. interface version number is reset.
</description> </description>
<interface name="zwlr_output_power_manager_v1" version="1"> <interface name="zwlr_output_power_manager_v1" version="2">
<description summary="manager to create per-output power management"> <description summary="manager to create per-output power management">
This interface is a manager that allows creating per-output power This interface is a manager that allows creating per-output power
management mode controls. management mode controls.
@ -65,7 +65,7 @@
</request> </request>
</interface> </interface>
<interface name="zwlr_output_power_v1" version="1"> <interface name="zwlr_output_power_v1" version="2">
<description summary="adjust power management mode for an output"> <description summary="adjust power management mode for an output">
This object offers requests to set the power management mode of This object offers requests to set the power management mode of
an output. an output.
@ -124,5 +124,31 @@
Destroys the output power management mode control object. Destroys the output power management mode control object.
</description> </description>
</request> </request>
<!-- Version 2 additions -->
<event name="brightness" since="2">
<description summary="current brightness state">
This event advertises the output's current brightness value.
The brightness is an integer between 0 and UINT32_MAX. 0 indicates
lowest light level (while still being powered on) and UINT32_MAX
indicates highest light level.
The brightness event is sent immediately when the object is created if
the output supports brightness. This event is also sent when the
brightness changes.
</description>
<arg name="value" type="uint" summary="brightness value between 0 and UINT32_MAX"/>
</event>
<request name="set_brightness" since="2">
<description summary="set brightness">
Set an output's brightness to the given value. The brightness change
is effective immediately. If the output does not support setting
brightness, a failed event is sent.
</description>
<arg name="value" type="uint" summary="brightness value between 0 and UINT32_MAX"/>
</request>
</interface> </interface>
</protocol> </protocol>

View file

@ -261,6 +261,10 @@ static void output_apply_state(struct wlr_output *output,
} }
} }
if (state->committed & WLR_OUTPUT_STATE_BRIGHTNESS) {
output->brightness = state->brightness;
}
bool geometry_updated = state->committed & bool geometry_updated = state->committed &
(WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM |
WLR_OUTPUT_STATE_SUBPIXEL); WLR_OUTPUT_STATE_SUBPIXEL);
@ -359,6 +363,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
.render_format = DRM_FORMAT_XRGB8888, .render_format = DRM_FORMAT_XRGB8888,
.transform = WL_OUTPUT_TRANSFORM_NORMAL, .transform = WL_OUTPUT_TRANSFORM_NORMAL,
.scale = 1, .scale = 1,
.brightness = 1,
.commit_seq = 0, .commit_seq = 0,
}; };
@ -590,6 +595,10 @@ static uint32_t output_compare_state(struct wlr_output *output,
output->color_range == state->color_range) { output->color_range == state->color_range) {
fields |= WLR_OUTPUT_STATE_COLOR_REPRESENTATION; fields |= WLR_OUTPUT_STATE_COLOR_REPRESENTATION;
} }
if ((state->committed & WLR_OUTPUT_STATE_BRIGHTNESS) &&
output->brightness == state->brightness) {
fields |= WLR_OUTPUT_STATE_BRIGHTNESS;
}
return fields; return fields;
} }
@ -687,6 +696,7 @@ static bool output_basic_test(struct wlr_output *output,
{ WLR_OUTPUT_STATE_SUBPIXEL, "subpixel" }, { WLR_OUTPUT_STATE_SUBPIXEL, "subpixel" },
{ WLR_OUTPUT_STATE_COLOR_TRANSFORM, "color transform" }, { WLR_OUTPUT_STATE_COLOR_TRANSFORM, "color transform" },
{ WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" }, { WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" },
{ WLR_OUTPUT_STATE_BRIGHTNESS, "brightness" },
}; };
if (!enabled) { if (!enabled) {
for (size_t i = 0; i < sizeof(needs_enabled) / sizeof(needs_enabled[0]); i++) { for (size_t i = 0; i < sizeof(needs_enabled) / sizeof(needs_enabled[0]); i++) {
@ -726,6 +736,12 @@ static bool output_basic_test(struct wlr_output *output,
} }
} }
if ((state->committed & WLR_OUTPUT_STATE_BRIGHTNESS) &&
!output->brightness_supported) {
wlr_log(WLR_DEBUG, "Brightness is not supported for this output");
return false;
}
return true; return true;
} }

View file

@ -149,6 +149,11 @@ void wlr_output_state_set_color_encoding_and_range(
state->color_range = range; state->color_range = range;
} }
void wlr_output_state_set_brightness(struct wlr_output_state *state, float value) {
state->committed |= WLR_OUTPUT_STATE_BRIGHTNESS;
state->brightness = value;
}
bool wlr_output_state_copy(struct wlr_output_state *dst, bool wlr_output_state_copy(struct wlr_output_state *dst,
const struct wlr_output_state *src) { const struct wlr_output_state *src) {
struct wlr_output_state copy = *src; struct wlr_output_state copy = *src;

View file

@ -9,7 +9,7 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "wlr-output-power-management-unstable-v1-protocol.h" #include "wlr-output-power-management-unstable-v1-protocol.h"
#define OUTPUT_POWER_MANAGER_V1_VERSION 1 #define OUTPUT_POWER_MANAGER_V1_VERSION 2
static void output_power_handle_destroy(struct wl_client *client, static void output_power_handle_destroy(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
@ -58,6 +58,11 @@ static void output_power_v1_send_mode(struct wlr_output_power_v1 *output_power)
zwlr_output_power_v1_send_mode(output_power->resource, mode); zwlr_output_power_v1_send_mode(output_power->resource, mode);
} }
static void output_power_v1_send_brightness(struct wlr_output_power_v1 *output_power) {
zwlr_output_power_v1_send_brightness(output_power->resource,
round((double)output_power->output->brightness * UINT32_MAX));
}
static void output_power_handle_output_commit(struct wl_listener *listener, static void output_power_handle_output_commit(struct wl_listener *listener,
void *data) { void *data) {
struct wlr_output_power_v1 *output_power = struct wlr_output_power_v1 *output_power =
@ -66,6 +71,9 @@ static void output_power_handle_output_commit(struct wl_listener *listener,
if (event->state->committed & WLR_OUTPUT_STATE_ENABLED) { if (event->state->committed & WLR_OUTPUT_STATE_ENABLED) {
output_power_v1_send_mode(output_power); output_power_v1_send_mode(output_power);
} }
if (event->state->committed & WLR_OUTPUT_STATE_BRIGHTNESS) {
output_power_v1_send_brightness(output_power);
}
} }
static void output_power_handle_set_mode(struct wl_client *client, static void output_power_handle_set_mode(struct wl_client *client,
@ -93,9 +101,31 @@ static void output_power_handle_set_mode(struct wl_client *client,
wl_signal_emit_mutable(&output_power->manager->events.set_mode, &event); wl_signal_emit_mutable(&output_power->manager->events.set_mode, &event);
} }
static void output_power_handle_set_brightness(struct wl_client *client,
struct wl_resource *output_power_resource,
uint32_t value_u32) {
struct wlr_output_power_v1 *output_power =
output_power_from_resource(output_power_resource);
if (output_power == NULL) {
return;
}
if (!output_power->output->brightness_supported) {
zwlr_output_power_v1_send_failed(output_power->resource);
return;
}
struct wlr_output_power_v1_set_brightness_event event = {
.output = output_power->output,
.value = (double)value_u32 / UINT32_MAX,
};
wl_signal_emit_mutable(&output_power->manager->events.set_brightness, &event);
}
static const struct zwlr_output_power_v1_interface output_power_impl = { static const struct zwlr_output_power_v1_interface output_power_impl = {
.destroy = output_power_handle_destroy, .destroy = output_power_handle_destroy,
.set_mode = output_power_handle_set_mode, .set_mode = output_power_handle_set_mode,
.set_brightness = output_power_handle_set_brightness,
}; };
static const struct zwlr_output_power_manager_v1_interface static const struct zwlr_output_power_manager_v1_interface
@ -162,6 +192,9 @@ static void output_power_manager_get_output_power(struct wl_client *client,
wl_list_insert(&manager->output_powers, &output_power->link); wl_list_insert(&manager->output_powers, &output_power->link);
output_power_v1_send_mode(output_power); output_power_v1_send_mode(output_power);
if (output->brightness_supported) {
output_power_v1_send_brightness(output_power);
}
} }
static void output_power_manager_destroy(struct wl_client *client, static void output_power_manager_destroy(struct wl_client *client,
@ -195,6 +228,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
wl_signal_emit_mutable(&manager->events.destroy, manager); wl_signal_emit_mutable(&manager->events.destroy, manager);
assert(wl_list_empty(&manager->events.set_mode.listener_list)); assert(wl_list_empty(&manager->events.set_mode.listener_list));
assert(wl_list_empty(&manager->events.set_brightness.listener_list));
assert(wl_list_empty(&manager->events.destroy.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_global_destroy(manager->global); wl_global_destroy(manager->global);
@ -217,6 +251,7 @@ struct wlr_output_power_manager_v1 *wlr_output_power_manager_v1_create(
} }
wl_signal_init(&manager->events.set_mode); wl_signal_init(&manager->events.set_mode);
wl_signal_init(&manager->events.set_brightness);
wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.destroy);
wl_list_init(&manager->output_powers); wl_list_init(&manager->output_powers);