From caa465a3eb8a57c12919e5788c1d8dbe13ca2d08 Mon Sep 17 00:00:00 2001 From: Oleg Shishlyannikov Date: Tue, 16 Dec 2025 06:13:17 +0300 Subject: [PATCH] in development --- clients/gstwestonimagesrc.c | 172 ++++++++++++++++++++++++++++++++---- clients/gstwestonimagesrc.h | 5 ++ clients/meson.build | 3 +- 3 files changed, 164 insertions(+), 16 deletions(-) diff --git a/clients/gstwestonimagesrc.c b/clients/gstwestonimagesrc.c index 61d8656..5d12588 100644 --- a/clients/gstwestonimagesrc.c +++ b/clients/gstwestonimagesrc.c @@ -1,20 +1,25 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include #include +#include #include "glib-object.h" #include "glib.h" #include "gst/gstbuffer.h" #include "gst/gstinfo.h" +#include "gst/gstobject.h" #include "gst/gstparamspecs.h" #include "gst/gstvalue.h" #include "gst/video/video-format.h" @@ -36,10 +41,7 @@ enum { N_PROPERTIES, }; -static GParamSpec *properties[N_PROPERTIES] = { - NULL, -}; - +// Gstreamer related static gboolean gst_westonimagesrc_start(GstBaseSrc *basesrc); static gboolean gst_westonimagesrc_stop(GstBaseSrc *basesrc); static GstFlowReturn gst_westonimagesrc_create(GstBaseSrc *basesrc, guint64 offset, guint size, GstBuffer **buf); @@ -55,6 +57,7 @@ static gboolean gst_westonimagesrc_negotiate(GstBaseSrc *basesrc); G_DEFINE_TYPE(GstWestonImageSrc, gst_westonimagesrc, GST_TYPE_BASE_SRC); +// Weston related static void weston_display_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int width, int height, int refresh); static void weston_display_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int physical_width, int physical_height, int subpixel, const char *make, const char *model, int transform); @@ -65,12 +68,159 @@ static void weston_handle_global_remove(void *data, struct wl_registry *registry static void weston_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { /* XXX: unimplemented */ } static struct wl_buffer *weston_screenshot_create_shm_buffer(int width, int height, void **data_out, struct wl_shm *shm); static int weston_screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list); -static void weston_capture_frame(); static const struct wl_output_listener output_listener = {weston_display_handle_geometry, weston_display_handle_mode}; static const struct weston_screenshooter_listener screenshooter_listener = {weston_screenshot_done}; static const struct wl_registry_listener registry_listener = {weston_handle_global, weston_handle_global_remove}; +// Remote control +static void rcontrol_init(GstWestonImageSrc *westonimagesrc); +static void rcontrol_init_uinput(GstWestonImageSrc *westonimagesrc); +static void rcontrol_init_server(GstWestonImageSrc *westonimagesrc); +static gpointer rcontrol_listener_thread(gpointer data); +static void rcontrol_destroy(GstWestonImageSrc *westonimagesrc); +static void rcontrol_emit(GstWestonImageSrc *westonimagesrc, int fd, int type, int code, int value); + +static void rcontrol_emit(GstWestonImageSrc *westonimagesrc, int fd, int type, int code, int value) { + struct input_event ev; + memset(&ev, 0, sizeof(ev)); + gettimeofday(&ev.time, NULL); + ev.type = type; + ev.code = code; + ev.value = value; + + if (write(fd, &ev, sizeof(ev)) < 0) { + GST_WARNING_OBJECT(westonimagesrc, "write() input_event %i:%i:%i:%i failed", fd, type, code, value); + } +} + +static void rcontrol_init(GstWestonImageSrc *westonimagesrc) { + rcontrol_init_uinput(westonimagesrc); + rcontrol_init_server(westonimagesrc); +} + +static gpointer rcontrol_listener_thread(gpointer data) { + const uint16_t port = 7755u; + GstWestonImageSrc *westonimagesrc = data; + g_mutex_lock(&westonimagesrc->lock); + + westonimagesrc->udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (westonimagesrc->udp_socket_fd < 0) { + GST_WARNING_OBJECT(westonimagesrc, "socket() UDP socket creation failed"); + return NULL; + } + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(westonimagesrc->udp_socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + GST_ERROR_OBJECT(westonimagesrc, "bind() UDP socket binding failed"); + close(westonimagesrc->udp_socket_fd); + return NULL; + } + + GST_INFO_OBJECT(westonimagesrc, "UDP listener started on port %d\n", port); + g_mutex_unlock(&westonimagesrc->lock); + + char buf[1024u]; + msgpack_zone mempool; + msgpack_zone_init(&mempool, sizeof(buf)); + + while (TRUE) { + struct sockaddr_in src; + socklen_t srclen = sizeof(src); + ssize_t len = recvfrom(westonimagesrc->udp_socket_fd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&src, &srclen); + + if (len < 0) { + GST_WARNING_OBJECT(westonimagesrc, "recvfrom() failed"); + continue; + } + + buf[len] = '\0'; + + msgpack_object deserialized; + msgpack_unpack(buf, sizeof(buf), NULL, &mempool, &deserialized); + GST_INFO_OBJECT(westonimagesrc, "[UDP %s:%d]: %s\r\n", inet_ntoa(src.sin_addr), ntohs(src.sin_port), deserialized.via.str.ptr); + } + + close(westonimagesrc->udp_socket_fd); + return NULL; +} + +static void rcontrol_init_server(GstWestonImageSrc *westonimagesrc) { + g_mutex_lock(&westonimagesrc->lock); + westonimagesrc->listener_thread = g_thread_new("UDP listener thread", rcontrol_listener_thread, westonimagesrc); + g_mutex_unlock(&westonimagesrc->lock); +} + +static void rcontrol_init_uinput(GstWestonImageSrc *westonimagesrc) { + struct uinput_user_dev uud; + + g_mutex_lock(&westonimagesrc->lock); + westonimagesrc->uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (westonimagesrc->uinput_fd < 0) { + GST_ERROR_OBJECT(westonimagesrc, "open() /dev/uinput failed"); + return; + } + + // Init keyboard + { + ioctl(westonimagesrc->uinput_fd, UI_SET_EVBIT, EV_KEY); + + for (int key = KEY_ESC; key <= KEY_MICMUTE; key++) { + ioctl(westonimagesrc->uinput_fd, UI_SET_KEYBIT, key); + } + } + + // Init mouse + { + // Mouse events + ioctl(westonimagesrc->uinput_fd, UI_SET_EVBIT, EV_REL); + + // Mouse buttons + ioctl(westonimagesrc->uinput_fd, UI_SET_KEYBIT, BTN_LEFT); + ioctl(westonimagesrc->uinput_fd, UI_SET_KEYBIT, BTN_RIGHT); + ioctl(westonimagesrc->uinput_fd, UI_SET_KEYBIT, BTN_MIDDLE); + + // Mouse rels + ioctl(westonimagesrc->uinput_fd, UI_SET_RELBIT, REL_X); + ioctl(westonimagesrc->uinput_fd, UI_SET_RELBIT, REL_Y); + ioctl(westonimagesrc->uinput_fd, UI_SET_RELBIT, REL_WHEEL); + ioctl(westonimagesrc->uinput_fd, UI_SET_RELBIT, REL_HWHEEL); + } + + // Init device + struct uinput_setup usetup; + memset(&usetup, 0, sizeof(usetup)); + + snprintf(usetup.name, UINPUT_MAX_NAME_SIZE, "Virtual Keyboard + Mouse"); + usetup.id.bustype = BUS_USB; + usetup.id.vendor = 0x7777; + usetup.id.product = 0x8888; + usetup.id.version = 1; + + ioctl(westonimagesrc->uinput_fd, UI_SET_EVBIT, EV_SYN); + if (ioctl(westonimagesrc->uinput_fd, UI_DEV_SETUP, &usetup) < 0) { + GST_ERROR_OBJECT(westonimagesrc, "ioctl() UI_DEV_SETUP failed"); + return; + } + + if (ioctl(westonimagesrc->uinput_fd, UI_DEV_CREATE) < 0) { + GST_ERROR_OBJECT(westonimagesrc, "ioctl() UI_DEV_CREATE failed"); + return; + } + + g_mutex_unlock(&westonimagesrc->lock); +} + +static void rcontrol_destroy(GstWestonImageSrc *westonimagesrc) { + ioctl(westonimagesrc->uinput_fd, UI_DEV_DESTROY); + close(westonimagesrc->uinput_fd); +} + static int weston_screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list) { struct screenshooter_output *output; buff_size->min_x = buff_size->min_y = INT_MAX; @@ -234,6 +384,8 @@ static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc) { GST_INFO_OBJECT(westonimagesrc, "Creating shared mem buffer ..."); output->buffer = weston_screenshot_create_shm_buffer(output->width, output->height, &output->data, sh_data.shm); } + + rcontrol_init(westonimagesrc); } static GstCaps *gst_westonimagesrc_get_caps(GstBaseSrc *src, GstCaps *filter) { @@ -318,16 +470,6 @@ static gboolean gst_westonimagesrc_stop(GstBaseSrc *basesrc) { return TRUE; } -static void weston_capture_frame() { - wl_list_for_each(output, &sh_data.output_list, link) { - weston_screenshooter_shoot(sh_data.screenshooter, output->output, output->buffer); - sh_data.buffer_copy_done = 0; - - while (!sh_data.buffer_copy_done) - wl_display_roundtrip(display); - } -} - static GstFlowReturn gst_westonimagesrc_create(GstBaseSrc *basesrc, guint64 offset, guint size, GstBuffer **buf) { GstWestonImageSrc *src = GST_WESTONIMAGESRC(basesrc); GstBuffer *buffer; diff --git a/clients/gstwestonimagesrc.h b/clients/gstwestonimagesrc.h index 0c5943c..9e8c8a9 100644 --- a/clients/gstwestonimagesrc.h +++ b/clients/gstwestonimagesrc.h @@ -19,6 +19,7 @@ #include #include "config.h" +#include "glib.h" #include "weston-screenshooter-client-protocol.h" #include "shared/os-compatibility.h" #include "shared/xalloc.h" @@ -61,6 +62,10 @@ struct _GstWestonImageSrc { guint64 frame_number; gboolean do_timestamp; + // Remote control + gint uinput_fd, udp_socket_fd; + GThread *listener_thread; + // Stat GstClockTime last_timestamp; }; diff --git a/clients/meson.build b/clients/meson.build index 9bda738..8cd1125 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -368,6 +368,7 @@ if get_option('shell-desktop') gstvideo_dep = dependency('gstreamer-video-1.0', version : '>=1.18', fallback : ['gstreamer', 'gstvideo_dep']) + msgpack_dep = dependency('msgpack-c') gst_westonimagesrc_plugin_c_args = ['-DHAVE_CONFIG_H'] gst_westonimagesrc_cdata = configuration_data() @@ -390,7 +391,7 @@ if get_option('shell-desktop') weston_screenshooter_protocol_c, include_directories: common_inc, c_args: gst_westonimagesrc_plugin_c_args, - dependencies : [gst_dep, gstbase_dep, gstvideo_dep, dep_toytoolkit], + dependencies : [gst_dep, gstbase_dep, gstvideo_dep, dep_toytoolkit, msgpack_dep], install : true, install_dir : plugins_install_dir, )