protected:
Surface* overlay;
+ Surface* offscreen;
};
TestWindow::TestWindow() :
}
OverlayTestWindow::OverlayTestWindow() :
- overlay(NULL)
+ overlay(NULL), offscreen(NULL)
{
}
overlay = new Surface(400, 200);
overlay->clear(0xff, 0x80, 0x00, 0xcc);
+
+ // X11 needs an off screen buffer for compositing to avoid flicker
+#if !defined(WIN32) && !defined(__APPLE__)
+ offscreen = new Surface(w(), h());
+#else
+ offscreen = NULL;
+#endif
}
void OverlayTestWindow::stop()
{
PartialTestWindow::stop();
+ delete offscreen;
+ offscreen = NULL;
delete overlay;
overlay = NULL;
}
int ox, oy, ow, oh;
int X, Y, W, H;
+ // We cannot update the damage region from inside the draw function,
+ // so delegate this to an idle function
+ Fl::add_idle(timer, this);
+
// 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;
fl_push_no_clip();
fl_push_clip(X, Y, W, H);
+ if (offscreen)
+ fb->draw(offscreen, X, Y, X, Y, W, H);
+ else
+ fb->draw(X, Y, X, Y, W, H);
+
+ pixels += W*H;
+ frames++;
+
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);
+ if ((W != 0) && (H != 0)) {
+ if (offscreen)
+ overlay->draw(offscreen, X - ox, Y - oy, X, Y, W, H);
+ else
+ overlay->draw(X - ox, Y - oy, X, Y, W, H);
+ }
fl_pop_clip();
fl_pop_clip();
+
+ if (offscreen) {
+ fl_clip_box(0, 0, w(), h(), X, Y, W, H);
+ offscreen->draw(X, Y, X, Y, W, H);
+ }
}
static void dosubtest(TestWindow* win, int width, int height,
#include "parameters.h"
#include "vncviewer.h"
#include "CConn.h"
+#include "Surface.h"
#include "Viewport.h"
#include <FL/Fl.H>
DesktopWindow::DesktopWindow(int w, int h, const char *name,
const rfb::PixelFormat& serverPF,
CConn* cc_)
- : Fl_Window(w, h), cc(cc_), firstUpdate(true),
+ : Fl_Window(w, h), cc(cc_), offscreen(NULL), firstUpdate(true),
delayedFullscreen(false), delayedDesktopSize(false)
{
Fl_Group* group;
OptionsDialog::removeCallback(handleOptions);
+ delete offscreen;
+
// FLTK automatically deletes all child widgets, so we shouldn't touch
// them ourselves here
}
{
bool redraw;
- int W, H;
+ int X, Y, W, H;
+
+ // X11 needs an off screen buffer for compositing to avoid flicker
+#if !defined(WIN32) && !defined(__APPLE__)
+
+ // Adjust offscreen surface dimensions
+ if ((offscreen == NULL) ||
+ (offscreen->width() != w()) || (offscreen->height() != h())) {
+ delete offscreen;
+ offscreen = new Surface(w(), h());
+ }
+
+#endif
// Active area inside scrollbars
W = w() - (vscroll->visible() ? vscroll->w() : 0);
// Redraw background only on full redraws
if (redraw) {
- if (viewport->h() < h()) {
- fl_rectf(0, 0, W, viewport->y(), 40, 40, 40);
- fl_rectf(0, viewport->y() + viewport->h(), W,
- h() - (viewport->y() + viewport->h()),
- 40, 40, 40);
- }
- if (viewport->w() < w()) {
- fl_rectf(0, 0, viewport->x(), H, 40, 40, 40);
- fl_rectf(viewport->x() + viewport->w(), 0,
- w() - (viewport->x() + viewport->w()),
- H, 40, 40, 40);
- }
+ if (offscreen)
+ offscreen->clear(40, 40, 40);
+ else
+ fl_rectf(0, 0, W, H, 40, 40, 40);
}
// Make sure the viewport isn't trampling on the scrollbars
fl_push_clip(0, 0, W, H);
- if (redraw)
- draw_child(*viewport);
- else
- update_child(*viewport);
+ if (offscreen) {
+ viewport->draw(offscreen);
+ viewport->clear_damage();
+ } else {
+ if (redraw)
+ draw_child(*viewport);
+ else
+ update_child(*viewport);
+ }
fl_pop_clip();
+ // Flush offscreen surface to screen
+ if (offscreen) {
+ fl_clip_box(0, 0, W, H, X, Y, W, H);
+ offscreen->draw(X, Y, X, Y, W, H);
+ }
+
// Finally the scrollbars
if (redraw) {
namespace rfb { class ModifiablePixelBuffer; }
class CConn;
+class Surface;
class Viewport;
class Fl_Scrollbar;
CConn* cc;
Fl_Scrollbar *hscroll, *vscroll;
Viewport *viewport;
+ Surface *offscreen;
bool firstUpdate;
bool delayedFullscreen;
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);
+ void draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h);
protected:
void alloc();
#include "Surface.h"
+static void render(CGContextRef gc, CGImageRef image,
+ int src_x, int src_y, int src_w, int src_h,
+ int x, int y, int w, int h)
+{
+ CGRect rect;
+
+ CGContextSaveGState(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 = src_w;
+ rect.size.height = src_h;
+
+ CGContextDrawImage(gc, rect, image);
+
+ CGContextRestoreGState(gc);
+}
+
void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
unsigned char* out;
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
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;
+ render(fl_gc, image, src_x, src_y, width(), height(), x, y, w, h);
- CGContextClipToRect(fl_gc, rect);
+ CGContextRestoreGState(fl_gc);
+}
- rect.origin.x = x - src_x;
- rect.origin.y = y - src_y;
- rect.size.width = width();
- rect.size.height = height();
+void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+ CGColorSpaceRef lut;
+ CGContextRef bitmap;
- CGContextDrawImage(fl_gc, rect, image);
+ lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
+ if (!lut) {
+ lut = CGColorSpaceCreateDeviceRGB();
+ if (!lut)
+ throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
+ }
- CGContextRestoreGState(fl_gc);
+ bitmap = CGBitmapContextCreate(dst->data, dst->width(),
+ dst->height(), 8, dst->width()*4, lut,
+ kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
+ CGColorSpaceRelease(lut);
+ if (!bitmap)
+ throw rdr::Exception("CGBitmapContextCreate");
+
+ // macOS Coordinates are from bottom left, not top left
+ src_y = height() - (src_y + h);
+ y = dst->height() - (y + h);
+
+ render(bitmap, image, src_x, src_y, width(), height(), x, y, w, h);
+
+ CGContextRelease(bitmap);
}
void Surface::alloc()
DeleteDC(dc);
}
+void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+ HDC origdc, dstdc;
+
+ dstdc = CreateCompatibleDC(NULL);
+ if (!dstdc)
+ throw rdr::SystemException("CreateCompatibleDC", GetLastError());
+
+ if (!SelectObject(dstdc, dst->bitmap))
+ throw rdr::SystemException("SelectObject", GetLastError());
+
+ origdc = fl_gc;
+ fl_gc = dstdc;
+ draw(src_x, src_y, x, y, w, h);
+ fl_gc = origdc;
+
+ DeleteDC(dstdc);
+}
+
void Surface::alloc()
{
BITMAPINFOHEADER bih;
XRenderFreePicture(fl_display, winPict);
}
+void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+ XRenderComposite(fl_display, PictOpSrc, picture, None, dst->picture,
+ src_x, src_y, 0, 0, x, y, w, h);
+}
+
void Surface::alloc()
{
XRenderPictFormat* format;
}
+void Viewport::draw(Surface* dst)
+{
+ int X, Y, W, H;
+
+ // Check what actually needs updating
+ fl_clip_box(x(), y(), w(), h(), X, Y, W, H);
+ if ((W == 0) || (H == 0))
+ return;
+
+ frameBuffer->draw(dst, X - x(), Y - y(), X, Y, W, H);
+}
+
+
void Viewport::draw()
{
int X, Y, W, H;
class CConn;
class PlatformPixelBuffer;
+class Surface;
class Viewport : public Fl_Widget {
public:
void setCursor(int width, int height, const rfb::Point& hotspot,
void* data, void* mask);
+ void draw(Surface* dst);
+
// Fl_Widget callback methods
void draw();