Skip to content

Conversation

@DisDis
Copy link
Contributor

@DisDis DisDis commented May 1, 2025

I added kms drm property set function.
The code is dirty, but it works.
Any suggestions for improvement?

It allows turn off/on screen.

For my case I use HDMI-0.
kmsprint -p
show:

Connector 0 (33) HDMI-A-1 (connected)
    EDID (1) = blob-id 678 len 256 (immutable)
    DPMS (2) = 0 (On) [On=0|Standby=1|Suspend=2|Off=3]
    TILE (4) = blob-id 0 (immutable)
    link-status (5) = 0 (Good) [Good=0|Bad=1]

Screen ON:

if (kmsDrmService.isAvailable()) {
  // DPMS (2) = 3 (Off) [On=0|Standby=1|Suspend=2|Off=3]
  kmsDrmService.setProperty(2, 0);
}

Screen OFF:

if (kmsDrmService.isAvailable()) {
  // DPMS (2) = 0 (On) [On=0|Standby=1|Suspend=2|Off=3]
  kmsDrmService.setProperty(2, 3);
}

Service:
static final KmsDrmService kmsDrmService = KmsDrmService();

/*
uint32_t KMS_DRM_isAvailable();

void KMS_DRM_setProperty(uint32_t propId, uint64_t value) ;

uint64_t KMS_DRM_getProperty(uint32_t propId);
*/

typedef KMS_DRM_setPropertyFuncType = ffi.Void Function(ffi.Uint32 propId, ffi.Uint64 value);
typedef KMS_DRM_setPropertyType = void Function(int propId, int value);

typedef KMS_DRM_isAvailableFuncType = ffi.Uint32 Function();
typedef KMS_DRM_isAvailableType = int Function();

class KmsDrmService {
  final _KmsDrmService _service = _KmsDrmService.instance;
  bool isAvailable() {
    if (_service.KMS_DRM_isAvailable == null || _service.KMS_DRM_isAvailable!() == 0) {
      return false;
    }
    return true;
  }

  void setProperty(int propId, int value) {
    if (_service.KMS_DRM_setProperty == null) {
      return;
    }
    _service.KMS_DRM_setProperty!(propId, value);
  }
}

class _KmsDrmService {
  final KMS_DRM_setPropertyType? KMS_DRM_setProperty;
  final KMS_DRM_isAvailableType? KMS_DRM_isAvailable;
  _KmsDrmService._constructor({required this.KMS_DRM_setProperty, required this.KMS_DRM_isAvailable});

  factory _KmsDrmService._() {
    final lib = ffi.DynamicLibrary.process();

    try {
      final KMS_DRM_setPropertyLFunc = lib.lookupFunction<KMS_DRM_setPropertyFuncType, KMS_DRM_setPropertyType>("KMS_DRM_setProperty");
      final KMS_DRM_isAvailableLFunc = lib.lookupFunction<KMS_DRM_isAvailableFuncType, KMS_DRM_isAvailableType>("KMS_DRM_isAvailable");

      return _KmsDrmService._constructor(
        KMS_DRM_setProperty: KMS_DRM_setPropertyLFunc,
        KMS_DRM_isAvailable: KMS_DRM_isAvailableLFunc,
      );
    } on ArgumentError {
      return _KmsDrmService._constructor(
        KMS_DRM_setProperty: null,
        KMS_DRM_isAvailable: null,
      );
    }
  }

  static _KmsDrmService? _instance;

  static _KmsDrmService get instance {
    if (_instance == null) {
      _instance = _KmsDrmService._();
    }

    return _instance!;
  }
}

@DisDis
Copy link
Contributor Author

DisDis commented May 1, 2025

@ardera
Copy link
Owner

ardera commented May 2, 2025

Thanks for the contribution @DisDis !

I think providing support for DPMS & backlight is definitely useful, but a generic way to set KMS properties might be a bit too powerful. It's very easy to shoot yourself in the foot with that and it can easily conflict with flutter-pi's modesetting.

Might be better to provide an abstraction specifically for DPMS (and/or backlight). Just a package where you can do something like:

void main() async {
  final dpms = await DPMS.getInstance(); // optionally allow passing the view id here, for multi-view

  await dpms.setMode(PowerMode.on);
  await Future.delayed(Duration(seconds: 5));
  await dpms.setMode(PowerMode.standby);

  // ...
}

For backlight, one could implement a platform interface for: https://github.com/aaassseee/screen_brightness/

Weston has its own little sub-library to help with backlight: https://gitlab.freedesktop.org/wayland/weston/-/blob/main/libweston/backend-drm/libbacklight.c, that could just be added to the flutter-pi source tree under third_party/libbacklight.


#define DPMS_PROPERTY_NAME "DPMS"
#define DPMS_ERROR_PROPERTY_ID 0
// FIXME: Is 0 error value?

Check notice

Code scanning / CodeQL

FIXME comment

FIXME comment: Is 0 error value?
@DisDis
Copy link
Contributor Author

DisDis commented May 3, 2025

Hi @ardera.
I implemented get/setDPMSValue.
When this MR merge, I will start implement DPMS package in "flutter_packages"

@DisDis DisDis changed the title [MVP/WIP] add support kms drm property set [WIP] Implement DPMS get/setValue (Screen Off/On for KMS) May 3, 2025
@ardera
Copy link
Owner

ardera commented Jan 25, 2026

I have to apologize @DisDis , I just realized I reviewed this, but never submitted the review

Copy link
Owner

@ardera ardera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comments, and can you rebase on top of dev?
and move the logic into kms_req_builder and kms_window (window.c), maybe compositor

Comment on lines 3000 to 3093
uint32_t kms_drm_getDPMSPropertyId(struct drmdev *drmdev, uint32_t connector_id){
ASSERT_NOT_NULL(drmdev);
ASSERT_NOT_NULL(drmdev->fd);
drmModeObjectProperties *props = drmModeObjectGetProperties(drmdev->fd, connector_id, DRM_MODE_OBJECT_CONNECTOR);
if (props == NULL){
int ok = errno;
LOG_ERROR("Could not get connector properties. drmModeObjectGetProperties: %s\n", strerror(ok));
return DPMS_ERROR_PROPERTY_ID;
}

uint32_t result_prop_id = DPMS_ERROR_PROPERTY_ID;
for (unsigned i = 0; i < props->count_props; ++i) {
uint32_t prop_id = props->props[i];
drmModePropertyRes *drm_prop = drmModeGetProperty(drmdev->fd, prop_id);
if (drm_prop != NULL) {
if (streq(drm_prop->name, DPMS_PROPERTY_NAME)){
result_prop_id = prop_id;
}
}
drmModeFreeProperty(drm_prop);
}

drmModeFreeObjectProperties(props);
return result_prop_id;
}

uint32_t kms_drm_getDPMSPropertyValue(struct drmdev *drmdev, uint32_t connector_id){
ASSERT_NOT_NULL(drmdev);
ASSERT_NOT_NULL(drmdev->fd);
drmModeObjectProperties *props = drmModeObjectGetProperties(drmdev->fd, connector_id, DRM_MODE_OBJECT_CONNECTOR);
if (props == NULL){
int ok = errno;
LOG_ERROR("Could not get connector properties. drmModeObjectGetProperties: %s\n", strerror(ok));
return DPMS_ERROR_PROPERTY_ID;
}

uint32_t result_prop_value = DPMS_ERROR_PROPERTY_VALUE;
for (unsigned i = 0; i < props->count_props; ++i) {
uint32_t prop_id = props->props[i];
drmModePropertyRes *drm_prop = drmModeGetProperty(drmdev->fd, prop_id);
if (drm_prop != NULL) {
if (streq(drm_prop->name, DPMS_PROPERTY_NAME)){
result_prop_value = props->prop_values[i];
}
}
drmModeFreeProperty(drm_prop);
}

drmModeFreeObjectProperties(props);
return result_prop_value;
}


uint64_t kms_dpms_getValue(struct drmdev *drmdev){
ASSERT_NOT_NULL(drmdev);

struct drm_connector *connectors = drmdev->connectors;
size_t n_connectors = drmdev->n_connectors;
uint64_t result = DPMS_ERROR_PROPERTY_VALUE;

for (int i = 0; i < n_connectors; i++) {
if (
connectors->variable_state.connection_state == kConnected_DrmConnectionState ||
connectors->variable_state.connection_state == kUnknown_DrmConnectionState ){
uint32_t property_id = kms_drm_getDPMSPropertyId(drmdev, connectors->id);
if (property_id != DPMS_ERROR_PROPERTY_ID){
// Return only first value
result = kms_drm_getDPMSPropertyValue(drmdev, connectors->id);
break;
}
}
}

return result;
}


void kms_dpms_setValue(struct drmdev *drmdev, uint64_t value){
ASSERT_NOT_NULL(drmdev);

struct drm_connector *connectors = drmdev->connectors;
size_t n_connectors = drmdev->n_connectors;

for (int i = 0; i < n_connectors; i++) {
if (
connectors->variable_state.connection_state == kConnected_DrmConnectionState ||
connectors->variable_state.connection_state == kUnknown_DrmConnectionState ){
uint32_t property_id = kms_drm_getDPMSPropertyId(drmdev, connectors->id);
if (property_id != DPMS_ERROR_PROPERTY_ID){
drmModeObjectSetProperty(drmdev->fd, connectors->id, DRM_MODE_OBJECT_CONNECTOR, property_id, value);
}
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mhh, this basically circumvents all the modesetting code that's already there in modesetting.c.

What you could do is create a new constructor for kms_req_builder that just turns a display off:

int kms_req_builder_new_dpms_off(struct drmdev *drmdev, uint32_t crtc_id) {
  /* ... */
}

And make all other kms reqs implicity turn DPMS on. (Because there's no reason to build a frame if the display is off)

And then the DPMS logic goes in the window.c, maybe compositor.c

Comment on lines 2994 to 2997
#define DPMS_PROPERTY_NAME "DPMS"
#define DPMS_ERROR_PROPERTY_ID 0
// FIXME: Is 0 error value?
#define DPMS_ERROR_PROPERTY_VALUE 0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already being parsed by the modesetting code, e.g. the DPMS string definition is at:

V("DPMS", dpms) \

The property id of the DPMS property for some given connector is contained in the struct drm_connector, e.g.:

uint32_t get_dpms_property_id(struct drmdev *drmdev, int connector_index) {
    return drmdev->connectors[connector_index].ids.dpms;
}

connectors->variable_state.connection_state == kUnknown_DrmConnectionState ){
uint32_t property_id = kms_drm_getDPMSPropertyId(drmdev, connectors->id);
if (property_id != DPMS_ERROR_PROPERTY_ID){
drmModeObjectSetProperty(drmdev->fd, connectors->id, DRM_MODE_OBJECT_CONNECTOR, property_id, value);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately that API is a bit "deceitful", it'll actually blockingly commit a frame, so it can wait up to 16ms

@DisDis
Copy link
Contributor Author

DisDis commented Jan 31, 2026

some comments, and can you rebase on top of dev? and move the logic into kms_req_builder and kms_window (window.c), maybe compositor

I'll try to do it.

DisDis and others added 4 commits January 31, 2026 19:17
If a device doesn't support rotation, there's really nothing we can do.
So just try to apply rotation, but don't fail if it doesn't work.

adds enforce_rotation property to struct kms_fb_layer, and makes
egl_gbm_render_surface, vk_gbm_render_surface, dmabuf_surface specify
false.
@ardera
Copy link
Owner

ardera commented Jan 31, 2026

I'll try to do it.

Thank you. 👍 I'm also here to assist if there's any problems

@DisDis
Copy link
Contributor Author

DisDis commented Jan 31, 2026

@ardera
If I move it to window.c , how do I replace flutterpi->drmdev ?
For example:

uint64_t dpms_getProperty(){
    return kms_dpms_getValue(flutterpi->drmdev);
}

@ardera
Copy link
Owner

ardera commented Jan 31, 2026

so the thing that communicates with the dart side I would put in the plugins dir, something like this maybe:
dpms_plugin.c:

// 0 => off, 1 => on, negative value => errno-style error
int flutterpi_dpms_get() {
  if (value == NULL) return -EINVAL;

  return compositor_get_dpms(flutterpi->compositor);
}

// 0 => success, non-zero value => errno-style error
int flutterpi_dpms_set(bool value) {
  return compositor_set_dpms(flutterpi->compositor, value);
}

void dpms_plugin_init(/*...*/) {
  // ...
}

@DisDis DisDis force-pushed the support_kms_drm_property_set branch from f8f45d5 to 621138d Compare January 31, 2026 17:23
@DisDis DisDis changed the base branch from master to dev January 31, 2026 17:25
@DisDis
Copy link
Contributor Author

DisDis commented Jan 31, 2026

mhh, this basically circumvents all the modesetting code that's already there in modesetting.c.

What you could do is create a new constructor for kms_req_builder that just turns a display off:

int kms_req_builder_new_dpms_off(struct drmdev *drmdev, uint32_t crtc_id) {
  /* ... */
}

And make all other kms reqs implicity turn DPMS on. (Because there's no reason to build a frame if the display is off)

And then the DPMS logic goes in the window.c, maybe compositor.c

@ardera

I'm not get.
What needs to be done?

@ardera
Copy link
Owner

ardera commented Jan 31, 2026

I'm not get. What needs to be done?

Roughly like this:

add a field in kms_req_builder:

struct kms_req_builder {
  // ...
  bool set_dpms_off;
}

create a new method drmdev_create_dpms_off_req() that works the same as drmdev_create_request_builder(), except you also set set_dpms_off to true. and make it return a kms_req instead of kms_req_builder, so you can't add layers to it.

in kms_req_commit_common: if set_dpms_off is set to true, do

drmModeAtomicAddProperty(
    /* ... */
    builder->connector->ids.dpms,
    builder->is_dpms_off ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON
);

then, in window.c: add a kms_window_set_dpms virtual function

static int kms_window_set_dpms(struct window *window, bool dpms) {
  if (!dpms) {
    window->active = false;
    struct kms_req_builder *b = drmdev_create_request_builder_dpms_off( /*...*/ );
    struct kms_req *r = kms_req_builder_build(b);
    return kms_req_commit_blocking(r, NULL); 
  } else {
    window->active = true;
    // present the last frame
    if (window->composition != NULL) {
        kms_window_push_composition_locked(window, window->composition);
    }
  }
}

virtual here means it should be called via function pointer, like window_push_composition or window_get_render_surface-

in kms_window_push_composition_locked, check if active is set. if it is set, don't actually present the frame, but save it for later in window->composition.

and then call all that from compositor_set_dpms, and that in turn from the plugin code.

@DisDis
Copy link
Contributor Author

DisDis commented Jan 31, 2026

@ardera I updated code.
But
'in kms_window_push_composition_locked, check if active is set. if it is set, don't actually present the frame, but save it for later in window->composition.'
I don't really understand what needs to be done here :(

I don't yet understand how we'll enable dpms. I created
"drmdev_create_dpms_on_req," but maybe that's unnecessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants