Fix ui being on wrong monitor/focused monitor capture incorrect on kde plasma wayland when vrr is enabled (fallback to window creation & window position trick)

This commit is contained in:
dec05eba
2025-05-02 12:32:08 +02:00
parent ac1d57e8ba
commit 180a3b73db
5 changed files with 105 additions and 26 deletions

View File

@@ -27,19 +27,20 @@ namespace gsr {
typedef struct {
uint64_t crtc_id;
mgl::vec2i size;
bool vrr_enabled;
} drm_connector;
typedef struct {
drm_connector connectors[MAX_CONNECTORS];
int num_connectors;
bool has_any_crtc_with_vrr_enabled;
} drm_connectors;
/* Returns plane_property_mask */
static uint32_t plane_get_properties(int drm_fd, uint32_t plane_id, int *crtc_x, int *crtc_y, int *crtc_id, bool *is_cursor) {
static uint32_t plane_get_properties(int drm_fd, uint32_t plane_id, int *crtc_x, int *crtc_y, int *crtc_id) {
*crtc_x = 0;
*crtc_y = 0;
*crtc_id = 0;
*is_cursor = false;
uint32_t property_mask = 0;
@@ -80,8 +81,8 @@ namespace gsr {
return property_mask;
}
static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) {
for(int i = 0; i < props->count_props; ++i) {
static bool get_drm_property_by_name(int drm_fd, drmModeObjectPropertiesPtr props, const char *name, uint64_t *result) {
for(uint32_t i = 0; i < props->count_props; ++i) {
drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]);
if(!prop)
continue;
@@ -96,6 +97,14 @@ namespace gsr {
return false;
}
static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) {
drmModeObjectProperties properties;
properties.count_props = (uint32_t)props->count_props;
properties.props = props->props;
properties.prop_values = props->prop_values;
return get_drm_property_by_name(drm_fd, &properties, name, result);
}
static drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) {
for(int i = 0; i < *num_type_counts; ++i) {
if(type_counts[i].type == connector_type)
@@ -325,7 +334,7 @@ namespace gsr {
};
/* Returns nullptr if not found */
static const drm_connector* get_drm_connector_by_crtc_id(const drm_connectors *connectors, uint32_t crtc_id) {
static drm_connector* get_drm_connector_by_crtc_id(drm_connectors *connectors, uint32_t crtc_id) {
for(int i = 0; i < connectors->num_connectors; ++i) {
if(connectors->connectors[i].crtc_id == crtc_id)
return &connectors->connectors[i];
@@ -335,6 +344,8 @@ namespace gsr {
static void get_drm_connectors(int drm_fd, drm_connectors *drm_connectors) {
drm_connectors->num_connectors = 0;
drm_connectors->has_any_crtc_with_vrr_enabled = false;
drmModeResPtr resources = drmModeGetResources(drm_fd);
if(!resources)
return;
@@ -350,23 +361,59 @@ namespace gsr {
uint64_t crtc_id = 0;
connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &crtc_id);
if(crtc_id == 0)
goto next;
goto next_connector;
crtc = drmModeGetCrtc(drm_fd, crtc_id);
if(!crtc)
goto next;
goto next_connector;
drm_connectors->connectors[drm_connectors->num_connectors].crtc_id = crtc_id;
drm_connectors->connectors[drm_connectors->num_connectors].size = mgl::vec2i{(int)crtc->width, (int)crtc->height};
drm_connectors->connectors[drm_connectors->num_connectors].vrr_enabled = false;
++drm_connectors->num_connectors;
next:
next_connector:
if(crtc)
drmModeFreeCrtc(crtc);
if(connector)
drmModeFreeConnector(connector);
}
for(int i = 0; i < resources->count_crtcs; ++i) {
drmModeCrtcPtr crtc = nullptr;
drmModeObjectPropertiesPtr properties = nullptr;
uint64_t vrr_enabled = 0;
drm_connector *connector = nullptr;
crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]);
if(!crtc)
continue;
properties = drmModeObjectGetProperties(drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
if(!properties)
goto next_crtc;
if(!get_drm_property_by_name(drm_fd, properties, "VRR_ENABLED", &vrr_enabled))
goto next_crtc;
connector = get_drm_connector_by_crtc_id(drm_connectors, crtc->crtc_id);
if(!connector)
goto next_crtc;
if(vrr_enabled) {
connector->vrr_enabled = true;
drm_connectors->has_any_crtc_with_vrr_enabled = true;
}
next_crtc:
if(properties)
drmModeFreeObjectProperties(properties);
if(crtc)
drmModeFreeCrtc(crtc);
}
drmModeFreeResources(resources);
}
@@ -392,19 +439,20 @@ namespace gsr {
drm_connectors connectors;
connectors.num_connectors = 0;
connectors.has_any_crtc_with_vrr_enabled = false;
get_drm_connectors(drm_fd, &connectors);
drmModePlaneResPtr planes = drmModeGetPlaneResources(drm_fd);
if(!planes)
return;
bool found_cursor = false;
for(uint32_t i = 0; i < planes->count_planes; ++i) {
drmModePlanePtr plane = nullptr;
const drm_connector *connector = nullptr;
int crtc_x = 0;
int crtc_y = 0;
int crtc_id = 0;
bool is_cursor = false;
uint32_t property_mask = 0;
plane = drmModeGetPlane(drm_fd, planes->planes[i]);
@@ -414,7 +462,7 @@ namespace gsr {
if(!plane->fb_id)
goto next;
property_mask = plane_get_properties(drm_fd, planes->planes[i], &crtc_x, &crtc_y, &crtc_id, &is_cursor);
property_mask = plane_get_properties(drm_fd, planes->planes[i], &crtc_x, &crtc_y, &crtc_id);
if(property_mask != plane_property_all || crtc_id <= 0)
goto next;
@@ -426,6 +474,7 @@ namespace gsr {
latest_cursor_position.x = crtc_x;
latest_cursor_position.y = crtc_y;
latest_crtc_id = crtc_id;
found_cursor = true;
drmModeFreePlane(plane);
break;
}
@@ -434,6 +483,11 @@ namespace gsr {
drmModeFreePlane(plane);
}
// On kde plasma wayland (and possibly other wayland compositors) it uses a software cursor only for the monitors with vrr enabled.
// In that case we cant know the cursor location and we instead want to fallback to getting focused monitor by using the hack of creating a window and getting the position.
if(!found_cursor && latest_crtc_id > 0 && connectors.has_any_crtc_with_vrr_enabled)
latest_crtc_id = -1;
drmModeFreePlaneResources(planes);
}

View File

@@ -2137,8 +2137,25 @@ namespace gsr {
cursor_info = cursor_tracker->get_latest_cursor_info();
}
if(cursor_info)
return cursor_info->monitor_name;
std::string focused_monitor_name;
if(cursor_info) {
focused_monitor_name = std::move(cursor_info->monitor_name);
} else {
mgl_context *context = mgl_get_context();
Display *display = (Display*)context->connection;
Window x11_cursor_window = None;
mgl::vec2i cursor_position = get_cursor_position(display, &x11_cursor_window);
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
auto monitors = get_monitors(display);
const Monitor *focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
if(focused_monitor)
focused_monitor_name = focused_monitor->name;
}
if(!focused_monitor_name.empty())
return focused_monitor_name;
else if(!capture_options.monitors.empty())
return capture_options.monitors.front().name;
else

View File

@@ -130,8 +130,6 @@ namespace gsr {
exit_status = -1;
break;
}
buffer[bytes_read] = '\0';
result.append(buffer, bytes_read);
}

View File

@@ -159,6 +159,21 @@ static bool is_flatpak() {
return getenv("FLATPAK_ID") != nullptr;
}
static void set_display_server_environment_variables() {
// Some users dont have properly setup environments (no display manager that does systemctl --user import-environment DISPLAY WAYLAND_DISPLAY)
const char *display = getenv("DISPLAY");
if(!display) {
display = ":0";
setenv("DISPLAY", display, true);
}
const char *wayland_display = getenv("WAYLAND_DISPLAY");
if(!wayland_display) {
wayland_display = "wayland-1";
setenv("WAYLAND_DISPLAY", wayland_display, true);
}
}
static void usage() {
printf("usage: gsr-ui [action]\n");
printf("OPTIONS:\n");
@@ -203,18 +218,7 @@ int main(int argc, char **argv) {
usage();
}
// Some users dont have properly setup environments (no display manager that does systemctl --user import-environment DISPLAY WAYLAND_DISPLAY)
const char *display = getenv("DISPLAY");
if(!display) {
display = ":0";
setenv("DISPLAY", display, true);
}
const char *wayland_display = getenv("WAYLAND_DISPLAY");
if(!wayland_display) {
wayland_display = "wayland-1";
setenv("WAYLAND_DISPLAY", wayland_display, true);
}
set_display_server_environment_variables();
// TODO: This is a shitty method to detect if multiple instances of gsr-ui is running but this will work properly even in flatpak
// that uses pid sandboxing. Replace this with a better method once we no longer rely on linux global hotkeys on some platform.