From a7ca90b1350b1135e9d34c310a736de973702115 Mon Sep 17 00:00:00 2001 From: Oleg Shishlyannikov Date: Thu, 18 Dec 2025 09:12:26 +0300 Subject: [PATCH] add mouse control --- clients/gstwestonimagesrc.c | 295 +++++++++++++++++++++++++++++++++--- clients/meson.build | 2 +- 2 files changed, 279 insertions(+), 18 deletions(-) diff --git a/clients/gstwestonimagesrc.c b/clients/gstwestonimagesrc.c index 9a0d9ab..fed2cfc 100644 --- a/clients/gstwestonimagesrc.c +++ b/clients/gstwestonimagesrc.c @@ -1,9 +1,13 @@ +#include #include #include #include #include #include +#include #include +#include +#include #include #include #include @@ -14,8 +18,6 @@ #include #include #include -#include -#include #include "glib-object.h" #include "glib.h" @@ -43,6 +45,23 @@ enum { N_PROPERTIES, }; +enum { + RCONTROL_EVENT_TYPE_UNDEFINED = 0, + RCONTROL_EVENT_TYPE_MOUSE_BTN_PRESS = 2, + RCONTROL_EVENT_TYPE_MOUSE_BTN_RELEASE = 3, + RCONTROL_EVENT_TYPE_MOUSE_MOVE = 5, + RCONTROL_EVENT_TYPE_MOUSE_WHEEL = 31, + N_EVENT_TYPES, +}; + +enum { + RCONTROL_MOUSE_BTN_UNDEFINED = 0, + RCONTROL_MOUSE_BTN_LEFT = 1, + RCONTROL_MOUSE_BTN_RIGHT = 2, + RCONTROL_MOUSE_BTN_MID = 4, + N_MOUSE_BTN, +}; + // Gstreamer related static gboolean gst_westonimagesrc_start(GstBaseSrc *basesrc); static gboolean gst_westonimagesrc_stop(GstBaseSrc *basesrc); @@ -80,9 +99,13 @@ 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 type, int code, int value); +static void rcontrol_handle_compound(GstWestonImageSrc *westonimagesrc, msgpack_object obj); +static int rcontrol_get_event_type(GstWestonImageSrc *westonimagesrc, msgpack_object obj); +static float rcontrol_get_mouse_pos(GstWestonImageSrc *westonimagesrc, msgpack_object obj, const char *key); +static float rcontrol_get_mouse_btn(GstWestonImageSrc *westonimagesrc, msgpack_object obj); -static void rcontrol_emit(GstWestonImageSrc *westonimagesrc, int fd, int type, int code, int value) { +static void rcontrol_emit(GstWestonImageSrc *westonimagesrc, int type, int code, int value) { struct input_event ev; memset(&ev, 0, sizeof(ev)); gettimeofday(&ev.time, NULL); @@ -90,8 +113,8 @@ static void rcontrol_emit(GstWestonImageSrc *westonimagesrc, int fd, int type, i 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); + if (write(westonimagesrc->uinput_fd, &ev, sizeof(ev)) < 0) { + GST_WARNING_OBJECT(westonimagesrc, "write() input_event %i:%i:%i:%i failed", westonimagesrc->uinput_fd, type, code, value); } } @@ -100,6 +123,208 @@ static void rcontrol_init(GstWestonImageSrc *westonimagesrc) { rcontrol_init_server(westonimagesrc); } +static int rcontrol_get_event_type(GstWestonImageSrc *westonimagesrc, msgpack_object obj) { + for (uint32_t i = 0; i < obj.via.map.size; i++) { + msgpack_object_kv kv = obj.via.map.ptr[i]; + if (kv.key.type == MSGPACK_OBJECT_STR) { + if (!strncmp(kv.key.via.str.ptr, "t", kv.key.via.str.size)) { + return kv.val.via.u64; + } + } + } + + return RCONTROL_EVENT_TYPE_UNDEFINED; +} + +static float rcontrol_get_mouse_pos(GstWestonImageSrc *westonimagesrc, msgpack_object obj, const char *key) { + for (uint32_t i = 0; i < obj.via.map.size; i++) { + msgpack_object_kv kv = obj.via.map.ptr[i]; + if (kv.key.type == MSGPACK_OBJECT_STR) { + if (!strncmp(kv.key.via.str.ptr, key, kv.key.via.str.size)) { + return (float)kv.val.via.f64; + } + } + } + + return -1.f; +} + +static float rcontrol_get_mouse_btn(GstWestonImageSrc *westonimagesrc, msgpack_object obj) { + for (uint32_t i = 0; i < obj.via.map.size; i++) { + msgpack_object_kv kv = obj.via.map.ptr[i]; + if (kv.key.type == MSGPACK_OBJECT_STR) { + if (!strncmp(kv.key.via.str.ptr, "b", kv.key.via.str.size)) { + return kv.val.via.u64; + } + } + } + + return RCONTROL_MOUSE_BTN_UNDEFINED; +} + +static void rcontrol_handle_compound(GstWestonImageSrc *westonimagesrc, msgpack_object obj) { + switch (obj.type) { + case MSGPACK_OBJECT_MAP: { + int event_type; + switch (event_type = rcontrol_get_event_type(westonimagesrc, obj)) { + case RCONTROL_EVENT_TYPE_MOUSE_MOVE: { + float x = rcontrol_get_mouse_pos(westonimagesrc, obj, "x"); + float y = rcontrol_get_mouse_pos(westonimagesrc, obj, "y"); + + if (x >= 0.f && y >= 0.f) { + rcontrol_emit(westonimagesrc, EV_ABS, ABS_X, buff_size.width * x); + rcontrol_emit(westonimagesrc, EV_ABS, ABS_Y, buff_size.height * y); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } + } break; + + case RCONTROL_EVENT_TYPE_MOUSE_BTN_PRESS: { + int btn; + switch (btn = rcontrol_get_mouse_btn(westonimagesrc, obj)) { + case RCONTROL_MOUSE_BTN_LEFT: { + rcontrol_emit(westonimagesrc, EV_KEY, BTN_LEFT, 1); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } break; + + case RCONTROL_MOUSE_BTN_RIGHT: { + rcontrol_emit(westonimagesrc, EV_KEY, BTN_RIGHT, 1); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } break; + + case RCONTROL_MOUSE_BTN_MID: { + rcontrol_emit(westonimagesrc, EV_KEY, BTN_MIDDLE, 1); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } break; + default: + break; + } + } break; + + case RCONTROL_EVENT_TYPE_MOUSE_BTN_RELEASE: { + int btn; + switch (btn = rcontrol_get_mouse_btn(westonimagesrc, obj)) { + case RCONTROL_MOUSE_BTN_LEFT: { + rcontrol_emit(westonimagesrc, EV_KEY, BTN_LEFT, 0); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } break; + + case RCONTROL_MOUSE_BTN_RIGHT: { + rcontrol_emit(westonimagesrc, EV_KEY, BTN_RIGHT, 0); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } break; + + case RCONTROL_MOUSE_BTN_MID: { + rcontrol_emit(westonimagesrc, EV_KEY, BTN_MIDDLE, 0); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } break; + default: + break; + } + } break; + + case RCONTROL_EVENT_TYPE_MOUSE_WHEEL: { + float y = rcontrol_get_mouse_pos(westonimagesrc, obj, "y"); + + rcontrol_emit(westonimagesrc, EV_REL, REL_WHEEL, y); + rcontrol_emit(westonimagesrc, EV_SYN, SYN_REPORT, 0); + } break; + + case RCONTROL_EVENT_TYPE_UNDEFINED: + default: + break; + } + + for (uint32_t i = 0; i < obj.via.map.size; i++) { + msgpack_object_kv kv = obj.via.map.ptr[i]; + if (kv.key.type == MSGPACK_OBJECT_STR) { + switch (kv.val.type) { + case MSGPACK_OBJECT_STR: { + GST_INFO_OBJECT(westonimagesrc, "%.*s: %.*s", kv.key.via.str.size, kv.key.via.str.ptr, kv.val.via.str.size, kv.val.via.str.ptr); + } break; + + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + GST_INFO_OBJECT(westonimagesrc, "%.*s: %llu", kv.key.via.str.size, kv.key.via.str.ptr, (unsigned long long)kv.val.via.u64); + } break; + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + GST_INFO_OBJECT(westonimagesrc, "%.*s: %lld", kv.key.via.str.size, kv.key.via.str.ptr, (long long)kv.val.via.i64); + } break; + + case MSGPACK_OBJECT_FLOAT: + case MSGPACK_OBJECT_FLOAT32: { + GST_INFO_OBJECT(westonimagesrc, "%.*s: %lf", kv.key.via.str.size, kv.key.via.str.ptr, (double)kv.val.via.f64); + } break; + + case MSGPACK_OBJECT_BOOLEAN: { + GST_INFO_OBJECT(westonimagesrc, "%.*s: %i", kv.key.via.str.size, kv.key.via.str.ptr, (gboolean)kv.val.via.boolean); + } break; + + case MSGPACK_OBJECT_ARRAY: { + GST_INFO_OBJECT(westonimagesrc, "%.*s: (array)", kv.key.via.str.size, kv.key.via.str.ptr); + rcontrol_handle_compound(westonimagesrc, kv.val); + } break; + + case MSGPACK_OBJECT_MAP: { + GST_INFO_OBJECT(westonimagesrc, "%.*s: (object)", kv.key.via.str.size, kv.key.via.str.ptr); + rcontrol_handle_compound(westonimagesrc, kv.val); + } break; + + default: + GST_INFO_OBJECT(westonimagesrc, "%.*s: (unknown)", kv.key.via.str.size, kv.key.via.str.ptr); + } + } + } + + break; + + case MSGPACK_OBJECT_ARRAY: { + for (uint32_t i = 0; i < obj.via.map.size; i++) { + msgpack_object o = obj.via.array.ptr[i]; + switch (o.type) { + case MSGPACK_OBJECT_STR: { + GST_INFO_OBJECT(westonimagesrc, "%i: %.*s", i, o.via.str.size, o.via.str.ptr); + } break; + + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + GST_INFO_OBJECT(westonimagesrc, "%i: %llu", i, (unsigned long long)o.via.u64); + } break; + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + GST_INFO_OBJECT(westonimagesrc, "%i: %lld", i, (long long)o.via.i64); + } break; + + case MSGPACK_OBJECT_FLOAT: + case MSGPACK_OBJECT_FLOAT32: { + GST_INFO_OBJECT(westonimagesrc, "%i: %lf", i, (double)o.via.f64); + } break; + + case MSGPACK_OBJECT_BOOLEAN: { + GST_INFO_OBJECT(westonimagesrc, "%i: %i", i, (gboolean)o.via.boolean); + } break; + + case MSGPACK_OBJECT_ARRAY: { + GST_INFO_OBJECT(westonimagesrc, "%i: (array)", i); + rcontrol_handle_compound(westonimagesrc, o); + } break; + + case MSGPACK_OBJECT_MAP: { + GST_INFO_OBJECT(westonimagesrc, "%i: (object)", i); + rcontrol_handle_compound(westonimagesrc, o); + } break; + + default: + GST_INFO_OBJECT(westonimagesrc, "%i: (unknown)", i); + } + } + break; + + default: + break; + } + } + } +} + static gpointer rcontrol_listener_thread(gpointer data) { const uint16_t port = 7755u; GstWestonImageSrc *westonimagesrc = data; @@ -164,11 +389,21 @@ static gpointer rcontrol_listener_thread(gpointer data) { strcpy(addrstr, "unknown"); } - msgpack_object deserialized; - msgpack_unpack(msgpack_buf, sizeof(msgpack_buf), NULL, &mempool, &deserialized); - msgpack_object_print_buffer(msg_buf, sizeof(msg_buf), deserialized); + msgpack_unpacked msg; + msgpack_unpacked_init(&msg); + gboolean success = msgpack_unpack_next(&msg, msgpack_buf, sizeof(msgpack_buf), NULL); + if (!success) { + GST_WARNING_OBJECT(westonimagesrc, "Msgpack unpacking failed"); + continue; + } - GST_INFO_OBJECT(westonimagesrc, "[UDP %s:%d]: %s", addrstr, src_port, msg_buf); + if (msg.data.type != MSGPACK_OBJECT_MAP) { + GST_WARNING_OBJECT(westonimagesrc, "Msgpack expected type is MAP"); + continue; + } + + GST_INFO_OBJECT(westonimagesrc, "[UDP %s:%d]: received MAP of size: %u", addrstr, src_port, msg.data.via.map.size); + rcontrol_handle_compound(westonimagesrc, msg.data); } close(westonimagesrc->udp_socket_fd); @@ -200,19 +435,44 @@ static void rcontrol_init_uinput(GstWestonImageSrc *westonimagesrc) { // 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 + // ABS bits + ioctl(westonimagesrc->uinput_fd, UI_SET_EVBIT, EV_ABS); + ioctl(westonimagesrc->uinput_fd, UI_SET_ABSBIT, ABS_X); + ioctl(westonimagesrc->uinput_fd, UI_SET_ABSBIT, ABS_Y); + + ioctl(westonimagesrc->uinput_fd, UI_SET_EVBIT, EV_REL); 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); + + struct uinput_abs_setup abs_x = { + .code = ABS_X, + .absinfo = + { + .minimum = 0, + .maximum = buff_size.width, + .resolution = 1, + }, + }; + + struct uinput_abs_setup abs_y = { + .code = ABS_Y, + .absinfo = + { + .minimum = 0, + .maximum = buff_size.height, + .resolution = 1, + }, + }; + + ioctl(westonimagesrc->uinput_fd, UI_ABS_SETUP, &abs_x); + ioctl(westonimagesrc->uinput_fd, UI_ABS_SETUP, &abs_y); } // Init device @@ -236,6 +496,7 @@ static void rcontrol_init_uinput(GstWestonImageSrc *westonimagesrc) { return; } + sleep(1); g_mutex_unlock(&westonimagesrc->lock); } @@ -359,7 +620,7 @@ static void gst_westonimagesrc_class_init(GstWestonImageSrcClass *klass) { gstbasesrc_class->start = GST_DEBUG_FUNCPTR(gst_westonimagesrc_start); gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_westonimagesrc_stop); gstbasesrc_class->create = GST_DEBUG_FUNCPTR(gst_westonimagesrc_create); - + gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR(gst_westonimagesrc_set_caps); gstbasesrc_class->get_caps = NULL; gstbasesrc_class->negotiate = NULL; @@ -383,7 +644,7 @@ static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc) { westonimagesrc->offset = 0; westonimagesrc->framerate_n = 30; westonimagesrc->framerate_d = 1; - + g_mutex_init(&westonimagesrc->lock); g_cond_init(&westonimagesrc->cond); @@ -432,7 +693,7 @@ static gboolean gst_westonimagesrc_set_caps(GstBaseSrc *basesrc, GstCaps *caps) westonimagesrc->format = gst_video_format_from_string(fmt); GST_INFO_OBJECT(westonimagesrc, "Caps accepted: %s %dx%d @ %d/%d", fmt, westonimagesrc->width, westonimagesrc->height, westonimagesrc->framerate_n, westonimagesrc->framerate_d); - + return TRUE; } diff --git a/clients/meson.build b/clients/meson.build index 21b6631..af72673 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -369,7 +369,7 @@ if get_option('shell-desktop') fallback : ['gstreamer', 'gstvideo_dep']) msgpack_dep = dependency('msgpack-c') - cjson_dep = dependency('cjson') + cjson_dep = dependency('cJSON') gst_westonimagesrc_plugin_c_args = ['-DHAVE_CONFIG_H']