diff options
Diffstat (limited to 'vncviewer/PlatformPixelBuffer.cxx')
-rw-r--r-- | vncviewer/PlatformPixelBuffer.cxx | 166 |
1 files changed, 161 insertions, 5 deletions
diff --git a/vncviewer/PlatformPixelBuffer.cxx b/vncviewer/PlatformPixelBuffer.cxx index 522bad3f..4802ba46 100644 --- a/vncviewer/PlatformPixelBuffer.cxx +++ b/vncviewer/PlatformPixelBuffer.cxx @@ -1,4 +1,4 @@ -/* Copyright 2011-2014 Pierre Ossman for Cendio AB +/* Copyright 2011-2016 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,70 @@ * USA. */ +#include <assert.h> + +#if !defined(WIN32) && !defined(__APPLE__) +#include <sys/ipc.h> +#include <sys/shm.h> +#endif + +#include <FL/Fl.H> +#include <FL/x.H> + +#include <rfb/LogWriter.h> +#include <rdr/Exception.h> + #include "PlatformPixelBuffer.h" -PlatformPixelBuffer::PlatformPixelBuffer(const rfb::PixelFormat& pf, - int width, int height, - rdr::U8* data, int stride) : - FullFramePixelBuffer(pf, width, height, data, stride) +static rfb::LogWriter vlog("PlatformPixelBuffer"); + +PlatformPixelBuffer::PlatformPixelBuffer(int width, int height) : + FullFramePixelBuffer(rfb::PixelFormat(32, 24, false, true, + 255, 255, 255, 16, 8, 0), + width, height, 0, stride), + Surface(width, height) +#if !defined(WIN32) && !defined(__APPLE__) + , shminfo(NULL), xim(NULL) +#endif { +#if !defined(WIN32) && !defined(__APPLE__) + if (!setupShm()) { + xim = XCreateImage(fl_display, CopyFromParent, 32, + ZPixmap, 0, 0, width, height, 32, 0); + if (!xim) + throw rdr::Exception("XCreateImage"); + + xim->data = (char*)malloc(xim->bytes_per_line * xim->height); + if (!xim->data) + throw rdr::Exception("malloc"); + + vlog.debug("Using standard XImage"); + } + + data = (rdr::U8*)xim->data; + stride = xim->bytes_per_line / (getPF().bpp/8); +#else + FullFramePixelBuffer::data = (rdr::U8*)Surface::data; + stride = width; +#endif +} + +PlatformPixelBuffer::~PlatformPixelBuffer() +{ +#if !defined(WIN32) && !defined(__APPLE__) + if (shminfo) { + vlog.debug("Freeing shared memory XImage"); + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid, IPC_RMID, 0); + delete shminfo; + shminfo = NULL; + } + + // XDestroyImage() will free(xim->data) if appropriate + if (xim) + XDestroyImage(xim); + xim = NULL; +#endif } void PlatformPixelBuffer::commitBufferRW(const rfb::Rect& r) @@ -42,5 +99,104 @@ rfb::Rect PlatformPixelBuffer::getDamage(void) damage.clear(); mutex.unlock(); +#if !defined(WIN32) && !defined(__APPLE__) + GC gc; + + gc = XCreateGC(fl_display, pixmap, 0, NULL); + if (shminfo) { + XShmPutImage(fl_display, pixmap, gc, xim, + r.tl.x, r.tl.y, r.tl.x, r.tl.y, + r.width(), r.height(), False); + // Need to make sure the X server has finished reading the + // shared memory before we return + XSync(fl_display, False); + } else { + XPutImage(fl_display, pixmap, gc, xim, + r.tl.x, r.tl.y, r.tl.x, r.tl.y, r.width(), r.height()); + } + XFreeGC(fl_display, gc); +#endif + return r; } + +#if !defined(WIN32) && !defined(__APPLE__) + +static bool caughtError; + +static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error) +{ + caughtError = true; + return 0; +} + +bool PlatformPixelBuffer::setupShm() +{ + int major, minor; + Bool pixmaps; + XErrorHandler old_handler; + const char *display_name = XDisplayName (NULL); + + /* Don't use MIT-SHM on remote displays */ + if (*display_name && *display_name != ':') + return false; + + if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps)) + return false; + + shminfo = new XShmSegmentInfo; + + xim = XShmCreateImage(fl_display, CopyFromParent, 32, + ZPixmap, 0, shminfo, width(), height()); + if (!xim) + goto free_shminfo; + + shminfo->shmid = shmget(IPC_PRIVATE, + xim->bytes_per_line * xim->height, + IPC_CREAT|0777); + if (shminfo->shmid == -1) + goto free_xim; + + shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0); + shmctl(shminfo->shmid, IPC_RMID, 0); // to avoid memory leakage + if (shminfo->shmaddr == (char *)-1) + goto free_xim; + + shminfo->readOnly = True; + + // This is the only way we can detect that shared memory won't work + // (e.g. because we're accessing a remote X11 server) + caughtError = false; + old_handler = XSetErrorHandler(XShmAttachErrorHandler); + + if (!XShmAttach(fl_display, shminfo)) { + XSetErrorHandler(old_handler); + goto free_shmaddr; + } + + XSync(fl_display, False); + + XSetErrorHandler(old_handler); + + if (caughtError) + goto free_shmaddr; + + vlog.debug("Using shared memory XImage"); + + return true; + +free_shmaddr: + shmdt(shminfo->shmaddr); + +free_xim: + XDestroyImage(xim); + xim = NULL; + +free_shminfo: + delete shminfo; + shminfo = NULL; + + return 0; +} + +#endif |