From 3586a776a44dcf79b9a59b49cfe97b81fa33aae1 Mon Sep 17 00:00:00 2001 From: Jerry Date: Fri, 11 Aug 2023 00:25:26 +0800 Subject: [PATCH] allow screen blank for privacy --- CMakeLists.txt | 2 +- drm.c | 84 ++++++++++++++++++++++++++++++++++++++++++ drm_master.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ drm_master.h | 5 +++ kmsvnc.c | 8 ++++ kmsvnc.h | 14 ++++++- 6 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 drm_master.c create mode 100644 drm_master.h 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..26f8ead 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 && 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; @@ -458,6 +474,14 @@ int drm_open() { { KMSVNC_FATAL("card %s open failed: %s\n", kmsvnc->card, strerror(errno)); } + if (kmsvnc->screen_blank && !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->drm_ver = drmGetVersion(drm->drm_fd); printf("drm driver is %s\n", drm->drm_ver->name); @@ -469,6 +493,66 @@ int drm_open() { if (drm_refresh_planes(1)) return 1; + if (kmsvnc->screen_blank) { + 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) { + 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; + if (kmsvnc->screen_blank_restore) { + int step = 0x10000 / drm->gamma->size; + for (int i = 0; i < drm->gamma->size; i++) { + drm->gamma->red[i] = drm->gamma->green[i] = drm->gamma->blue[i] = step * i; + } + } + else { + // 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]); + } + } + uint16_t *new_gamma_red = malloc(drm->gamma->size*sizeof(uint16_t)*3); + if (!new_gamma_red) { + fprintf(stderr, "memory allocation error at %s:%d\n", __FILE__, __LINE__); + fprintf(stderr, "not setting gamma.\n"); + } + else { + memset(new_gamma_red, 0, drm->gamma->size*sizeof(uint16_t)*3); + uint16_t *new_gamma_green = new_gamma_red + drm->gamma->size; + uint16_t *new_gamma_blue = new_gamma_red + drm->gamma->size*2; + if (drmModeCrtcSetGamma(drm->drm_master_fd ?: drm->drm_fd, drm->plane->crtc_id, drm->gamma->size, new_gamma_red, new_gamma_green, new_gamma_blue)) perror("Failed to set gamma"); + } + if (new_gamma_red) { + free(new_gamma_red); + new_gamma_red = 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..b924068 --- /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 && drmIsMaster(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 = count - 1; n > 0; 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.c b/kmsvnc.c index 17504ba..541e327 100644 --- a/kmsvnc.c +++ b/kmsvnc.c @@ -238,6 +238,8 @@ static struct argp_option kmsvnc_main_options[] = { {"input-height", 0xff07, "0", 0, "Explicitly set input height"}, {"input-offx", 0xff08, "0", 0, "Set input offset of x axis on a multi display system"}, {"input-offy", 0xff09, "0", 0, "Set input offset of y axis on a multi display system"}, + {"screen-blank", 0xff0a, 0, OPTION_ARG_OPTIONAL, "Blank screen with gamma set on crtc"}, + {"screen-blank-restore-linear", 0xff0b, 0, OPTION_ARG_OPTIONAL, "Restore linear values on exit in case of messed up gamma"}, {"wakeup", 'w', 0, OPTION_ARG_OPTIONAL, "Move mouse to wake the system up before start"}, {"disable-input", 'i', 0, OPTION_ARG_OPTIONAL, "Disable uinput"}, {"desktop-name", 'n', "kmsvnc", 0, "Specify vnc desktop name"}, @@ -337,6 +339,12 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { kmsvnc->input_offy = offset_y; } break; + case 0xff0a: + kmsvnc->screen_blank = 1; + break; + case 0xff0b: + kmsvnc->screen_blank_restore = 1; + break; case 'w': kmsvnc->input_wakeup = 1; break; diff --git a/kmsvnc.h b/kmsvnc.h index 9593ed8..1a5c0d1 100644 --- a/kmsvnc.h +++ b/kmsvnc.h @@ -42,6 +42,8 @@ struct kmsvnc_data int input_height; int input_offx; int input_offy; + char screen_blank; + char screen_blank_restore; struct kmsvnc_drm_data *drm; struct kmsvnc_input_data *input; struct kmsvnc_keymap_data *keymap; @@ -85,9 +87,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 +106,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 +124,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