diff --git a/CMakeLists.txt b/CMakeLists.txt index d4d1fdb..4440bc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ IF(NOT HAVE_LINUX_API_HEADERS) message(FATAL_ERROR "linux-api-headers not found") ENDIF() -add_executable(kmsvnc kmsvnc.c drm.c input.c keymap.c va.c) +add_executable(kmsvnc kmsvnc.c drm.c input.c keymap.c va.c drm_master.c) target_include_directories(kmsvnc PUBLIC ${LIBDRM_INCLUDEDIR} ${LIBDRM_INCLUDEDIR}/libdrm diff --git a/drm.c b/drm.c index e1722e2..e1d7284 100644 --- a/drm.c +++ b/drm.c @@ -10,6 +10,7 @@ #include "drm.h" #include "va.h" +#include "drm_master.h" extern struct kmsvnc_data *kmsvnc; @@ -154,6 +155,17 @@ void drm_sync_noop(int drmfd) void drm_cleanup() { if (kmsvnc->drm) { + if (kmsvnc->drm->gamma && kmsvnc->drm->gamma->size && kmsvnc->drm->gamma->red && kmsvnc->drm->gamma->green && kmsvnc->drm->gamma->blue) { + if (drmModeCrtcSetGamma(kmsvnc->drm->drm_master_fd ?: kmsvnc->drm->drm_fd, kmsvnc->drm->plane->crtc_id, kmsvnc->drm->gamma->size, kmsvnc->drm->gamma->red, kmsvnc->drm->gamma->green, kmsvnc->drm->gamma->blue)) perror("Failed to restore gamma"); + } + if (kmsvnc->drm->gamma->red) { + free(kmsvnc->drm->gamma->red); + kmsvnc->drm->gamma->red = kmsvnc->drm->gamma->green = kmsvnc->drm->gamma->blue = NULL; + } + if (kmsvnc->drm->gamma) { + free(kmsvnc->drm->gamma); + kmsvnc->drm->gamma = NULL; + } if (kmsvnc->drm->drm_ver) { drmFreeVersion(kmsvnc->drm->drm_ver); kmsvnc->drm->drm_ver = NULL; @@ -202,6 +214,10 @@ void drm_cleanup() { close(kmsvnc->drm->drm_fd); kmsvnc->drm->drm_fd = 0; } + if (kmsvnc->drm->drm_master_fd > 0) { + close(kmsvnc->drm->drm_master_fd); + kmsvnc->drm->drm_master_fd = 0; + } if (kmsvnc->drm->plane_res) { drmModeFreePlaneResources(kmsvnc->drm->plane_res); kmsvnc->drm->plane_res = NULL; @@ -469,6 +485,67 @@ int drm_open() { if (drm_refresh_planes(1)) return 1; + drm->gamma = malloc(sizeof(struct kmsvnc_drm_gamma_data)); + if (!drm->gamma) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__); + memset(drm->gamma, 0, sizeof(struct kmsvnc_drm_gamma_data)); + drmModeCrtc *target_crtc = drmModeGetCrtc(drm->drm_fd, drm->plane->crtc_id); + if (target_crtc) { + if (!drmIsMaster(drm->drm_fd)) { + drm->drm_master_fd = drm_get_master_fd(); + drm->drm_master_fd = drm->drm_master_fd > 0 ? drm->drm_master_fd : 0; + if (kmsvnc->debug_enabled) { + fprintf(stderr, "not master client, master fd %d\n", drm->drm_master_fd); + } + } + drm->gamma->size = (uint32_t)target_crtc->gamma_size; + drm->gamma->red = malloc(drm->gamma->size*sizeof(uint16_t)*3); + if (!drm->gamma->size) { + fprintf(stderr, "drm->gamma->size = %u, not setting gamma.\n", drm->gamma->size); + } + else if (!drm->gamma->red) { + fprintf(stderr, "memory allocation error at %s:%d\n", __FILE__, __LINE__); + fprintf(stderr, "not setting gamma.\n"); + } + else { + memset(drm->gamma->red, 0, drm->gamma->size*sizeof(uint16_t)*3); + drm->gamma->green = drm->gamma->red + drm->gamma->size; + drm->gamma->blue = drm->gamma->red + drm->gamma->size*2; + // legacy api, but weston also uses this, so whatever + drmModeCrtcGetGamma(drm->drm_fd, drm->plane->crtc_id, drm->gamma->size, drm->gamma->red, drm->gamma->green, drm->gamma->blue); + if (kmsvnc->debug_enabled) { + for (int i = 0; i < drm->gamma->size; i++) { + fprintf(stderr, "gamma: %05d %05hu %05hu %05hu\n", i, drm->gamma->red[i], drm->gamma->green[i], drm->gamma->blue[i]); + } + } + uint32_t new_gamma_size = drm->gamma->size; + uint16_t *new_gammas = malloc(new_gamma_size*sizeof(uint16_t)*3); + if (!new_gammas) { + fprintf(stderr, "memory allocation error at %s:%d\n", __FILE__, __LINE__); + fprintf(stderr, "not setting gamma.\n"); + } + else { + memset(new_gammas, 0, new_gamma_size*sizeof(uint16_t)*3); + if (drmModeCrtcSetGamma(drm->drm_master_fd ?: drm->drm_fd, drm->plane->crtc_id, new_gamma_size, new_gammas, new_gammas+new_gamma_size*sizeof(uint16_t), new_gammas+new_gamma_size*sizeof(uint16_t)*2)) perror("Failed to set gamma"); + if (kmsvnc->debug_enabled) { + for (int i = 0; i < new_gamma_size; i++) { + fprintf(stderr, "new gamma: %05d %05hu %05hu %05hu\n", i, new_gammas[i], new_gammas[i+new_gamma_size], new_gammas[i+new_gamma_size*2]); + } + } + } + if (new_gammas) { + free(new_gammas); + new_gammas = NULL; + } + } + } + else { + fprintf(stderr, "Did not get a crtc structure, not setting gamma.\n"); + } + if (target_crtc) { + drmModeFreeCrtc(target_crtc); + target_crtc = NULL; + } + drm->mfb = drmModeGetFB2(drm->drm_fd, drm->plane->fb_id); if (!drm->mfb) { KMSVNC_FATAL("Failed to get framebuffer %u: %s\n", drm->plane->fb_id, strerror(errno)); diff --git a/drm_master.c b/drm_master.c new file mode 100644 index 0000000..7101dec --- /dev/null +++ b/drm_master.c @@ -0,0 +1,99 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "drm_master.h" + +extern struct kmsvnc_data *kmsvnc; + + +static inline int clone_fd(pid_t pid, int target_fd) { + int pidfd = syscall(SYS_pidfd_open, pid, 0); + if (pidfd <= 0) { + perror("pidfd_open"); + return -1; + } + int cloned = syscall(SYS_pidfd_getfd, pidfd, target_fd, 0); + if (cloned <= 0) { + perror("pidfd_getfd"); + } + close(pidfd); + return cloned; +} + +static inline int cmp_fds(pid_t pid, const char *drm_pth) { + if (pid == 1) return -1; + char path[PATH_MAX+1]; + snprintf(path, PATH_MAX+1, "/proc/%d/fd", pid); + + struct dirent **fdlist; + int count = scandir(path, &fdlist, NULL, versionsort); + int ret = -1; + if (count >= 0) { + for (int n = 0; n < count; n++) { + if (ret == -1 && fdlist[n]->d_type == DT_LNK) { + char link_pth[PATH_MAX+1]; + char real_pth[PATH_MAX+1]; + snprintf(link_pth, PATH_MAX+1, "%s/%s", path, fdlist[n]->d_name); + memset(real_pth, 0, PATH_MAX+1); + realpath(link_pth, real_pth); + if (!strncmp(real_pth, drm_pth, PATH_MAX)) { + int fd = atoi(fdlist[n]->d_name); + if (fd > 0) { + int cloned = clone_fd(pid, fd); + if (cloned > 0 && !drmSetMaster(cloned)) { + ret = cloned; + //if (kmsvnc->debug_enabled) { + fprintf(stderr, "found drm master pid=%d, fd=%d, cloned=%d\n", pid, fd, cloned); + //} + } + else { + if (cloned > 0) close(cloned); + } + } + } + } + free(fdlist[n]); + fdlist[n] = NULL; + } + free(fdlist); + fdlist = NULL; + } + return ret; +} + +int drm_get_master_fd() { + char drm_pth[PATH_MAX+1]; + memset(drm_pth, 0, PATH_MAX+1); + realpath(kmsvnc->card, drm_pth); + + struct dirent **proclist; + int count = scandir("/proc", &proclist, NULL, versionsort); + int ret = -1; + if (count >= 0) { + for (int n = 0; n < count; n++) { + if (ret == -1 && proclist[n]->d_type == DT_DIR) { + pid_t pid = (pid_t)atoi(proclist[n]->d_name); + if (pid > 0) { + int cloned = cmp_fds(pid, drm_pth); + if (cloned > 0) { + ret = cloned; + } + } + } + free(proclist[n]); + proclist[n] = NULL; + } + free(proclist); + proclist = NULL; + } + else { + perror("open /proc"); + } + return ret; +} diff --git a/drm_master.h b/drm_master.h new file mode 100644 index 0000000..6572b79 --- /dev/null +++ b/drm_master.h @@ -0,0 +1,5 @@ +#pragma once + +#include "kmsvnc.h" + +int drm_get_master_fd(); diff --git a/kmsvnc.h b/kmsvnc.h index 9593ed8..630fdfc 100644 --- a/kmsvnc.h +++ b/kmsvnc.h @@ -85,9 +85,18 @@ struct kmsvnc_drm_funcs void (*convert)(const char *, int, int, char *); }; +struct kmsvnc_drm_gamma_data +{ + uint32_t size; + uint16_t *red; + uint16_t *green; + uint16_t *blue; +}; + struct kmsvnc_drm_data { int drm_fd; + int drm_master_fd; drmVersionPtr drm_ver; int prime_fd; drmModePlane *plane; @@ -95,7 +104,7 @@ struct kmsvnc_drm_data drmModePlaneRes *plane_res; drmModeFB2 *mfb; drmModeFB2 *cursor_mfb; - u_int32_t plane_id; + uint32_t plane_id; int mmap_fd; size_t mmap_size; off_t mmap_offset; @@ -113,6 +122,7 @@ struct kmsvnc_drm_data size_t kms_cpy_tmp_buf_len; char *kms_cursor_buf; size_t kms_cursor_buf_len; + struct kmsvnc_drm_gamma_data *gamma; }; struct kmsvnc_va_data