Skip to content

Commit

Permalink
Add support for X Present extension
Browse files Browse the repository at this point in the history
This makes it possible for applications to synchronize their updates to
the updates sent out to clients. This avoids tearing, and could in the
future also help with rate limiting applications to what the client can
actually show.
  • Loading branch information
CendioOssman committed Jun 19, 2024
1 parent 5322b8f commit 3875912
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 8 deletions.
4 changes: 4 additions & 0 deletions common/rfb/SDesktop.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ namespace rfb {
return resultProhibited;
}

// frameTick() is called whenever a frame update has been processed,
// signalling that a good time to render new data
virtual void frameTick(uint64_t msc) { (void)msc; }

// InputHandler interface
// pointerEvent(), keyEvent() and clientCutText() are called in response to
// the relevant RFB protocol messages from clients.
Expand Down
2 changes: 2 additions & 0 deletions common/rfb/VNCServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ namespace rfb {
virtual void blockUpdates() = 0;
virtual void unblockUpdates() = 0;

virtual uint64_t getMsc() = 0;

// setPixelBuffer() tells the server to use the given pixel buffer (and
// optionally a modified screen layout). If this differs in size from
// the previous pixel buffer, this may result in protocol messages being
Expand Down
12 changes: 10 additions & 2 deletions common/rfb/VNCServerST.cxx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2019 Pierre Ossman for Cendio AB
* Copyright 2009-2024 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
Expand Down Expand Up @@ -88,7 +88,7 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
renderedCursorInvalid(false),
keyRemapper(&KeyRemapper::defInstance),
idleTimer(this), disconnectTimer(this), connectTimer(this),
frameTimer(this)
msc(0), frameTimer(this)
{
slog.debug("creating single-threaded server %s", name.c_str());

Expand Down Expand Up @@ -257,6 +257,11 @@ void VNCServerST::unblockUpdates()
}
}

uint64_t VNCServerST::getMsc()
{
return msc;
}

void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
{
if (comparer)
Expand Down Expand Up @@ -634,6 +639,9 @@ void VNCServerST::handleTimeout(Timer* t)

writeUpdate();

msc++;
desktop->frameTick(msc);

// If this is the first iteration then we need to adjust the timeout
frameTimer.repeat(1000/rfb::Server::frameRate);
} else if (t == &idleTimer) {
Expand Down
2 changes: 2 additions & 0 deletions common/rfb/VNCServerST.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ namespace rfb {

virtual void blockUpdates();
virtual void unblockUpdates();
virtual uint64_t getMsc();
virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout);
virtual void setPixelBuffer(PixelBuffer* pb);
virtual void setScreenLayout(const ScreenSet& layout);
Expand Down Expand Up @@ -206,6 +207,7 @@ namespace rfb {
Timer disconnectTimer;
Timer connectTimer;

uint64_t msc;
Timer frameTimer;
};

Expand Down
4 changes: 2 additions & 2 deletions unix/xserver/hw/vnc/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ COMMON_LIBS=$(NETWORK_LIB) $(RFB_LIB) $(RDR_LIB) $(OS_LIB) $(UNIXCOMMON_LIB)
noinst_LTLIBRARIES = libvnccommon.la

HDRS = vncExtInit.h vncHooks.h \
vncBlockHandler.h vncSelection.h \
vncBlockHandler.h vncPresent.h vncSelection.h \
XorgGlue.h XserverDesktop.h xorg-version.h \
vncInput.h RFBGlue.h

libvnccommon_la_SOURCES = $(HDRS) \
vncExt.c vncExtInit.cc vncHooks.c vncSelection.c \
vncExt.c vncExtInit.cc vncHooks.c vncPresent.c vncSelection.c \
vncBlockHandler.c XorgGlue.c RandrGlue.c RFBGlue.cc XserverDesktop.cc \
vncInput.c vncInputXKB.c qnum_to_xorgevdev.c qnum_to_xorgkbd.c

Expand Down
34 changes: 33 additions & 1 deletion unix/xserver/hw/vnc/XserverDesktop.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2019 Pierre Ossman for Cendio AB
* Copyright 2009-2024 Pierre Ossman for Cendio AB
* Copyright 2014 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -54,6 +54,7 @@

extern "C" {
void vncSetGlueContext(int screenIndex);
void vncPresentMscEvent(uint64_t id, uint64_t msc);
}

using namespace rfb;
Expand Down Expand Up @@ -145,6 +146,21 @@ void XserverDesktop::refreshScreenLayout()
server->setScreenLayout(::computeScreenLayout(&outputIdMap));
}

uint64_t XserverDesktop::getMsc()
{
return server->getMsc();
}

void XserverDesktop::queueMsc(uint64_t id, uint64_t msc)
{
pendingMsc[id] = msc;
}

void XserverDesktop::abortMsc(uint64_t id)
{
pendingMsc.erase(id);
}

void XserverDesktop::init(rfb::VNCServer* vs)
{
// We already own the server object, and we always keep it in a
Expand Down Expand Up @@ -471,6 +487,22 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
return result;
}

void XserverDesktop::frameTick(uint64_t msc)
{
std::map<uint64_t, uint64_t>::iterator iter, next;

for (iter = pendingMsc.begin(); iter != pendingMsc.end();) {
next = iter; next++;

if (iter->second <= msc) {
pendingMsc.erase(iter->first);
vncPresentMscEvent(iter->first, msc);
}

iter = next;
}
}

void XserverDesktop::handleClipboardRequest()
{
vncHandleClipboardRequest();
Expand Down
8 changes: 7 additions & 1 deletion unix/xserver/hw/vnc/XserverDesktop.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2019 Pierre Ossman for Cendio AB
* Copyright 2009-2024 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
Expand Down Expand Up @@ -59,6 +59,9 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
void unblockUpdates();
void setFramebuffer(int w, int h, void* fbptr, int stride);
void refreshScreenLayout();
uint64_t getMsc();
void queueMsc(uint64_t id, uint64_t msc);
void abortMsc(uint64_t id);
void requestClipboard();
void announceClipboard(bool available);
void sendClipboardData(const char* data);
Expand Down Expand Up @@ -96,6 +99,7 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
virtual void keyEvent(uint32_t keysym, uint32_t keycode, bool down);
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout);
virtual void frameTick(uint64_t msc);
virtual void handleClipboardRequest();
virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardData(const char* data);
Expand Down Expand Up @@ -128,6 +132,8 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,

OutputIdMap outputIdMap;

std::map<uint64_t, uint64_t> pendingMsc;

rfb::Point oldCursorPos;
};
#endif
29 changes: 28 additions & 1 deletion unix/xserver/hw/vnc/vncExtInit.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2019 Pierre Ossman for Cendio AB
* Copyright 2011-2024 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
Expand Down Expand Up @@ -482,6 +482,33 @@ void vncRefreshScreenLayout(int scrIdx)
}
}

uint64_t vncGetMsc(int scrIdx)
{
try {
return desktop[scrIdx]->getMsc();
} catch (rdr::Exception& e) {
vncFatalError("vncGetMsc: %s\n", e.str());
}
}

void vncQueueMsc(int scrIdx, uint64_t id, uint64_t msc)
{
try {
desktop[scrIdx]->queueMsc(id, msc);
} catch (rdr::Exception& e) {
vncFatalError("vncQueueMsc: %s\n", e.str());
}
}

void vncAbortMsc(int scrIdx, uint64_t id)
{
try {
desktop[scrIdx]->abortMsc(id);
} catch (rdr::Exception& e) {
vncFatalError("vncAbortMsc: %s\n", e.str());
}
}

int vncOverrideParam(const char *nameAndValue)
{
const char* equalSign = strchr(nameAndValue, '=');
Expand Down
6 changes: 5 additions & 1 deletion unix/xserver/hw/vnc/vncExtInit.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2019 Pierre Ossman for Cendio AB
* Copyright 2011-2024 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
Expand Down Expand Up @@ -87,6 +87,10 @@ void vncPreScreenResize(int scrIdx);
void vncPostScreenResize(int scrIdx, int success, int width, int height);
void vncRefreshScreenLayout(int scrIdx);

uint64_t vncGetMsc(int scrIdx);
void vncQueueMsc(int scrIdx, uint64_t id, uint64_t msc);
void vncAbortMsc(int scrIdx, uint64_t id);

int vncOverrideParam(const char *nameAndValue);

#ifdef __cplusplus
Expand Down
93 changes: 93 additions & 0 deletions unix/xserver/hw/vnc/vncPresent.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* Copyright 2024 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_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include "vncExtInit.h"
#include "vncPresent.h"

#include <present.h>

static RRCrtcPtr vncPresentGetCrtc(WindowPtr window)
{
ScreenPtr pScreen = window->drawable.pScreen;
rrScrPrivPtr rp = rrGetScrPriv(pScreen);

/* All output is synchronized, so just pick the first active crtc */
for (int c = 0; c < rp->numCrtcs; c++) {
RRCrtcPtr crtc;

crtc = rp->crtcs[c];
if (crtc->mode == NULL)
continue;

return crtc;
}

return NULL;
}

static int vncPresentGetUstMsc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
{
*ust = GetTimeInMicros();
*msc = vncGetMsc(crtc->pScreen->myNum);

return Success;
}

static int vncPresentQueueVBlank(RRCrtcPtr crtc, uint64_t event_id,
uint64_t msc)
{
vncQueueMsc(crtc->pScreen->myNum, event_id, msc);
return Success;
}

void vncPresentMscEvent(uint64_t id, uint64_t msc)
{
present_event_notify(id, GetTimeInMicros(), msc);
}

static void vncPresentAbortVBlank(RRCrtcPtr crtc, uint64_t event_id,
uint64_t msc)
{
vncAbortMsc(crtc->pScreen->myNum, event_id);
}

static void vncPresentFlush(WindowPtr window)
{
}

static present_screen_info_rec vncPresentScreenInfo = {
.version = PRESENT_SCREEN_INFO_VERSION,

.get_crtc = vncPresentGetCrtc,
.get_ust_msc = vncPresentGetUstMsc,
.queue_vblank = vncPresentQueueVBlank,
.abort_vblank = vncPresentAbortVBlank,
.flush = vncPresentFlush,

.capabilities = PresentCapabilityNone,
};

Bool
vncPresentInit(ScreenPtr screen)
{
return present_screen_init(screen, &vncPresentScreenInfo);
}
27 changes: 27 additions & 0 deletions unix/xserver/hw/vnc/vncPresent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* Copyright 2024 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 __PRESENT_H__
#define __PRESENT_H__

#include <dix.h>

Bool vncPresentInit(ScreenPtr screen);
void vncPresentMscEvent(uint64_t id, uint64_t msc);

#endif
5 changes: 5 additions & 0 deletions unix/xserver/hw/vnc/xvnc.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ from the X Consortium.
#include "RFBGlue.h"
#include "XorgGlue.h"
#include "RandrGlue.h"
#include "vncPresent.h"
#include "xorg-version.h"

#include <stdio.h>
Expand Down Expand Up @@ -1085,6 +1086,10 @@ vncScreenInit(ScreenPtr pScreen, int argc, char **argv)
if (!ret)
return FALSE;

ret = vncPresentInit(pScreen);
if (!ret)
ErrorF("Failed to initialize Present extension\n");

return TRUE;

} /* end vncScreenInit */
Expand Down

0 comments on commit 3875912

Please sign in to comment.