| /**************************************************************************** |
| * drivers/video/vnc/vnc_raw.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <assert.h> |
| #include <errno.h> |
| |
| #if defined(CONFIG_VNCSERVER_DEBUG) && !defined(CONFIG_DEBUG_GRAPHICS) |
| # undef CONFIG_DEBUG_ERROR |
| # undef CONFIG_DEBUG_WARN |
| # undef CONFIG_DEBUG_INFO |
| # undef CONFIG_DEBUG_GRAPHICS_ERROR |
| # undef CONFIG_DEBUG_GRAPHICS_WARN |
| # undef CONFIG_DEBUG_GRAPHICS_INFO |
| # define CONFIG_DEBUG_ERROR 1 |
| # define CONFIG_DEBUG_WARN 1 |
| # define CONFIG_DEBUG_INFO 1 |
| # define CONFIG_DEBUG_GRAPHICS 1 |
| # define CONFIG_DEBUG_GRAPHICS_ERROR 1 |
| # define CONFIG_DEBUG_GRAPHICS_WARN 1 |
| # define CONFIG_DEBUG_GRAPHICS_INFO 1 |
| #endif |
| #include <debug.h> |
| |
| #include "vnc_server.h" |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: vnc_copy8 |
| * |
| * Description: |
| * Copy a 16/32-bit pixels from the source rectangle to a 8-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_copy8(FAR struct vnc_session_s *session, |
| fb_coord_t row, fb_coord_t col, |
| fb_coord_t height, fb_coord_t width, |
| vnc_convert8_t convert) |
| { |
| FAR struct rfb_framebufferupdate_s *update; |
| FAR const lfb_color_t *srcleft; |
| FAR const lfb_color_t *src; |
| FAR uint8_t *dest; |
| fb_coord_t x; |
| fb_coord_t y; |
| |
| /* Destination rectangle start address */ |
| |
| update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; |
| dest = (FAR uint8_t *)update->rect[0].data; |
| |
| /* Source rectangle start address (left/top) */ |
| |
| srcleft = (FAR lfb_color_t *) |
| (session->fb + RFB_STRIDE * row + RFB_BYTESPERPIXEL * col); |
| |
| /* Transfer each row from the source buffer into the update buffer */ |
| |
| for (y = 0; y < height; y++) |
| { |
| src = srcleft; |
| for (x = 0; x < width; x++) |
| { |
| *dest++ = convert(*src); |
| src++; |
| } |
| |
| srcleft = (FAR lfb_color_t *)((uintptr_t)srcleft + RFB_STRIDE); |
| } |
| |
| return (size_t)((uintptr_t)dest - (uintptr_t)update->rect[0].data); |
| } |
| |
| /**************************************************************************** |
| * 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, |
| fb_coord_t row, fb_coord_t col, |
| fb_coord_t height, fb_coord_t width, |
| vnc_convert16_t convert) |
| { |
| FAR struct rfb_framebufferupdate_s *update; |
| FAR const lfb_color_t *srcleft; |
| FAR const lfb_color_t *src; |
| FAR uint8_t *dest; |
| uint16_t pixel; |
| fb_coord_t x; |
| fb_coord_t y; |
| bool bigendian; |
| |
| /* Destination rectangle start address */ |
| |
| update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; |
| dest = (FAR uint8_t *)update->rect[0].data; |
| |
| /* Source rectangle start address (left/top) */ |
| |
| srcleft = (FAR lfb_color_t *) |
| (session->fb + RFB_STRIDE * row + RFB_BYTESPERPIXEL * col); |
| |
| /* Transfer each row from the source buffer into the update buffer */ |
| |
| bigendian = session->bigendian; |
| for (y = 0; y < height; y++) |
| { |
| src = srcleft; |
| for (x = 0; x < width; x++) |
| { |
| pixel = convert(*src); |
| |
| if (bigendian) |
| { |
| rfb_putbe16(dest, pixel); |
| } |
| else |
| { |
| rfb_putle16(dest, pixel); |
| } |
| |
| dest += sizeof(uint16_t); |
| src++; |
| } |
| |
| srcleft = (FAR lfb_color_t *)((uintptr_t)srcleft + RFB_STRIDE); |
| } |
| |
| return (size_t)((uintptr_t)dest - (uintptr_t)update->rect[0].data); |
| } |
| |
| /**************************************************************************** |
| * 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, |
| fb_coord_t row, fb_coord_t col, |
| fb_coord_t height, fb_coord_t width, |
| vnc_convert32_t convert) |
| { |
| FAR struct rfb_framebufferupdate_s *update; |
| FAR const lfb_color_t *srcleft; |
| FAR const lfb_color_t *src; |
| FAR uint8_t *dest; |
| fb_coord_t x; |
| fb_coord_t y; |
| uint32_t pixel; |
| bool bigendian; |
| |
| /* Destination rectangle start address */ |
| |
| update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; |
| dest = (FAR uint8_t *)update->rect[0].data; |
| |
| /* Source rectangle start address (left/top) */ |
| |
| srcleft = (FAR lfb_color_t *) |
| (session->fb + RFB_STRIDE * row + RFB_BYTESPERPIXEL * col); |
| |
| /* Transfer each row from the source buffer into the update buffer */ |
| |
| bigendian = session->bigendian; |
| for (y = 0; y < height; y++) |
| { |
| src = srcleft; |
| for (x = 0; x < width; x++) |
| { |
| pixel = convert(*src); |
| |
| if (bigendian) |
| { |
| rfb_putbe32(dest, pixel); |
| } |
| else |
| { |
| rfb_putle32(dest, pixel); |
| } |
| |
| dest += sizeof(uint32_t); |
| src++; |
| } |
| |
| srcleft = (FAR lfb_color_t *)((uintptr_t)srcleft + RFB_STRIDE); |
| } |
| |
| return (size_t)((uintptr_t)dest - (uintptr_t)update->rect[0].data); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: vnc_raw |
| * |
| * Description: |
| * As a fallback, send the framebuffer update using the RAW encoding which |
| * must be supported by all VNC clients. |
| * |
| * Input Parameters: |
| * session - An instance of the session structure. |
| * rect - Describes the rectangle in the local framebuffer. |
| * |
| * Returned Value: |
| * Zero (OK) on success; A negated errno value is returned on failure that |
| * indicates the nature of the failure. A failure is only returned |
| * in cases of a network failure and unexpected internal failures. |
| * |
| ****************************************************************************/ |
| |
| int vnc_raw(FAR struct vnc_session_s *session, FAR struct fb_area_s *rect) |
| { |
| FAR struct rfb_framebufferupdate_s *update; |
| FAR const uint8_t *src; |
| fb_coord_t srcwidth; |
| fb_coord_t srcheight; |
| fb_coord_t destwidth; |
| fb_coord_t destheight; |
| fb_coord_t deststride; |
| fb_coord_t updwidth; |
| fb_coord_t updheight; |
| fb_coord_t width; |
| fb_coord_t x; |
| fb_coord_t y; |
| unsigned int bytesperpixel; |
| unsigned int maxwidth; |
| size_t size; |
| ssize_t nsent; |
| uint8_t colorfmt; |
| |
| union |
| { |
| vnc_convert8_t bpp8; |
| vnc_convert16_t bpp16; |
| vnc_convert32_t bpp32; |
| } convert; |
| |
| /* Set up characteristics of the client pixel format to use on this |
| * update. These can change at any time if a SetPixelFormat is |
| * received asynchronously. |
| */ |
| |
| bytesperpixel = (session->bpp + 7) >> 3; |
| maxwidth = CONFIG_VNCSERVER_UPDATE_BUFSIZE / bytesperpixel; |
| |
| /* Set up the color conversion */ |
| |
| colorfmt = session->colorfmt; |
| switch (colorfmt) |
| { |
| case FB_FMT_RGB8_222: |
| convert.bpp8 = vnc_convert_rgb8_222; |
| break; |
| |
| case FB_FMT_RGB8_332: |
| convert.bpp8 = vnc_convert_rgb8_332; |
| break; |
| |
| 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; |
| break; |
| |
| default: |
| gerr("ERROR: Unrecognized color format: %d\n", session->colorfmt); |
| return -EINVAL; |
| } |
| |
| /* 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. |
| */ |
| |
| srcwidth = rect->w; |
| srcheight = rect->h; |
| |
| deststride = srcwidth * bytesperpixel; |
| if (deststride > maxwidth) |
| { |
| deststride = maxwidth; |
| } |
| |
| DEBUGASSERT(CONFIG_VNCSERVER_UPDATE_BUFSIZE > |
| SIZEOF_RFB_FRAMEBUFFERUPDATE_S(SIZEOF_RFB_RECTANGE_S(0))); |
| |
| destwidth = deststride / bytesperpixel; |
| |
| /* Reserve some space for message header */ |
| |
| destheight = (CONFIG_VNCSERVER_UPDATE_BUFSIZE - |
| SIZEOF_RFB_FRAMEBUFFERUPDATE_S(SIZEOF_RFB_RECTANGE_S(0))) / |
| 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). |
| * |
| * NOTE that the loop also terminates of the color format changes |
| * asynchronously. |
| */ |
| |
| for (y = rect->y; |
| srcheight > 0 && colorfmt == session->colorfmt; |
| srcheight -= updheight, y += 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 been 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). |
| * |
| * NOTE that the loop also terminates of the color format |
| * changes asynchronously. |
| */ |
| |
| for (width = srcwidth, x = rect->x; |
| width > 0 && colorfmt == session->colorfmt; |
| width -= updwidth, x += 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 (bytesperpixel == 1) |
| { |
| size = vnc_copy8(session, y, x, updheight, updwidth, |
| convert.bpp8); |
| } |
| else if (bytesperpixel == 2) |
| { |
| size = vnc_copy16(session, y, x, updheight, updwidth, |
| convert.bpp16); |
| } |
| else /* bytesperpixel == 4 */ |
| { |
| size = vnc_copy32(session, y, x, updheight, updwidth, |
| convert.bpp32); |
| } |
| |
| /* Format the FramebufferUpdate message */ |
| |
| update = (FAR struct rfb_framebufferupdate_s *)session->outbuf; |
| |
| update->msgtype = RFB_FBUPDATE_MSG; |
| update->padding = 0; |
| rfb_putbe16(update->nrect, 1); |
| |
| rfb_putbe16(update->rect[0].xpos, x); |
| rfb_putbe16(update->rect[0].ypos, y); |
| rfb_putbe16(update->rect[0].width, updwidth); |
| rfb_putbe16(update->rect[0].height, updheight); |
| rfb_putbe32(update->rect[0].encoding, RFB_ENCODING_RAW); |
| |
| DEBUGASSERT(size <= CONFIG_VNCSERVER_UPDATE_BUFSIZE); |
| |
| /* We are ready to send the update packet to the VNC client */ |
| |
| size += SIZEOF_RFB_FRAMEBUFFERUPDATE_S(SIZEOF_RFB_RECTANGE_S(0)); |
| src = session->outbuf; |
| |
| /* At the very last most, make certain that the color format |
| * has not changed asynchronously. |
| */ |
| |
| if (colorfmt == session->colorfmt) |
| { |
| /* Okay send until all of the bytes are out. This may |
| * loop for the case where TCP write buffering is enabled |
| * and there are a limited number of IOBs available. |
| */ |
| |
| do |
| { |
| nsent = psock_send(&session->connect, src, size, 0); |
| if (nsent < 0) |
| { |
| gerr("ERROR: Send FrameBufferUpdate failed: %d\n", |
| (int)nsent); |
| return (int)nsent; |
| } |
| |
| DEBUGASSERT(nsent <= size); |
| src += nsent; |
| size -= nsent; |
| } |
| while (size > 0); |
| |
| updinfo("Sent {(%d, %d),(%d, %d)}\n", |
| x, y, x + updwidth -1, y + updheight - 1); |
| } |
| } |
| } |
| |
| return OK; |
| } |