diff --git a/graphics/Kconfig b/graphics/Kconfig index 40344d6ef74..02e02052299 100644 --- a/graphics/Kconfig +++ b/graphics/Kconfig @@ -745,4 +745,7 @@ config NXSTART_VPLANE endif # NX_NXSTART endif # NX_MULTIUSER + +source "graphics/vnc/Kconfig" + endif # NX diff --git a/graphics/Makefile b/graphics/Makefile index a8111002329..f23072a75c0 100644 --- a/graphics/Makefile +++ b/graphics/Makefile @@ -50,6 +50,7 @@ include nxbe/Make.defs include nxmu/Make.defs include nxsu/Make.defs include nxterm/Make.defs +include vnc/Make.defs AOBJS = $(ASRCS:.S=$(OBJEXT)) COBJS = $(CSRCS:.c=$(OBJEXT)) diff --git a/graphics/README.txt b/graphics/README.txt index ab24d090d6c..3c61000cb15 100644 --- a/graphics/README.txt +++ b/graphics/README.txt @@ -112,6 +112,10 @@ nuttx/../NxWidgets The NxWidgets code is provided as a separate package located outside of the NuttX source tree (probably at this location). +graphics/vnc + The future home of the VNC Remote Frame Buffer (RFB) server and client + implementations. + Installing New Fonts ^^^^^^^^^^^^^^^^^^^^ @@ -295,6 +299,8 @@ CONFIG_NXTK_AUTORAISE CONFIG_NXFONTS_CHARBITS The number of bits in the character set. Current options are only 7 and 8. The default is 7. +CONFIG_VNCSERVER and CONFIG_VNCCLIENT + Enable the VNC RFB server and client, respecitively. Font Selections --------------- diff --git a/graphics/vnc/Kconfig b/graphics/vnc/Kconfig new file mode 100644 index 00000000000..fed2283d62d --- /dev/null +++ b/graphics/vnc/Kconfig @@ -0,0 +1,7 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +source "graphics/vnc/server/Kconfig" +source "graphics/vnc/client/Kconfig" diff --git a/graphics/vnc/Make.defs b/graphics/vnc/Make.defs new file mode 100644 index 00000000000..cd9866723c7 --- /dev/null +++ b/graphics/vnc/Make.defs @@ -0,0 +1,37 @@ +############################################################################ +# graphics/vnc/Make.defs +# +# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +include vnc/server/Make.defs +include vnc/client/Make.defs diff --git a/graphics/vnc/client/Kconfig b/graphics/vnc/client/Kconfig new file mode 100644 index 00000000000..212cb5cce8e --- /dev/null +++ b/graphics/vnc/client/Kconfig @@ -0,0 +1,15 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig VNCCLIENT + bool "VNC client" + default n + depends on NET_TCP && !NX_LCDDRIVER && !VNC_SERVER && EXPERIMENTAL + ---help--- + Enable support for a VNC Remote Frame Buffer (RFB) client + +if VNCCLIENT + +endif # VNCCLIENT diff --git a/graphics/vnc/client/Make.defs b/graphics/vnc/client/Make.defs new file mode 100644 index 00000000000..03d6f5b5aee --- /dev/null +++ b/graphics/vnc/client/Make.defs @@ -0,0 +1,42 @@ +############################################################################ +# graphics/vnc/client/Make.defs +# +# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_VNCCLIENT),y) + +DEPPATH += --dep-path vnc/client +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)/graphics/vnc/client} +VPATH += :vnc/client + +endif diff --git a/graphics/vnc/server/Kconfig b/graphics/vnc/server/Kconfig new file mode 100644 index 00000000000..1a5ad3ad713 --- /dev/null +++ b/graphics/vnc/server/Kconfig @@ -0,0 +1,106 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig VNCSERVER + bool "VNC server" + default n + depends on NET_TCP && !NX_LCDDRIVER && EXPERIMENTAL + select NET_TCP_READAHEAD + select NX_UPDATE + ---help--- + Enable support for a VNC Remote Frame Buffer (RFB) server. + +if VNCSERVER + +choice + prompt "VNC server protocol" + default VNCSERVER_PROTO3p3 + +config VNCSERVER_PROTO3p3 + bool "Version 3.3" + +config VNCSERVER_PROTO3p8 + bool "Version 3.8" + depends on EXPERIMENTAL + +endchoice # VNC server protocol + +config VNCSERVER_NDISPLAYS + int "Number of displays" + default 1 + range 1 99 + ---help--- + Specifies the number of RFB displays supported by the server. + Normally this should be one. + +config VNCSERVER_PRIO + int "VNC server task priority" + default 100 + +config VNCSERVER_STACKSIZE + int "VNC server stack size" + default 2048 + +config VNCSERVER_UPDATER_PRIO + int "VNC updater thread priority" + default 100 + +config VNCSERVER_UPDATER_STACKSIZE + int "VNC updater thread stack size" + default 2048 + +choice + prompt "VNC color format" + default VNCSERVER_COLORFMT_RGB16 + +config VNCSERVER_COLORFMT_RGB16 + bool "RGB16 5:6:5" + +config VNCSERVER_COLORFMT_RGB32 + bool "RGB24 (32-bit) or RGB32 (w/tranparency)" + +endchoice # VNC color format + +config VNCSERVER_SCREENWIDTH + int "Framebuffer width (pixels)" + default 320 + +config VNCSERVER_SCREENHEIGHT + int "Framebuffer height (rows)" + default 240 + +config VNCSERVER_NUPDATES + int "Number of pre-allocate update structures" + default 48 + ---help--- + This setting provides the number of pre-allocated update structures + that will be used. Dynamic memory allocations are never made. In + the likely event that we run out of update structures, the graphics + subsystem will pause and wait for the next structures to be released. + + Overhead is 12-bytes per update structure. + +config VNCSERVER_UPDATE_BUFSIZE + int "Max update buffer size (bytes)" + default 4096 + ---help--- + A single buffer is pre-allocated for rendering updates. This + setting specifies the maximum in bytes of that update buffer. For + example, an update buffers of 32 pixels at 32-bits per pixel and + 32-rows would yield a buffer size of 4096. + +config VNCSERVER_KBDENCODE + bool "Encode keyboard input" + default n + depends on NXTERM_NXKBDIN + ---help--- + Use a special encoding of keyboard characters as defined in + include/nuttx/input/kbd_coded.h. + +config VNCSERVER_INBUFFER_SIZE + int "Input buffer size + default 80 + +endif # VNCSERVER diff --git a/graphics/vnc/server/Make.defs b/graphics/vnc/server/Make.defs new file mode 100644 index 00000000000..263be54df6e --- /dev/null +++ b/graphics/vnc/server/Make.defs @@ -0,0 +1,48 @@ +############################################################################ +# graphics/vnc/server/Make.defs +# +# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_VNCSERVER),y) + +CSRCS += vnc_server.c vnc_negotiate.c vnc_updater.c vnc_receiver.c vnc_fbdev.c + +ifeq ($(CONFIG_NX_KBD),y) +CSRCS += vnc_keymap.c +endif + +DEPPATH += --dep-path vnc/server +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)/graphics/vnc/server} +VPATH += :vnc/server + +endif diff --git a/graphics/vnc/server/vnc_fbdev.c b/graphics/vnc/server/vnc_fbdev.c new file mode 100644 index 00000000000..2d67f980cab --- /dev/null +++ b/graphics/vnc/server/vnc_fbdev.c @@ -0,0 +1,566 @@ +/**************************************************************************** + * graphics/vnc/server/vnc_fbdev.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "vnc_server.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure provides the frame buffer interface and also incapulates + * information about the frame buffer instances for each display. + */ + +struct vnc_fbinfo_s +{ + /* The publically visible frame buffer interface. This must appear first + * so that struct vnc_fbinfo_s is cast compatible with struct fb_vtable_s. + */ + + struct fb_vtable_s vtable; + + /* Our private per-display information */ + + bool initialized; /* True: This instance has been initialized */ + uint8_t display; /* Display number */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Get information about the video controller configuration and the + * configuration of each color plane. + */ + +static int up_getvideoinfo(FAR struct fb_vtable_s *vtable, + FAR struct fb_videoinfo_s *vinfo); +static int up_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno, + FAR struct fb_planeinfo_s *pinfo); + +/* The following are provided only if the video hardware supports RGB color + * mapping. + */ + +#ifdef CONFIG_FB_CMAP +static int up_getcmap(FAR struct fb_vtable_s *vtable, + FAR struct fb_cmap_s *cmap); +static int up_putcmap(FAR struct fb_vtable_s *vtable, + FAR const struct fb_cmap_s *cmap); +#endif + +/* The following are provided only if the video hardware supports a hardware + * cursor. + */ + +#ifdef CONFIG_FB_HWCURSOR +static int up_getcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_cursorattrib_s *attrib); +static int up_setcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_setcursor_s *setttings); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Current cursor position */ + +#ifdef CONFIG_FB_HWCURSOR +static struct fb_cursorpos_s g_cpos; + +/* Current cursor size */ + +#ifdef CONFIG_FB_HWCURSORSIZE +static struct fb_cursorsize_s g_csize; +#endif +#endif + +/* The framebuffer object -- There is no private state information in this simple + * framebuffer simulation. + */ + +static struct vnc_fbinfo_s g_fbinfo[RFB_MAX_DISPLAYS]; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_getvideoinfo + ****************************************************************************/ + +static int up_getvideoinfo(FAR struct fb_vtable_s *vtable, + FAR struct fb_videoinfo_s *vinfo) +{ + FAR struct vnc_fbinfo_s *fbinfo = (FAR struct vnc_fbinfo_s *)vtable; + FAR struct vnc_session_s *session; + + gvdbg("vtable=%p vinfo=%p\n", vtable, vinfo); + + DEBUGASSERT(fbinfo != NULL && vinfo != NULL); + if (fbinfo != NULL && vinfo != NULL) + { + session = vnc_find_session(fbinfo->display); + if (session == NULL || session->state != VNCSERVER_RUNNING) + { + gdbg("ERROR: session is not connected\n"); + return -ENOTCONN; + } + + /* Return the requested video info. We are committed to using the + * configured color format in the framebuffer, but performing color + * conversions on the fly for the remote framebuffer as necessary. + */ + + vinfo->fmt = RFB_COLORFMT; + vinfo->xres = CONFIG_VNCSERVER_SCREENWIDTH; + vinfo->yres = CONFIG_VNCSERVER_SCREENHEIGHT; + vinfo->nplanes = 1; + + return OK; + } + + gdbg("ERROR: Invalid arguments\n"); + return -EINVAL; +} + +/**************************************************************************** + * Name: up_getplaneinfo + ****************************************************************************/ + +static int up_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno, + FAR struct fb_planeinfo_s *pinfo) +{ + FAR struct vnc_fbinfo_s *fbinfo = (FAR struct vnc_fbinfo_s *)vtable; + FAR struct vnc_session_s *session; + + gvdbg("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo); + + DEBUGASSERT(fbinfo != NULL && pinfo != NULL && planeno == 0); + if (fbinfo != NULL && pinfo != NULL && planeno == 0) + { + session = vnc_find_session(fbinfo->display); + if (session == NULL || session->state != VNCSERVER_RUNNING) + { + gdbg("ERROR: session is not connected\n"); + return -ENOTCONN; + } + + DEBUGASSERT(session->fb != NULL); + + /* Return the requested plane info. We are committed to using the + * configured bits-per-pixels in the framebuffer, but performing color + * conversions on the fly for the remote framebuffer as necessary. + */ + + pinfo->fbmem = (FAR void *)session->fb; + pinfo->fblen = RFB_SIZE; + pinfo->stride = RFB_STRIDE; + pinfo->display = fbinfo->display; + pinfo->bpp = RFB_BITSPERPIXEL; + + return OK; + } + + gdbg("Returning EINVAL\n"); + return -EINVAL; +} + +/**************************************************************************** + * Name: up_getcmap + ****************************************************************************/ + +#ifdef CONFIG_FB_CMAP +static int up_getcmap(FAR struct fb_vtable_s *vtable, + FAR struct fb_cmap_s *cmap) +{ + FAR struct vnc_fbinfo_s *fbinfo = (FAR struct vnc_fbinfo_s *)vtable; + FAR struct vnc_session_s *session; + int i; + + gvdbg("vtable=%p cmap=%p\n", vtable, cmap); + + DEBUGASSERT(fbinfo != NULL && cmap != NULL); + + if (fbinfo != NULL && cmap != NULL) + { + session = vnc_find_session(fbinfo->display); + if (session == NULL || session->state != VNCSERVER_RUNNING) + { + gdbg("ERROR: session is not connected\n"); + return -ENOTCONN; + } + + gvdbg("first=%d len=%d\n", vcmap->first, cmap->len); +#warning Missing logic + + return OK; + } + + gdbg("Returning EINVAL\n"); + return -EINVAL; +} +#endif + +/**************************************************************************** + * Name: up_putcmap + ****************************************************************************/ + +#ifdef CONFIG_FB_CMAP +static int up_putcmap(FAR struct fb_vtable_s *vtable, FAR const struct fb_cmap_s *cmap) +{ + FAR struct vnc_fbinfo_s *fbinfo = (FAR struct vnc_fbinfo_s *)vtable; + FAR struct vnc_session_s *session; + int i; + + gvdbg("vtable=%p cmap=%p\n", vtable, cmap); + + DEBUGASSERT(fbinfo != NULL && cmap != NULL); + + if (fbinfo != NULL && cmap != NULL) + { + session = vnc_find_session(fbinfo->display); + if (session == NULL || session->state != VNCSERVER_RUNNING) + { + gdbg("ERROR: session is not connected\n"); + return -ENOTCONN; + } + + gvdbg("first=%d len=%d\n", vcmap->first, cmap->len); +#warning Missing logic + + return OK; + } + + gdbg("Returning EINVAL\n"); + return -EINVAL; +} +#endif + +/**************************************************************************** + * Name: up_getcursor + ****************************************************************************/ + +#ifdef CONFIG_FB_HWCURSOR +static int up_getcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_cursorattrib_s *attrib) +{ + FAR struct vnc_fbinfo_s *fbinfo = (FAR struct vnc_fbinfo_s *)vtable; + FAR struct vnc_session_s *session; + int i; + + gvdbg("vtable=%p attrib=%p\n", vtable, attrib); + + DEBUGASSERT(fbinfo != NULL && attrib != NULL); + + if (fbinfo != NULL && attrib != NULL) + { + session = vnc_find_session(fbinfo->display); + if (session == NULL || session->state != VNCSERVER_RUNNING) + { + gdbg("ERROR: session is not connected\n"); + return -ENOTCONN; + } + +#warning Missing logic + + return OK; + } + gdbg("Returning EINVAL\n"); + return -EINVAL; +} +#endif + +/**************************************************************************** + * Name: + ****************************************************************************/ + +#ifdef CONFIG_FB_HWCURSOR +static int up_setcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_setcursor_s *settings) +{ + FAR struct vnc_fbinfo_s *fbinfo = (FAR struct vnc_fbinfo_s *)vtable; + FAR struct vnc_session_s *session; + int i; + + gvdbg("vtable=%p settings=%p\n", vtable, settings); + + DEBUGASSERT(fbinfo != NULL && settings != NULL); + + if (fbinfo != NULL && settings != NULL) + { + session = vnc_find_session(fbinfo->display); + if (session == NULL || session->state != VNCSERVER_RUNNING) + { + gdbg("ERROR: session is not connected\n"); + return -ENOTCONN; + } + + gvdbg("flags: %02x\n", settings->flags); + if ((settings->flags & FB_CUR_SETPOSITION) != 0) + { +#warning Missing logic + } + +#ifdef CONFIG_FB_HWCURSORSIZE + if ((settings->flags & FB_CUR_SETSIZE) != 0) + { +#warning Missing logic + } +#endif +#ifdef CONFIG_FB_HWCURSORIMAGE + if ((settings->flags & FB_CUR_SETIMAGE) != 0) + { +#warning Missing logic + } +#endif + return OK; + } + + gdbg("Returning EINVAL\n"); + return -EINVAL; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_fbinitialize + * + * Description: + * Initialize the framebuffer video hardware associated with the display. + * + * Input parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +int up_fbinitialize(int display) +{ + FAR char *argv[2]; + char str[8]; + pid_t pid; + + /* Start the VNC server kernel thread. + * REVISIT: There is no protection for the case where this function is + * called more that once. + */ + + gvdbg("Starting the VNC server for display %d\n", display); + DEBUGASSERT(display >= 0 && display < RFB_MAX_DISPLAYS); + + (void)itoa(display, str, 10); + argv[0] = str; + argv[1] = NULL; + + pid = kernel_thread("vnc_server", CONFIG_VNCSERVER_PRIO, + CONFIG_VNCSERVER_STACKSIZE, + (main_t)vnc_server, argv); + if (pid < 0) + { + gdbg("ERROR: Failed to start the VNC server: %d\n", (int)pid); + return (int)pid; + } + + /* Wait for the VNC client to connect and for the RFB to be ready */ +#warning Missing logic + + return OK; +} + +/**************************************************************************** + * Name: up_fbgetvplane + * + * Description: + * Return a a reference to the framebuffer object for the specified video + * plane of the specified plane. Many OSDs support multiple planes of video. + * + * Input parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * vplane - Identifies the plane being queried. + * + * Returned Value: + * A non-NULL pointer to the frame buffer access structure is returned on + * success; NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct fb_vtable_s *up_fbgetvplane(int display, int vplane) +{ + FAR struct vnc_session_s *session = vnc_find_session(display); + FAR struct vnc_fbinfo_s *fbinfo; + + /* Verify that the session is still valid */ + + if (session->state != VNCSERVER_RUNNING) + { + return NULL; + } + + if (vplane == 0) + { + /* Has the framebuffer information been initialized for this display? */ + + fbinfo = &g_fbinfo[display]; + if (!fbinfo->initialized) + { + fbinfo->vtable.getvideoinfo = up_getvideoinfo, + fbinfo->vtable.getplaneinfo = up_getplaneinfo, +#ifdef CONFIG_FB_CMAP + fbinfo->vtable.getcmap = up_getcmap, + fbinfo->vtable.putcmap = up_putcmap, +#endif +#ifdef CONFIG_FB_HWCURSOR + fbinfo->vtable.getcursor = up_getcursor, + fbinfo->vtable.setcursor = up_setcursor, +#endif + fbinfo->display = display; + fbinfo->initialized = true; + } + + return &fbinfo->vtable; + } + else + { + return NULL; + } +} + +/**************************************************************************** + * Name: up_fbuninitialize + * + * Description: + * Uninitialize the framebuffer support for the specified display. + * + * Input Parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void up_fbuninitialize(int display) +{ + FAR struct vnc_session_s *session = vnc_find_session(display); + FAR struct vnc_fbinfo_s *fbinfo; + + DEBUGASSERT(session != NULL); + fbinfo = &g_fbinfo[display]; +#warning Missing logic + UNUSED(session); + UNUSED(fbinfo); +} + +/**************************************************************************** + * Name: nx_notify_rectangle + * + * Description: + * When CONFIG_NX_UPDATE=y, then the graphics system will callout to + * inform some external module that the display has been updated. This + * would be useful in a couple for cases. + * + * - When a serial LCD is used, but a framebuffer is used to access the + * LCD. In this case, the update callout can be used to refresh the + * affected region of the display. + * + * - When VNC is enabled. This is case, this callout is necessary to + * update the remote frame buffer to match the local framebuffer. + * + * When this feature is enabled, some external logic must provide this + * interface. This is the function that will handle the notification. It + * receives the rectangular region that was updated on the provided plane. + * + ****************************************************************************/ + +#ifdef CONFIG_NX_UPDATE +void nx_notify_rectangle(FAR NX_PLANEINFOTYPE *pinfo, + FAR const struct nxgl_rect_s *rect) +{ + FAR struct vnc_session_s *session; + int ret; + + DEBUGASSERT(pinfo != NULL && rect != NULL); + + /* Recover the session informatin from the display number in the planeinfo + * structure. + */ + + DEBUGASSERT(pinfo->display >= 0 && pinfo->display < RFB_MAX_DISPLAYS); + session = vnc_find_session(pinfo->display); + + /* Verify that the session is still valid */ + + if (session != NULL && session->state == VNCSERVER_RUNNING) + { + /* Queue the rectangular update */ + + ret = vnc_update_rectangle(session, rect); + if (ret < 0) + { + gdbg("ERROR: vnc_update_rectangle failed: %d\n", ret); + } + } +} +#endif diff --git a/graphics/vnc/server/vnc_keymap.c b/graphics/vnc/server/vnc_keymap.c new file mode 100644 index 00000000000..e63f99fe8e2 --- /dev/null +++ b/graphics/vnc/server/vnc_keymap.c @@ -0,0 +1,632 @@ +/**************************************************************************** + * graphics/vnc/vnc_keymap.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include + +#define XK_MISCELLANY 1 /* Select X11 character set */ +#define XK_LATIN1 1 +#define XK_XKB_KEYS 1 + +#include +#include + +#include "vnc_server.h" + +#ifdef CONFIG_NX_KBD + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FIRST_PRTCHAR ASCII_SPACE +#define LAST_PRTCHAR ASCII_TILDE +#define NPRTCHARS (ASCII_TILDE + ASCII_SPACE - 1) + +#define ISPRINTABLE(c) ((c) >= FIRST_PRTCHAR && (c) <= LAST_PRTCHAR) +#define ISLOWERCASE(c) ((c) >= ASCII_a && (c) <= ASCII_z) +#define ISUPPERCASE(c) ((c) >= ASCII_A && (c) <= ASCII_Z) +#define ISALPHABETIC(c) (ISLOWERCASE(c) || ISUPPERCASE(c)) + +/**************************************************************************** + * Private types + ****************************************************************************/ + +enum vnc_modifier_e +{ + MOD_SHIFT = 0, /* Left or right shift key */ + MOD_CONTROL, /* Left or right control key */ + MOD_ALT, /* Alt key */ + MOD_CAPSLOCK, /* Caps lock */ + MOD_SHIFTLOCK, /* Shift lock */ +#ifdef CONFIG_VNCSERVER_KBDENCODE + MOD_SCROLLLOCK, /* Scroll lock */ + MOD_NUMLOCK, /* Num lock */ +#endif + NMODIFIERS +}; + +struct vnc_keymap_s +{ + uint16_t nxcode; + uint16_t x11code; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Special key modifiers */ + +static const struct vnc_keymap_s g_modifiers[] = +{ + {MOD_SHIFT, XK_Shift_L}, + {MOD_SHIFT, XK_Shift_R}, + {MOD_CONTROL, XK_Control_L}, + {MOD_CONTROL, XK_Control_R}, + {MOD_ALT, XK_Alt_L}, + {MOD_ALT, XK_Alt_R}, + {MOD_CAPSLOCK, XK_Caps_Lock}, + {MOD_SHIFTLOCK, XK_Shift_Lock}, +#ifdef CONFIG_VNCSERVER_KBDENCODE + {MOD_SCROLLLOCK, XK_Scroll_Lock}, + {MOD_NUMLOCK, XK_Num_Lock}, +#endif +}; + +#define G_MODIFIERS_NELEM (sizeof(g_modifiers) / sizeof(struct vnc_keymap_s)) + +/* Map special mappings for X11 codes to ASCII characters */ + +static const struct vnc_keymap_s g_asciimap[] = +{ + /* Control characters */ + +#ifdef CONFIG_VNCSERVER_KBDENCODE + {ASCII_BS, XK_BackSpace}, +#endif + {ASCII_TAB, XK_Tab}, + {ASCII_LF, XK_Linefeed}, + {ASCII_CR, XK_Return}, + {ASCII_ESC, XK_Escape}, +#ifdef CONFIG_VNCSERVER_KBDENCODE + {ASCII_DEL, XK_Delete}, +#endif + + /* Alternative encodings */ + + {'`', XK_dead_grave}, + {'´', XK_dead_acute}, + {ASCII_TILDE, XK_dead_tilde}, + {ASCII_CARET, XK_dead_circumflex}, + + /* Keypad aliases */ + + {ASCII_0, XK_KP_0}, + {ASCII_1, XK_KP_1}, + {ASCII_2, XK_KP_2}, + {ASCII_3, XK_KP_3}, + {ASCII_4, XK_KP_4}, + {ASCII_5, XK_KP_5}, + {ASCII_6, XK_KP_6}, + {ASCII_7, XK_KP_7}, + {ASCII_8, XK_KP_8}, + {ASCII_9, XK_KP_9}, + + {ASCII_ASTERISK, XK_KP_Multiply}, + {ASCII_PLUS, XK_KP_Add}, + {ASCII_COMMA, XK_KP_Separator}, + {ASCII_HYPHEN, XK_KP_Subtract}, + {ASCII_PERIOD, XK_KP_Decimal}, + {ASCII_DIVIDE, XK_KP_Divide}, + + {ASCII_SPACE, XK_KP_Space}, + {ASCII_TAB, XK_KP_Tab}, + {ASCII_CR, XK_KP_Enter} +#ifdef CONFIG_VNCSERVER_KBDENCODE + , {ASCII_DEL, XK_KP_Delete} +#endif +}; + +#define G_ASCIIMAP_NELEM (sizeof(g_asciimap) / sizeof(struct vnc_keymap_s)) + +#ifdef CONFIG_VNCSERVER_KBDENCODE +static const struct vnc_keymap_s g_cursor[] = +{ + {KEYCODE_BACKDEL, XK_BackSpace}, + {KEYCODE_FWDDEL, XK_Delete}, + {KEYCODE_FWDDEL, XK_KP_Delete}, + {KEYCODE_HOME, XK_Home}, + {KEYCODE_HOME, XK_KP_Home}, + {KEYCODE_END, XK_End}, + {KEYCODE_END, XK_KP_End}, + {KEYCODE_LEFT, XK_Left}, + {KEYCODE_LEFT, XK_KP_Left}, + {KEYCODE_RIGHT, XK_Right}, + {KEYCODE_RIGHT, XK_KP_Right}, + {KEYCODE_UP, XK_Up}, + {KEYCODE_UP, XK_KP_Up}, + {KEYCODE_DOWN, XK_Down}, + {KEYCODE_DOWN, XK_KP_Down}, + {KEYCODE_PAGEUP, XK_Page_Up}, + {KEYCODE_PAGEUP, XK_KP_Prior}, + {KEYCODE_PAGEUP, XK_KP_Page_Up}, + {KEYCODE_PAGEDOWN, XK_Page_Down}, + {KEYCODE_PAGEDOWN, XK_KP_Next}, + {KEYCODE_PAGEDOWN, XK_KP_Page_Down}, + {KEYCODE_INSERT, XK_Insert}, + {KEYCODE_INSERT, XK_KP_Insert}, + + {KEYCODE_SELECT, XK_Select}, + {KEYCODE_EXECUTE, XK_Execute}, + {KEYCODE_HELP, XK_Help}, + {KEYCODE_MENU, XK_Alt_L}, + {KEYCODE_MENU, XK_Alt_R}, + {KEYCODE_PAUSE, XK_Pause}, + {KEYCODE_PRTSCRN, XK_Print}, + {KEYCODE_CLEAR, XK_Clear}, + {MOD_SCROLLLOCK, XK_Scroll_Lock}, + {MOD_NUMLOCK, XK_Num_Lock}, + + {KEYCODE_F1, XK_KP_F1}, + {KEYCODE_F1, XK_F1}, + {KEYCODE_F2, XK_KP_F2}, + {KEYCODE_F2, XK_F2}, + {KEYCODE_F3, XK_KP_F3}, + {KEYCODE_F3, XK_F3}, + {KEYCODE_F4, XK_KP_F4}, + {KEYCODE_F4, XK_F4}, + {KEYCODE_F5, XK_F5}, + {KEYCODE_F6, XK_F6}, + {KEYCODE_F7, XK_F7}, + {KEYCODE_F8, XK_F8}, + {KEYCODE_F9, XK_F9}, + {KEYCODE_F10, XK_F10}, + {KEYCODE_F11, XK_F11}, + {KEYCODE_F12, XK_F12}, + {KEYCODE_F13, XK_F13}, + {KEYCODE_F14, XK_F14}, + {KEYCODE_F15, XK_F15}, + {KEYCODE_F16, XK_F16}, + {KEYCODE_F17, XK_F17}, + {KEYCODE_F18, XK_F18}, + {KEYCODE_F19, XK_F19}, + {KEYCODE_F20, XK_F20}, + {KEYCODE_F21, XK_F21}, + {KEYCODE_F22, XK_F22}, + {KEYCODE_F23, XK_F23}, + {KEYCODE_F24, XK_F24}, +}; +#endif + +/* Changes the case of a character. Based on US keyboard layout */ + +static const uint8_t g_caseswap[NPRTCHARS] = +{ + ASCII_SPACE, ASCII_1, ASCII_RSQUOTE, ASCII_3, /* ! " # */ + ASCII_4, ASCII_5, ASCII_7, ASCII_QUOTE, /* $ % & ' */ + ASCII_9, ASCII_0, ASCII_8, ASCII_EQUAL, /* ( ) * + */ + ASCII_LT, ASCII_UNDERSCORE, ASCII_GT, ASCII_QUESTION, /* , - . / */ + ASCII_RPAREN, ASCII_EXCLAM, ASCII_AT, ASCII_NUMBER, /* 0 1 2 3 */ + ASCII_DOLLAR, ASCII_PERCENT, ASCII_CIRCUMFLEX, ASCII_AMPERSAND, /* 4 5 6 7 */ + ASCII_ASTERISK, ASCII_LPAREN, ASCII_SEMICOLON, ASCII_COLON, /* 8 9 : ; */ + ASCII_COMMA, ASCII_PLUS, ASCII_PERIOD, ASCII_SLASH, /* < = > ? */ + ASCII_2, ASCII_a, ASCII_b, ASCII_c, /* @ A B C */ + ASCII_d, ASCII_e, ASCII_f, ASCII_g, /* D E F G */ + ASCII_h, ASCII_i, ASCII_j, ASCII_k, /* H I J K */ + ASCII_l, ASCII_m, ASCII_n, ASCII_o, /* L M N O */ + ASCII_p, ASCII_q, ASCII_r, ASCII_s, /* P Q R S */ + ASCII_t, ASCII_u, ASCII_v, ASCII_v, /* T U V W */ + ASCII_x, ASCII_y, ASCII_z, ASCII_LBRACE, /* X Y Z [ */ + ASCII_VERTBAR, ASCII_RBRACE, ASCII_6, ASCII_HYPHEN, /* \ ] ^ _ */ + ASCII_TILDE, ASCII_A, ASCII_B, ASCII_C, /* ' a b c */ + ASCII_D, ASCII_E, ASCII_F, ASCII_G, /* c e f g */ + ASCII_H, ASCII_I, ASCII_J, ASCII_K, /* h i j k */ + ASCII_L, ASCII_M, ASCII_N, ASCII_O, /* l m n o */ + ASCII_P, ASCII_Q, ASCII_R, ASCII_S, /* p q r s */ + ASCII_T, ASCII_U, ASCII_V, ASCII_W, /* t u v w */ + ASCII_X, ASCII_Y, ASCII_Z, ASCII_LBRACKET, /* x y z { */ + ASCII_BACKSLASH, ASCII_RBRACKET, ASCII_RSQUOTE, /* | } ~ */ +}; + +/* State of each modifier */ + +static bool g_modstate[NMODIFIERS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_kbd_encode + * + * Description: + * Encode one escape sequence command into the proivded buffer. + * + * Input Parameters: + * buffer - The location to write the sequence + * keycode - The command to be added to the output stream. + * terminator - Escape sequence terminating character. + * + * Returned Value: + * Number of bytes written + * + ****************************************************************************/ + +#ifdef CONFIG_VNCSERVER_KBDENCODE +static inline int vnc_kbd_encode(FAR uint8_t *buffer, uint8_t keycode, + uint8_t terminator) +{ + *buffer++ = ASCII_ESC; + *buffer++ = ASCII_LBRACKET; + *buffer++ = keycode; + *buffer = terminator; + return 4; +} +#endif + +/**************************************************************************** + * Name: vnc_kbd_press + * + * Description: + * Indicates a normal key press event. Put one byte of normal keyboard + * data into the user provided buffer. + * + * Input Parameters: + * buffer - The location to write the sequence + * ch - The character to be added to the output stream. + * + * Returned Value: + * Number of bytes written + * + ****************************************************************************/ + +#ifdef CONFIG_VNCSERVER_KBDENCODE +static inline void vnc_kbd_press(FAR uint8_t *buffer, uint8_t ch) +{ + *buffer = ch; + return 1; +} +#endif + +/**************************************************************************** + * Name: vnc_kbd_release + * + * Description: + * Encode the release of a normal key. + * + * Input Parameters: + * buffer - The location to write the sequence + * ch - The character associated with the key that was releared. + * + * Returned Value: + * Number of bytes written + * + ****************************************************************************/ + +#ifdef CONFIG_VNCSERVER_KBDENCODE +static inline void vnc_kbd_release(FAR uint8_t *buffer, uint8_t ch) +{ + return vnc_kbd_encode(buffer, ch, ('a' + KBD_RELEASE)); +} +#endif + +/**************************************************************************** + * Name: vnc_kbd_specpress + * + * Description: + * Denotes a special key press event. Put one special keyboard command + * into the user provided buffer. + * + * Input Parameters: + * buffer - The location to write the sequence + * keycode - The command to be added to the output stream. + * + * Returned Value: + * Number of bytes written + * + ****************************************************************************/ + +#ifdef CONFIG_VNCSERVER_KBDENCODE +static inline void vnc_kbd_specpress(FAR uint8_t *buffer, uint8_t keycode) +{ + return vnc_kbd_encode(buffer, keycode, stream, ('a' + KBD_SPECPRESS)); +} +#endif + +/**************************************************************************** + * Name: vnc_kbd_specrel + * + * Description: + * Denotes a special key release event. Put one special keyboard + * command into the user provided buffer. + * + * Input Parameters: + * buffer - The location to write the sequence + * keycode - The command to be added to the output stream. + * + * Returned Value: + * Number of bytes written + * + ****************************************************************************/ + +#ifdef CONFIG_VNCSERVER_KBDENCODE +static inline void vnc_kbd_specrel(FAR uint8_t *buffer, uint8_t keycode) +{ + return vnc_kbd_encode(buffer, keycode, stream, ('a' + KBD_SPECREL)); +} +#endif + +/**************************************************************************** + * Name: vnc_kbd_lookup + * + * Description: + * Attempt to map the X11 keycode by searching in a lookup table. + * + ****************************************************************************/ + +static int vnc_kbd_lookup(FAR const struct vnc_keymap_s *table, + unsigned int nelem, uint16_t keysym) +{ + int i; + + /* First just try to map the virtual keycode using our lookup-table */ + + for (i = 0; i < nelem; i++) + { + if (table[i].x11code == keysym) + { + /* We have a match */ + + return (int)table[i].nxcode; + } + } + + /* No match */ + + return -EINVAL; +} + +/**************************************************************************** + * Name: vnc_kbd_ascii + * + * Description: + * Attempt to map the X11 keycode into the corresponding ASCII code. + * + ****************************************************************************/ + +static int vnc_kbd_ascii(uint16_t keysym) +{ + /* ISO/IEC 8859-1 Latin1 matches C ASCII in this range: */ + +#ifdef CONFIG_VNCSERVER_KBDENCODE + if (keysym >= ASCII_SPACE && keysym < ASCII_DEL) +#else + if (keysym >= ASCII_SPACE && keysym <= ASCII_DEL) +#endif + { + return (int)keysym; + } + + /* Perform a lookup to handler some special cases */ + + return vnc_kbd_lookup(g_asciimap, G_ASCIIMAP_NELEM, keysym); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_key_map + * + * Description: + * Map the receive X11 keysym into something understood by NuttX and route + * that through NX to the appropriate window. + * + * Input Parameters: + * session - An instance of the session structure allocated by + * vnc_create_session(). + * keysym - The X11 keysym value (see include/nuttx/inputx11_keysymdef) + * keydown - True: Key pressed; False: Key released + * + * Returned Value: + * None + * + ****************************************************************************/ + +void vnc_key_map(FAR struct vnc_session_s *session, uint16_t keysym, + bool keydown) +{ +#ifdef CONFIG_VNCSERVER_KBDENCODE + uint8_t buffer[4] + int nch; +#endif + int16_t keych; + int ret; + + /* Check for modifier keys */ + + keych = vnc_kbd_lookup(g_modifiers, G_MODIFIERS_NELEM, keysym); + if (keych >= 0) + { + g_modstate[keych] = keydown; + return; + } + +#ifndef CONFIG_VNCSERVER_KBDENCODE + /* If we are not encoding key presses, then we have to ignore key release + * events. + */ + + if (!keydown) + { + return; + } +#endif + + /* Try to convert the keycode to an ASCII value */ + + keych = vnc_kbd_ascii((char)(keysym & 255)); + if (keych >= 0) + { + /* It is a simple ASCII-mappable LATIN1 character. Now we need + * to apply any modifiers. + */ + + if (g_modstate[MOD_CONTROL]) + { + /* Make into a control character */ + + keych &= 0x1f; + } + + /* Other modifiers apply only to printable characters */ + + else if (ISPRINTABLE(keych)) + { + /* If Shift Lock is selected, then the case of all printable + * characters should be reversed (unless the Shift key is also + * pressed) + */ + + if (g_modstate[MOD_SHIFTLOCK]) + { + if (g_modstate[MOD_SHIFT]) + { + /* Swap case */ + + keych = g_caseswap[keych]; + } + } + + /* If Caps Lock is selected, then the case of alphabetic + * characters should be reversed (unless the Shift key is also + * pressed) + */ + + else if (g_modstate[MOD_CAPSLOCK] && ISALPHABETIC(keych)) + { + if (g_modstate[MOD_SHIFT]) + { + /* Swap case */ + + keych = g_caseswap[keych]; + } + } + + /* If (1) only the Shift Key is pressed or (2) the Shift key is + * pressed with Caps Lock, but the character is not alphabetic, + * then the case of all printable characters should be reversed. + */ + + else if (g_modstate[MOD_SHIFT]) + { + keych = g_caseswap[keych]; + } + } + +#ifdef CONFIG_VNCSERVER_KBDENCODE + /* Encode the normal character */ + + if (keydown) + { + nch = vnc_kbd_press(buffer, keych); + } + else + { + nch = vnc_kbd_release(buffer, keych); + } + + /* Inject the normal character sequence into NX */ + + ret = nx_kbdin(session->handle, nch, buffer); + if (ret < 0) + { + gdbg("ERROR: nx_kbdin() failed: %d\n", ret) + } +#else + /* Inject the single key press into NX */ + + ret = nx_kbdchin(session->handle,(uint8_t)keych); + if (ret < 0) + { + gdbg("ERROR: nx_kbdchin() failed: %d\n", ret) + } +#endif + } + + /* Not mappable to an ASCII LATIN1 character */ + +#ifdef CONFIG_VNCSERVER_KBDENCODE + else + { + /* Lookup cursor movement/screen control keysyms */ + + keych = vnc_kbd_lookup(g_modifiers, G_MODIFIERS_NELEM, keysym); + if (keych >= 0) + { + /* Encode the speical character */ + + if (keydown) + { + nch = vnc_kbd_specpress(buffer, keych); + } + else + { + nch = vnc_kbd_specrel(buffer, keych); + } + + /* Inject the special character sequence into NX */ + + ret = nx_kbdin(session->handle, nch, buffer); + if (ret < 0) + { + gdbg("ERROR: nx_kbdin() failed: %d\n", ret) + } + } + } +#endif +} + +#endif /* CONFIG_NX_KBD */ \ No newline at end of file diff --git a/graphics/vnc/server/vnc_negotiate.c b/graphics/vnc/server/vnc_negotiate.c new file mode 100644 index 00000000000..34430cf19f7 --- /dev/null +++ b/graphics/vnc/server/vnc_negotiate.c @@ -0,0 +1,323 @@ +/**************************************************************************** + * graphics/vnc/vnc_negotiate.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "vnc_server.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if defined(CONFIG_VNCSERVER_PROTO3p3) +static const char g_vncproto[] = RFB_PROTOCOL_VERSION_3p3; +#elif defined(CONFIG_VNCSERVER_PROTO3p8) +static const char g_vncproto[] = RFB_PROTOCOL_VERSION_3p8; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_negotiate + * + * Description: + * Perform the VNC initialization sequence after the client has sucessfully + * connected to the server. Negotiate security, framebuffer and color + * properties. + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * Returns zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_VNCSERVER_PROTO3p3 +int vnc_negotiate(FAR struct vnc_session_s *session) +{ + FAR struct rfb_sectype_s *sectype; + FAR struct rfb_serverinit_s *serverinit; + FAR struct rfb_pixelfmt_s *pixelfmt; + FAR struct rfb_setpixelformat_s *setformat; + ssize_t nsent; + ssize_t nrecvd; + size_t len; + int errcode; + + /* Inform the client of the VNC protocol version */ + + len = strlen(g_vncproto); + nsent = psock_send(&session->connect, g_vncproto, len, 0); + if (nsent < 0) + { + errcode = get_errno(); + gdbg("ERROR: Send ProtocolVersion failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + DEBUGASSERT(nsent == len); + + /* Receive the echo of the protocol string */ + + nrecvd = psock_recv(&session->connect, session->inbuf, len, 0); + if (nrecvd < 0) + { + errcode = get_errno(); + gdbg("ERROR: Receive protocol confirmation failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + DEBUGASSERT(nrecvd == len); + + /* Tell the client that we won't use any stinkin' security. + * + * "Version 3.3 The server decides the security type and sends a single + * word:" + */ + + sectype = (FAR struct rfb_sectype_s *)session->outbuf; + rfb_putbe32(sectype->type, RFB_SECTYPE_NONE); + + nsent = psock_send(&session->connect, sectype, + sizeof(struct rfb_sectype_s), 0); + if (nsent < 0) + { + errcode = get_errno(); + gdbg("ERROR: Send Security failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + DEBUGASSERT(nsent == sizeof(struct rfb_sectype_s)); + + /* Receive the ClientInit message + * + * "Once the client and server are sure that they’re happy to talk to one + * another using the agreed security type, the protocol passes to the + * initialisation phase. The client sends a ClientInit message followed + * by the server sending a ServerInit message." + * + * In this implementation, the sharing flag is ignored. + */ + + nrecvd = psock_recv(&session->connect, session->inbuf, + sizeof(struct rfb_clientinit_s), 0); + if (nrecvd < 0) + { + errcode = get_errno(); + gdbg("ERROR: Receive ClientInit failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + DEBUGASSERT(nrecvd == sizeof(struct rfb_clientinit_s)); + + /* Send the ClientInit message + * + * "After receiving the ClientInit message, the server sends a ServerInit + * message. This tells the client the width and height of the server’s + * framebuffer, its pixel format and the name associated with the desktop: + */ + + serverinit = (FAR struct rfb_serverinit_s *)session->outbuf; + + rfb_putbe16(serverinit->width, CONFIG_VNCSERVER_SCREENWIDTH); + rfb_putbe16(serverinit->height, CONFIG_VNCSERVER_SCREENHEIGHT); + rfb_putbe32(serverinit->namelen, 0); + + pixelfmt = &serverinit->format; + pixelfmt->bpp = RFB_BITSPERPIXEL; + pixelfmt->depth = RFB_PIXELDEPTH; + pixelfmt->bigendian = 0; + pixelfmt->truecolor = RFB_TRUECOLOR; + + rfb_putbe16(pixelfmt->rmax, RFB_RMAX); + rfb_putbe16(pixelfmt->gmax, RFB_GMAX); + rfb_putbe16(pixelfmt->bmax, RFB_BMAX); + + pixelfmt->rshift = RFB_RSHIFT; + pixelfmt->gshift = RFB_GSHIFT; + pixelfmt->bshift = RFB_BSHIFT; + + nsent = psock_send(&session->connect, serverinit, + SIZEOF_RFB_SERVERINIT_S(0), 0); + if (nsent < 0) + { + errcode = get_errno(); + gdbg("ERROR: Send ServerInit failed: %d\n", errcode); + return -errcode; + } + + DEBUGASSERT(nsent == SIZEOF_RFB_SERVERINIT_S(0)); + + /* We now expect to receive the SetPixelFormat message from the client. + * This may override some of our framebuffer settings. + */ + + setformat = (FAR struct rfb_setpixelformat_s *)session->inbuf; + + nrecvd = psock_recv(&session->connect, setformat, + sizeof(struct rfb_setpixelformat_s), 0); + if (nrecvd < 0) + { + errcode = get_errno(); + gdbg("ERROR: Receive SetFormat failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + else if (nrecvd != sizeof(struct rfb_setpixelformat_s)) + { + /* Must not be a SetPixelFormat message? */ + + gdbg("ERROR: SetFormat wrong size: %d\n", (int)nrecvd); + return -EPROTO; + } + else if (setformat->msgtype != RFB_SETPIXELFMT_MSG) + { + gdbg("ERROR: Not a SetFormat message: %d\n", (int)setformat->type); + return -EPROTO; + } + + /* Check if the client request format is one that we can handle. */ + + pixelfmt = &setformat->format; + + if (pixelfmt->truecolor == 0) + { + /* At present, we support only TrueColor formats */ + + gdbg("ERROR: No support for palette colors\n"); + return -ENOSYS; + } + + if (pixelfmt->bpp == 16 && pixelfmt->depth == 15) + { + session->colorfmt = FB_FMT_RGB16_555; + session->bpp = 16; + } + else if (pixelfmt->bpp == 16 && pixelfmt->depth == 16) + { + session->colorfmt = FB_FMT_RGB16_565; + session->bpp = 16; + } + else if (pixelfmt->bpp == 32 && pixelfmt->depth == 24) + { + session->colorfmt = FB_FMT_RGB32; + session->bpp = 32; + } + else if (pixelfmt->bpp == 32 && pixelfmt->depth == 32) + { + session->colorfmt = FB_FMT_RGB32; + session->bpp = 32; + } + else + { + /* We do not support any other conversions */ + + gdbg("ERROR: No support for this BPP=%d and depth=%d\n", + pixelfmt->bpp, pixelfmt->depth); + return -ENOSYS; + } + + /* Receive supported encoding types from client, but ignore them. + * we will do only raw format. + */ + + (void)psock_recv(&session->connect, session->inbuf, + CONFIG_VNCSERVER_INBUFFER_SIZE, 0); + + session->state = VNCSERVER_CONFIGURED; + return OK; +} +#endif + +#ifdef CONFIG_VNCSERVER_PROTO3p8 +int vnc_negotiate(FAR struct vnc_session_s *session) +{ + ssize_t nsent; + ssize_t nrecvd; + size_t len; + + /* Inform the client of the VNC protocol version */ + + len = strlen(g_vncproto); + nsent = psock_send(&session->connect, g_vncproto, len, 0); + if (nsent < 0) + { + errcode = get_errno(); + gdbg("ERROR: Send ProtocolVersion failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + DEBUGASSERT(nsent == len); + + /* Receive the echo of the protocol string */ + + nrecvd = psock_recv(&session->connect, session->inbuf, len, 0); + if (nrecvd <= 0) + { + errcode = get_errno(); + gdbg("ERROR: Receive protocol confirmation failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + DEBUGASSERT(nrecvd == len); + + /* Offer the client a choice of security -- where None is the only option. */ +#warning Missing logic + + return OK; +} +#endif diff --git a/graphics/vnc/server/vnc_receiver.c b/graphics/vnc/server/vnc_receiver.c new file mode 100644 index 00000000000..03749a544fb --- /dev/null +++ b/graphics/vnc/server/vnc_receiver.c @@ -0,0 +1,369 @@ +/**************************************************************************** + * graphics/vnc/vnc_receiver.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "vnc_server.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_read_remainder + * + * Description: + * After receiving the first byte of a client-to-server message, this + * reads in the remainder of the message. + * + * Input Parameters: + * session - An instance of the session structure. + * msglen - The full length of the message + * + * Returned Value: + * At present, always returns OK + * + ****************************************************************************/ + +int vnc_read_remainder(FAR struct vnc_session_s *session, size_t msglen, + size_t offset) +{ + ssize_t nrecvd; + size_t ntotal; + int errcode; + + /* Loop until the rest of the message is recieved. */ + + for (ntotal = 0; ntotal < msglen; offset += nrecvd, ntotal += nrecvd) + { + /* Receive more of the message */ + + nrecvd = psock_recv(&session->connect, &session->inbuf[offset], + msglen - ntotal, 0); + if (nrecvd < 0) + { + errcode = get_errno(); + gdbg("ERROR: Receive message failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_receiver + * + * Description: + * This function handles all Client-to-Server messages. + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * At present, always returns OK + * + ****************************************************************************/ + +int vnc_receiver(FAR struct vnc_session_s *session) +{ + ssize_t nrecvd; + int errcode; + int ret; + + /* Loop until the client disconnects or an unhandled error occurs */ + + for (; ; ) + { + /* Set up to read one byte which should be the message type of the + * next Client-to-Server message. We will block here until the message + * is received. + */ + + nrecvd = psock_recv(&session->connect, session->inbuf, 1, 0); + if (nrecvd < 0) + { + errcode = get_errno(); + gdbg("ERROR: Receive byte failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + DEBUGASSERT(nrecvd == 1); + + /* The single byte received should be the message type. Handle the + * message according to this message type. + */ + + switch (session->inbuf[0]) + { + case RFB_SETPIXELFMT_MSG: /* SetPixelFormat */ + { + /* Read the rest of the SetPixelFormat message */ + + ret = vnc_read_remainder(session, + sizeof(struct rfb_setpixelformat_s) - 1, + 1); + if (ret < 0) + { + gdbg("ERROR: Failed to read SetPixelFormat message: %d\n", + ret); + } + else + { +#warning Missing logic + } + } + break; + + case RFB_SETENCODINGS_MSG: /* SetEncodings */ + { + FAR struct rfb_setencodings_s *encodings; + uint32_t nencodings; + + /* Read the SetEncodings message without the following + * encodings. + */ + + ret = vnc_read_remainder(session, + SIZEOF_RFB_SERVERINIT_S(0) - 1, + 1); + if (ret < 0) + { + gdbg("ERROR: Failed to read SetEncodings message: %d\n", + ret); + } + else + { + /* Read the following encodings */ + + encodings = (FAR struct rfb_setencodings_s *)session->inbuf; + nencodings = rfb_getbe32(encodings->nencodings); + + ret = vnc_read_remainder(session, + nencodings * sizeof(uint32_t), + SIZEOF_RFB_SERVERINIT_S(0)); + if (ret < 0) + { + gdbg("ERROR: Failed to read encodings: %d\n", + ret); + } + else + { +#warning Missing logic + } + } + } + break; + + case RFB_FBUPDATEREQ_MSG: /* FramebufferUpdateRequest */ + { + FAR struct rfb_fbupdatereq_s *update; + struct nxgl_rect_s rect; + + /* Read the rest of the SetPixelFormat message */ + + ret = vnc_read_remainder(session, + sizeof(struct rfb_fbupdatereq_s) - 1, + 1); + if (ret < 0) + { + gdbg("ERROR: Failed to read FramebufferUpdateRequest message: %d\n", + ret); + } + else + { + /* Enqueue the update */ + + update = (FAR struct rfb_fbupdatereq_s *)session->inbuf; + + rect.pt1.x = rfb_getbe16(update->xpos); + rect.pt1.y = rfb_getbe16(update->ypos); + rect.pt2.x = rect.pt1.x + rfb_getbe16(update->width); + rect.pt2.y = rect.pt1.y + rfb_getbe16(update->height); + + ret = vnc_update_rectangle(session, &rect); + if (ret < 0) + { + gdbg("ERROR: Failed to queue update: %d\n", ret); + } + } + } + break; + + case RFB_KEYEVENT_MSG: /* KeyEvent */ + { + FAR struct rfb_keyevent_s *keyevent; + + /* Read the rest of the KeyEvent message */ + + ret = vnc_read_remainder(session, + sizeof(struct rfb_keyevent_s) - 1, + 1); + if (ret < 0) + { + gdbg("ERROR: Failed to read KeyEvent message: %d\n", + ret); + } + else + { + /* Inject the key press/release event into NX */ + + keyevent = (FAR struct rfb_keyevent_s *)session->inbuf; + vnc_key_map(session, rfb_getbe16(keyevent->key), + (bool)keyevent->down); + } + } + break; + + case RFB_POINTEREVENT_MSG: /* PointerEvent */ + { +#ifdef CONFIG_NX_XYINPUT + FAR struct rfb_pointerevent_s *event; + uint8_t buttons; +#endif + + /* Read the rest of the PointerEvent message */ + + ret = vnc_read_remainder(session, + sizeof(struct rfb_pointerevent_s) - 1, + 1); + if (ret < 0) + { + gdbg("ERROR: Failed to read PointerEvent message: %d\n", + ret); + } +#ifdef CONFIG_NX_XYINPUT + else + { + event = (FAR struct rfb_pointerevent_s *)session->inbuf; + + /* Map buttons bitmap. Bits 0-7 are buttons 1-8, 0=up, + * 1=down. By convention Bit 0 = left button, Bit 1 = + * middle button, and Bit 2 = right button. + */ + + buttons = 0; + if ((event->buttons & (1 << 0)) != 0) + { + buttons |= NX_MOUSE_LEFTBUTTON; + } + + if ((event->buttons & (1 << 1)) != 0) + { + buttons |= NX_MOUSE_CENTERBUTTON; + } + + if ((event->buttons & (1 << 2)) != 0) + { + buttons |= NX_MOUSE_RIGHTBUTTON; + } + + ret = nx_mousein(session->handle, + (nxgl_coord_t)rfb_getbe16(event->xpos), + (nxgl_coord_t)rfb_getbe16(event->ypos), + buttons); + if (ret < 0) + { + gdbg("ERROR: nx_mousein failed: %d\n", ret); + } + } +#endif + } + break; + + case RFB_CLIENTCUTTEXT_MSG: /* ClientCutText */ + { + FAR struct rfb_clientcuttext_s *cuttext; + uint32_t length; + + /* Read the ClientCutText message without the following + * text. + */ + + ret = vnc_read_remainder(session, + SIZEOF_RFB_CLIENTCUTTEXT_S(0) - 1, + 1); + if (ret < 0) + { + gdbg("ERROR: Failed to read ClientCutText message: %d\n", + ret); + } + else + { + /* Read the following text */ + + cuttext = (FAR struct rfb_clientcuttext_s *)session->inbuf; + length = rfb_getbe32(cuttext->length); + + ret = vnc_read_remainder(session, length, + SIZEOF_RFB_CLIENTCUTTEXT_S(0)); + if (ret < 0) + { + gdbg("ERROR: Failed to read text: %d\n", + ret); + } + else + { +#warning Missing logic + } + } + } + break; + + default: + gdbg("ERROR: Unsynchronized, msgtype=%d\n", session->inbuf[0]); + return -EPROTO; + } + } + + return -ENOSYS; +} diff --git a/graphics/vnc/server/vnc_server.c b/graphics/vnc/server/vnc_server.c new file mode 100644 index 00000000000..12043ec5a5a --- /dev/null +++ b/graphics/vnc/server/vnc_server.c @@ -0,0 +1,368 @@ +/**************************************************************************** + * graphics/vnc/vnc_server.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "nuttx/config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "vnc_server.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Given a display number as an index, the following array can be used to + * look-up the session structure for that display. + */ + +static FAR struct vnc_session_s *g_vnc_sessions[RFB_MAX_DISPLAYS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_reset_session + * + * Description: + * Conclude the current VNC session. This function re-initializes the + * session structure; it does not free either the session structure nor + * the framebuffer so that they may be re-used. + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void vnc_reset_session(FAR struct vnc_session_s *session, + FAR uint8_t *fb) +{ + FAR struct vnc_fbupdate_s *curr; + FAR struct vnc_fbupdate_s *next; + int i; + + /* Close any open sockets */ + + if (session->state >= VNCSERVER_CONNECTED) + { + psock_close(&session->connect); + psock_close(&session->listen); + } + + /* [Re-]initialize the session. */ + /* Put all of the pre-allocated update structures into the freelist */ + + sq_init(&session->updqueue); + + session->updfree.head = + (FAR sq_entry_t *)&session->updpool[0]; + session->updfree.tail = + (FAR sq_entry_t *)&session->updpool[CONFIG_VNCSERVER_NUPDATES-1]; + + next = &session->updpool[0]; + for (i = 1; i < CONFIG_VNCSERVER_NUPDATES-1; i++) + { + curr = next; + next = &session->updpool[i]; + curr->flink = next; + } + + next->flink = NULL; + + /* Set the INITIALIZED state */ + + sem_reset(&session->freesem, CONFIG_VNCSERVER_NUPDATES); + sem_reset(&session->queuesem, 0); + session->fb = fb; + session->state = VNCSERVER_INITIALIZED; +} + +/**************************************************************************** + * Name: vnc_connect + * + * Description: + * Wait for a connection from the VNC client + * + * Input Parameters: + * session - An instance of the session structure. + * port - The listen port to use + * + * Returned Value: + * Returns zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int vnc_connect(FAR struct vnc_session_s *session, int port) +{ + struct sockaddr_in addr; + int ret; + + /* Create a listening socket */ + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + ret = psock_socket(AF_INET, SOCK_STREAM, 0, &session->listen); + if (ret < 0) + { + ret = -get_errno(); + return ret; + } + + /* Bind the listening socket to a local address */ + + ret = psock_bind(&session->listen, (struct sockaddr *)&addr, + sizeof(struct sockaddr_in)); + if (ret < 0) + { + ret = -get_errno(); + goto errout_with_listener; + } + + /* Listen for a connection */ + + ret = psock_listen(&session->listen, 5); + if (ret < 0) + { + ret = -get_errno(); + goto errout_with_listener; + } + + /* Connect to the client */ + + ret = psock_accept(&session->listen, NULL, NULL, &session->connect); + if (ret < 0) + { + ret = -get_errno(); + goto errout_with_listener; + } + + session->state = VNCSERVER_CONNECTED; + return OK; + +errout_with_listener: + psock_close(&session->listen); + return ret; +} + +/**************************************************************************** + * Pubic Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_server + * + * Description: + * The VNC server daemon. This daemon is implemented as a kernel thread. + * + * Input Parameters: + * Standard kernel thread arguments (all ignored) + * + * Returned Value: + * This function does not return. + * + ****************************************************************************/ + +int vnc_server(int argc, FAR char *argv[]) +{ + FAR struct vnc_session_s *session; + FAR uint8_t *fb; + int display; + int ret; + + DEBUGASSERT(session != NULL); + + /* A single argument is expected: A diplay port number in ASCII form */ + + if (argc != 2) + { + gdbg("ERROR: Unexpected number of arguments: %d\n", argc); + return EXIT_FAILURE; + } + + display = atoi(argv[1]); + if (display < 0 || display >= RFB_MAX_DISPLAYS) + { + gdbg("ERROR: Invalid display number: %d\n", display); + return EXIT_FAILURE; + } + + /* Allocate the framebuffer memory. We rely on the fact that + * the KMM allocator will align memory to 32-bits or better. + */ + + fb = (FAR uint8_t *)kmm_zalloc(RFB_SIZE); + if (fb == NULL) + { + gdbg("ERROR: Failed to allocate framebuffer memory: %lu\n", + (unsigned long)alloc); + return -ENOMEM; + } + + /* Allocate a session structure for this display */ + + session = kmm_zalloc(sizeof(struct vnc_session_s)); + if (session == NULL) + { + gdbg("ERROR: Failed to allocate session\n"); + goto errout_with_fb; + } + + g_vnc_sessions[display] = session; + sem_init(&session->freesem, 0, CONFIG_VNCSERVER_NUPDATES); + sem_init(&session->queuesem, 0, 0); + + /* Loop... handling each each VNC client connection to this display. Only + * a single client is allowed for each display. + */ + + for (; ; ) + { + /* Release the last sesstion and [Re-]initialize the session structure + * for the next connection. + */ + + vnc_reset_session(session, fb); + + /* Establish a connection with the VNC client */ + + ret = vnc_connect(session, RFB_DISPLAY_PORT(display)); + if (ret >= 0) + { + gvdbg("New VNC connection\n"); + + /* Perform the VNC initialization sequence after the client has + * sucessfully connected to the server. Negotiate security, + * framebuffer and color properties. + */ + + ret = vnc_negotiate(session); + if (ret < 0) + { + gdbg("ERROR: Failed to negotiate security/framebuffer: %d\n", + ret); + continue; + } + + /* Start the VNC updater thread that sends all Server-to-Client + * messages. + */ + + ret = vnc_start_updater(session); + if (ret < 0) + { + gdbg("ERROR: Failed to start updater thread: %d\n", ret); + continue; + } + + /* Start the VNC receiver on this this. The VNC receiver handles + * all Client-to-Server messages. The VNC receiver function does + * not return until the session has been terminated (or an error + * occurs). + */ + + ret = vnc_receiver(session); + gvdbg("Session terminated with %d\n", ret); + + /* Stop the VNC updater thread. */ + + ret = vnc_stop_updater(session); + if (ret < 0) + { + gdbg("ERROR: Failed to stop updater thread: %d\n", ret); + } + } + } + +errout_with_fb: + kmm_free(fb); + return EXIT_FAILURE; +} + +/**************************************************************************** + * Name: vnc_find_session + * + * Description: + * Return the session structure associated with this display. + * + * Input Parameters: + * display - The display number of interest. + * + * Returned Value: + * Returns the instance of the session structure for this display. NULL + * will be returned if the server has not yet been started or if the + * display number is out of range. + * + ****************************************************************************/ + +FAR struct vnc_session_s *vnc_find_session(int display) +{ + FAR struct vnc_session_s *session = NULL; + + DEBUGASSERT(display >= 0 && display < RFB_MAX_DISPLAYS); + + if (display >= 0 && display < RFB_MAX_DISPLAYS) + { + session = g_vnc_sessions[display]; + } + + return session; +} diff --git a/graphics/vnc/server/vnc_server.h b/graphics/vnc/server/vnc_server.h new file mode 100644 index 00000000000..8433924cba8 --- /dev/null +++ b/graphics/vnc/server/vnc_server.h @@ -0,0 +1,392 @@ +/**************************************************************************** + * graphics/vnc/server/vnc_server.h + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __GRAPHICS_VNC_SERVER_VNC_SERVER_H +#define __GRAPHICS_VNC_SERVER_VNC_SERVER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration */ + +#ifndef CONFIG_NET_TCP_READAHEAD +# error CONFIG_NET_TCP_READAHEAD must be set to use VNC +#endif + +#ifndef CONFIG_NX_UPDATE +# error CONFIG_NX_UPDATE must be set to use VNC +#endif + +#if !defined(CONFIG_VNCSERVER_PROTO3p3) && !defined(CONFIG_VNCSERVER_PROTO3p8) +# error No VNC protocol selected +#endif + +#if defined(CONFIG_VNCSERVER_PROTO3p3) && defined(CONFIG_VNCSERVER_PROTO3p8) +# error Too many VNC protocols selected +#endif + +#ifndef CONFIG_VNCSERVER_NDISPLAYS +# define CONFIG_VNCSERVER_NDISPLAYS 1 +#endif + +#if defined(CONFIG_VNCSERVER_COLORFMT_RGB16) +# define RFB_COLORFMT FB_FMT_RGB16_565 +# define RFB_BITSPERPIXEL 16 +# define RFB_PIXELDEPTH 16 +# define RFB_TRUECOLOR 1 +# define RFB_RMAX 0x1f +# define RFB_GMAX 0x3f +# define RFB_BMAX 0x1f +# define RFB_RSHIFT 11 +# define RFB_GSHIFT 5 +# define RFB_BSHIFT 0 +#elif defined(CONFIG_VNCSERVER_COLORFMT_RGB32) +# define RFB_COLORFMT FB_FMT_RGB32 +# define RFB_BITSPERPIXEL 32 +# define RFB_PIXELDEPTH 24 +# define RFB_TRUECOLOR 1 +# define RFB_RMAX 0xff +# define RFB_GMAX 0xff +# define RFB_BMAX 0xff +# define RFB_RSHIFT 16 +# define RFB_GSHIFT 8 +# define RFB_BSHIFT 0 +#else +# error Unspecified/unsupported color format +#endif + +#ifndef CONFIG_VNCSERVER_SCREENWIDTH +# define CONFIG_VNCSERVER_SCREENWIDTH 320 +#endif + +#ifndef CONFIG_VNCSERVER_SCREENHEIGHT +# define CONFIG_VNCSERVER_SCREENHEIGHT 240 +#endif + +#ifndef CONFIG_VNCSERVER_PRIO +# define CONFIG_VNCSERVER_PRIO 100 +#endif + +#ifndef CONFIG_VNCSERVER_STACKSIZE +# define CONFIG_VNCSERVER_STACKSIZE 2048 +#endif + +#ifndef CONFIG_VNCSERVER_UPDATER_PRIO +# define CONFIG_VNCSERVER_UPDATER_PRIO 100 +#endif + +#ifndef CONFIG_VNCSERVER_UPDATER_STACKSIZE +# define CONFIG_VNCSERVER_UPDATER_STACKSIZE 2048 +#endif + +#ifndef CONFIG_VNCSERVER_INBUFFER_SIZE +# define CONFIG_VNCSERVER_INBUFFER_SIZE 80 +#endif + +#ifndef CONFIG_VNCSERVER_NUPDATES +# define CONFIG_VNCSERVER_NUPDATES 48 +#endif + +#ifndef CONFIG_VNCSERVER_UPDATE_BUFSIZE +# define CONFIG_VNCSERVER_UPDATE_BUFSIZE 4096 +#endif + +#define VNCSERVER_UPDATE_BUFSIZE \ + (CONFIG_VNCSERVER_UPDATE_BUFSIZE + SIZEOF_RFB_FRAMEBUFFERUPDATE_S(0)) + +/* Local framebuffer characteristics in bytes */ + +#define RFB_BYTESPERPIXEL ((RFB_BITSPERPIXEL + 7) >> 8) +#define RFB_STRIDE (RFB_BYTESPERPIXEL * CONFIG_VNCSERVER_SCREENWIDTH) +#define RFB_SIZE (RFB_STRIDE * CONFIG_VNCSERVER_SCREENHEIGHT) + +/* RFB Port Number */ + +#define RFB_PORT_BASE 5900 +#define RFB_MAX_DISPLAYS CONFIG_VNCSERVER_NDISPLAYS +#define RFB_DISPLAY_PORT(d) (RFB_PORT_BASE + (d)) + +/* Miscellaneous */ + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This enumeration indicates the state of the VNC server */ + +enum vnc_server_e +{ + VNCSERVER_UNINITIALIZED = 0, /* Initial state */ + VNCSERVER_INITIALIZED, /* State structured initialized, but not connected */ + VNCSERVER_CONNECTED, /* Connect to a client, but not yet configured */ + VNCSERVER_CONFIGURED, /* Configured and ready to transfer graphics */ + VNCSERVER_RUNNING, /* Running and activly transferring graphics */ + VNCSERVER_STOPPING, /* The updater has been asked to stop */ + VNCSERVER_STOPPED /* The updater has stopped */ +}; + +/* This structure is used to queue FrameBufferUpdate event. It includes a + * pointer to support singly linked list. + */ + +struct vnc_fbupdate_s +{ + FAR struct vnc_fbupdate_s *flink; + struct nxgl_rect_s rect; /* The enqueued update rectangle */ +}; + +struct vnc_session_s +{ + /* NX graphics system */ + + NXHANDLE handle; /* NX graphics handle */ + + /* Connection data */ + + struct socket listen; /* Listen socket */ + struct socket connect; /* Connected socket */ + volatile uint8_t state; /* See enum vnc_server_e */ + + /* Display geometry and color characteristics */ + + uint8_t colorfmt; /* Remote color format (See include/nuttx/fb.h) */ + uint8_t bpp; /* Remote bits per pixel */ + FAR uint8_t *fb; /* Allocated local frame buffer */ + + /* Updater information */ + + pthread_t updater; /* Updater thread ID */ + + /* Update list information */ + + struct vnc_fbupdate_s updpool[CONFIG_VNCSERVER_NUPDATES]; + sq_queue_t updfree; + sq_queue_t updqueue; + sem_t freesem; + sem_t queuesem; + + /* I/O buffers for misc network send/receive */ + + uint8_t inbuf[CONFIG_VNCSERVER_INBUFFER_SIZE]; + uint8_t outbuf[VNCSERVER_UPDATE_BUFSIZE]; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: vnc_server + * + * Description: + * The VNC server daemon. This daemon is implemented as a kernel thread. + * + * Input Parameters: + * Standard kernel thread arguments (all ignored) + * + * Returned Value: + * This function does not return. + * + ****************************************************************************/ + +int vnc_server(int argc, FAR char *argv[]); + +/**************************************************************************** + * Name: vnc_negotiate + * + * Description: + * Perform the VNC initialization sequence after the client has sucessfully + * connected to the server. Negotiate security, framebuffer and color + * properties. + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * Returns zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int vnc_negotiate(FAR struct vnc_session_s *session); + +/**************************************************************************** + * Name: vnc_start_updater + * + * Description: + * Start the updater thread + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int vnc_start_updater(FAR struct vnc_session_s *session); + +/**************************************************************************** + * Name: vnc_stop_updater + * + * Description: + * Stop the updater thread + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int vnc_stop_updater(FAR struct vnc_session_s *session); + +/**************************************************************************** + * Name: vnc_update_rectangle + * + * Description: + * Queue an update of the specified rectangular region on the display. + * + * Input Parameters: + * session - An instance of the session structure. + * rect - The rectanglular region to be updated. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int vnc_update_rectangle(FAR struct vnc_session_s *session, + FAR const struct nxgl_rect_s *rect); + +/**************************************************************************** + * Name: vnc_receiver + * + * Description: + * This function handles all Client-to-Server messages. + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * At present, always returns OK + * + ****************************************************************************/ + +int vnc_receiver(FAR struct vnc_session_s *session); + +/**************************************************************************** + * Name: vnc_key_map + * + * Description: + * Map the receive X11 keysym into something understood by NuttX and route + * that through NX to the appropriate window. + * + * Input Parameters: + * session - An instance of the session structure. + * keysym - The X11 keysym value (see include/nuttx/inputx11_keysymdef) + * keydown - True: Key pressed; False: Key released + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NX_KBD +void vnc_key_map(FAR struct vnc_session_s *session, uint16_t keysym, + bool keydown); +#endif + +/**************************************************************************** + * Name: vnc_find_session + * + * Description: + * Return the session structure associated with this display. + * + * Input Parameters: + * display - The display number of interest. + * + * Returned Value: + * Returns the instance of the session structure for this display. NULL + * will be returned if the server has not yet been started or if the + * display number is out of range. + * + ****************************************************************************/ + +FAR struct vnc_session_s *vnc_find_session(int display); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __GRAPHICS_VNC_SERVER_VNC_SERVER_H */ diff --git a/graphics/vnc/server/vnc_updater.c b/graphics/vnc/server/vnc_updater.c new file mode 100644 index 00000000000..067eba160b1 --- /dev/null +++ b/graphics/vnc/server/vnc_updater.c @@ -0,0 +1,852 @@ +/**************************************************************************** + * graphics/vnc/vnc_updater.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "vnc_server.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Color conversion functions */ + +#if defined(CONFIG_VNCSERVER_COLORFMT_RGB16) +typedef CODE uint16_t(*vnc_convert16_t)(uint16_t rgb); +typedef CODE uint32_t(*vnc_convert32_t)(uint16_t rgb); +#elif defined(CONFIG_VNCSERVER_COLORFMT_RGB32) +typedef CODE uint16_t(*vnc_convert16_t)(uint32_t rgb); +typedef CODE uint32_t(*vnc_convert32_t)(uint32_t rgb); +#else +# error Unspecified/unsupported color format +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_alloc_update + * + * Description: + * Allocate one update structure by taking it from the freelist. + * + * Input Parameters: + * session - A reference to the VNC session structure. + * + * Returned Value: + * A non-NULL structure pointer should always be returned. This function + * will wait if no structure is available. + * + ****************************************************************************/ + +static FAR struct vnc_fbupdate_s * +vnc_alloc_update(FAR struct vnc_session_s *session) +{ + FAR struct vnc_fbupdate_s *update; + + /* Reserve one element from the free list. Lock the scheduler to assure + * that the sq_remfirst() and the successful return from sem_wait are + * atomic. Of course, the scheduler will be unlocked while we wait. + */ + + sched_lock(); + while (sem_wait(&session->freesem) < 0) + { + DEBUGASSERT(get_errno() == EINTR); + } + + /* It is reserved.. go get it */ + + update = (FAR struct vnc_fbupdate_s *)sq_remfirst(&session->updfree); + sched_unlock(); + + DEBUGASSERT(update != NULL); + return update; +} + +/**************************************************************************** + * Name: vnc_free_update + * + * Description: + * Free one update structure by returning it from the freelist. + * + * Input Parameters: + * session - A reference to the VNC session structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void vnc_free_update(FAR struct vnc_session_s *session, + FAR struct vnc_fbupdate_s *update) +{ + /* Reserve one element from the free list. Lock the scheduler to assure + * that the sq_addlast() and the sem_post() are atomic. + */ + + sched_lock(); + + /* Put the entry into the free list */ + + sq_addlast((FAR sq_entry_t *)update, &session->updfree); + + /* Post the semaphore to indicate the availability of one more update */ + + sem_post(&session->freesem); + DEBUGASSERT(session->freesem.semcount <= CONFIG_VNCSERVER_NUPDATES); + + sched_unlock(); +} + +/**************************************************************************** + * Name: vnc_remove_queue + * + * Description: + * Remove one entry from the list of queued rectangles, waiting if + * necessary if the queue is empty. + * + * Input Parameters: + * session - A reference to the VNC session structure. + * + * Returned Value: + * A non-NULL structure pointer should always be returned. This function + * will wait if no structure is available. + * + ****************************************************************************/ + +static FAR struct vnc_fbupdate_s * +vnc_remove_queue(FAR struct vnc_session_s *session) +{ + FAR struct vnc_fbupdate_s *rect; + + /* Reserve one element from the list of queued rectangle. Lock the + * scheduler to assure that the sq_remfirst() and the successful return + * from sem_wait are atomic. Of course, the scheduler will be unlocked + * while we wait. + */ + + sched_lock(); + while (sem_wait(&session->queuesem) < 0) + { + DEBUGASSERT(get_errno() == EINTR); + } + + /* It is reserved.. go get it */ + + rect = (FAR struct vnc_fbupdate_s *)sq_remfirst(&session->updqueue); + sched_unlock(); + + DEBUGASSERT(rect != NULL); + return rect; +} + +/**************************************************************************** + * Name: vnc_add_queue + * + * Description: + * Add one rectangle entry to the list of queued rectangles to be updated. + * + * Input Parameters: + * session - A reference to the VNC session structure. + * rect - The rectangle to be added to the queue. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void vnc_add_queue(FAR struct vnc_session_s *session, + FAR struct vnc_fbupdate_s *rect) +{ + /* Lock the scheduler to assure that the sq_addlast() and the sem_post() + * are atomic. + */ + + sched_lock(); + + /* Put the entry into the list of queued rectangles. */ + + sq_addlast((FAR sq_entry_t *)rect, &session->updqueue); + + /* Post the semaphore to indicate the availability of one more rectangle + * in the queue. This may wakeup the updater. + */ + + sem_post(&session->queuesem); + DEBUGASSERT(session->queuesem.semcount <= CONFIG_VNCSERVER_NUPDATES); + + sched_unlock(); +} + +/**************************************************************************** + * Name: vnc_convert_rgbNN + * + * Description: + * Convert the native framebuffer color format (either RGB16 5:6:5 or RGB32 + * 8:8:8) to the remote framebuffer color format (either RGB16 5:6:5, + * RGB16 5:5:5, or RGB32 8:8:) + * + * Input Parameters: + * pixel - The src color in local framebuffer format. + * + * Returned Value: + * The pixel in the remote framebuffer color format. + * + ****************************************************************************/ + +#if defined(CONFIG_VNCSERVER_COLORFMT_RGB16) + +uint16_t vnc_convert_rgb16_555(uint16_t rgb) +{ + /* 111111 + * 54321098 76543210 + * ----------------- + * RRRRRGGG GGGBBBBB + * .RRRRRGG GGGBBBBB + */ + + return (((rgb >> 1) & ~0x1f) | (rgb & 0x1f)); +} + +uint16_t vnc_convert_rgb16_565(uint16_t rgb) +{ + /* Identity mapping */ + + return rgb; +} + +uint32_t vnc_convert_rgb32_888(uint16_t rgb) +{ + /* 33222222 22221111 111111 + * 10987654 32109876 54321098 76543210 + * ---------------------------------- + * RRRRRGGG GGGBBBBB + * RRRRR... GGGGGG.. BBBBB... + */ + + return (((uint32_t)rgb << 8) & 0x00f80000) | + (((uint32_t)rgb << 6) & 0x0000fc00) | + (((uint32_t)rgb << 3) & 0x000000f8); +} + +#elif defined(CONFIG_VNCSERVER_COLORFMT_RGB32) +uint16_t vnc_convert_rgb16_555(uint32_t rgb) +{ + /* 33222222 22221111 111111 + * 10987654 32109876 54321098 76543210 + * ---------------------------------- + * RRRRR... GGGGG... BBBBB... + * .RRRRRGG GGGBBBBB + */ + + return (uint16_t) + (((rgb >> 9) & 0x00007c00) | + ((rgb >> 6) & 0x000003e0) | + ((rgb >> 3) & 0x0000001f)); +} + +uint16_t vnc_convert_rgb16_565(uint32_t rgb) +{ + /* 33222222 22221111 111111 + * 10987654 32109876 54321098 76543210 + * ---------------------------------- + * RRRRR... GGGGGG.. BBBBB... + * RRRRRGGG GGGBBBBB + */ + + return (uint16_t) + (((rgb >> 8) & 0x0000f800) | + ((rgb >> 5) & 0x000007e0) | + ((rgb >> 3) & 0x0000001f)); +} + +uint32_t vnc_convert_rgb32_888(uint32_t rgb) +{ + /* Identity mapping */ + + return rgb; +} +#else +# error Unspecified/unsupported color format +#endif + +/**************************************************************************** + * Name: vnc_copy16 + * + * Description: + * Copy a 16/32-bit pixels from the source rectangle to a 16-bit pixel + * destination rectangle. + * + * Input Parameters: + * session - A reference to the VNC session structure. + * row,col - The upper left X/Y (pixel/row) position of the rectangle + * width,height - The width (pixels) and height (rows of the rectangle) + * convert - The function to use to convert from the local framebuffer + * color format to the remote framebuffer color format. + * + * Returned Value: + * The size of the transfer in bytes. + * + ****************************************************************************/ + +static size_t vnc_copy16(FAR struct vnc_session_s *session, + nxgl_coord_t row, nxgl_coord_t col, + nxgl_coord_t height, nxgl_coord_t width, + vnc_convert16_t convert) +{ +#if defined(CONFIG_VNCSERVER_COLORFMT_RGB16) + FAR struct rfb_framebufferupdate_s *update; + FAR const uint16_t *srcleft; + FAR const uint16_t *src; + FAR uint16_t *dest; + nxgl_coord_t x; + nxgl_coord_t y; + + /* Destination rectangle start address */ + + update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; + dest = (FAR uint16_t *)update->rect[0].data; + + /* Source rectangle start address (left/top)*/ + + srcleft = (FAR uint16_t *)(session->fb + RFB_STRIDE * y + RFB_BYTESPERPIXEL * x); + + /* Transfer each row from the source buffer into the update buffer */ + + for (y = 0; y < row; y++) + { + src = srcleft; + for (y = 0; y < row; y++) + { + *dest++ = convert(*src); + src++; + } + + srcleft = (FAR uint16_t *)((uintptr_t)srcleft + RFB_STRIDE); + } + + return (size_t)((uintptr_t)dest - (uintptr_t)update->rect[0].data); + +#elif defined(CONFIG_VNCSERVER_COLORFMT_RGB32) + FAR struct rfb_framebufferupdate_s *update; + FAR const uint32_t *srcleft; + FAR const uint32_t *src; + FAR uint16_t *dest; + nxgl_coord_t x; + nxgl_coord_t y; + + /* Destination rectangle start address */ + + update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; + dest = (FAR uint16_t *)update->rect[0].data; + + /* Source rectangle start address */ + + srcleft = (FAR uint32_t *)(session->fb + RFB_STRIDE * y + RFB_BYTESPERPIXEL * x); + + for (y = 0; y < row; y++) + { + src = srcleft; + for (y = 0; y < row; y++) + { + *dest++ = convert(*src); + src++; + } + + srcleft = (FAR uint32_t *)((uintptr_t)srcleft + RFB_STRIDE); + } + + return (size_t)((uintptr_t)dest - (uintptr_t)update->rect[0].data); +#endif +} + +/**************************************************************************** + * Name: vnc_copy32 + * + * Description: + * Copy a 16/32-bit pixels from the source rectangle to a 32-bit pixel + * destination rectangle. + * + * Input Parameters: + * session - A reference to the VNC session structure. + * row,col - The upper left X/Y (pixel/row) position of the rectangle + * width,height - The width (pixels) and height (rows of the rectangle) + * convert - The function to use to convert from the local framebuffer + * color format to the remote framebuffer color format. + * + * Returned Value: + * The size of the transfer in bytes. + * + ****************************************************************************/ + +static size_t vnc_copy32(FAR struct vnc_session_s *session, + nxgl_coord_t row, nxgl_coord_t col, + nxgl_coord_t height, nxgl_coord_t width, + vnc_convert32_t convert) +{ +#if defined(CONFIG_VNCSERVER_COLORFMT_RGB16) + FAR struct rfb_framebufferupdate_s *update; + FAR const uint16_t *srcleft; + FAR const uint16_t *src; + FAR uint32_t *dest; + nxgl_coord_t x; + nxgl_coord_t y; + + /* Destination rectangle start address */ + + update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; + dest = (FAR uint32_t *)update->rect[0].data; + + /* Source rectangle start address (left/top)*/ + + srcleft = (FAR uint16_t *)(session->fb + RFB_STRIDE * y + RFB_BYTESPERPIXEL * x); + + /* Transfer each row from the source buffer into the update buffer */ + + for (y = 0; y < row; y++) + { + src = srcleft; + for (y = 0; y < row; y++) + { + *dest++ = convert(*src); + src++; + } + + srcleft = (FAR uint16_t *)((uintptr_t)srcleft + RFB_STRIDE); + } + + return (size_t)((uintptr_t)srcleft - (uintptr_t)update->rect[0].data); + +#elif defined(CONFIG_VNCSERVER_COLORFMT_RGB32) + FAR struct rfb_framebufferupdate_s *update; + FAR const uint32_t *srcleft; + FAR const uint32_t *src; + FAR uint32_t *dest; + nxgl_coord_t x; + nxgl_coord_t y; + + /* Destination rectangle start address */ + + update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; + dest = (FAR uint32_t *)update->rect[0].data; + + /* Source rectangle start address */ + + srcleft = (FAR uint32_t *)(session->fb + RFB_STRIDE * y + RFB_BYTESPERPIXEL * x); + + for (y = 0; y < row; y++) + { + src = srcleft; + for (y = 0; y < row; y++) + { + *dest++ = convert(*src); + src++; + } + + srcleft = (FAR uint32_t *)((uintptr_t)srcleft + RFB_STRIDE); + } + + return (size_t)((uintptr_t)srcleft - (uintptr_t)update->rect[0].data); +#endif +} + +/**************************************************************************** + * Name: vnc_updater + * + * Description: + * This is the "updater" thread. It is the sender of all Server-to-Client + * messages + * + * Input Parameters: + * Standard pthread arguments. + * + * Returned Value: + * NULL is always returned. + * + ****************************************************************************/ + +static FAR void *vnc_updater(FAR void *arg) +{ + FAR struct vnc_session_s *session = (FAR struct vnc_session_s *)arg; + FAR struct rfb_framebufferupdate_s *update; + + FAR struct rfb_rectangle_s *destrect; + FAR struct vnc_fbupdate_s *srcrect; + FAR const uint8_t *srcrow; + FAR const uint8_t *src; + nxgl_coord_t srcwidth; + nxgl_coord_t srcheight; + nxgl_coord_t destwidth; + nxgl_coord_t destheight; + nxgl_coord_t deststride; + nxgl_coord_t updwidth; + nxgl_coord_t updheight; + nxgl_coord_t width; + nxgl_coord_t x; + nxgl_coord_t y; + unsigned int bytesperpixel; + unsigned int maxwidth; + size_t size; + ssize_t nsent; + + union + { + vnc_convert16_t bpp16; + vnc_convert32_t bpp32; + } convert; + bool color32 = false; + + /* Set up some constant pointers and values */ + + DEBUGASSERT(session != NULL); + update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; + destrect = update->rect; + + bytesperpixel = (session->bpp + 7) >> 3; + maxwidth = CONFIG_VNCSERVER_UPDATE_BUFSIZE / bytesperpixel; + + /* Set up the color conversion */ + + switch (session->colorfmt) + { + case FB_FMT_RGB16_555: + convert.bpp16 = vnc_convert_rgb16_555; + break; + + case FB_FMT_RGB16_565: + convert.bpp16 = vnc_convert_rgb16_565; + break; + + case FB_FMT_RGB32: + convert.bpp32 = vnc_convert_rgb32_888; + color32 = true; + break; + + default: + gdbg("ERROR: Unrecognized color format: %d\n", session->colorfmt); + goto errout; + } + + /* Then loop, processing updates until we are asked to stop. + * REVISIT: Probably need some kind of signal mechanism to wake up + * vnc_remove_queue() in order to stop. Or perhaps a special STOP + * message in the queue? + */ + + while (session->state == VNCSERVER_RUNNING) + { + /* Get the next queued rectangle update. This call will block until an + * upate is available for the case where the update queue is empty. + */ + + srcrect = vnc_remove_queue(session); + DEBUGASSERT(srcrect != NULL); + + /* Get with width and height of the source and destination rectangles. + * The source rectangle many be larger than the destination rectangle. + * In that case, we will have to emit multiple rectangles. + */ + + DEBUGASSERT(srcrect->rect.pt1.x <= srcrect->rect.pt2.x); + srcwidth = srcrect->rect.pt2.x - srcrect->rect.pt1.x + 1; + + DEBUGASSERT(srcrect->rect.pt1.y <= srcrect->rect.pt2.y); + srcheight = srcrect->rect.pt2.y - srcrect->rect.pt1.y + 1; + + srcrow = session->fb + + RFB_STRIDE * srcrect->rect.pt1.y + + RFB_BYTESPERPIXEL * srcrect->rect.pt1.x; + + deststride = srcwidth * bytesperpixel; + if (deststride > maxwidth) + { + deststride = maxwidth; + } + + destwidth = deststride / bytesperpixel; + destheight = CONFIG_VNCSERVER_UPDATE_BUFSIZE / deststride; + + if (destheight > srcheight) + { + destheight = srcheight; + } + + /* Format the rectangle header. We may have to send several update + * messages if the pre-allocated outbuf is smaller than the rectangle. + * Each update contains a small "sub-rectangle" of the origin update. + * + * Loop until all sub-rectangles have been output. Start with the + * top row and transfer rectangles horizontally across each swath. + * The height of the swath is destwidth (the last may be shorter). + */ + + for (y = srcrect->rect.pt1.y; + srcheight > 0; + srcheight -= updheight, y += updheight, + srcrow += RFB_STRIDE * updheight) + { + /* updheight = Height to update on this pass through the loop. + * This will be destheight unless fewer than that number of rows + * remain. + */ + + updheight = destheight; + if (updheight > srcheight) + { + updheight = srcheight; + } + + /* Loop until this horizontal swath has sent to the VNC client. + * Start with the leftmost pixel and transfer rectangles + * horizontally with width of destwidth until all srcwidth + * columns have been transferred (the last rectangle may be + * narrower). + */ + + for (width = srcwidth, x = srcrect->rect.pt1.x, src = srcrow; + width > 0; + width -= updwidth, x += updwidth, src += updwidth) + { + /* updwidth = Width to update on this pass through the loop. + * This will be destwidth unless fewer than that number of + * columns remain. + */ + + updwidth = destwidth; + if (updwidth > width) + { + updwidth = width; + } + + /* Transfer the frame buffer data into the rectangle, + * performing the necessary color conversions. + */ + + if (color32) + { + size = vnc_copy32(session, y, x, updheight, updwidth, + convert.bpp32); + } + else + { + size = vnc_copy16(session, y, x, updheight, updwidth, + convert.bpp16); + } + + /* Format the FramebufferUpdate message */ + + update->msgtype = RFB_FBUPDATE_MSG; + update->padding = 0; + rfb_putbe16(update->nrect, 1); + + rfb_putbe16(destrect->xpos, x); + rfb_putbe16(destrect->ypos, y); + rfb_putbe16(destrect->width, updwidth); + rfb_putbe16(destrect->height, updheight); + rfb_putbe16(destrect->encoding, RFB_ENCODING_RAW); + + DEBUGASSERT(size <= CONFIG_VNCSERVER_UPDATE_BUFSIZE); + + /* Then send the update packet to the VNC client */ + + size += SIZEOF_RFB_FRAMEBUFFERUPDATE_S(0); + nsent = psock_send(&session->connect, session->outbuf, size, 0); + if (nsent < 0) + { + gdbg("ERROR: Send FrameBufferUpdate failed: %d\n", + get_errno()); + goto errout; + } + + DEBUGASSERT(nsent == size); + } + } + + vnc_free_update(session, srcrect); + } + +errout: + session->state = VNCSERVER_STOPPED; + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: vnc_start_updater + * + * Description: + * Start the updater thread + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int vnc_start_updater(FAR struct vnc_session_s *session) +{ + pthread_attr_t attr; + struct sched_param param; + int status; + + /* Create thread that is gonna send rectangles to the client */ + + session->state = VNCSERVER_RUNNING; + + DEBUGVERIFY(pthread_attr_init(&attr)); + DEBUGVERIFY(pthread_attr_setstacksize(&attr, CONFIG_VNCSERVER_UPDATER_STACKSIZE)); + + param.sched_priority = CONFIG_VNCSERVER_UPDATER_PRIO; + DEBUGVERIFY(pthread_attr_setschedparam(&attr, ¶m)); + + status = pthread_create(&session->updater, &attr, vnc_updater, + (pthread_addr_t)session); + if (status != 0) + { + return -status; + } + + return OK; +} + +/**************************************************************************** + * Name: vnc_stop_updater + * + * Description: + * Stop the updater thread + * + * Input Parameters: + * session - An instance of the session structure. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int vnc_stop_updater(FAR struct vnc_session_s *session) +{ + pthread_addr_t result; + int status; + + /* Is the update thread running? */ + + if (session->state == VNCSERVER_RUNNING) + { + /* Yes.. ask it to please stop */ + + session->state = VNCSERVER_STOPPING; + + /* Wait for the thread to comply with our request */ + + status = pthread_join(session->updater, &result); + if (status != 0) + { + return -status; + } + + /* Return what the thread returned */ + + return (int)((intptr_t)result); + } + + /* Not running? Just say we stopped the thread successfully. */ + + return OK; +} + +/**************************************************************************** + * Name: vnc_update_rectangle + * + * Description: + * Queue an update of the specified rectangular region on the display. + * + * Input Parameters: + * session - An instance of the session structure. + * rect - The rectanglular region to be updated. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int vnc_update_rectangle(FAR struct vnc_session_s *session, + FAR const struct nxgl_rect_s *rect) +{ + FAR struct vnc_fbupdate_s *update; + + /* Make sure that the rectangle has a area */ + + if (!nxgl_nullrect(rect)) + { + /* Allocate an update structure... waiting if necessary */ + + update = vnc_alloc_update(session); + DEBUGASSERT(update != NULL); + + /* Copy the rectangle into the update structure */ + + memcpy(&update->rect, rect, sizeof(struct nxgl_rect_s)); + + /* Add the upate to the end of the update queue. */ + + vnc_add_queue(session, update); + } + + return -ENOSYS; +} diff --git a/net/socket/recvfrom.c b/net/socket/recvfrom.c index a6921315fd1..01259753933 100644 --- a/net/socket/recvfrom.c +++ b/net/socket/recvfrom.c @@ -261,7 +261,7 @@ static inline void recvfrom_newtcpdata(FAR struct net_driver_s *dev, size_t recvlen = recvfrom_newdata(dev, pstate); - /* If there is more data left in the packet that we could not buffer, than + /* If there is more data left in the packet that we could not buffer, then * add it to the read-ahead buffers. */