This will allow us to render more things than just the framebuffer.tags/v1.7.90
@@ -22,14 +22,14 @@ target_link_libraries(hostport rfb) | |||
set(FBPERF_SOURCES | |||
fbperf.cxx | |||
../vncviewer/FLTKPixelBuffer.cxx | |||
../vncviewer/PlatformPixelBuffer.cxx) | |||
../vncviewer/PlatformPixelBuffer.cxx | |||
../vncviewer/Surface.cxx) | |||
if(WIN32) | |||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Win32PixelBuffer.cxx) | |||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_Win32.cxx) | |||
elseif(APPLE) | |||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/OSXPixelBuffer.cxx) | |||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_OSX.cxx) | |||
else() | |||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/X11PixelBuffer.cxx) | |||
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_X11.cxx) | |||
endif() | |||
add_executable(fbperf ${FBPERF_SOURCES}) | |||
target_link_libraries(fbperf test_util rfb ${FLTK_LIBRARIES} ${GETTEXT_LIBRARIES}) |
@@ -27,15 +27,6 @@ | |||
#include <rfb/util.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" | |||
@@ -70,6 +61,19 @@ protected: | |||
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() : | |||
Fl_Window(0, 0, "Framebuffer Performance Test"), | |||
fb(NULL) | |||
@@ -93,17 +97,7 @@ void TestWindow::start(int width, int height) | |||
frames = 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; | |||
fb->fillRect(fb->getRect(), &pixel); | |||
@@ -207,6 +201,60 @@ void PartialTestWindow::changefb() | |||
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, | |||
unsigned long long* pixels, | |||
unsigned long long* frames, | |||
@@ -311,5 +359,11 @@ int main(int argc, char** argv) | |||
delete win; | |||
fprintf(stderr, "\n"); | |||
fprintf(stderr, "Partial window update with overlay:\n\n"); | |||
win = new OverlayTestWindow(); | |||
dotest(win); | |||
delete win; | |||
fprintf(stderr, "\n"); | |||
return 0; | |||
} |
@@ -6,9 +6,9 @@ set(VNCVIEWER_SOURCES | |||
menukey.cxx | |||
CConn.cxx | |||
DesktopWindow.cxx | |||
FLTKPixelBuffer.cxx | |||
UserDialog.cxx | |||
ServerDialog.cxx | |||
Surface.cxx | |||
OptionsDialog.cxx | |||
PlatformPixelBuffer.cxx | |||
Viewport.cxx | |||
@@ -33,11 +33,11 @@ elseif(APPLE) | |||
endif() | |||
if(WIN32) | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Win32PixelBuffer.cxx) | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_Win32.cxx) | |||
elseif(APPLE) | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} OSXPixelBuffer.cxx) | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_OSX.cxx) | |||
else() | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} X11PixelBuffer.cxx) | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_X11.cxx) | |||
endif() | |||
if(WIN32 AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") |
@@ -1,114 +0,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. | |||
*/ | |||
#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); | |||
} |
@@ -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 |
@@ -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 | |||
@@ -19,24 +19,43 @@ | |||
#ifndef __PLATFORMPIXELBUFFER_H__ | |||
#define __PLATFORMPIXELBUFFER_H__ | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
#include <X11/extensions/XShm.h> | |||
#endif | |||
#include <list> | |||
#include <os/Mutex.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/Region.h> | |||
class PlatformPixelBuffer: public rfb::FullFramePixelBuffer { | |||
#include "Surface.h" | |||
class PlatformPixelBuffer: public rfb::FullFramePixelBuffer, public Surface { | |||
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 draw(int src_x, int src_y, int x, int y, int w, int h) = 0; | |||
rfb::Rect getDamage(void); | |||
using rfb::FullFramePixelBuffer::width; | |||
using rfb::FullFramePixelBuffer::height; | |||
protected: | |||
os::Mutex mutex; | |||
rfb::Region damage; | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
protected: | |||
bool setupShm(); | |||
protected: | |||
XShmSegmentInfo *shminfo; | |||
XImage *xim; | |||
#endif | |||
}; | |||
#endif |
@@ -1,4 +1,4 @@ | |||
/* 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 | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -16,23 +16,24 @@ | |||
* 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(); | |||
} |
@@ -1,4 +1,4 @@ | |||
/* 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 | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -16,24 +16,53 @@ | |||
* 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 | |||
typedef struct CGImage* CGImageRef; | |||
#else | |||
#include <X11/extensions/Xrender.h> | |||
#endif | |||
class Fl_RGB_Image; | |||
class OSXPixelBuffer: public PlatformPixelBuffer { | |||
class Surface { | |||
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: | |||
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; | |||
#else | |||
Pixmap pixmap; | |||
Picture picture; | |||
XRenderPictFormat* visFormat; | |||
#endif | |||
}; | |||
#endif | |||
@@ -0,0 +1,162 @@ | |||
/* 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(); | |||
} | |||
} |
@@ -0,0 +1,146 @@ | |||
/* 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(); | |||
} | |||
} | |||
@@ -0,0 +1,139 @@ | |||
/* 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); | |||
} | |||
@@ -62,15 +62,6 @@ | |||
#include "vncviewer.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_ask.H> | |||
@@ -111,7 +102,7 @@ Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_) | |||
// We need to intercept keyboard events early | |||
Fl::add_system_handler(handleSystemEvent, this); | |||
frameBuffer = createFramebuffer(w, h); | |||
frameBuffer = new PlatformPixelBuffer(w, h); | |||
assert(frameBuffer); | |||
cc->setFramebuffer(frameBuffer); | |||
@@ -272,7 +263,7 @@ void Viewport::resize(int x, int y, int w, int h) | |||
vlog.debug("Resizing framebuffer from %dx%d to %dx%d", | |||
frameBuffer->width(), frameBuffer->height(), w, h); | |||
frameBuffer = createFramebuffer(w, h); | |||
frameBuffer = new PlatformPixelBuffer(w, h); | |||
assert(frameBuffer); | |||
cc->setFramebuffer(frameBuffer); | |||
} | |||
@@ -374,29 +365,6 @@ int Viewport::handle(int 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) | |||
{ | |||
Viewport *self = (Viewport *)data; |
@@ -56,8 +56,6 @@ public: | |||
private: | |||
PlatformPixelBuffer* createFramebuffer(int w, int h); | |||
static void handleClipboardChange(int source, void *data); | |||
void handlePointerEvent(const rfb::Point& pos, int buttonMask); |
@@ -1,93 +0,0 @@ | |||
/* 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); | |||
} |
@@ -1,229 +0,0 @@ | |||
/* 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; | |||
} |
@@ -1,47 +0,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 |