summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuilherme Iscaro <iscaro@profusion.mobi>2016-09-21 13:52:57 -0300
committerBruno Dilly <bdilly@profusion.mobi>2016-09-26 22:06:59 -0300
commitbc6e8d2692084a808c0cf7012e7db4a9849601e7 (patch)
treed8482ed0b7b353ecde9096e1171727a3db3ef90a
parent7e2d700d06ff970d68ae208d59bca76f6ea07467 (diff)
downloadefl-bc6e8d2692084a808c0cf7012e7db4a9849601e7.tar.gz
Ecore_Evas_X: Add VNC draw support.
This patch adds the support to draw the X11 screen contents to all remove VNC clients.
-rw-r--r--configure.ac24
-rw-r--r--src/lib/ecore_evas/Ecore_Evas.h36
-rw-r--r--src/lib/ecore_evas/ecore_evas.c31
-rw-r--r--src/lib/ecore_evas/ecore_evas_x11.h4
-rw-r--r--src/modules/ecore_evas/engines/x/ecore_evas_x.c353
5 files changed, 446 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 61d3c9d7f9..9c187806bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -433,6 +433,24 @@ AC_DEFINE_IF([ENABLE_LIBLZ4], [test "${want_liblz4}" = "yes"], [1], [Use liblz4
AC_SUBST([want_liblz4])
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@:>@])],
+ [
+ if test "x${enableval}" = "xyes" ; then
+ want_vnc_server="yes"
+ else
+ want_vnc_server="no"
+ fi
+ ],
+ [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):
@@ -4622,7 +4640,8 @@ AM_CONDITIONAL([BUILD_ECORE_EVAS_WIN32],
# XXX TODO: ecore_evas_x11
-ECORE_EVAS_MODULE([software-x11], [${want_x11_any}])
+ECORE_EVAS_MODULE([software-x11], [${want_x11_any}],
+ [EFL_OPTIONAL_DEPEND_PKG([ECORE_EVAS], [${want_vnc_server}], [VNC_SERVER], [libvncserver])])
have_ecore_evas_software_xlib="no"
have_ecore_evas_software_xcb="no"
@@ -4645,7 +4664,8 @@ fi
# XXX TODO: ecore_evas_opengl_x11
-ECORE_EVAS_MODULE([opengl-x11], [${want_x11_any_opengl}])
+ECORE_EVAS_MODULE([opengl-x11], [${want_x11_any_opengl}],
+ [EFL_OPTIONAL_DEPEND_PKG([ECORE_EVAS], [${want_vnc_server}], [VNC_SERVER], [libvncserver])])
have_ecore_evas_opengl_xlib="no"
have_ecore_evas_opengl_xcb="no"
diff --git a/src/lib/ecore_evas/Ecore_Evas.h b/src/lib/ecore_evas/Ecore_Evas.h
index 30e45b1756..d9827ac4bf 100644
--- a/src/lib/ecore_evas/Ecore_Evas.h
+++ b/src/lib/ecore_evas/Ecore_Evas.h
@@ -2398,6 +2398,42 @@ EAPI void ecore_evas_x11_shape_input_reset(Ecore_Evas *ee);
EAPI void ecore_evas_x11_shape_input_apply(Ecore_Evas *ee);
/**
+ * @brief A callback used to accept a new client.
+ * @param data The callback data
+ * @param ee The Ecore_Evas
+ * @param client_host The address of the new client.
+ * @return @c EINA_TRUE to accep the client, @c EINA_FALSE otherwise.
+ * @see ecore_evas_vnc_start()
+ * @since 1.19
+ */
+typedef Eina_Bool (*Ecore_Evas_Vnc_Client_Accept_Cb)(void *data, Ecore_Evas *ee, const char *client_host);
+
+/**
+ * @brief Starts a VNC server.
+ *
+ * @param ee The Ecore_Evas to start the VNC server
+ * @param addr The address that will be used to bind the VNC server. Use @c NULL to bind to any interface.
+ * @param port The port number to start the VNC server. Use @c -1 to set the default VNC port (5900)
+ * @param cb A callback used to accept a new client. If @c NULL all clients will be accepted.
+ * @param data Data to @a cb
+ * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
+ * @see ecore_evas_vnc_stop()
+ * @see Ecore_Evas_Vnc_Client_Accept_Cb()
+ * @since 1.19
+ */
+EAPI Eina_Bool ecore_evas_vnc_start(Ecore_Evas *ee, const char *addr, int port, Ecore_Evas_Vnc_Client_Accept_Cb cb, void *data);
+
+/**
+ * @brief Stop a running VNC server
+ *
+ * @param ee Ecore_Evas to stop the VNC server
+ * @return @c EINA_TRUE if the VNC server was stopped, @c EINA_FALSE otherwise.
+ * @see ecore_evas_vnc_start()
+ * @since 1.19
+ */
+EAPI Eina_Bool ecore_evas_vnc_stop(Ecore_Evas *ee);
+
+/**
* @defgroup Ecore_Evas_Ews Ecore_Evas Single Process Windowing System.
* @ingroup Ecore_Evas_Group
*
diff --git a/src/lib/ecore_evas/ecore_evas.c b/src/lib/ecore_evas/ecore_evas.c
index 72e46992cd..979a6ae947 100644
--- a/src/lib/ecore_evas/ecore_evas.c
+++ b/src/lib/ecore_evas/ecore_evas.c
@@ -3955,6 +3955,37 @@ ecore_evas_x11_shape_input_apply(Ecore_Evas *ee)
iface->shape_input_apply(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_Interface_X11 *iface;
+
+ 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_start, EINA_FALSE);
+
+ return iface->vnc_start(ee, addr, port, cb, data);
+}
+
+EAPI Eina_Bool
+ecore_evas_vnc_stop(Ecore_Evas *ee)
+{
+ Ecore_Evas_Interface_X11 *iface;
+
+ 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);
+}
+
EAPI Ecore_Evas *
ecore_evas_extn_socket_new(int w, int h)
{
diff --git a/src/lib/ecore_evas/ecore_evas_x11.h b/src/lib/ecore_evas/ecore_evas_x11.h
index b349b7f2ac..7d5e809d04 100644
--- a/src/lib/ecore_evas/ecore_evas_x11.h
+++ b/src/lib/ecore_evas/ecore_evas_x11.h
@@ -17,6 +17,10 @@ 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 e3bfaa89f9..88086e2401 100644
--- a/src/modules/ecore_evas/engines/x/ecore_evas_x.c
+++ b/src/modules/ecore_evas/engines/x/ecore_evas_x.c
@@ -26,6 +26,11 @@
#include "ecore_evas_private.h"
#include "ecore_evas_x11.h"
+#ifdef ENABLE_VNC_SERVER
+# include <rfb/rfb.h>
+# include <rfb/rfbregion.h>
+#endif
+
#ifdef EAPI
# undef EAPI
#endif
@@ -131,6 +136,17 @@ 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);
@@ -152,6 +168,123 @@ 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;
+} 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 '%p'\n", cdata);
+ }
+
+ //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;
+
+ 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);
+ memcpy(new_fb, pixels, size);
+ edata->frame_buffer = new_fb;
+ edata->last_w = ee->w;
+ edata->last_h = ee->h;
+ 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);
+ }
+ }
+ else
+ {
+ //Partial update
+ int dy;
+ const int 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);
+ }
+ }
+
+ 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)
{
@@ -2059,6 +2192,16 @@ _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();
@@ -4110,6 +4253,7 @@ 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)
{
@@ -4298,6 +4442,7 @@ 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)
@@ -5131,6 +5276,211 @@ _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 '%p' gone", cdata);
+
+ ecore_main_fd_handler_del(cdata->handler);
+ 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;
+
+ 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_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);
+
+ 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_handler:
+ free(cdata);
+ return RFB_CLIENT_REFUSE;
+}
+
+static void
+_ecore_evas_x11_vnc_client_keyboard_event(rfbBool down EINA_UNUSED,
+ rfbKeySym keySym EINA_UNUSED,
+ rfbClientRec *client EINA_UNUSED)
+{
+ EDBG("Keyboard event\n");
+}
+
+static void
+_ecore_evas_x11_vnc_client_pointer_event(int buttonMask EINA_UNUSED,
+ int x EINA_UNUSED, int y EINA_UNUSED,
+ rfbClientPtr client EINA_UNUSED)
+{
+ EDBG("Pointer event\n");
+}
+#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)
{
@@ -5151,6 +5501,8 @@ _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;
}
@@ -5205,3 +5557,4 @@ _ecore_evas_x_interface_gl_x11_new(void)
return iface;
}
#endif
+