diff --git a/usr.sbin/bhyve/rfb.c b/usr.sbin/bhyve/rfb.c --- a/usr.sbin/bhyve/rfb.c +++ b/usr.sbin/bhyve/rfb.c @@ -139,6 +139,11 @@ uint32_t *crc; /* WxH crc cells */ uint32_t *crc_tmp; /* buffer to store single crc row */ int crc_width, crc_height; + + uint32_t *pixrow; + uint8_t red_shift; + uint8_t green_shift; + uint8_t blue_shift; }; struct rfb_pixfmt { @@ -179,6 +184,10 @@ #define RFB_MAX_HEIGHT 1200 #define RFB_ZLIB_BUFSZ RFB_MAX_WIDTH*RFB_MAX_HEIGHT*4 +#define PIXEL_RED_SHIFT 16 +#define PIXEL_GREEN_SHIFT 8 +#define PIXEL_BLUE_SHIFT 0 + /* percentage changes to screen before sending the entire screen */ #define RFB_SEND_ALL_THRESH 25 @@ -261,9 +270,9 @@ sinfo.pixfmt.red_max = htons(255); sinfo.pixfmt.green_max = htons(255); sinfo.pixfmt.blue_max = htons(255); - sinfo.pixfmt.red_shift = 16; - sinfo.pixfmt.green_shift = 8; - sinfo.pixfmt.blue_shift = 0; + sinfo.pixfmt.red_shift = PIXEL_RED_SHIFT; + sinfo.pixfmt.green_shift = PIXEL_GREEN_SHIFT; + sinfo.pixfmt.blue_shift = PIXEL_BLUE_SHIFT; sinfo.pixfmt.pad[0] = 0; sinfo.pixfmt.pad[1] = 0; sinfo.pixfmt.pad[2] = 0; @@ -318,9 +327,54 @@ rfb_recv_set_pixfmt_msg(struct rfb_softc *rc __unused, int cfd) { struct rfb_pixfmt_msg pixfmt_msg; + uint32_t *pixelp; + uint8_t red_shift, green_shift, blue_shift; (void)stream_read(cfd, (uint8_t *)&pixfmt_msg + 1, sizeof(pixfmt_msg) - 1); + + /* + * The framebuffer is fixed at 32 bit and orders the colors + * as RGB bytes. However, some VNC clients request a different + * ordering. We will still require the same bit depth and size + * but allow the colors to be shifted when sent to the client. + */ + if ((pixfmt_msg.pixfmt.bpp != 32) || (pixfmt_msg.pixfmt.truecolor != 1)) + return; + + /* Check for valid max values */ + if ((htons(pixfmt_msg.pixfmt.red_max) != 255) || + (htons(pixfmt_msg.pixfmt.green_max) != 255) || + (htons(pixfmt_msg.pixfmt.blue_max) != 255)) + return; + + red_shift = pixfmt_msg.pixfmt.red_shift; + green_shift = pixfmt_msg.pixfmt.green_shift; + blue_shift = pixfmt_msg.pixfmt.blue_shift; + + /* Check if this is already our default shift */ + if ((red_shift == PIXEL_RED_SHIFT) && + (green_shift == PIXEL_GREEN_SHIFT) && + (blue_shift == PIXEL_BLUE_SHIFT)) + return; + + /* Check shifts are 8 bit aligned */ + if (((red_shift & 0x7) != 0) || + ((green_shift & 0x7) != 0) || + ((blue_shift & 0x7) != 0)) + return; + + /* Allocate a pixel array to contain the swapped values */ + if (rc->pixrow == NULL) { + /* Will be using rfb_send_rect which does a row at a time */ + pixelp = malloc(RFB_MAX_WIDTH * sizeof(uint32_t)); + if (pixelp != NULL) { + rc->red_shift = red_shift; + rc->green_shift = green_shift; + rc->blue_shift = blue_shift; + rc->pixrow = pixelp; + } + } } static void @@ -388,6 +442,30 @@ sizeof(struct rfb_srvr_updt_msg)); } +static uint32_t * +rfb_adjust_pixels(struct rfb_softc *rc, uint32_t *gcptr, int width, int height) +{ + uint32_t *zp; + uint32_t red, green, blue; + int i; + + /* If no pixrow allocated, send in server format */ + if (rc->pixrow == NULL) { + return (gcptr); + } + + for (i = 0, zp = rc->pixrow; i < width * height; i++, zp++, gcptr++) { + red = (*gcptr >> 16) & 0xFF; + green = (*gcptr >> 8) & 0xFF; + blue = (*gcptr & 0xFF); + *zp = (red << rc->red_shift) | + (green << rc->green_shift) | + (blue << rc->blue_shift); + } + + return (rc->pixrow); +} + static int rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc, int x, int y, int w, int h) @@ -395,8 +473,8 @@ struct rfb_srvr_rect_hdr srect_hdr; unsigned long zlen; ssize_t nwrite, total; - int err; - uint32_t *p; + int err, width; + uint32_t *p, *pixelp; uint8_t *zbufp; /* @@ -409,6 +487,7 @@ srect_hdr.width = htons(w); srect_hdr.height = htons(h); + width = w; h = y + h; w *= sizeof(uint32_t); if (rc->enc_zlib_ok) { @@ -416,7 +495,8 @@ rc->zstream.total_in = 0; rc->zstream.total_out = 0; for (p = &gc->data[y * gc->width + x]; y < h; y++) { - rc->zstream.next_in = (Bytef *)p; + pixelp = rfb_adjust_pixels(rc, p, width, 1); + rc->zstream.next_in = (Bytef *)pixelp; rc->zstream.avail_in = w; rc->zstream.next_out = (Bytef *)zbufp; rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16 - @@ -452,7 +532,8 @@ total = 0; zbufp = rc->zbuf; for (p = &gc->data[y * gc->width + x]; y < h; y++) { - memcpy(zbufp, p, w); + pixelp = rfb_adjust_pixels(rc, p, width, 1); + memcpy(zbufp, pixelp, w); zbufp += w; total += w; p += gc->width; @@ -491,6 +572,11 @@ if (nwrite <= 0) return (nwrite); + if (rc->pixrow != 0) { + return (rfb_send_rect(rc, cfd, gc, 0, 0, + gc->width, gc->height)); + } + /* Rectangle header */ srect_hdr.x = 0; srect_hdr.y = 0; @@ -1229,6 +1315,8 @@ close(rc->sfd); free(rc->crc); free(rc->crc_tmp); + free(rc->zbuf); + free(rc->pixrow); free(rc); return (-1); }