This will allow us to render more things than just the framebuffer.tags/v1.7.90
set(FBPERF_SOURCES | set(FBPERF_SOURCES | ||||
fbperf.cxx | fbperf.cxx | ||||
../vncviewer/FLTKPixelBuffer.cxx | |||||
../vncviewer/PlatformPixelBuffer.cxx) | |||||
../vncviewer/PlatformPixelBuffer.cxx | |||||
../vncviewer/Surface.cxx) | |||||
if(WIN32) | if(WIN32) | ||||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Win32PixelBuffer.cxx) | |||||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_Win32.cxx) | |||||
elseif(APPLE) | elseif(APPLE) | ||||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/OSXPixelBuffer.cxx) | |||||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_OSX.cxx) | |||||
else() | else() | ||||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/X11PixelBuffer.cxx) | |||||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_X11.cxx) | |||||
endif() | endif() | ||||
add_executable(fbperf ${FBPERF_SOURCES}) | add_executable(fbperf ${FBPERF_SOURCES}) | ||||
target_link_libraries(fbperf test_util rfb ${FLTK_LIBRARIES} ${GETTEXT_LIBRARIES}) | target_link_libraries(fbperf test_util rfb ${FLTK_LIBRARIES} ${GETTEXT_LIBRARIES}) |
#include <rfb/util.h> | #include <rfb/util.h> | ||||
#include "../vncviewer/PlatformPixelBuffer.h" | #include "../vncviewer/PlatformPixelBuffer.h" | ||||
#include "../vncviewer/FLTKPixelBuffer.h" | |||||
#if defined(WIN32) | |||||
#include "../vncviewer/Win32PixelBuffer.h" | |||||
#elif defined(__APPLE__) | |||||
#include "../vncviewer/OSXPixelBuffer.h" | |||||
#else | |||||
#include "../vncviewer/X11PixelBuffer.h" | |||||
#endif | |||||
#include "util.h" | #include "util.h" | ||||
virtual void changefb(); | virtual void changefb(); | ||||
}; | }; | ||||
class OverlayTestWindow: public PartialTestWindow { | |||||
public: | |||||
OverlayTestWindow(); | |||||
virtual void start(int width, int height); | |||||
virtual void stop(); | |||||
virtual void draw(); | |||||
protected: | |||||
Surface* overlay; | |||||
}; | |||||
TestWindow::TestWindow() : | TestWindow::TestWindow() : | ||||
Fl_Window(0, 0, "Framebuffer Performance Test"), | Fl_Window(0, 0, "Framebuffer Performance Test"), | ||||
fb(NULL) | fb(NULL) | ||||
frames = 0; | frames = 0; | ||||
time = 0; | time = 0; | ||||
try { | |||||
#if defined(WIN32) | |||||
fb = new Win32PixelBuffer(w(), h()); | |||||
#elif defined(__APPLE__) | |||||
fb = new OSXPixelBuffer(w(), h()); | |||||
#else | |||||
fb = new X11PixelBuffer(w(), h()); | |||||
#endif | |||||
} catch (rdr::Exception& e) { | |||||
fb = new FLTKPixelBuffer(w(), h()); | |||||
} | |||||
fb = new PlatformPixelBuffer(w(), h()); | |||||
pixel = 0; | pixel = 0; | ||||
fb->fillRect(fb->getRect(), &pixel); | fb->fillRect(fb->getRect(), &pixel); | ||||
fb->fillRect(r, &pixel); | fb->fillRect(r, &pixel); | ||||
} | } | ||||
OverlayTestWindow::OverlayTestWindow() : | |||||
overlay(NULL) | |||||
{ | |||||
} | |||||
void OverlayTestWindow::start(int width, int height) | |||||
{ | |||||
PartialTestWindow::start(width, height); | |||||
overlay = new Surface(400, 200); | |||||
overlay->clear(0xff, 0x80, 0x00, 0xcc); | |||||
} | |||||
void OverlayTestWindow::stop() | |||||
{ | |||||
PartialTestWindow::stop(); | |||||
delete overlay; | |||||
overlay = NULL; | |||||
} | |||||
void OverlayTestWindow::draw() | |||||
{ | |||||
int ox, oy, ow, oh; | |||||
int X, Y, W, H; | |||||
// Check what actually needs updating | |||||
fl_clip_box(0, 0, w(), h(), X, Y, W, H); | |||||
if ((W == 0) || (H == 0)) | |||||
return; | |||||
PartialTestWindow::draw(); | |||||
// We might get a redraw before we are fully ready | |||||
if (!overlay) | |||||
return; | |||||
// Simplify the clip region to a simple rectangle in order to | |||||
// properly draw all the layers even if they only partially overlap | |||||
fl_push_no_clip(); | |||||
fl_push_clip(X, Y, W, H); | |||||
ox = (w() - overlay->width()) / 2; | |||||
oy = h() / 4 - overlay->height() / 2; | |||||
ow = overlay->width(); | |||||
oh = overlay->height(); | |||||
fl_clip_box(ox, oy, ow, oh, X, Y, W, H); | |||||
if ((W != 0) && (H != 0)) | |||||
overlay->draw(X - ox, Y - oy, X, Y, W, H); | |||||
fl_pop_clip(); | |||||
fl_pop_clip(); | |||||
} | |||||
static void dosubtest(TestWindow* win, int width, int height, | static void dosubtest(TestWindow* win, int width, int height, | ||||
unsigned long long* pixels, | unsigned long long* pixels, | ||||
unsigned long long* frames, | unsigned long long* frames, | ||||
delete win; | delete win; | ||||
fprintf(stderr, "\n"); | fprintf(stderr, "\n"); | ||||
fprintf(stderr, "Partial window update with overlay:\n\n"); | |||||
win = new OverlayTestWindow(); | |||||
dotest(win); | |||||
delete win; | |||||
fprintf(stderr, "\n"); | |||||
return 0; | return 0; | ||||
} | } |
menukey.cxx | menukey.cxx | ||||
CConn.cxx | CConn.cxx | ||||
DesktopWindow.cxx | DesktopWindow.cxx | ||||
FLTKPixelBuffer.cxx | |||||
UserDialog.cxx | UserDialog.cxx | ||||
ServerDialog.cxx | ServerDialog.cxx | ||||
Surface.cxx | |||||
OptionsDialog.cxx | OptionsDialog.cxx | ||||
PlatformPixelBuffer.cxx | PlatformPixelBuffer.cxx | ||||
Viewport.cxx | Viewport.cxx | ||||
endif() | endif() | ||||
if(WIN32) | if(WIN32) | ||||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Win32PixelBuffer.cxx) | |||||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_Win32.cxx) | |||||
elseif(APPLE) | elseif(APPLE) | ||||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} OSXPixelBuffer.cxx) | |||||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_OSX.cxx) | |||||
else() | else() | ||||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} X11PixelBuffer.cxx) | |||||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_X11.cxx) | |||||
endif() | endif() | ||||
if(WIN32 AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") | if(WIN32 AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") |
/* Copyright 2011-2014 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 | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifdef HAVE_CONFIG_H | |||||
#include <config.h> | |||||
#endif | |||||
#include <ApplicationServices/ApplicationServices.h> | |||||
#include <FL/Fl_Window.H> | |||||
#include <FL/x.H> | |||||
#include <rfb/LogWriter.h> | |||||
#include <rfb/Exception.h> | |||||
#include "i18n.h" | |||||
#include "OSXPixelBuffer.h" | |||||
using namespace rfb; | |||||
static rfb::LogWriter vlog("OSXPixelBuffer"); | |||||
OSXPixelBuffer::OSXPixelBuffer(int width, int height) : | |||||
PlatformPixelBuffer(rfb::PixelFormat(32, 24, false, true, | |||||
255, 255, 255, 16, 8, 0), | |||||
width, height, NULL, width), | |||||
image(NULL) | |||||
{ | |||||
CGColorSpaceRef lut; | |||||
CGDataProviderRef provider; | |||||
data = new rdr::U8[width * height * format.bpp/8]; | |||||
if (data == NULL) | |||||
throw rfb::Exception(_("Not enough memory for framebuffer")); | |||||
lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay); | |||||
if (!lut) { | |||||
vlog.error(_("Could not get screen color space. Using slower fallback.")); | |||||
lut = CGColorSpaceCreateDeviceRGB(); | |||||
if (!lut) | |||||
throw rfb::Exception("CGColorSpaceCreateDeviceRGB"); | |||||
} | |||||
provider = CGDataProviderCreateWithData(NULL, data, | |||||
width * height * 4, NULL); | |||||
if (!provider) | |||||
throw rfb::Exception("CGDataProviderCreateWithData"); | |||||
image = CGImageCreate(width, height, 8, 32, width * 4, lut, | |||||
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, | |||||
provider, NULL, false, kCGRenderingIntentDefault); | |||||
CGColorSpaceRelease(lut); | |||||
CGDataProviderRelease(provider); | |||||
if (!image) | |||||
throw rfb::Exception("CGImageCreate"); | |||||
} | |||||
OSXPixelBuffer::~OSXPixelBuffer() | |||||
{ | |||||
CGImageRelease(image); | |||||
delete [] data; | |||||
} | |||||
void OSXPixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h) | |||||
{ | |||||
CGContextRef gc; | |||||
CGRect rect; | |||||
gc = fl_gc; | |||||
CGContextSaveGState(gc); | |||||
// macOS Coordinates are from bottom left, not top left | |||||
src_y = height() - (src_y + h); | |||||
y = Fl_Window::current()->h() - (y + h); | |||||
// Reset the transformation matrix back to the default identity | |||||
// matrix as otherwise we get a massive performance hit | |||||
CGContextConcatCTM(gc, CGAffineTransformInvert(CGContextGetCTM(gc))); | |||||
// We have to use clipping to partially display an image | |||||
rect.origin.x = x; | |||||
rect.origin.y = y; | |||||
rect.size.width = w; | |||||
rect.size.height = h; | |||||
CGContextClipToRect(gc, rect); | |||||
rect.origin.x = x - src_x; | |||||
rect.origin.y = y - src_y; | |||||
rect.size.width = width(); | |||||
rect.size.height = height(); | |||||
CGContextDrawImage(gc, rect, image); | |||||
CGContextRestoreGState(gc); | |||||
} |
/* 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 | * This is free software; you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* USA. | * 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" | #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) | void PlatformPixelBuffer::commitBufferRW(const rfb::Rect& r) | ||||
damage.clear(); | damage.clear(); | ||||
mutex.unlock(); | 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; | 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 |
/* 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 | * This is free software; you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
#ifndef __PLATFORMPIXELBUFFER_H__ | #ifndef __PLATFORMPIXELBUFFER_H__ | ||||
#define __PLATFORMPIXELBUFFER_H__ | #define __PLATFORMPIXELBUFFER_H__ | ||||
#if !defined(WIN32) && !defined(__APPLE__) | |||||
#include <X11/extensions/XShm.h> | |||||
#endif | |||||
#include <list> | |||||
#include <os/Mutex.h> | #include <os/Mutex.h> | ||||
#include <rfb/PixelBuffer.h> | #include <rfb/PixelBuffer.h> | ||||
#include <rfb/Region.h> | #include <rfb/Region.h> | ||||
class PlatformPixelBuffer: public rfb::FullFramePixelBuffer { | |||||
#include "Surface.h" | |||||
class PlatformPixelBuffer: public rfb::FullFramePixelBuffer, public Surface { | |||||
public: | public: | ||||
PlatformPixelBuffer(const rfb::PixelFormat& pf, int width, int height, | |||||
rdr::U8* data, int stride); | |||||
PlatformPixelBuffer(int width, int height); | |||||
~PlatformPixelBuffer(); | |||||
virtual void commitBufferRW(const rfb::Rect& r); | virtual void commitBufferRW(const rfb::Rect& r); | ||||
virtual void draw(int src_x, int src_y, int x, int y, int w, int h) = 0; | |||||
rfb::Rect getDamage(void); | rfb::Rect getDamage(void); | ||||
using rfb::FullFramePixelBuffer::width; | |||||
using rfb::FullFramePixelBuffer::height; | |||||
protected: | protected: | ||||
os::Mutex mutex; | os::Mutex mutex; | ||||
rfb::Region damage; | rfb::Region damage; | ||||
#if !defined(WIN32) && !defined(__APPLE__) | |||||
protected: | |||||
bool setupShm(); | |||||
protected: | |||||
XShmSegmentInfo *shminfo; | |||||
XImage *xim; | |||||
#endif | |||||
}; | }; | ||||
#endif | #endif |
/* Copyright 2011-2014 Pierre Ossman for Cendio AB | |||||
/* Copyright 2016 Pierre Ossman for Cendio AB | |||||
* | * | ||||
* This is free software; you can redistribute it and/or modify | * This is free software; you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* USA. | * USA. | ||||
*/ | */ | ||||
#ifndef __WIN32PIXELBUFFER_H__ | |||||
#define __WIN32PIXELBUFFER_H__ | |||||
#include <FL/Fl_RGB_Image.H> | |||||
#include <windows.h> | |||||
#include "Surface.h" | |||||
#include "PlatformPixelBuffer.h" | |||||
Surface::Surface(int width, int height) : | |||||
w(width), h(height) | |||||
{ | |||||
alloc(); | |||||
} | |||||
class Win32PixelBuffer: public PlatformPixelBuffer { | |||||
public: | |||||
Win32PixelBuffer(int width, int height); | |||||
~Win32PixelBuffer(); | |||||
Surface::Surface(const Fl_RGB_Image* image) : | |||||
w(image->w()), h(image->h()) | |||||
{ | |||||
alloc(); | |||||
update(image); | |||||
} | |||||
virtual void draw(int src_x, int src_y, int x, int y, int w, int h); | |||||
protected: | |||||
HBITMAP bitmap; | |||||
}; | |||||
#endif | |||||
Surface::~Surface() | |||||
{ | |||||
dealloc(); | |||||
} |
/* Copyright 2011-2014 Pierre Ossman for Cendio AB | |||||
/* Copyright 2016 Pierre Ossman for Cendio AB | |||||
* | * | ||||
* This is free software; you can redistribute it and/or modify | * This is free software; you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* USA. | * USA. | ||||
*/ | */ | ||||
#ifndef __OSXPIXELBUFFER_H__ | |||||
#define __OSXPIXELBUFFER_H__ | |||||
#include "PlatformPixelBuffer.h" | |||||
#ifndef __SURFACE_H__ | |||||
#define __SURFACE_H__ | |||||
#if defined(WIN32) | |||||
#include <windows.h> | |||||
#elif defined(__APPLE__) | |||||
// Apple headers conflict with FLTK, so redefine types here | // Apple headers conflict with FLTK, so redefine types here | ||||
typedef struct CGImage* CGImageRef; | typedef struct CGImage* CGImageRef; | ||||
#else | |||||
#include <X11/extensions/Xrender.h> | |||||
#endif | |||||
class Fl_RGB_Image; | |||||
class OSXPixelBuffer: public PlatformPixelBuffer { | |||||
class Surface { | |||||
public: | public: | ||||
OSXPixelBuffer(int width, int height); | |||||
~OSXPixelBuffer(); | |||||
Surface(int width, int height); | |||||
Surface(const Fl_RGB_Image* image); | |||||
~Surface(); | |||||
virtual void draw(int src_x, int src_y, int x, int y, int w, int h); | |||||
int width() { return w; } | |||||
int height() { return h; } | |||||
void clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255); | |||||
void draw(int src_x, int src_y, int x, int y, int w, int h); | |||||
protected: | protected: | ||||
void alloc(); | |||||
void dealloc(); | |||||
void update(const Fl_RGB_Image* image); | |||||
protected: | |||||
int w, h; | |||||
#if defined(WIN32) | |||||
RGBQUAD* data; | |||||
HBITMAP bitmap; | |||||
#elif defined(__APPLE__) | |||||
unsigned char* data; | |||||
CGImageRef image; | CGImageRef image; | ||||
#else | |||||
Pixmap pixmap; | |||||
Picture picture; | |||||
XRenderPictFormat* visFormat; | |||||
#endif | |||||
}; | }; | ||||
#endif | #endif | ||||
/* Copyright 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 | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#include <assert.h> | |||||
#include <ApplicationServices/ApplicationServices.h> | |||||
#include <FL/Fl_RGB_Image.H> | |||||
#include <FL/Fl_Window.H> | |||||
#include <FL/x.H> | |||||
#include <rdr/Exception.h> | |||||
#include "Surface.h" | |||||
void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a) | |||||
{ | |||||
unsigned char* out; | |||||
int x, y; | |||||
r = (unsigned)r * a / 255; | |||||
g = (unsigned)g * a / 255; | |||||
b = (unsigned)b * a / 255; | |||||
out = data; | |||||
for (y = 0;y < width();y++) { | |||||
for (x = 0;x < height();x++) { | |||||
*out++ = b; | |||||
*out++ = g; | |||||
*out++ = r; | |||||
*out++ = a; | |||||
} | |||||
} | |||||
} | |||||
void Surface::draw(int src_x, int src_y, int x, int y, int w, int h) | |||||
{ | |||||
CGRect rect; | |||||
CGContextSaveGState(fl_gc); | |||||
// Reset the transformation matrix back to the default identity | |||||
// matrix as otherwise we get a massive performance hit | |||||
CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc))); | |||||
// macOS Coordinates are from bottom left, not top left | |||||
src_y = height() - (src_y + h); | |||||
y = Fl_Window::current()->h() - (y + h); | |||||
// We have to use clipping to partially display an image | |||||
rect.origin.x = x; | |||||
rect.origin.y = y; | |||||
rect.size.width = w; | |||||
rect.size.height = h; | |||||
CGContextClipToRect(fl_gc, rect); | |||||
rect.origin.x = x - src_x; | |||||
rect.origin.y = y - src_y; | |||||
rect.size.width = width(); | |||||
rect.size.height = height(); | |||||
CGContextDrawImage(fl_gc, rect, image); | |||||
CGContextRestoreGState(fl_gc); | |||||
} | |||||
void Surface::alloc() | |||||
{ | |||||
CGColorSpaceRef lut; | |||||
CGDataProviderRef provider; | |||||
data = new unsigned char[width() * height() * 4]; | |||||
lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay); | |||||
if (!lut) { | |||||
lut = CGColorSpaceCreateDeviceRGB(); | |||||
if (!lut) | |||||
throw rdr::Exception("CGColorSpaceCreateDeviceRGB"); | |||||
} | |||||
provider = CGDataProviderCreateWithData(NULL, data, | |||||
width() * height() * 4, NULL); | |||||
if (!provider) | |||||
throw rdr::Exception("CGDataProviderCreateWithData"); | |||||
image = CGImageCreate(width(), height(), 8, 32, width() * 4, lut, | |||||
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, | |||||
provider, NULL, false, kCGRenderingIntentDefault); | |||||
CGColorSpaceRelease(lut); | |||||
CGDataProviderRelease(provider); | |||||
if (!image) | |||||
throw rdr::Exception("CGImageCreate"); | |||||
} | |||||
void Surface::dealloc() | |||||
{ | |||||
CGImageRelease(image); | |||||
delete [] data; | |||||
} | |||||
void Surface::update(const Fl_RGB_Image* image) | |||||
{ | |||||
int x, y; | |||||
const unsigned char* in; | |||||
unsigned char* out; | |||||
assert(image->w() == width()); | |||||
assert(image->h() == height()); | |||||
// Convert data and pre-multiply alpha | |||||
in = (const unsigned char*)image->data()[0]; | |||||
out = data; | |||||
for (y = 0;y < image->h();y++) { | |||||
for (x = 0;x < image->w();x++) { | |||||
switch (image->d()) { | |||||
case 1: | |||||
*out++ = in[0]; | |||||
*out++ = in[0]; | |||||
*out++ = in[0]; | |||||
*out++ = 0xff; | |||||
break; | |||||
case 2: | |||||
*out++ = (unsigned)in[0] * in[1] / 255; | |||||
*out++ = (unsigned)in[0] * in[1] / 255; | |||||
*out++ = (unsigned)in[0] * in[1] / 255; | |||||
*out++ = in[1]; | |||||
break; | |||||
case 3: | |||||
*out++ = in[2]; | |||||
*out++ = in[1]; | |||||
*out++ = in[0]; | |||||
*out++ = 0xff; | |||||
break; | |||||
case 4: | |||||
*out++ = (unsigned)in[2] * in[3] / 255; | |||||
*out++ = (unsigned)in[1] * in[3] / 255; | |||||
*out++ = (unsigned)in[0] * in[3] / 255; | |||||
*out++ = in[3]; | |||||
break; | |||||
} | |||||
in += image->d(); | |||||
} | |||||
if (image->ld() != 0) | |||||
in += image->ld() - image->w() * image->d(); | |||||
} | |||||
} |
/* Copyright 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 | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#include <assert.h> | |||||
#include <FL/Fl_RGB_Image.H> | |||||
#include <FL/x.H> | |||||
#include <rdr/Exception.h> | |||||
#include "Surface.h" | |||||
void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a) | |||||
{ | |||||
RGBQUAD* out; | |||||
int x, y; | |||||
r = (unsigned)r * a / 255; | |||||
g = (unsigned)g * a / 255; | |||||
b = (unsigned)b * a / 255; | |||||
out = data; | |||||
for (y = 0;y < width();y++) { | |||||
for (x = 0;x < height();x++) { | |||||
out->rgbRed = r; | |||||
out->rgbGreen = g; | |||||
out->rgbBlue = b; | |||||
out->rgbReserved = a; | |||||
out++; | |||||
} | |||||
} | |||||
} | |||||
void Surface::draw(int src_x, int src_y, int x, int y, int w, int h) | |||||
{ | |||||
HDC dc; | |||||
dc = CreateCompatibleDC(fl_gc); | |||||
if (!dc) | |||||
throw rdr::SystemException("CreateCompatibleDC", GetLastError()); | |||||
if (!SelectObject(dc, bitmap)) | |||||
throw rdr::SystemException("SelectObject", GetLastError()); | |||||
if (!BitBlt(fl_gc, x, y, w, h, dc, src_x, src_y, SRCCOPY)) { | |||||
// If the desktop we're rendering to is inactive (like when the screen | |||||
// is locked or the UAC is active), then GDI calls will randomly fail. | |||||
// This is completely undocumented so we have no idea how best to deal | |||||
// with it. For now, we've only seen this error and for this function | |||||
// so only ignore this combination. | |||||
if (GetLastError() != ERROR_INVALID_HANDLE) | |||||
throw rdr::SystemException("BitBlt", GetLastError()); | |||||
} | |||||
DeleteDC(dc); | |||||
} | |||||
void Surface::alloc() | |||||
{ | |||||
BITMAPINFOHEADER bih; | |||||
data = new RGBQUAD[width() * height()]; | |||||
memset(&bih, 0, sizeof(bih)); | |||||
bih.biSize = sizeof(BITMAPINFOHEADER); | |||||
bih.biBitCount = 32; | |||||
bih.biPlanes = 1; | |||||
bih.biWidth = width(); | |||||
bih.biHeight = -height(); // Negative to get top-down | |||||
bih.biCompression = BI_RGB; | |||||
bitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bih, | |||||
DIB_RGB_COLORS, (void**)&data, NULL, 0); | |||||
if (!bitmap) | |||||
throw rdr::SystemException("CreateDIBSection", GetLastError()); | |||||
} | |||||
void Surface::dealloc() | |||||
{ | |||||
DeleteObject(bitmap); | |||||
} | |||||
void Surface::update(const Fl_RGB_Image* image) | |||||
{ | |||||
const unsigned char* in; | |||||
RGBQUAD* out; | |||||
int x, y; | |||||
assert(image->w() == width()); | |||||
assert(image->h() == height()); | |||||
// Convert data and pre-multiply alpha | |||||
in = (const unsigned char*)image->data()[0]; | |||||
out = data; | |||||
for (y = 0;y < image->w();y++) { | |||||
for (x = 0;x < image->h();x++) { | |||||
switch (image->d()) { | |||||
case 1: | |||||
out->rgbBlue = in[0]; | |||||
out->rgbGreen = in[0]; | |||||
out->rgbRed = in[0]; | |||||
out->rgbReserved = 0xff; | |||||
break; | |||||
case 2: | |||||
out->rgbBlue = (unsigned)in[0] * in[1] / 255; | |||||
out->rgbGreen = (unsigned)in[0] * in[1] / 255; | |||||
out->rgbRed = (unsigned)in[0] * in[1] / 255; | |||||
out->rgbReserved = in[1]; | |||||
break; | |||||
case 3: | |||||
out->rgbBlue = in[2]; | |||||
out->rgbGreen = in[1]; | |||||
out->rgbRed = in[0]; | |||||
out->rgbReserved = 0xff; | |||||
break; | |||||
case 4: | |||||
out->rgbBlue = (unsigned)in[2] * in[3] / 255; | |||||
out->rgbGreen = (unsigned)in[1] * in[3] / 255; | |||||
out->rgbRed = (unsigned)in[0] * in[3] / 255; | |||||
out->rgbReserved = in[3]; | |||||
break; | |||||
} | |||||
in += image->d(); | |||||
out++; | |||||
} | |||||
if (image->ld() != 0) | |||||
in += image->ld() - image->w() * image->d(); | |||||
} | |||||
} | |||||
/* Copyright 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 | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#include <assert.h> | |||||
#include <FL/Fl_RGB_Image.H> | |||||
#include <FL/x.H> | |||||
#include <rdr/Exception.h> | |||||
#include "Surface.h" | |||||
void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a) | |||||
{ | |||||
XRenderColor color; | |||||
color.red = (unsigned)r * 65535 / 255 * a / 255; | |||||
color.green = (unsigned)g * 65535 / 255 * a / 255; | |||||
color.blue = (unsigned)b * 65535 / 255 * a / 255; | |||||
color.alpha = (unsigned)a * 65535 / 255; | |||||
XRenderFillRectangle(fl_display, PictOpSrc, picture, &color, | |||||
0, 0, width(), height()); | |||||
} | |||||
void Surface::draw(int src_x, int src_y, int x, int y, int w, int h) | |||||
{ | |||||
Picture winPict; | |||||
winPict = XRenderCreatePicture(fl_display, fl_window, visFormat, 0, NULL); | |||||
XRenderComposite(fl_display, PictOpSrc, picture, None, winPict, | |||||
src_x, src_y, 0, 0, x, y, w, h); | |||||
XRenderFreePicture(fl_display, winPict); | |||||
} | |||||
void Surface::alloc() | |||||
{ | |||||
XRenderPictFormat* format; | |||||
// Might not be open at this point | |||||
fl_open_display(); | |||||
pixmap = XCreatePixmap(fl_display, XDefaultRootWindow(fl_display), | |||||
width(), height(), 32); | |||||
format = XRenderFindStandardFormat(fl_display, PictStandardARGB32); | |||||
picture = XRenderCreatePicture(fl_display, pixmap, format, 0, NULL); | |||||
visFormat = XRenderFindVisualFormat(fl_display, fl_visual->visual); | |||||
} | |||||
void Surface::dealloc() | |||||
{ | |||||
XRenderFreePicture(fl_display, picture); | |||||
XFreePixmap(fl_display, pixmap); | |||||
} | |||||
void Surface::update(const Fl_RGB_Image* image) | |||||
{ | |||||
XImage* img; | |||||
GC gc; | |||||
int x, y; | |||||
const unsigned char* in; | |||||
unsigned char* out; | |||||
assert(image->w() == width()); | |||||
assert(image->h() == height()); | |||||
img = XCreateImage(fl_display, CopyFromParent, 32, | |||||
ZPixmap, 0, NULL, width(), height(), | |||||
32, 0); | |||||
if (!img) | |||||
throw rdr::Exception("XCreateImage"); | |||||
img->data = (char*)malloc(img->bytes_per_line * img->height); | |||||
if (!img->data) | |||||
throw rdr::Exception("malloc"); | |||||
// Convert data and pre-multiply alpha | |||||
in = (const unsigned char*)image->data()[0]; | |||||
out = (unsigned char*)img->data; | |||||
for (y = 0;y < img->height;y++) { | |||||
for (x = 0;x < img->width;x++) { | |||||
switch (image->d()) { | |||||
case 1: | |||||
*out++ = in[0]; | |||||
*out++ = in[0]; | |||||
*out++ = in[0]; | |||||
*out++ = 0xff; | |||||
break; | |||||
case 2: | |||||
*out++ = (unsigned)in[0] * in[1] / 255; | |||||
*out++ = (unsigned)in[0] * in[1] / 255; | |||||
*out++ = (unsigned)in[0] * in[1] / 255; | |||||
*out++ = in[1]; | |||||
break; | |||||
case 3: | |||||
*out++ = in[2]; | |||||
*out++ = in[1]; | |||||
*out++ = in[0]; | |||||
*out++ = 0xff; | |||||
break; | |||||
case 4: | |||||
*out++ = (unsigned)in[2] * in[3] / 255; | |||||
*out++ = (unsigned)in[1] * in[3] / 255; | |||||
*out++ = (unsigned)in[0] * in[3] / 255; | |||||
*out++ = in[3]; | |||||
break; | |||||
} | |||||
in += image->d(); | |||||
} | |||||
if (image->ld() != 0) | |||||
in += image->ld() - image->w() * image->d(); | |||||
} | |||||
gc = XCreateGC(fl_display, pixmap, 0, NULL); | |||||
XPutImage(fl_display, pixmap, gc, img, | |||||
0, 0, 0, 0, img->width, img->height); | |||||
XFreeGC(fl_display, gc); | |||||
XDestroyImage(img); | |||||
} | |||||
#include "vncviewer.h" | #include "vncviewer.h" | ||||
#include "PlatformPixelBuffer.h" | #include "PlatformPixelBuffer.h" | ||||
#include "FLTKPixelBuffer.h" | |||||
#if defined(WIN32) | |||||
#include "Win32PixelBuffer.h" | |||||
#elif defined(__APPLE__) | |||||
#include "OSXPixelBuffer.h" | |||||
#else | |||||
#include "X11PixelBuffer.h" | |||||
#endif | |||||
#include <FL/fl_draw.H> | #include <FL/fl_draw.H> | ||||
#include <FL/fl_ask.H> | #include <FL/fl_ask.H> | ||||
// We need to intercept keyboard events early | // We need to intercept keyboard events early | ||||
Fl::add_system_handler(handleSystemEvent, this); | Fl::add_system_handler(handleSystemEvent, this); | ||||
frameBuffer = createFramebuffer(w, h); | |||||
frameBuffer = new PlatformPixelBuffer(w, h); | |||||
assert(frameBuffer); | assert(frameBuffer); | ||||
cc->setFramebuffer(frameBuffer); | cc->setFramebuffer(frameBuffer); | ||||
vlog.debug("Resizing framebuffer from %dx%d to %dx%d", | vlog.debug("Resizing framebuffer from %dx%d to %dx%d", | ||||
frameBuffer->width(), frameBuffer->height(), w, h); | frameBuffer->width(), frameBuffer->height(), w, h); | ||||
frameBuffer = createFramebuffer(w, h); | |||||
frameBuffer = new PlatformPixelBuffer(w, h); | |||||
assert(frameBuffer); | assert(frameBuffer); | ||||
cc->setFramebuffer(frameBuffer); | cc->setFramebuffer(frameBuffer); | ||||
} | } | ||||
return Fl_Widget::handle(event); | return Fl_Widget::handle(event); | ||||
} | } | ||||
PlatformPixelBuffer* Viewport::createFramebuffer(int w, int h) | |||||
{ | |||||
PlatformPixelBuffer *fb; | |||||
try { | |||||
#if defined(WIN32) | |||||
fb = new Win32PixelBuffer(w, h); | |||||
#elif defined(__APPLE__) | |||||
fb = new OSXPixelBuffer(w, h); | |||||
#else | |||||
fb = new X11PixelBuffer(w, h); | |||||
#endif | |||||
} catch (rdr::Exception& e) { | |||||
vlog.error(_("Unable to create platform specific framebuffer: %s"), e.str()); | |||||
vlog.error(_("Using platform independent framebuffer")); | |||||
fb = new FLTKPixelBuffer(w, h); | |||||
} | |||||
return fb; | |||||
} | |||||
void Viewport::handleClipboardChange(int source, void *data) | void Viewport::handleClipboardChange(int source, void *data) | ||||
{ | { | ||||
Viewport *self = (Viewport *)data; | Viewport *self = (Viewport *)data; |
private: | private: | ||||
PlatformPixelBuffer* createFramebuffer(int w, int h); | |||||
static void handleClipboardChange(int source, void *data); | static void handleClipboardChange(int source, void *data); | ||||
void handlePointerEvent(const rfb::Point& pos, int buttonMask); | void handlePointerEvent(const rfb::Point& pos, int buttonMask); |
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||||
* Copyright 2011-2014 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 | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifdef HAVE_CONFIG_H | |||||
#include <config.h> | |||||
#endif | |||||
#include <stdlib.h> | |||||
#include <windows.h> | |||||
#include <FL/x.H> | |||||
#include <rfb/LogWriter.h> | |||||
#include <rfb/Exception.h> | |||||
#include "i18n.h" | |||||
#include "Win32PixelBuffer.h" | |||||
using namespace rfb; | |||||
static rfb::LogWriter vlog("Win32PixelBuffer"); | |||||
Win32PixelBuffer::Win32PixelBuffer(int width, int height) : | |||||
PlatformPixelBuffer(rfb::PixelFormat(32, 24, false, true, | |||||
255, 255, 255, 16, 8, 0), | |||||
width, height, NULL, width), | |||||
bitmap(NULL) | |||||
{ | |||||
BITMAPINFOHEADER bih; | |||||
memset(&bih, 0, sizeof(bih)); | |||||
bih.biSize = sizeof(BITMAPINFOHEADER); | |||||
bih.biBitCount = getPF().bpp; | |||||
bih.biSizeImage = (getPF().bpp / 8) * width * height; | |||||
bih.biPlanes = 1; | |||||
bih.biWidth = width; | |||||
bih.biHeight = -height; // Negative to get top-down | |||||
bih.biCompression = BI_RGB; | |||||
bitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bih, | |||||
DIB_RGB_COLORS, (void**)&data, NULL, 0); | |||||
if (!bitmap) | |||||
throw rdr::SystemException("CreateDIBSection", GetLastError()); | |||||
} | |||||
Win32PixelBuffer::~Win32PixelBuffer() | |||||
{ | |||||
DeleteObject(bitmap); | |||||
} | |||||
void Win32PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h) | |||||
{ | |||||
HDC dc; | |||||
dc = CreateCompatibleDC(fl_gc); | |||||
if (!dc) | |||||
throw rdr::SystemException("CreateCompatibleDC", GetLastError()); | |||||
if (!SelectObject(dc, bitmap)) | |||||
throw rdr::SystemException("SelectObject", GetLastError()); | |||||
if (!BitBlt(fl_gc, x, y, w, h, dc, src_x, src_y, SRCCOPY)) { | |||||
// If the desktop we're rendering to is inactive (like when the screen | |||||
// is locked or the UAC is active), then GDI calls will randomly fail. | |||||
// This is completely undocumented so we have no idea how best to deal | |||||
// with it. For now, we've only seen this error and for this function | |||||
// so only ignore this combination. | |||||
if (GetLastError() != ERROR_INVALID_HANDLE) | |||||
throw rdr::SystemException("BitBlt", GetLastError()); | |||||
} | |||||
DeleteDC(dc); | |||||
} |
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||||
* Copyright 2011-2014 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 | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifdef HAVE_CONFIG_H | |||||
#include <config.h> | |||||
#endif | |||||
#include <assert.h> | |||||
#include <stdlib.h> | |||||
#include <FL/Fl.H> | |||||
#include <FL/x.H> | |||||
#include <rfb/LogWriter.h> | |||||
#include <rfb/Exception.h> | |||||
#include "i18n.h" | |||||
#include "X11PixelBuffer.h" | |||||
using namespace rfb; | |||||
static rfb::LogWriter vlog("X11PixelBuffer"); | |||||
static PixelFormat display_pf() | |||||
{ | |||||
int i; | |||||
int bpp; | |||||
int trueColour, bigEndian; | |||||
int redShift, greenShift, blueShift; | |||||
int redMax, greenMax, blueMax; | |||||
int nformats; | |||||
XPixmapFormatValues* format; | |||||
// Might not be open at this point | |||||
fl_open_display(); | |||||
format = XListPixmapFormats(fl_display, &nformats); | |||||
for (i = 0; i < nformats; i++) | |||||
if (format[i].depth == fl_visual->depth) break; | |||||
if (i == nformats) | |||||
// TRANSLATORS: "pixmap" is an X11 concept and may not be suitable | |||||
// to translate. | |||||
throw rfb::Exception(_("Display lacks pixmap format for default depth")); | |||||
switch (format[i].bits_per_pixel) { | |||||
case 8: | |||||
case 16: | |||||
case 32: | |||||
bpp = format[i].bits_per_pixel; | |||||
break; | |||||
default: | |||||
// TRANSLATORS: "pixmap" is an X11 concept and may not be suitable | |||||
// to translate. | |||||
throw rfb::Exception(_("Couldn't find suitable pixmap format")); | |||||
} | |||||
XFree(format); | |||||
bigEndian = (ImageByteOrder(fl_display) == MSBFirst); | |||||
trueColour = (fl_visual->c_class == TrueColor); | |||||
if (!trueColour) | |||||
throw rfb::Exception(_("Only true colour displays supported")); | |||||
vlog.info(_("Using default colormap and visual, TrueColor, depth %d."), | |||||
fl_visual->depth); | |||||
redShift = ffs(fl_visual->red_mask) - 1; | |||||
greenShift = ffs(fl_visual->green_mask) - 1; | |||||
blueShift = ffs(fl_visual->blue_mask) - 1; | |||||
redMax = fl_visual->red_mask >> redShift; | |||||
greenMax = fl_visual->green_mask >> greenShift; | |||||
blueMax = fl_visual->blue_mask >> blueShift; | |||||
return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour, | |||||
redMax, greenMax, blueMax, | |||||
redShift, greenShift, blueShift); | |||||
} | |||||
X11PixelBuffer::X11PixelBuffer(int width, int height) : | |||||
PlatformPixelBuffer(display_pf(), width, height, NULL, 0), | |||||
shminfo(NULL), xim(NULL) | |||||
{ | |||||
// Might not be open at this point | |||||
fl_open_display(); | |||||
if (!setupShm()) { | |||||
xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth, | |||||
ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0); | |||||
if (!xim) | |||||
throw rfb::Exception("XCreateImage"); | |||||
xim->data = (char*)malloc(xim->bytes_per_line * xim->height); | |||||
if (!xim->data) | |||||
throw rfb::Exception(_("Not enough memory for framebuffer")); | |||||
vlog.debug("Using standard XImage"); | |||||
} | |||||
data = (rdr::U8*)xim->data; | |||||
stride = xim->bytes_per_line / (getPF().bpp/8); | |||||
} | |||||
X11PixelBuffer::~X11PixelBuffer() | |||||
{ | |||||
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; | |||||
} | |||||
void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h) | |||||
{ | |||||
if (shminfo) { | |||||
XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, 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, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h); | |||||
} | |||||
} | |||||
static bool caughtError; | |||||
static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error) | |||||
{ | |||||
caughtError = true; | |||||
return 0; | |||||
} | |||||
int X11PixelBuffer::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 0; | |||||
if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps)) | |||||
return 0; | |||||
shminfo = new XShmSegmentInfo; | |||||
xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth, | |||||
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 1; | |||||
free_shmaddr: | |||||
shmdt(shminfo->shmaddr); | |||||
free_xim: | |||||
XDestroyImage(xim); | |||||
xim = NULL; | |||||
free_shminfo: | |||||
delete shminfo; | |||||
shminfo = NULL; | |||||
return 0; | |||||
} |
/* Copyright 2011-2014 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 | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This software is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this software; if not, write to the Free Software | |||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||||
* USA. | |||||
*/ | |||||
#ifndef __X11PIXELBUFFER_H__ | |||||
#define __X11PIXELBUFFER_H__ | |||||
#include <X11/Xlib.h> | |||||
#include <sys/ipc.h> | |||||
#include <sys/shm.h> | |||||
#include <X11/extensions/XShm.h> | |||||
#include <list> | |||||
#include "PlatformPixelBuffer.h" | |||||
class X11PixelBuffer: public PlatformPixelBuffer { | |||||
public: | |||||
X11PixelBuffer(int width, int height); | |||||
~X11PixelBuffer(); | |||||
virtual void draw(int src_x, int src_y, int x, int y, int w, int h); | |||||
protected: | |||||
int setupShm(); | |||||
protected: | |||||
XShmSegmentInfo *shminfo; | |||||
XImage *xim; | |||||
}; | |||||
#endif |