diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index daa8ba9bf..7a2ff5e37 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -394,6 +394,11 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo 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->gamma_lut = gamma_lut; 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->colorspace = colorspace; state->hdr_output_metadata = hdr_output_metadata; + state->luminance = luminance; return true; } @@ -573,6 +579,9 @@ static void atomic_connector_add(struct atomic *atom, if (conn->props.hdr_output_metadata != 0) { 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.active, active); if (active) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index d2f75f71f..6f66d23e8 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -44,7 +44,8 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_SIGNAL_TIMELINE | WLR_OUTPUT_STATE_COLOR_TRANSFORM | 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 = 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"); } #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) { 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; + 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; uint8_t *edid = get_drm_prop_blob(drm->fd, wlr_conn->id, wlr_conn->props.edid, &edid_len); diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 4c6bdcb36..9336209d4 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -26,6 +26,7 @@ static const struct prop_info connector_info[] = { { "DPMS", INDEX(dpms) }, { "EDID", INDEX(edid) }, { "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) }, + { "LUMINANCE", INDEX(luminance) }, { "PATH", INDEX(path) }, { "content type", INDEX(content_type) }, { "link-status", INDEX(link_status) }, diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index a8c5e077a..ef294e1c5 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -164,6 +164,7 @@ struct wlr_drm_connector_state { bool vrr_enabled; uint32_t colorspace; uint32_t hdr_output_metadata; + uint64_t luminance; }; /** @@ -199,6 +200,7 @@ struct wlr_drm_connector { drmModeConnection status; uint32_t id; uint64_t max_bpc_bounds[2]; + uint64_t luminance_bounds[2]; struct wlr_drm_lease *lease; struct wlr_drm_crtc *crtc; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 20e0a5bff..bd6bed641 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -28,6 +28,7 @@ struct wlr_drm_connector_props { uint32_t crtc_id; uint32_t colorspace; uint32_t hdr_output_metadata; + uint32_t luminance; }; struct wlr_drm_crtc_props { diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index c8e44b0e6..ca3ed90e3 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -78,6 +78,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, WLR_OUTPUT_STATE_COLOR_REPRESENTATION = 1 << 14, + WLR_OUTPUT_STATE_BRIGHTNESS = 1 << 15, }; enum wlr_output_state_mode_type { @@ -165,6 +166,8 @@ struct wlr_output_state { struct wlr_color_transform *color_transform; struct wlr_output_image_description *image_description; + + float brightness; }; struct wlr_output_impl; @@ -213,11 +216,14 @@ struct wlr_output { enum wlr_color_encoding color_encoding; enum wlr_color_range color_range; const struct wlr_output_image_description *image_description; + float brightness; // Indicates whether making changes to adaptive sync status is supported. // If false, changes to adaptive sync status is guaranteed to fail. If // true, changes may either succeed or fail. bool adaptive_sync_supported; + // Indicates whether brightness changes are supported. + bool brightness_supported; bool needs_frame; // 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, 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. * diff --git a/include/wlr/types/wlr_output_power_management_v1.h b/include/wlr/types/wlr_output_power_management_v1.h index ef8da8e55..f093bdd3a 100644 --- a/include/wlr/types/wlr_output_power_management_v1.h +++ b/include/wlr/types/wlr_output_power_management_v1.h @@ -10,6 +10,7 @@ struct wlr_output_power_manager_v1 { struct { 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; } events; @@ -39,6 +40,11 @@ struct wlr_output_power_v1_set_mode_event { 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 wl_display *display); diff --git a/protocol/wlr-output-power-management-unstable-v1.xml b/protocol/wlr-output-power-management-unstable-v1.xml index 20dbb7760..72b4fac39 100644 --- a/protocol/wlr-output-power-management-unstable-v1.xml +++ b/protocol/wlr-output-power-management-unstable-v1.xml @@ -42,7 +42,7 @@ interface version number is reset. - + This interface is a manager that allows creating per-output power management mode controls. @@ -65,7 +65,7 @@ - + This object offers requests to set the power management mode of an output. @@ -124,5 +124,31 @@ Destroys the output power management mode control object. + + + + + + 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. + + + + + + + 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. + + + diff --git a/types/output/output.c b/types/output/output.c index 46da1f425..273cbfed3 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -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 & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | 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, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .scale = 1, + .brightness = 1, .commit_seq = 0, }; @@ -590,6 +595,10 @@ static uint32_t output_compare_state(struct wlr_output *output, output->color_range == state->color_range) { fields |= WLR_OUTPUT_STATE_COLOR_REPRESENTATION; } + if ((state->committed & WLR_OUTPUT_STATE_BRIGHTNESS) && + output->brightness == state->brightness) { + fields |= WLR_OUTPUT_STATE_BRIGHTNESS; + } return fields; } @@ -687,6 +696,7 @@ static bool output_basic_test(struct wlr_output *output, { WLR_OUTPUT_STATE_SUBPIXEL, "subpixel" }, { WLR_OUTPUT_STATE_COLOR_TRANSFORM, "color transform" }, { WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" }, + { WLR_OUTPUT_STATE_BRIGHTNESS, "brightness" }, }; if (!enabled) { 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; } diff --git a/types/output/state.c b/types/output/state.c index 5f44c18e8..f62f3ab87 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -149,6 +149,11 @@ void wlr_output_state_set_color_encoding_and_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, const struct wlr_output_state *src) { struct wlr_output_state copy = *src; diff --git a/types/wlr_output_power_management_v1.c b/types/wlr_output_power_management_v1.c index 2a9948393..b1fa70e21 100644 --- a/types/wlr_output_power_management_v1.c +++ b/types/wlr_output_power_management_v1.c @@ -9,7 +9,7 @@ #include #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, 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); } +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, void *data) { 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) { 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, @@ -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); } +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 = { .destroy = output_power_handle_destroy, .set_mode = output_power_handle_set_mode, + .set_brightness = output_power_handle_set_brightness, }; 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); 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, @@ -195,6 +228,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { 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_brightness.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list)); 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_brightness); wl_signal_init(&manager->events.destroy); wl_list_init(&manager->output_powers);