diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index 560ee7ff2..94e4b0286 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -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. diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 3f97634b1..314367eb6 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -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 diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 8d8dbfd74..bea32abe1 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -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 @@ -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()); @@ -257,6 +257,11 @@ void VNCServerST::unblockUpdates() } } +uint64_t VNCServerST::getMsc() +{ + return msc; +} + void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout) { if (comparer) @@ -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) { diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 55d0c889d..719b3f366 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -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); @@ -206,6 +207,7 @@ namespace rfb { Timer disconnectTimer; Timer connectTimer; + uint64_t msc; Timer frameTimer; }; diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am index 1e9859661..40eba4f20 100644 --- a/unix/xserver/hw/vnc/Makefile.am +++ b/unix/xserver/hw/vnc/Makefile.am @@ -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 diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index c4117c74d..6fe055ea3 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -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 @@ -54,6 +54,7 @@ extern "C" { void vncSetGlueContext(int screenIndex); +void vncPresentMscEvent(uint64_t id, uint64_t msc); } using namespace rfb; @@ -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 @@ -471,6 +487,22 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, return result; } +void XserverDesktop::frameTick(uint64_t msc) +{ + std::map::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(); diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 3cac21211..f3c684c7f 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -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 @@ -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); @@ -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); @@ -128,6 +132,8 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, OutputIdMap outputIdMap; + std::map pendingMsc; + rfb::Point oldCursorPos; }; #endif diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 1dfe76d74..c0610fe80 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -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 @@ -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, '='); diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index c317d8a21..9ca00b06b 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -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 @@ -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 diff --git a/unix/xserver/hw/vnc/vncPresent.c b/unix/xserver/hw/vnc/vncPresent.c new file mode 100644 index 000000000..89dcc1d05 --- /dev/null +++ b/unix/xserver/hw/vnc/vncPresent.c @@ -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 +#endif + +#include "vncExtInit.h" +#include "vncPresent.h" + +#include + +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); +} \ No newline at end of file diff --git a/unix/xserver/hw/vnc/vncPresent.h b/unix/xserver/hw/vnc/vncPresent.h new file mode 100644 index 000000000..174074020 --- /dev/null +++ b/unix/xserver/hw/vnc/vncPresent.h @@ -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 + +Bool vncPresentInit(ScreenPtr screen); +void vncPresentMscEvent(uint64_t id, uint64_t msc); + +#endif diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c index a953240d1..706c9d5a0 100644 --- a/unix/xserver/hw/vnc/xvnc.c +++ b/unix/xserver/hw/vnc/xvnc.c @@ -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 @@ -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 */