From 4bffa7bfa79c2a1b5f1f5eb2d80e8667d1b49b67 Mon Sep 17 00:00:00 2001 From: Guilherme Iscaro Date: Fri, 28 Oct 2016 09:56:42 -0700 Subject: [PATCH] ecore_evas: refactor VNC as an Eina Module. Summary: This change removes the necessity to link EFL against the libvncserver Please ignore the first three commits, they're being reviewed here: https://phab.enlightenment.org/D4323 Reviewers: bdilly, cedric Reviewed By: cedric Subscribers: cedric, jpeg Differential Revision: https://phab.enlightenment.org/D4338 Signed-off-by: Cedric Bail --- configure.ac | 24 +- src/Makefile_Ecore_Evas.am | 27 + src/lib/ecore_evas/ecore_evas.c | 52 +- src/lib/ecore_evas/ecore_evas_module.c | 67 ++ src/lib/ecore_evas/ecore_evas_private.h | 4 + src/lib/ecore_evas/ecore_evas_x11.h | 4 - .../ecore_evas/engines/x/ecore_evas_x.c | 643 +--------------- .../vnc_server/ecore_evas_vnc_server.c | 722 ++++++++++++++++++ 8 files changed, 869 insertions(+), 674 deletions(-) create mode 100644 src/modules/ecore_evas/vnc_server/ecore_evas_vnc_server.c diff --git a/configure.ac b/configure.ac index 4e61cc5d40..4f68b1eeb3 100644 --- a/configure.ac +++ b/configure.ac @@ -436,9 +436,10 @@ AC_SUBST([ENABLE_LIBLZ4]) want_vnc_server="no" AC_ARG_ENABLE([vnc-server], - [AS_HELP_STRING([--enable-vnc-server],[Enable VNC server support for Ecore_Evas_X. @<:@default=disabled@:>@])], + [AS_HELP_STRING([--enable-vnc-server],[Build Ecore_Evas VNC module. @<:@default=disabled@:>@])], [ if test "x${enableval}" = "xyes" ; then + PKG_CHECK_MODULES([LIBVNCSERVER], [libvncserver]) want_vnc_server="yes" else want_vnc_server="no" @@ -446,11 +447,6 @@ AC_ARG_ENABLE([vnc-server], ], [want_vnc_server="no"]) -AM_CONDITIONAL([ENABLE_VNC_SERVER], [test "${want_vnc_server}" = "yes"]) -AC_DEFINE_IF([ENABLE_VNC_SERVER], [test "${want_vnc_server}" = "yes"], [1], [Use VNC server support for Ecore_Evas_X]) -AC_SUBST([want_vnc_server]) -AC_SUBST([ENABLE_VNC_SERVER]) - #### Checks for header files # Common Checks (keep names sorted for ease of use): @@ -4649,10 +4645,7 @@ AM_CONDITIONAL([BUILD_ECORE_EVAS_WIN32], # XXX TODO: ecore_evas_x11 - -ECORE_EVAS_MODULE([software-x11], [${want_x11_any}], - [EFL_OPTIONAL_DEPEND_PKG([ECORE_EVAS], [${want_vnc_server}], [VNC_SERVER], [libvncserver])]) -EFL_ADD_FEATURE([ECORE_EVAS], [vnc_server]) +ECORE_EVAS_MODULE([software-x11], [${want_x11_any}]) have_ecore_evas_software_xlib="no" have_ecore_evas_software_xcb="no" @@ -4675,8 +4668,7 @@ fi # XXX TODO: ecore_evas_opengl_x11 -ECORE_EVAS_MODULE([opengl-x11], [${want_x11_any_opengl}], - [EFL_OPTIONAL_DEPEND_PKG([ECORE_EVAS], [${want_vnc_server}], [VNC_SERVER], [libvncserver])]) +ECORE_EVAS_MODULE([opengl-x11], [${want_x11_any_opengl}]) have_ecore_evas_opengl_xlib="no" have_ecore_evas_opengl_xcb="no" @@ -4708,14 +4700,22 @@ if test "x${have_ecore_evas_opengl_x11}" = "xyes" || test "x${have_ecore_evas_op fi build_ecore_evas_x11="no" +build_ecore_evas_vnc="no" if test "x$have_ecore_evas_software_x11" = "xyes" || \ test "x$have_ecore_evas_opengl_x11" = "xyes" || \ test "x$have_ecore_evas_software_xcb" = "xyes"; then AC_DEFINE([BUILD_ECORE_EVAS_X11], [1], [Support for X Window Engines in Ecore_Evas]) build_ecore_evas_x11="yes" + if test "$want_vnc_server" = "yes"; then + build_ecore_evas_vnc="yes" + fi fi AM_CONDITIONAL([BUILD_ECORE_EVAS_X11], [test "${build_ecore_evas_x11}" = "yes"]) +AM_CONDITIONAL([BUILD_ECORE_EVAS_VNC_SERVER], [test "${build_ecore_evas_vnc}" = "yes"]) +AC_DEFINE_IF([BUILD_ECORE_EVAS_VNC_SERVER], [test "${build_ecore_evas_vnc}" = "yes"], [1], [Build Ecore_Evas VNC module]) +EFL_ADD_FEATURE([ECORE_EVAS], [vnc_server], [${build_ecore_evas_vnc}]) + EFL_EVAL_PKGS([ECORE_EVAS]) ### Checks for header files diff --git a/src/Makefile_Ecore_Evas.am b/src/Makefile_Ecore_Evas.am index 747a426b8a..5d6a38797c 100644 --- a/src/Makefile_Ecore_Evas.am +++ b/src/Makefile_Ecore_Evas.am @@ -282,6 +282,33 @@ modules_ecore_evas_engines_drm_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ modules_ecore_evas_engines_drm_module_la_LIBTOOLFLAGS = --tag=disable-static endif +if BUILD_ECORE_EVAS_VNC_SERVER +VNCSERVERSOURCES = \ +modules/ecore_evas/vnc_server/ecore_evas_vnc_server.c +ecoreevasenginevncserverpkgdir = $(libdir)/ecore_evas/vnc_server/$(MODULE_ARCH) +ecoreevasenginevncserverpkg_LTLIBRARIES = modules/ecore_evas/vnc_server/module.la + +# Workaround for broken parallel install support in automake (relink issue) +# http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 +install_ecoreevasenginevncserverpkgLTLIBRARIES = install-ecoreevasenginevncserverpkgLTLIBRARIES +$(install_ecoreevasenginevncserverpkgLTLIBRARIES): install-libLTLIBRARIES + +modules_ecore_evas_vnc_server_module_la_SOURCES = $(VNCSERVERSOURCES) +modules_ecore_evas_vnc_server_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +@ECORE_EVAS_CFLAGS@ \ +@LIBVNCSERVER_CFLAGS@ \ +@ECORE_X_CFLAGS@ \ +-I$(top_srcdir)/src/modules/evas/engines/software_x11 +modules_ecore_evas_vnc_server_module_la_LIBADD = \ +@USE_ECORE_EVAS_LIBS@ \ +@LIBVNCSERVER_LIBS@ \ +@USE_ECORE_X_LIBS@ +modules_ecore_evas_vnc_server_module_la_DEPENDENCIES = \ +@USE_ECORE_EVAS_INTERNAL_LIBS@ +modules_ecore_evas_vnc_server_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@ +modules_ecore_evas_vnc_server_module_la_LIBTOOLFLAGS = --tag=disable-static +endif + ### Binary bin_PROGRAMS += \ diff --git a/src/lib/ecore_evas/ecore_evas.c b/src/lib/ecore_evas/ecore_evas.c index 979a6ae947..b06fe1f43e 100644 --- a/src/lib/ecore_evas/ecore_evas.c +++ b/src/lib/ecore_evas/ecore_evas.c @@ -3274,6 +3274,23 @@ _ecore_evas_unref(Ecore_Evas *ee) ERR("Ecore_Evas %p->refcount=%d < 0", ee, ee->refcount); } +static Eina_Bool +_ecore_evas_vnc_stop(Ecore_Evas *ee) +{ + Eina_Module *mod; + void (*vnc_del)(void *); + + mod = _ecore_evas_vnc_server_module_load(); + EINA_SAFETY_ON_NULL_RETURN_VAL(mod, EINA_FALSE); + + vnc_del = eina_module_symbol_get(mod, "ecore_evas_vnc_server_del"); + EINA_SAFETY_ON_NULL_RETURN_VAL(vnc_del, EINA_FALSE); + + vnc_del(ee->vnc_server); + ee->vnc_server = NULL; + return EINA_TRUE; +} + EAPI void _ecore_evas_free(Ecore_Evas *ee) { @@ -3295,6 +3312,7 @@ _ecore_evas_free(Ecore_Evas *ee) ee->anim = NULL; if (ee->func.fn_pre_free) ee->func.fn_pre_free(ee); + if (ee->vnc_server) _ecore_evas_vnc_stop(ee); while (ee->sub_ecore_evas) { _ecore_evas_free(ee->sub_ecore_evas->data); @@ -3957,33 +3975,35 @@ ecore_evas_x11_shape_input_apply(Ecore_Evas *ee) EAPI Eina_Bool ecore_evas_vnc_start(Ecore_Evas *ee, const char *addr, int port, - Ecore_Evas_Vnc_Client_Accept_Cb cb, void *data) + Ecore_Evas_Vnc_Client_Accept_Cb cb, void *data) { - Ecore_Evas_Interface_X11 *iface; + Eina_Module *mod; + void *(*vnc_new)(Ecore_Evas *, int, const char *, + Ecore_Evas_Vnc_Client_Accept_Cb, void *); - if (strcmp(ee->driver, "software_x11")) + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, EINA_FALSE); + + if (ee->vnc_server) return EINA_FALSE; - iface = (Ecore_Evas_Interface_X11 *)_ecore_evas_interface_get(ee, "x11"); - EINA_SAFETY_ON_NULL_RETURN_VAL(iface, EINA_FALSE); - EINA_SAFETY_ON_NULL_RETURN_VAL(iface->vnc_start, EINA_FALSE); + mod = _ecore_evas_vnc_server_module_load(); + EINA_SAFETY_ON_NULL_RETURN_VAL(mod, EINA_FALSE); - return iface->vnc_start(ee, addr, port, cb, data); + vnc_new = eina_module_symbol_get(mod, "ecore_evas_vnc_server_new"); + EINA_SAFETY_ON_NULL_RETURN_VAL(vnc_new, EINA_FALSE); + + ee->vnc_server = vnc_new(ee, port, addr, cb, data); + EINA_SAFETY_ON_NULL_RETURN_VAL(ee->vnc_server, EINA_FALSE); + return EINA_TRUE; } EAPI Eina_Bool ecore_evas_vnc_stop(Ecore_Evas *ee) { - Ecore_Evas_Interface_X11 *iface; + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ee->vnc_server, EINA_FALSE); - if (strcmp(ee->driver, "software_x11")) - return EINA_FALSE; - - iface = (Ecore_Evas_Interface_X11 *)_ecore_evas_interface_get(ee, "x11"); - EINA_SAFETY_ON_NULL_RETURN_VAL(iface, EINA_FALSE); - EINA_SAFETY_ON_NULL_RETURN_VAL(iface->vnc_stop, EINA_FALSE); - - return iface->vnc_stop(ee); + return _ecore_evas_vnc_stop(ee); } EAPI Ecore_Evas * diff --git a/src/lib/ecore_evas/ecore_evas_module.c b/src/lib/ecore_evas/ecore_evas_module.c index 59fb6010cb..320d035ee5 100644 --- a/src/lib/ecore_evas/ecore_evas_module.c +++ b/src/lib/ecore_evas/ecore_evas_module.c @@ -12,6 +12,7 @@ static Eina_Hash *_registered_engines = NULL; static Eina_List *_engines_paths = NULL; static Eina_List *_engines_available = NULL; +static Eina_Module *_ecore_evas_vnc = NULL; #ifdef _WIN32 # define ECORE_EVAS_ENGINE_NAME "module.dll" @@ -19,6 +20,72 @@ static Eina_List *_engines_available = NULL; # define ECORE_EVAS_ENGINE_NAME "module.so" #endif +static Eina_Module * +_ecore_evas_vnc_server_module_try_load(const char *prefix, + Eina_Bool use_prefix_only) +{ + Eina_Module *m; + + if (use_prefix_only) + m = eina_module_new(prefix); + else + { + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "%s/vnc_server/%s/%s", prefix, + MODULE_ARCH, ECORE_EVAS_ENGINE_NAME); + m = eina_module_new(path); + } + + if (!m) + return NULL; + if (!eina_module_load(m)) + { + eina_module_free(m); + _ecore_evas_vnc = NULL; + return NULL; + } + + return m; +} + +Eina_Module * +_ecore_evas_vnc_server_module_load(void) +{ + char *prefix; + + if (_ecore_evas_vnc) + return _ecore_evas_vnc; + +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) + if (getuid() == geteuid()) +#endif + { + if (getenv("EFL_RUN_IN_TREE")) + { + _ecore_evas_vnc = _ecore_evas_vnc_server_module_try_load(PACKAGE_BUILD_DIR + "/src/modules/ecore_evas/vnc_server/.libs/" + ECORE_EVAS_ENGINE_NAME, + EINA_TRUE); + if (_ecore_evas_vnc) + return _ecore_evas_vnc; + } + } + + prefix = eina_module_symbol_path_get(_ecore_evas_vnc_server_module_load, + "/ecore_evas"); + _ecore_evas_vnc = _ecore_evas_vnc_server_module_try_load(prefix, EINA_FALSE); + free(prefix); + //Last try... + if (!_ecore_evas_vnc) + { + _ecore_evas_vnc = _ecore_evas_vnc_server_module_try_load(PACKAGE_LIB_DIR"/ecore_evas", + EINA_FALSE); + if (!_ecore_evas_vnc) + ERR("Could not find a valid VNC module to load!"); + } + return _ecore_evas_vnc; +} Eina_Module * _ecore_evas_engine_load(const char *engine) diff --git a/src/lib/ecore_evas/ecore_evas_private.h b/src/lib/ecore_evas/ecore_evas_private.h index ee75fd0782..b38e8d56b8 100644 --- a/src/lib/ecore_evas/ecore_evas_private.h +++ b/src/lib/ecore_evas/ecore_evas_private.h @@ -199,6 +199,8 @@ struct _Ecore_Evas Eina_Hash *data; + void *vnc_server; /* @since 1.19 */ + struct { int x, y, w, h; } req; @@ -432,6 +434,8 @@ void _ecore_evas_engine_shutdown(void); EAPI void ecore_evas_animator_tick(Ecore_Evas *ee, Eina_Rectangle *viewport); +Eina_Module *_ecore_evas_vnc_server_module_load(void); + #undef EAPI #define EAPI diff --git a/src/lib/ecore_evas/ecore_evas_x11.h b/src/lib/ecore_evas/ecore_evas_x11.h index 7d5e809d04..b349b7f2ac 100644 --- a/src/lib/ecore_evas/ecore_evas_x11.h +++ b/src/lib/ecore_evas/ecore_evas_x11.h @@ -17,10 +17,6 @@ struct _Ecore_Evas_Interface_X11 { void (*shape_input_empty)(Ecore_Evas *ee); void (*shape_input_reset)(Ecore_Evas *ee); void (*shape_input_apply)(Ecore_Evas *ee); - Eina_Bool (*vnc_start)(Ecore_Evas *ee, const char *addr, int port, - Ecore_Evas_Vnc_Client_Accept_Cb cb, - void *data); - Eina_Bool (*vnc_stop)(Ecore_Evas *ee); }; struct _Ecore_Evas_Interface_Software_X11 { diff --git a/src/modules/ecore_evas/engines/x/ecore_evas_x.c b/src/modules/ecore_evas/engines/x/ecore_evas_x.c index 48abefede7..3b6f11beaf 100644 --- a/src/modules/ecore_evas/engines/x/ecore_evas_x.c +++ b/src/modules/ecore_evas/engines/x/ecore_evas_x.c @@ -26,12 +26,6 @@ #include "ecore_evas_private.h" #include "ecore_evas_x11.h" -#ifdef ENABLE_VNC_SERVER -# include -# include -# include -#endif - #ifdef EAPI # undef EAPI #endif @@ -137,17 +131,6 @@ struct _Ecore_Evas_Engine_Data_X11 { unsigned long colormap; // store colormap used to create pixmap } pixmap; Eina_Bool destroyed : 1; // X window has been deleted and cannot be used - -#ifdef ENABLE_VNC_SERVER - char *frame_buffer; - rfbScreenInfoPtr vnc_screen; - Ecore_Fd_Handler *vnc_listen_handler; - Ecore_Fd_Handler *vnc_listen6_handler; - Ecore_Evas_Vnc_Client_Accept_Cb accept_cb; - void *accept_cb_data; - int last_w; - int last_h; -#endif }; static Ecore_Evas_Interface_X11 * _ecore_evas_x_interface_x11_new(void); @@ -169,175 +152,6 @@ static void _transparent_do(Ecore_Evas *, int); static void _avoid_damage_do(Ecore_Evas *, int); static void _rotation_do(Ecore_Evas *, int, int); -#ifdef ENABLE_VNC_SERVER - -typedef struct _Ecore_Evas_X11_Vnc_Client_Data { - Ecore_Fd_Handler *handler; - unsigned int key_modifiers; - time_t last_mouse_button_down; - Eina_Bool double_click; - Eina_Bool triple_click; - Evas_Device *keyboard; - Evas_Device *mouse; - Evas_Device *seat; -} Ecore_Evas_X11_Vnc_Client_Data; - -static unsigned int _available_seat = 1; - -#define VNC_BITS_PER_SAMPLE (8) -#define VNC_SAMPLES_PER_PIXEL (3) -#define VNC_BYTES_PER_PIXEL (4) - -static void -_ecore_evas_x11_update_vnc_clients(rfbScreenInfoPtr vnc_screen) -{ - rfbClientIteratorPtr itr; - rfbClientRec *client; - - itr = rfbGetClientIterator(vnc_screen); - - //No clients. - if (!itr) return; - - while ((client = rfbClientIteratorNext(itr))) { - rfbBool r; - - r = rfbUpdateClient(client); - - if (!r) - { - Ecore_Evas_X11_Vnc_Client_Data *cdata = client->clientData; - - WRN("Could not update the VNC client on seat '%s'\n", - evas_device_name_get(cdata->seat)); - } - - //Client disconnected - if (client->sock == -1) rfbClientConnectionGone(client); - } - - rfbReleaseClientIterator(itr); -} - -static void -_ecore_evas_x11_vnc_server_format_setup(Ecore_Evas_Engine_Data_X11 *edata) -{ - int aux; - - //FIXME: Using BGR - Is there a better way to do this? - aux = edata->vnc_screen->serverFormat.redShift; - edata->vnc_screen->serverFormat.redShift = edata->vnc_screen->serverFormat.blueShift; - edata->vnc_screen->serverFormat.blueShift = aux; -} - -static void -_ecore_evas_x11_region_push_hook(Evas *e, int x, int y, int w, int h, - const void *pixels) -{ - Ecore_Evas *ee; - Ecore_Evas_Engine_Data_X11 *edata; - size_t size, src_stride; - int dy; - Eina_Bool new_buf = EINA_FALSE; - - ee = evas_data_attach_get(e); - edata = ee->engine.data; - - if (!edata->frame_buffer || edata->last_w != ee->w || edata->last_h != ee->h) - { - char *new_fb; - - size = ee->w * ee->h * VNC_BYTES_PER_PIXEL; - new_fb = malloc(size); - EINA_SAFETY_ON_NULL_RETURN(new_fb); - free(edata->frame_buffer); - edata->frame_buffer = new_fb; - edata->last_w = ee->w; - edata->last_h = ee->h; - new_buf = EINA_TRUE; - - if (edata->vnc_screen) - { - rfbNewFramebuffer(edata->vnc_screen, edata->frame_buffer, ee->w, - ee->h, VNC_BITS_PER_SAMPLE, VNC_SAMPLES_PER_PIXEL, - VNC_BYTES_PER_PIXEL); - _ecore_evas_x11_vnc_server_format_setup(edata); - } - } - - if (y > edata->last_h || x > edata->last_w) - return; - - //Do not paint outside the VNC canvas - if (y + h > edata->last_h) - h = edata->last_h - y; - - //Do not paint outside the VNC canvas - if (x + w > edata->last_w) - w = edata->last_w - x; - - src_stride = w * VNC_BYTES_PER_PIXEL; - - for (dy = 0; dy < h; dy++) - { - memcpy(edata->frame_buffer + (x * VNC_BYTES_PER_PIXEL) - + ((dy + y) * (edata->last_w * VNC_BYTES_PER_PIXEL)), - (char *)pixels + (dy * src_stride), src_stride); - } - - //We did not receive the whole buffer yet, zero the missing bytes for now. - if (new_buf) - { - //Missing width - if (edata->last_w != w || x != 0) - { - for (dy = 0; dy < h; dy++) - { - if (x) - { - memset(edata->frame_buffer - + ((dy + y) * (edata->last_w * VNC_BYTES_PER_PIXEL)), - 0, x * VNC_BYTES_PER_PIXEL); - } - - memset(edata->frame_buffer + - ((dy + y) * (edata->last_w * VNC_BYTES_PER_PIXEL)) - + ((x + w) * VNC_BYTES_PER_PIXEL), - 0, (edata->last_w - (w + x)) * VNC_BYTES_PER_PIXEL); - } - } - - //Missing height - if (edata->last_h != h || y != 0) - { - src_stride = edata->last_w * VNC_BYTES_PER_PIXEL; - for (dy = 0; dy < y; dy++) - memset(edata->frame_buffer + (dy * src_stride), 0, src_stride); - - for (dy = y + h; dy < edata->last_h; dy++) - memset(edata->frame_buffer + (dy * src_stride), 0, src_stride); - } - } - - if (edata->vnc_screen) - { - rfbMarkRectAsModified(edata->vnc_screen, x, y, x+w, y+h); - _ecore_evas_x11_update_vnc_clients(edata->vnc_screen); - } -} - -#else - -static void -_ecore_evas_x11_region_push_hook(Evas *e EINA_UNUSED, int x EINA_UNUSED, - int y EINA_UNUSED, int w EINA_UNUSED, - int h EINA_UNUSED, - const void *pixels EINA_UNUSED) -{ -} - -#endif //ENABLE_VNC_SERVER - static void _ecore_evas_x_hints_update(Ecore_Evas *ee) { @@ -2248,16 +2062,7 @@ _ecore_evas_x_free(Ecore_Evas *ee) ecore_timer_del(edata->outdelay); edata->outdelay = NULL; } -#ifdef ENABLE_VNC_SERVER - if (edata->vnc_screen) - { - ecore_main_fd_handler_del(edata->vnc_listen6_handler); - ecore_main_fd_handler_del(edata->vnc_listen_handler); - free(edata->frame_buffer); - rfbScreenCleanup(edata->vnc_screen); - edata->vnc_screen = NULL; - } -#endif + free(edata); _ecore_evas_x_shutdown(); ecore_x_shutdown(); @@ -4307,7 +4112,6 @@ ecore_evas_software_x11_new_internal(const char *disp_name, Ecore_X_Window paren einfo->info.screen = NULL; # endif einfo->info.drawable = ee->prop.window; - einfo->func.region_push_hook = _ecore_evas_x11_region_push_hook; if (argb) { @@ -4496,7 +4300,6 @@ ecore_evas_software_x11_pixmap_new_internal(const char *disp_name, Ecore_X_Windo } } - einfo->func.region_push_hook = _ecore_evas_x11_region_push_hook; einfo->info.destination_alpha = argb; if (redraw_debug < 0) @@ -5330,448 +5133,6 @@ _ecore_evas_x11_shape_input_apply(Ecore_Evas *ee) ecore_x_window_shape_input_window_set(ee->prop.window, edata->win_shaped_input); } -#ifdef ENABLE_VNC_SERVER -static Eina_Bool -_ecore_evas_x11_vnc_socket_listen_activity(void *data, - Ecore_Fd_Handler *fd_handler EINA_UNUSED) -{ - rfbProcessNewConnection(data); - return ECORE_CALLBACK_RENEW; -} - -static void -_ecore_evas_x11_client_gone(rfbClientRec *client) -{ - Ecore_Evas_X11_Vnc_Client_Data *cdata = client->clientData; - - EDBG("VNC client on seat '%s' gone", evas_device_name_get(cdata->seat)); - - ecore_main_fd_handler_del(cdata->handler); - evas_device_del(cdata->keyboard); - evas_device_del(cdata->mouse); - evas_device_del(cdata->seat); - free(cdata); - _available_seat--; -} - -static Eina_Bool -_ecore_evas_x11_client_activity(void *data, - Ecore_Fd_Handler *fd_handler EINA_UNUSED) -{ - Eina_Bool r = ECORE_CALLBACK_RENEW; - rfbClientRec *client = data; - - rfbProcessClientMessage(client); - - //macro from rfb.h - if (FB_UPDATE_PENDING(client)) - rfbSendFramebufferUpdate(client, client->modifiedRegion); - - //Client disconnected. - if (client->sock == -1) - { - rfbClientConnectionGone(client); - r = ECORE_CALLBACK_DONE; - } - - return r; -} - -static enum rfbNewClientAction -_ecore_evas_x11_vnc_client_connection_new(rfbClientRec *client) -{ - Ecore_Evas *ee; - Ecore_Evas_Engine_Data_X11 *edata; - Ecore_Evas_X11_Vnc_Client_Data *cdata; - char buf[32]; - - EINA_SAFETY_ON_TRUE_RETURN_VAL(_available_seat == UINT_MAX, - RFB_CLIENT_REFUSE); - - ee = client->screen->screenData; - edata = ee->engine.data; - - if (edata->accept_cb && !edata->accept_cb(edata->accept_cb_data, ee, client->host)) - return RFB_CLIENT_REFUSE; - - cdata = calloc(1, sizeof(Ecore_Evas_X11_Vnc_Client_Data)); - EINA_SAFETY_ON_NULL_RETURN_VAL(cdata, RFB_CLIENT_REFUSE); - - cdata->handler = ecore_main_fd_handler_add(client->sock, ECORE_FD_READ, - _ecore_evas_x11_client_activity, - client, NULL, NULL); - EINA_SAFETY_ON_NULL_GOTO(cdata->handler, err_handler); - - snprintf(buf, sizeof(buf), "seat-%u", _available_seat); - - cdata->seat = evas_device_add_full(ee->evas, buf, - "A remote VNC seat", - NULL, NULL, EVAS_DEVICE_CLASS_SEAT, - EVAS_DEVICE_SUBCLASS_NONE); - EINA_SAFETY_ON_NULL_GOTO(cdata->seat, err_handler); - cdata->keyboard = evas_device_add_full(ee->evas, "Keyboard", - "A remote VNC keyboard", - cdata->seat, NULL, - EVAS_DEVICE_CLASS_KEYBOARD, - EVAS_DEVICE_SUBCLASS_NONE); - EINA_SAFETY_ON_NULL_GOTO(cdata->keyboard, err_dev); - cdata->mouse = evas_device_add_full(ee->evas, "Mouse", - "A remote VNC mouse", - cdata->seat, NULL, - EVAS_DEVICE_CLASS_MOUSE, - EVAS_DEVICE_SUBCLASS_NONE); - EINA_SAFETY_ON_NULL_GOTO(cdata->mouse, err_mouse); - client->clientGoneHook = _ecore_evas_x11_client_gone; - client->clientData = cdata; - - EDBG("New VNC client on seat '%u'", _available_seat); - _available_seat++; - - return RFB_CLIENT_ACCEPT; - - err_mouse: - evas_device_del(cdata->keyboard); - err_dev: - evas_device_del(cdata->seat); - err_handler: - free(cdata); - return RFB_CLIENT_REFUSE; -} - -static unsigned int -_ecore_evas_x11_vnc_vnc_modifier_to_ecore_x_modifier(int mod) -{ - if (mod == XK_Shift_L || mod == XK_Shift_R) - return ECORE_EVENT_MODIFIER_SHIFT; - if (mod == XK_Control_L || mod == XK_Control_R) - return ECORE_EVENT_MODIFIER_CTRL; - if (mod == XK_Alt_L || mod == XK_Alt_R) - return ECORE_EVENT_MODIFIER_ALT; - if (mod == XK_Super_L || mod == XK_Super_R) - return ECORE_EVENT_MODIFIER_WIN; - if (mod == XK_Scroll_Lock) - return ECORE_EVENT_LOCK_SCROLL; - if (mod == XK_Num_Lock) - return ECORE_EVENT_LOCK_NUM; - if (mod == XK_Caps_Lock) - return ECORE_EVENT_LOCK_CAPS; - if (mod == XK_Shift_Lock) - return ECORE_EVENT_LOCK_SHIFT; - return 0; -} - -static void -_ecore_evas_x11_ecore_event_generic_free(void *user_data, - void *func_data) -{ - efl_unref(user_data); - free(func_data); -} - -static void -_ecore_evas_x11_vnc_client_keyboard_event(rfbBool down, - rfbKeySym key, - rfbClientRec *client) -{ - Ecore_Event_Key *e; - Ecore_Evas_X11_Vnc_Client_Data *cdata = client->clientData; - rfbScreenInfoPtr screen = client->screen; - Ecore_Evas *ee = screen->screenData; - const char *key_string; - char buf[10]; - - if (key >= XK_Shift_L && key <= XK_Hyper_R) - { - int mod = _ecore_evas_x11_vnc_vnc_modifier_to_ecore_x_modifier(key); - - if (down) - cdata->key_modifiers |= mod; - else - cdata->key_modifiers &= ~mod; - } - - if (ee->ignore_events) - return; - - key_string = ecore_x_keysym_string_get(key); - EINA_SAFETY_ON_NULL_RETURN(key_string); - - snprintf(buf, sizeof(buf), "%lc", key); - - e = calloc(1, sizeof(Ecore_Event_Key) + strlen(buf) + 1); - EINA_SAFETY_ON_NULL_RETURN(e); - - e->timestamp = (unsigned int)time(NULL); - e->modifiers = cdata->key_modifiers; - e->same_screen = 1; - e->window = e->root_window = e->event_window = ee->prop.window; - e->dev = cdata->keyboard; - efl_ref(cdata->keyboard); - e->keycode = ecore_x_keysym_keycode_get(key_string); - e->keyname = e->key = key_string; - e->compose = (char *)(e + 1); - strcpy((char *)e->compose, buf); - e->string = e->compose; - - ecore_event_add(down ? ECORE_EVENT_KEY_DOWN : ECORE_EVENT_KEY_UP, - e, _ecore_evas_x11_ecore_event_generic_free, - cdata->keyboard); -} - -static int -_ecore_evas_x11_vnc_pointer_button_get(int mask) -{ - int i; - for (i = 0; i < 32; i++) - if (mask >> i & 1) - return i + 1; - return 0; -} - -static void -_ecore_evas_x11_vnc_client_pointer_event(int button_mask, - int x, int y, - rfbClientPtr client) -{ - Ecore_Evas_X11_Vnc_Client_Data *cdata = client->clientData; - rfbScreenInfoPtr screen = client->screen; - Ecore_Evas *ee = screen->screenData; - Ecore_Event_Mouse_Move *move_event; - Ecore_Event_Mouse_Button *button_event; - Ecore_Event_Mouse_Wheel *wheel_event; - int button_changed, button, event, z = 0, direction = 0; - time_t now = time(NULL); - - if (ee->ignore_events) - return; - - if (client->lastPtrX != x || client->lastPtrY != y) - { - move_event = calloc(1, sizeof(Ecore_Event_Mouse_Move)); - EINA_SAFETY_ON_NULL_RETURN(move_event); - - move_event->x = move_event->multi.x = x; - move_event->y = move_event->multi.y = y; - move_event->same_screen = 1; - move_event->timestamp = (unsigned int)now; - move_event->window = move_event->event_window = - move_event->root_window = ee->prop.window; - move_event->multi.pressure = 1.0; - move_event->modifiers = cdata->key_modifiers; - move_event->dev = cdata->mouse; - efl_ref(cdata->mouse); - ecore_event_add(ECORE_EVENT_MOUSE_MOVE, move_event, - _ecore_evas_x11_ecore_event_generic_free, cdata->mouse); - client->lastPtrX = x; - client->lastPtrY = y; - } - - button_changed = button_mask - client->lastPtrButtons; - - if (button_changed > 0) - { - button = _ecore_evas_x11_vnc_pointer_button_get(button_changed); - - switch (button) - { - case 4: - event = ECORE_EVENT_MOUSE_WHEEL; - direction = 0; //Vertical - z = -1; //Up - break; - case 5: - event = ECORE_EVENT_MOUSE_WHEEL; - direction = 0; - z = 1; //Down - break; - case 6: - event = ECORE_EVENT_MOUSE_WHEEL; - direction = 1; //Horizontal - z = -1; - break; - case 7: - event = ECORE_EVENT_MOUSE_WHEEL; - direction = 1; - z = 1; - break; - default: - event = ECORE_EVENT_MOUSE_BUTTON_DOWN; - } - - if (now - cdata->last_mouse_button_down <= 1000 * ecore_x_double_click_time_get()) - cdata->double_click = EINA_TRUE; - else - cdata->double_click = cdata->triple_click = EINA_FALSE; - - if (now - cdata->last_mouse_button_down <= 2000 * ecore_x_double_click_time_get()) - cdata->triple_click = EINA_TRUE; - else - cdata->triple_click = EINA_FALSE; - - cdata->last_mouse_button_down = now; - } - else if (button_changed < 0) - { - button = _ecore_evas_x11_vnc_pointer_button_get(-button_changed); - //Ignore, it was already report. - if (button > 3 && button < 8) - return; - event = ECORE_EVENT_MOUSE_BUTTON_UP; - } - else - return; - - if (event == ECORE_EVENT_MOUSE_BUTTON_DOWN || - event == ECORE_EVENT_MOUSE_BUTTON_UP) - { - button_event = calloc(1, sizeof(Ecore_Event_Mouse_Button)); - EINA_SAFETY_ON_NULL_RETURN(button_event); - - button_event->timestamp = (unsigned int)now; - button_event->window = button_event->event_window = - button_event->root_window = ee->prop.window; - button_event->x = button_event->multi.x = x; - button_event->y = button_event->multi.y = y; - button_event->multi.pressure = 1.0; - button_event->same_screen = 1; - button_event->buttons = button; - button_event->modifiers = cdata->key_modifiers; - button_event->double_click = cdata->double_click ? 1 : 0; - button_event->triple_click = cdata->triple_click ? 1 : 0; - button_event->dev = cdata->mouse; - efl_ref(cdata->mouse); - - ecore_event_add(event, button_event, - _ecore_evas_x11_ecore_event_generic_free, cdata->mouse); - return; - } - - //Mouse wheel - wheel_event = calloc(1, sizeof(Ecore_Event_Mouse_Wheel)); - EINA_SAFETY_ON_NULL_RETURN(wheel_event); - wheel_event->dev = cdata->mouse; - efl_ref(cdata->mouse); - wheel_event->window = wheel_event->event_window = - wheel_event->root_window = ee->prop.window; - wheel_event->same_screen = 1; - wheel_event->modifiers = cdata->key_modifiers; - wheel_event->x = x; - wheel_event->y = y; - wheel_event->direction = direction; - wheel_event->z = z; - - ecore_event_add(event, wheel_event, - _ecore_evas_x11_ecore_event_generic_free, cdata->mouse); -} -#endif - -static Eina_Bool -_ecore_evas_x11_vnc_start(Ecore_Evas *ee, const char *addr, int port, - Ecore_Evas_Vnc_Client_Accept_Cb cb, void *data) -{ -#ifdef ENABLE_VNC_SERVER - Ecore_Evas_Engine_Data_X11 *edata = ee->engine.data; - Eina_Bool can_listen = EINA_FALSE; - - if (edata->vnc_screen) - return EINA_FALSE; - - edata->vnc_screen = rfbGetScreen(0, NULL, ee->w, ee->h, VNC_BITS_PER_SAMPLE, - VNC_SAMPLES_PER_PIXEL, VNC_BYTES_PER_PIXEL); - EINA_SAFETY_ON_NULL_RETURN_VAL(edata->vnc_screen, EINA_FALSE); - - edata->vnc_screen->newClientHook = _ecore_evas_x11_vnc_client_connection_new; - edata->vnc_screen->kbdAddEvent = _ecore_evas_x11_vnc_client_keyboard_event; - edata->vnc_screen->ptrAddEvent = _ecore_evas_x11_vnc_client_pointer_event; - - //This enables multiple client connections at the same time. - edata->vnc_screen->alwaysShared = TRUE; - edata->vnc_screen->frameBuffer = edata->frame_buffer; - - _ecore_evas_x11_vnc_server_format_setup(edata); - - if (port > 0) - edata->vnc_screen->port = edata->vnc_screen->ipv6port= port; - - if (addr) - { - int err; - - //rfbStringToAddr() does not change the addr contents. - err = rfbStringToAddr((char *)addr, &edata->vnc_screen->listenInterface); - EINA_SAFETY_ON_TRUE_GOTO(err == 0, err_screen); - } - - rfbInitServer(edata->vnc_screen); - if (edata->vnc_screen->listenSock >= 0) - { - edata->vnc_listen_handler = ecore_main_fd_handler_add(edata->vnc_screen->listenSock, - ECORE_FD_READ, - _ecore_evas_x11_vnc_socket_listen_activity, - edata->vnc_screen, NULL, - NULL); - EINA_SAFETY_ON_NULL_GOTO(edata->vnc_listen_handler, err_listen); - can_listen = EINA_TRUE; - } - - if (edata->vnc_screen->listen6Sock >= 0) - { - edata->vnc_listen6_handler = ecore_main_fd_handler_add(edata->vnc_screen->listen6Sock, - ECORE_FD_READ, - _ecore_evas_x11_vnc_socket_listen_activity, - edata->vnc_screen, - NULL, NULL); - EINA_SAFETY_ON_NULL_GOTO(edata->vnc_listen6_handler, err_listen6); - can_listen = EINA_TRUE; - } - - //rfbInitServer() failed and could not setup the sockets. - EINA_SAFETY_ON_FALSE_GOTO(can_listen, err_listen); - - edata->vnc_screen->screenData = ee; - edata->accept_cb_data = data; - edata->accept_cb = cb; - - return EINA_TRUE; - - err_listen6: - ecore_main_fd_handler_del(edata->vnc_listen_handler); - edata->vnc_listen_handler = NULL; - err_listen: - rfbScreenCleanup(edata->vnc_screen); - edata->vnc_screen = NULL; - err_screen: - return EINA_FALSE; -#else - (void)ee; - (void)addr; - (void)port; - (void)cb; - (void)data; - return EINA_FALSE; -#endif -} - -static Eina_Bool -_ecore_evas_x11_vnc_stop(Ecore_Evas *ee) -{ -#ifdef ENABLE_VNC_SERVER - Ecore_Evas_Engine_Data_X11 *edata = ee->engine.data; - - if (!edata->vnc_screen) - return EINA_FALSE; - - ecore_main_fd_handler_del(edata->vnc_listen6_handler); - ecore_main_fd_handler_del(edata->vnc_listen_handler); - rfbScreenCleanup(edata->vnc_screen); - edata->vnc_screen = NULL; - return EINA_TRUE; -#else - (void)ee; - return EINA_FALSE; -#endif -} - static Ecore_Evas_Interface_X11 * _ecore_evas_x_interface_x11_new(void) { @@ -5792,8 +5153,6 @@ _ecore_evas_x_interface_x11_new(void) iface->shape_input_empty = _ecore_evas_x11_shape_input_empty; iface->shape_input_reset = _ecore_evas_x11_shape_input_reset; iface->shape_input_apply = _ecore_evas_x11_shape_input_apply; - iface->vnc_start = _ecore_evas_x11_vnc_start; - iface->vnc_stop = _ecore_evas_x11_vnc_stop; return iface; } diff --git a/src/modules/ecore_evas/vnc_server/ecore_evas_vnc_server.c b/src/modules/ecore_evas/vnc_server/ecore_evas_vnc_server.c new file mode 100644 index 0000000000..63df8d08b8 --- /dev/null +++ b/src/modules/ecore_evas/vnc_server/ecore_evas_vnc_server.c @@ -0,0 +1,722 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ecore_private.h" +#include "ecore_evas_private.h" + +static int _ecore_evas_vnc_server_log_dom; +static unsigned int _available_seat = 1; + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ + +#ifdef WRN +#undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_ecore_evas_vnc_server_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_ecore_evas_vnc_server_log_dom, __VA_ARGS__) + +typedef struct _Ecore_Evas_Vnc_Server { + char *frame_buffer; + rfbScreenInfoPtr vnc_screen; + Ecore_Fd_Handler *vnc_listen_handler; + Ecore_Fd_Handler *vnc_listen6_handler; + Ecore_Evas_Vnc_Client_Accept_Cb accept_cb; + void *accept_cb_data; + Ecore_Evas *ee; + double double_click_time; + int last_w; + int last_h; +} Ecore_Evas_Vnc_Server; + +typedef struct _Ecore_Evas_Vnc_Server_Client_Data { + Ecore_Fd_Handler *handler; + Evas_Device *keyboard; + Evas_Device *mouse; + Evas_Device *seat; + unsigned int key_modifiers; + time_t last_mouse_button_down; + Eina_Bool double_click; + Eina_Bool triple_click; +} Ecore_Evas_Vnc_Server_Client_Data; + +#define VNC_BITS_PER_SAMPLE (8) +#define VNC_SAMPLES_PER_PIXEL (3) +#define VNC_BYTES_PER_PIXEL (4) + +static void +_ecore_evas_vnc_server_update_clients(rfbScreenInfoPtr vnc_screen) +{ + rfbClientIteratorPtr itr; + rfbClientRec *client; + + itr = rfbGetClientIterator(vnc_screen); + + //No clients. + if (!itr) return; + + while ((client = rfbClientIteratorNext(itr))) { + rfbBool r; + + r = rfbUpdateClient(client); + + if (!r) + { + Ecore_Evas_Vnc_Server_Client_Data *cdata = client->clientData; + + WRN("Could not update the VNC client on seat '%s'\n", + evas_device_name_get(cdata->seat)); + } + + //Client disconnected + if (client->sock == -1) rfbClientConnectionGone(client); + } + + rfbReleaseClientIterator(itr); +} + +static void +_ecore_evas_vnc_server_format_setup(rfbScreenInfoPtr vnc_screen) +{ + int aux; + + //FIXME: Using BGR - Is there a better way to do this? + aux = vnc_screen->serverFormat.redShift; + vnc_screen->serverFormat.redShift = vnc_screen->serverFormat.blueShift; + vnc_screen->serverFormat.blueShift = aux; +} + +static Eina_Bool +_ecore_evas_vnc_server_socket_listen_activity(void *data, + Ecore_Fd_Handler *fd_handler EINA_UNUSED) +{ + rfbProcessNewConnection(data); + return ECORE_CALLBACK_RENEW; +} + +static void +_ecore_evas_vnc_server_client_gone(rfbClientRec *client) +{ + Ecore_Evas_Vnc_Server_Client_Data *cdata = client->clientData; + + DBG("VNC client on seat '%s' gone", evas_device_name_get(cdata->seat)); + + ecore_main_fd_handler_del(cdata->handler); + evas_device_del(cdata->keyboard); + evas_device_del(cdata->mouse); + evas_device_del(cdata->seat); + free(cdata); + _available_seat--; +} + +static Eina_Bool +_ecore_evas_vnc_server_client_activity(void *data, + Ecore_Fd_Handler *fd_handler EINA_UNUSED) +{ + rfbClientRec *client = data; + rfbScreenInfoPtr screen = client->screen; + + rfbProcessClientMessage(client); + + //macro from rfb.h + if (screen->frameBuffer && FB_UPDATE_PENDING(client)) + rfbSendFramebufferUpdate(client, client->modifiedRegion); + + //Client disconnected. + if (client->sock == -1) + { + rfbClientConnectionGone(client); + return ECORE_CALLBACK_DONE; + } + + return ECORE_CALLBACK_RENEW; +} + +static enum rfbNewClientAction +_ecore_evas_vnc_server_client_connection_new(rfbClientRec *client) +{ + Ecore_Evas_Vnc_Server *server; + Ecore_Evas_Vnc_Server_Client_Data *cdata; + char buf[32]; + + EINA_SAFETY_ON_TRUE_RETURN_VAL(_available_seat == UINT_MAX, + RFB_CLIENT_REFUSE); + + server = client->screen->screenData; + + if (server->accept_cb && !server->accept_cb(server->accept_cb_data, + server->ee, client->host)) + return RFB_CLIENT_REFUSE; + + cdata = calloc(1, sizeof(Ecore_Evas_Vnc_Server_Client_Data)); + EINA_SAFETY_ON_NULL_RETURN_VAL(cdata, RFB_CLIENT_REFUSE); + + cdata->handler = ecore_main_fd_handler_add(client->sock, ECORE_FD_READ, + _ecore_evas_vnc_server_client_activity, + client, NULL, NULL); + EINA_SAFETY_ON_NULL_GOTO(cdata->handler, err_handler); + + snprintf(buf, sizeof(buf), "seat-%u", _available_seat); + + cdata->seat = evas_device_add_full(server->ee->evas, buf, + "A remote VNC seat", + NULL, NULL, EVAS_DEVICE_CLASS_SEAT, + EVAS_DEVICE_SUBCLASS_NONE); + EINA_SAFETY_ON_NULL_GOTO(cdata->seat, err_handler); + cdata->keyboard = evas_device_add_full(server->ee->evas, "Keyboard", + "A remote VNC keyboard", + cdata->seat, NULL, + EVAS_DEVICE_CLASS_KEYBOARD, + EVAS_DEVICE_SUBCLASS_NONE); + EINA_SAFETY_ON_NULL_GOTO(cdata->keyboard, err_dev); + cdata->mouse = evas_device_add_full(server->ee->evas, "Mouse", + "A remote VNC mouse", + cdata->seat, NULL, + EVAS_DEVICE_CLASS_MOUSE, + EVAS_DEVICE_SUBCLASS_NONE); + EINA_SAFETY_ON_NULL_GOTO(cdata->mouse, err_mouse); + client->clientGoneHook = _ecore_evas_vnc_server_client_gone; + client->clientData = cdata; + + DBG("New VNC client on seat '%u'", _available_seat); + _available_seat++; + + return RFB_CLIENT_ACCEPT; + + err_mouse: + evas_device_del(cdata->keyboard); + err_dev: + evas_device_del(cdata->seat); + err_handler: + free(cdata); + return RFB_CLIENT_REFUSE; +} + +static unsigned int +_ecore_evas_vnc_server_modifier_to_ecore_modifier(int mod) +{ + if (mod == XK_Shift_L || mod == XK_Shift_R) + return ECORE_EVENT_MODIFIER_SHIFT; + if (mod == XK_Control_L || mod == XK_Control_R) + return ECORE_EVENT_MODIFIER_CTRL; + if (mod == XK_Alt_L || mod == XK_Alt_R) + return ECORE_EVENT_MODIFIER_ALT; + if (mod == XK_Super_L || mod == XK_Super_R) + return ECORE_EVENT_MODIFIER_WIN; + if (mod == XK_Scroll_Lock) + return ECORE_EVENT_LOCK_SCROLL; + if (mod == XK_Num_Lock) + return ECORE_EVENT_LOCK_NUM; + if (mod == XK_Caps_Lock) + return ECORE_EVENT_LOCK_CAPS; + if (mod == XK_Shift_Lock) + return ECORE_EVENT_LOCK_SHIFT; + return 0; +} + +static void +_ecore_evas_vnc_server_ecore_event_generic_free(void *user_data, + void *func_data) +{ + efl_unref(user_data); + free(func_data); +} + +static void +_ecore_evas_vnc_server_client_keyboard_event(rfbBool down, + rfbKeySym key, + rfbClientRec *client) +{ + Ecore_Event_Key *e; + Ecore_Evas_Vnc_Server_Client_Data *cdata = client->clientData; + rfbScreenInfoPtr screen = client->screen; + Ecore_Evas_Vnc_Server *server = screen->screenData; + const char *key_string; + char buf[10]; + + if (key >= XK_Shift_L && key <= XK_Hyper_R) + { + int mod = _ecore_evas_vnc_server_modifier_to_ecore_modifier(key); + + if (down) + cdata->key_modifiers |= mod; + else + cdata->key_modifiers &= ~mod; + } + + if (server->ee->ignore_events) + return; + + key_string = ecore_x_keysym_string_get(key); + EINA_SAFETY_ON_NULL_RETURN(key_string); + + snprintf(buf, sizeof(buf), "%lc", key); + + e = calloc(1, sizeof(Ecore_Event_Key) + strlen(buf) + 1); + EINA_SAFETY_ON_NULL_RETURN(e); + + e->timestamp = (unsigned int)time(NULL); + e->modifiers = cdata->key_modifiers; + e->same_screen = 1; + e->window = e->root_window = e->event_window = + server->ee->prop.window; + e->dev = cdata->keyboard; + efl_ref(cdata->keyboard); + e->keycode = ecore_x_keysym_keycode_get(key_string); + e->keyname = e->key = key_string; + e->compose = (char *)(e + 1); + strcpy((char *)e->compose, buf); + e->string = e->compose; + + ecore_event_add(down ? ECORE_EVENT_KEY_DOWN : ECORE_EVENT_KEY_UP, + e, _ecore_evas_vnc_server_ecore_event_generic_free, + cdata->keyboard); +} + +static int +_ecore_evas_vnc_server_pointer_button_get(int mask) +{ + int i; + for (i = 0; i < 32; i++) + if (mask >> i & 1) + return i + 1; + return 0; +} + +static void +_ecore_evas_vnc_server_client_pointer_event(int button_mask, + int x, int y, + rfbClientPtr client) +{ + Ecore_Evas_Vnc_Server_Client_Data *cdata = client->clientData; + rfbScreenInfoPtr screen = client->screen; + Ecore_Evas_Vnc_Server *server = screen->screenData; + Ecore_Event_Mouse_Move *move_event; + Ecore_Event_Mouse_Button *button_event; + Ecore_Event_Mouse_Wheel *wheel_event; + int button_changed, button, event, z = 0, direction = 0; + time_t now = time(NULL); + + if (server->ee->ignore_events) + return; + + if (client->lastPtrX != x || client->lastPtrY != y) + { + move_event = calloc(1, sizeof(Ecore_Event_Mouse_Move)); + EINA_SAFETY_ON_NULL_RETURN(move_event); + + move_event->x = move_event->multi.x = x; + move_event->y = move_event->multi.y = y; + move_event->same_screen = 1; + move_event->timestamp = (unsigned int)now; + move_event->window = move_event->event_window = + move_event->root_window = server->ee->prop.window; + move_event->multi.pressure = 1.0; + move_event->modifiers = cdata->key_modifiers; + move_event->dev = cdata->mouse; + efl_ref(cdata->mouse); + ecore_event_add(ECORE_EVENT_MOUSE_MOVE, move_event, + _ecore_evas_vnc_server_ecore_event_generic_free, + cdata->mouse); + client->lastPtrX = x; + client->lastPtrY = y; + } + + button_changed = button_mask - client->lastPtrButtons; + + if (button_changed > 0) + { + button = _ecore_evas_vnc_server_pointer_button_get(button_changed); + + switch (button) + { + case 4: + event = ECORE_EVENT_MOUSE_WHEEL; + direction = 0; //Vertical + z = -1; //Up + break; + case 5: + event = ECORE_EVENT_MOUSE_WHEEL; + direction = 0; + z = 1; //Down + break; + case 6: + event = ECORE_EVENT_MOUSE_WHEEL; + direction = 1; //Horizontal + z = -1; + break; + case 7: + event = ECORE_EVENT_MOUSE_WHEEL; + direction = 1; + z = 1; + break; + default: + event = ECORE_EVENT_MOUSE_BUTTON_DOWN; + } + + if (now - cdata->last_mouse_button_down <= 1000 * server->double_click_time) + cdata->double_click = EINA_TRUE; + else + cdata->double_click = cdata->triple_click = EINA_FALSE; + + if (now - cdata->last_mouse_button_down <= 2000 * server->double_click_time) + cdata->triple_click = EINA_TRUE; + else + cdata->triple_click = EINA_FALSE; + + cdata->last_mouse_button_down = now; + } + else if (button_changed < 0) + { + button = _ecore_evas_vnc_server_pointer_button_get(-button_changed); + //Ignore, it was already report. + if (button > 3 && button < 8) + return; + event = ECORE_EVENT_MOUSE_BUTTON_UP; + } + else + return; + + if (event == ECORE_EVENT_MOUSE_BUTTON_DOWN || + event == ECORE_EVENT_MOUSE_BUTTON_UP) + { + button_event = calloc(1, sizeof(Ecore_Event_Mouse_Button)); + EINA_SAFETY_ON_NULL_RETURN(button_event); + + button_event->timestamp = (unsigned int)now; + button_event->window = button_event->event_window = + button_event->root_window = server->ee->prop.window; + button_event->x = button_event->multi.x = x; + button_event->y = button_event->multi.y = y; + button_event->multi.pressure = 1.0; + button_event->same_screen = 1; + button_event->buttons = button; + button_event->modifiers = cdata->key_modifiers; + button_event->double_click = cdata->double_click ? 1 : 0; + button_event->triple_click = cdata->triple_click ? 1 : 0; + button_event->dev = cdata->mouse; + efl_ref(cdata->mouse); + + ecore_event_add(event, button_event, + _ecore_evas_vnc_server_ecore_event_generic_free, + cdata->mouse); + return; + } + + //Mouse wheel + wheel_event = calloc(1, sizeof(Ecore_Event_Mouse_Wheel)); + EINA_SAFETY_ON_NULL_RETURN(wheel_event); + wheel_event->dev = cdata->mouse; + efl_ref(cdata->mouse); + wheel_event->window = wheel_event->event_window = + wheel_event->root_window = server->ee->prop.window; + wheel_event->same_screen = 1; + wheel_event->modifiers = cdata->key_modifiers; + wheel_event->x = x; + wheel_event->y = y; + wheel_event->direction = direction; + wheel_event->z = z; + + ecore_event_add(event, wheel_event, + _ecore_evas_vnc_server_ecore_event_generic_free, + cdata->mouse); +} + +static Eina_Bool +_ecore_evas_vnc_server_init(void) +{ + if (!eina_init()) + { + EINA_LOG_ERR("Could not init Eina"); + return EINA_FALSE; + } + + if (!ecore_init()) + { + EINA_LOG_ERR("Could not init Ecore"); + goto err_ecore; + } + + if (!ecore_evas_init()) + { + EINA_LOG_ERR("Could not init Ecore_Evas"); + goto err_ecore_evas; + } + + if (!ecore_event_init()) + { + EINA_LOG_ERR("Could not init Ecore_Event"); + goto err_ecore_event; + } + + _ecore_evas_vnc_server_log_dom = eina_log_domain_register("Ecore_Evas_Vnc_Server", + EINA_COLOR_LIGHTBLUE); + if (_ecore_evas_vnc_server_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: Ecore_Evas_Vnc_Server"); + goto err_domain; + } + + return EINA_TRUE; + + err_domain: + ecore_event_shutdown(); + err_ecore_event: + ecore_evas_shutdown(); + err_ecore_evas: + ecore_shutdown(); + err_ecore: + eina_shutdown(); + return EINA_FALSE; +} + +static void +_ecore_evas_vnc_server_shutdown(void) +{ + eina_log_domain_unregister(_ecore_evas_vnc_server_log_dom); + _ecore_evas_vnc_server_log_dom = -1; + ecore_event_shutdown(); + ecore_evas_shutdown(); + ecore_shutdown(); + eina_shutdown(); +} + +static void +_ecore_evas_vnc_server_draw(Evas *evas, int x, int y, + int w, int h, const void *pixels) +{ + Ecore_Evas_Vnc_Server *server; + Ecore_Evas *ee; + size_t size, src_stride; + int dy; + Eina_Bool new_buf = EINA_FALSE; + + ee = evas_data_attach_get(evas); + server = ee->vnc_server; + + if (!server->frame_buffer || server->last_w != ee->w || server->last_h != ee->h) + { + char *new_fb; + + size = ee->w * ee->h * VNC_BYTES_PER_PIXEL; + new_fb = malloc(size); + EINA_SAFETY_ON_NULL_RETURN(new_fb); + free(server->frame_buffer); + server->frame_buffer = new_fb; + server->last_w = ee->w; + server->last_h = ee->h; + new_buf = EINA_TRUE; + + rfbNewFramebuffer(server->vnc_screen, server->frame_buffer, ee->w, + ee->h, VNC_BITS_PER_SAMPLE, VNC_SAMPLES_PER_PIXEL, + VNC_BYTES_PER_PIXEL); + _ecore_evas_vnc_server_format_setup(server->vnc_screen); + } + + if (y > server->last_h || x > server->last_w) + return; + + //Do not paint outside the VNC canvas + if (y + h > server->last_h) + h = server->last_h - y; + + //Do not paint outside the VNC canvas + if (x + w > server->last_w) + w = server->last_w - x; + + src_stride = w * VNC_BYTES_PER_PIXEL; + + for (dy = 0; dy < h; dy++) + { + memcpy(server->frame_buffer + (x * VNC_BYTES_PER_PIXEL) + + ((dy + y) * (server->last_w * VNC_BYTES_PER_PIXEL)), + (char *)pixels + (dy * src_stride), src_stride); + } + + //We did not receive the whole buffer yet, zero the missing bytes for now. + if (new_buf) + { + //Missing width + if (server->last_w != w || x != 0) + { + for (dy = 0; dy < h; dy++) + { + if (x) + { + memset(server->frame_buffer + + ((dy + y) * (server->last_w * VNC_BYTES_PER_PIXEL)), + 0, x * VNC_BYTES_PER_PIXEL); + } + + memset(server->frame_buffer + + ((dy + y) * (server->last_w * VNC_BYTES_PER_PIXEL)) + + ((x + w) * VNC_BYTES_PER_PIXEL), + 0, (server->last_w - (w + x)) * VNC_BYTES_PER_PIXEL); + } + } + + //Missing height + if (server->last_h != h || y != 0) + { + src_stride = server->last_w * VNC_BYTES_PER_PIXEL; + for (dy = 0; dy < y; dy++) + memset(server->frame_buffer + (dy * src_stride), 0, src_stride); + + for (dy = y + h; dy < server->last_h; dy++) + memset(server->frame_buffer + (dy * src_stride), 0, src_stride); + } + } + + rfbMarkRectAsModified(server->vnc_screen, x, y, x+w, y+h); + _ecore_evas_vnc_server_update_clients(server->vnc_screen); +} + +EAPI Ecore_Evas_Vnc_Server * +ecore_evas_vnc_server_new(Ecore_Evas *ee, int port, const char *addr, + Ecore_Evas_Vnc_Client_Accept_Cb cb, void *data) +{ + Ecore_Evas_Vnc_Server *server; + Eina_Bool can_listen = EINA_FALSE; + Evas_Engine_Info_Software_X11 *einfo; + Eina_Bool err; + + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, NULL); + + EINA_SAFETY_ON_TRUE_RETURN_VAL(strcmp(ee->driver, "software_x11"), NULL); + + einfo = (Evas_Engine_Info_Software_X11 *)evas_engine_info_get(ee->evas); + EINA_SAFETY_ON_NULL_RETURN_VAL(einfo, NULL); + + server = calloc(1, sizeof(Ecore_Evas_Vnc_Server)); + EINA_SAFETY_ON_NULL_RETURN_VAL(server, NULL); + + server->vnc_screen = rfbGetScreen(0, NULL, ee->w, ee->h, VNC_BITS_PER_SAMPLE, + VNC_SAMPLES_PER_PIXEL, VNC_BYTES_PER_PIXEL); + EINA_SAFETY_ON_NULL_GOTO(server->vnc_screen, err_screen); + + server->vnc_screen->newClientHook = _ecore_evas_vnc_server_client_connection_new; + server->vnc_screen->kbdAddEvent = _ecore_evas_vnc_server_client_keyboard_event; + server->vnc_screen->ptrAddEvent = _ecore_evas_vnc_server_client_pointer_event; + + //This enables multiple client connections at the same time. + server->vnc_screen->alwaysShared = TRUE; + server->vnc_screen->frameBuffer = server->frame_buffer; + + _ecore_evas_vnc_server_format_setup(server->vnc_screen); + + if (port > 0) + server->vnc_screen->port = server->vnc_screen->ipv6port= port; + + if (addr) + { + int err; + + //rfbStringToAddr() does not change the addr contents. + err = rfbStringToAddr((char *)addr, &server->vnc_screen->listenInterface); + EINA_SAFETY_ON_TRUE_GOTO(err == 0, err_addr); + } + + rfbInitServer(server->vnc_screen); + if (server->vnc_screen->listenSock >= 0) + { + server->vnc_listen_handler = ecore_main_fd_handler_add(server->vnc_screen->listenSock, + ECORE_FD_READ, + _ecore_evas_vnc_server_socket_listen_activity, + server->vnc_screen, + NULL, NULL); + EINA_SAFETY_ON_NULL_GOTO(server->vnc_listen_handler, err_addr); + can_listen = EINA_TRUE; + } + + if (server->vnc_screen->listen6Sock >= 0) + { + server->vnc_listen6_handler = ecore_main_fd_handler_add(server->vnc_screen->listen6Sock, + ECORE_FD_READ, + _ecore_evas_vnc_server_socket_listen_activity, + server->vnc_screen, + NULL, NULL); + EINA_SAFETY_ON_NULL_GOTO(server->vnc_listen6_handler, err_listen); + can_listen = EINA_TRUE; + } + + //rfbInitServer() failed and could not setup the sockets. + EINA_SAFETY_ON_FALSE_GOTO(can_listen, err_addr); + + einfo->func.region_push_hook = _ecore_evas_vnc_server_draw; + err = evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo); + EINA_SAFETY_ON_FALSE_GOTO(err, err_engine); + + server->vnc_screen->screenData = server; + server->accept_cb_data = data; + server->accept_cb = cb; + server->ee = ee; + + return server; + + err_engine: + einfo->func.region_push_hook = NULL; + ecore_main_fd_handler_del(server->vnc_listen6_handler); + err_listen: + ecore_main_fd_handler_del(server->vnc_listen_handler); + err_addr: + rfbScreenCleanup(server->vnc_screen); + err_screen: + free(server); + return NULL; +} + +EAPI void +ecore_evas_vnc_server_del(Ecore_Evas_Vnc_Server *server) +{ + EINA_SAFETY_ON_NULL_RETURN(server); + + ecore_main_fd_handler_del(server->vnc_listen6_handler); + ecore_main_fd_handler_del(server->vnc_listen_handler); + free(server->frame_buffer); + rfbScreenCleanup(server->vnc_screen); + free(server); +} + +EINA_MODULE_INIT(_ecore_evas_vnc_server_init); +EINA_MODULE_SHUTDOWN(_ecore_evas_vnc_server_shutdown);