From 546b2ad80a68e80a737aade06f0685cccb5e9716 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 2 May 2019 12:32:03 +0200 Subject: Make sure clipboard uses \n line endings This is required by the protocol so we should make sure it is enforced. We are tolerant of clients that violate this though and convert incoming clipboard data. --- unix/xserver/hw/vnc/RFBGlue.cc | 16 +++++++++++++++- unix/xserver/hw/vnc/RFBGlue.h | 5 ++++- unix/xserver/hw/vnc/vncSelection.c | 22 ++++++++++++++++++---- 3 files changed, 37 insertions(+), 6 deletions(-) (limited to 'unix') diff --git a/unix/xserver/hw/vnc/RFBGlue.cc b/unix/xserver/hw/vnc/RFBGlue.cc index 160177bd..d9c456e8 100644 --- a/unix/xserver/hw/vnc/RFBGlue.cc +++ b/unix/xserver/hw/vnc/RFBGlue.cc @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2015 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -210,3 +210,17 @@ int vncIsTCPPortUsed(int port) } return 0; } + +char* vncConvertLF(const char* src, size_t bytes) +{ + try { + return convertLF(src, bytes); + } catch (...) { + return NULL; + } +} + +void vncStrFree(char* str) +{ + strFree(str); +} diff --git a/unix/xserver/hw/vnc/RFBGlue.h b/unix/xserver/hw/vnc/RFBGlue.h index a63afd07..8e70c680 100644 --- a/unix/xserver/hw/vnc/RFBGlue.h +++ b/unix/xserver/hw/vnc/RFBGlue.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2015 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -49,6 +49,9 @@ void vncListParams(int width, int nameWidth); int vncGetSocketPort(int fd); int vncIsTCPPortUsed(int port); +char* vncConvertLF(const char* src, size_t bytes); +void vncStrFree(char* str); + #ifdef __cplusplus } #endif diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 4f3538d4..5ddcaf00 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -1,4 +1,4 @@ -/* Copyright 2016 Pierre Ossman for Cendio AB +/* Copyright 2016-2019 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 @@ -415,13 +415,22 @@ static void vncHandleSelection(Atom selection, Atom target, else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) vncSelectionRequest(selection, xaUTF8_STRING); } else if (target == xaSTRING) { + char* filtered; + if (prop->format != 8) return; if (prop->type != xaSTRING) return; - vncServerCutText(prop->data, prop->size); + filtered = vncConvertLF(prop->data, prop->size); + if (filtered == NULL) + return; + + vncServerCutText(filtered, strlen(filtered)); + + vncStrFree(filtered); } else if (target == xaUTF8_STRING) { + char *filtered; unsigned char* buffer; unsigned char* out; size_t len; @@ -470,9 +479,14 @@ static void vncHandleSelection(Atom selection, Atom target, } } - vncServerCutText((const char*)buffer, len); - + filtered = vncConvertLF(buffer, len); free(buffer); + if (filtered == NULL) + return; + + vncServerCutText(filtered, strlen(filtered)); + + vncStrFree(filtered); } } -- cgit v1.2.3 From 66f1db543b425f6fe64d437477e6f78924ec57be Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 2 May 2019 12:32:03 +0200 Subject: Clean up internal clipboard handling We now filter incoming data, which means we can start assuming the clipboard data is always null terminated. This allows us to clean up a lot of the internal handling. --- common/rfb/CMsgHandler.h | 2 +- common/rfb/CMsgReader.cxx | 2 +- common/rfb/CMsgWriter.cxx | 7 +++++-- common/rfb/CMsgWriter.h | 2 +- common/rfb/InputHandler.h | 3 +-- common/rfb/SMsgReader.cxx | 2 +- common/rfb/SMsgWriter.cxx | 7 +++++-- common/rfb/SMsgWriter.h | 2 +- common/rfb/VNCSConnectionST.cxx | 8 ++++---- common/rfb/VNCSConnectionST.h | 4 ++-- common/rfb/VNCServer.h | 2 +- common/rfb/VNCServerST.cxx | 10 +++++----- common/rfb/VNCServerST.h | 4 ++-- tests/decperf.cxx | 4 ++-- tests/encperf.cxx | 4 ++-- unix/x0vncserver/XDesktop.cxx | 2 +- unix/x0vncserver/XDesktop.h | 2 +- unix/xserver/hw/vnc/XserverDesktop.cc | 8 ++++---- unix/xserver/hw/vnc/XserverDesktop.h | 4 ++-- unix/xserver/hw/vnc/vncExtInit.cc | 4 ++-- unix/xserver/hw/vnc/vncExtInit.h | 2 +- unix/xserver/hw/vnc/vncSelection.c | 21 +++++++-------------- unix/xserver/hw/vnc/vncSelection.h | 4 ++-- vncviewer/CConn.cxx | 4 ++-- vncviewer/CConn.h | 2 +- vncviewer/DesktopWindow.cxx | 4 ++-- vncviewer/DesktopWindow.h | 2 +- vncviewer/Viewport.cxx | 10 +++++----- vncviewer/Viewport.h | 4 ++-- win/rfb_win32/Clipboard.cxx | 4 ++-- win/rfb_win32/Clipboard.h | 3 ++- win/rfb_win32/SDisplay.cxx | 12 +++++------- win/rfb_win32/SDisplay.h | 5 +++-- 33 files changed, 79 insertions(+), 81 deletions(-) (limited to 'unix') diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h index effdaabf..1581f792 100644 --- a/common/rfb/CMsgHandler.h +++ b/common/rfb/CMsgHandler.h @@ -70,7 +70,7 @@ namespace rfb { virtual void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs) = 0; virtual void bell() = 0; - virtual void serverCutText(const char* str, rdr::U32 len) = 0; + virtual void serverCutText(const char* str) = 0; virtual void setLEDState(unsigned int state); diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx index a928eb15..86288ad9 100644 --- a/common/rfb/CMsgReader.cxx +++ b/common/rfb/CMsgReader.cxx @@ -160,7 +160,7 @@ void CMsgReader::readServerCutText() CharArray ca(len); is->readBytes(ca.buf, len); CharArray filtered(convertLF(ca.buf, len)); - handler->serverCutText(filtered.buf, strlen(filtered.buf)); + handler->serverCutText(filtered.buf); } void CMsgReader::readFence() diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx index fed0bd27..f1fa58dd 100644 --- a/common/rfb/CMsgWriter.cxx +++ b/common/rfb/CMsgWriter.cxx @@ -179,11 +179,14 @@ void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask) } -void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len) +void CMsgWriter::writeClientCutText(const char* str) { - if (memchr(str, '\r', len) != NULL) + size_t len; + + if (strchr(str, '\r') != NULL) throw Exception("Invalid carriage return in clipboard data"); + len = strlen(str); startMsg(msgTypeClientCutText); os->pad(3); os->writeU32(len); diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h index 4d533d42..d3ac19c9 100644 --- a/common/rfb/CMsgWriter.h +++ b/common/rfb/CMsgWriter.h @@ -55,7 +55,7 @@ namespace rfb { void writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); void writePointerEvent(const Point& pos, int buttonMask); - void writeClientCutText(const char* str, rdr::U32 len); + void writeClientCutText(const char* str); protected: void startMsg(int type); diff --git a/common/rfb/InputHandler.h b/common/rfb/InputHandler.h index 6c072849..b91f0e42 100644 --- a/common/rfb/InputHandler.h +++ b/common/rfb/InputHandler.h @@ -37,8 +37,7 @@ namespace rfb { bool __unused_attr down) { } virtual void pointerEvent(const Point& __unused_attr pos, int __unused_attr buttonMask) { } - virtual void clientCutText(const char* __unused_attr str, - int __unused_attr len) { } + virtual void clientCutText(const char* __unused_attr str) { } }; } diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index 0c0e8b26..5efbfe2b 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -215,7 +215,7 @@ void SMsgReader::readClientCutText() CharArray ca(len); is->readBytes(ca.buf, len); CharArray filtered(convertLF(ca.buf, len)); - handler->clientCutText(filtered.buf, strlen(filtered.buf)); + handler->clientCutText(filtered.buf); } void SMsgReader::readQEMUMessage() diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index f0748ff2..3d5a64ca 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -78,11 +78,14 @@ void SMsgWriter::writeBell() endMsg(); } -void SMsgWriter::writeServerCutText(const char* str, int len) +void SMsgWriter::writeServerCutText(const char* str) { - if (memchr(str, '\r', len) != NULL) + size_t len; + + if (strchr(str, '\r') != NULL) throw Exception("Invalid carriage return in clipboard data"); + len = strlen(str); startMsg(msgTypeServerCutText); os->pad(3); os->writeU32(len); diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 4f4c9cc0..8cf2ae77 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -56,7 +56,7 @@ namespace rfb { // writeBell() and writeServerCutText() do the obvious thing. void writeBell(); - void writeServerCutText(const char* str, int len); + void writeServerCutText(const char* str); // writeFence() sends a new fence request or response to the client. void writeFence(rdr::U32 flags, unsigned len, const char data[]); diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index fe00dab6..002ae925 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -282,13 +282,13 @@ void VNCSConnectionST::bellOrClose() } } -void VNCSConnectionST::serverCutTextOrClose(const char *str, int len) +void VNCSConnectionST::serverCutTextOrClose(const char *str) { try { if (!accessCheck(AccessCutText)) return; if (!rfb::Server::sendCutText) return; if (state() == RFBSTATE_NORMAL) - writer()->writeServerCutText(str, len); + writer()->writeServerCutText(str); } catch(rdr::Exception& e) { close(e.str()); } @@ -596,11 +596,11 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { server->keyEvent(keysym, keycode, down); } -void VNCSConnectionST::clientCutText(const char* str, int len) +void VNCSConnectionST::clientCutText(const char* str) { if (!accessCheck(AccessCutText)) return; if (!rfb::Server::acceptCutText) return; - server->clientCutText(str, len); + server->clientCutText(str); } void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index a9a8d3a4..54266e3c 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -72,7 +72,7 @@ namespace rfb { void screenLayoutChangeOrClose(rdr::U16 reason); void setCursorOrClose(); void bellOrClose(); - void serverCutTextOrClose(const char *str, int len); + void serverCutTextOrClose(const char *str); void setDesktopNameOrClose(const char *name); void setLEDStateOrClose(unsigned int state); void approveConnectionOrClose(bool accept, const char* reason); @@ -115,7 +115,7 @@ namespace rfb { virtual void setPixelFormat(const PixelFormat& pf); virtual void pointerEvent(const Point& pos, int buttonMask); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); - virtual void clientCutText(const char* str, int len); + virtual void clientCutText(const char* str); virtual void framebufferUpdateRequest(const Rect& r, bool incremental); virtual void setDesktopSize(int fb_width, int fb_height, const ScreenSet& layout); diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 298326f5..5398e9fd 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -57,7 +57,7 @@ namespace rfb { // serverCutText() tells the server that the cut text has changed. This // will normally be sent to all clients. - virtual void serverCutText(const char* str, int len) = 0; + virtual void serverCutText(const char* str) = 0; // bell() tells the server that it should make all clients make a bell sound. virtual void bell() = 0; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 7820aef5..21af0dac 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -340,14 +340,14 @@ void VNCServerST::bell() } } -void VNCServerST::serverCutText(const char* str, int len) +void VNCServerST::serverCutText(const char* str) { - if (memchr(str, '\r', len) != NULL) + if (strchr(str, '\r') != NULL) throw Exception("Invalid carriage return in clipboard data"); std::list::iterator ci, ci_next; for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { ci_next = ci; ci_next++; - (*ci)->serverCutTextOrClose(str, len); + (*ci)->serverCutTextOrClose(str); } } @@ -461,9 +461,9 @@ void VNCServerST::pointerEvent(VNCSConnectionST* client, desktop->pointerEvent(pos, buttonMask); } -void VNCServerST::clientCutText(const char* str, int len) +void VNCServerST::clientCutText(const char* str) { - desktop->clientCutText(str, len); + desktop->clientCutText(str); } unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester, diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 43a3bb95..5231977d 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -85,7 +85,7 @@ namespace rfb { virtual void setPixelBuffer(PixelBuffer* pb); virtual void setScreenLayout(const ScreenSet& layout); virtual const PixelBuffer* getPixelBuffer() const { return pb; } - virtual void serverCutText(const char* str, int len); + virtual void serverCutText(const char* str); virtual void approveConnection(network::Socket* sock, bool accept, const char* reason); @@ -115,7 +115,7 @@ namespace rfb { // Event handlers void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); void pointerEvent(VNCSConnectionST* client, const Point& pos, int buttonMask); - void clientCutText(const char* str, int len); + void clientCutText(const char* str); unsigned int setDesktopSize(VNCSConnectionST* requester, int fb_width, int fb_height, diff --git a/tests/decperf.cxx b/tests/decperf.cxx index 301e45e0..df5214f2 100644 --- a/tests/decperf.cxx +++ b/tests/decperf.cxx @@ -54,7 +54,7 @@ public: virtual void framebufferUpdateEnd(); virtual void setColourMapEntries(int, int, rdr::U16*); virtual void bell(); - virtual void serverCutText(const char*, rdr::U32); + virtual void serverCutText(const char*); public: double cpuTime; @@ -122,7 +122,7 @@ void CConn::bell() { } -void CConn::serverCutText(const char*, rdr::U32) +void CConn::serverCutText(const char*) { } diff --git a/tests/encperf.cxx b/tests/encperf.cxx index 6523eb72..e461197e 100644 --- a/tests/encperf.cxx +++ b/tests/encperf.cxx @@ -96,7 +96,7 @@ public: virtual void dataRect(const rfb::Rect&, int); virtual void setColourMapEntries(int, int, rdr::U16*); virtual void bell(); - virtual void serverCutText(const char*, rdr::U32); + virtual void serverCutText(const char*); public: double decodeTime; @@ -254,7 +254,7 @@ void CConn::bell() { } -void CConn::serverCutText(const char*, rdr::U32) +void CConn::serverCutText(const char*) { } diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx index 564b2d51..8be9aa3d 100644 --- a/unix/x0vncserver/XDesktop.cxx +++ b/unix/x0vncserver/XDesktop.cxx @@ -406,7 +406,7 @@ void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) { #endif } -void XDesktop::clientCutText(const char* str, int len) { +void XDesktop::clientCutText(const char* str) { } ScreenSet XDesktop::computeScreenLayout() diff --git a/unix/x0vncserver/XDesktop.h b/unix/x0vncserver/XDesktop.h index 3e85aac3..840d4331 100644 --- a/unix/x0vncserver/XDesktop.h +++ b/unix/x0vncserver/XDesktop.h @@ -56,7 +56,7 @@ public: virtual void pointerEvent(const rfb::Point& pos, int buttonMask); KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym); virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down); - virtual void clientCutText(const char* str, int len); + virtual void clientCutText(const char* str); virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout); diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index d8b3a4d4..cd9b1f9b 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -192,10 +192,10 @@ void XserverDesktop::setLEDState(unsigned int state) server->setLEDState(state); } -void XserverDesktop::serverCutText(const char* str, int len) +void XserverDesktop::serverCutText(const char* str) { try { - server->serverCutText(str, len); + server->serverCutText(str); } catch (rdr::Exception& e) { vlog.error("XserverDesktop::serverCutText: %s",e.str()); } @@ -436,9 +436,9 @@ void XserverDesktop::pointerEvent(const Point& pos, int buttonMask) vncPointerButtonAction(buttonMask); } -void XserverDesktop::clientCutText(const char* str, int len) +void XserverDesktop::clientCutText(const char* str) { - vncClientCutText(str, len); + vncClientCutText(str); } unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 1253935f..c6c4eb10 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -61,7 +61,7 @@ public: void refreshScreenLayout(); void bell(); void setLEDState(unsigned int state); - void serverCutText(const char* str, int len); + void serverCutText(const char* str); void setDesktopName(const char* name); void setCursor(int width, int height, int hotX, int hotY, const unsigned char *rgbaData); @@ -92,7 +92,7 @@ public: const char* userName); virtual void pointerEvent(const rfb::Point& pos, int buttonMask); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); - virtual void clientCutText(const char* str, int len); + virtual void clientCutText(const char* str); virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout); diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 20072f48..10143f6f 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -285,10 +285,10 @@ void vncUpdateDesktopName(void) desktop[scr]->setDesktopName(desktopName); } -void vncServerCutText(const char *text, size_t len) +void vncServerCutText(const char *text) { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->serverCutText(text, len); + desktop[scr]->serverCutText(text); } int vncConnectClient(const char *addr) diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 5f97f96d..bdcce83a 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -53,7 +53,7 @@ int vncGetSendPrimary(void); void vncUpdateDesktopName(void); -void vncServerCutText(const char *text, size_t len); +void vncServerCutText(const char *text); int vncConnectClient(const char *addr); diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 5ddcaf00..8f4146d9 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -48,7 +48,6 @@ static WindowPtr pWindow; static Window wid; static char* clientCutText; -static int clientCutTextLen; static int vncCreateSelectionWindow(void); static int vncOwnSelection(Atom selection); @@ -82,23 +81,20 @@ void vncSelectionInit(void) FatalError("Add VNC SelectionCallback failed\n"); } -void vncClientCutText(const char* str, int len) +void vncClientCutText(const char* str) { int rc; if (clientCutText != NULL) free(clientCutText); - clientCutText = malloc(len); + clientCutText = strdup(str); if (clientCutText == NULL) { LOG_ERROR("Could not allocate clipboard buffer"); DeleteWindowFromAnySelections(pWindow); return; } - memcpy(clientCutText, str, len); - clientCutTextLen = len; - if (vncGetSetPrimary()) { rc = vncOwnSelection(xaPRIMARY); if (rc != Success) @@ -246,7 +242,7 @@ static int vncConvertSelection(ClientPtr client, Atom selection, } else if ((target == xaSTRING) || (target == xaTEXT)) { rc = dixChangeWindowProperty(serverClient, pWin, realProperty, XA_STRING, 8, PropModeReplace, - clientCutTextLen, clientCutText, + strlen(clientCutText), clientCutText, TRUE); if (rc != Success) return rc; @@ -258,25 +254,22 @@ static int vncConvertSelection(ClientPtr client, Atom selection, const unsigned char* in; size_t in_len; - buffer = malloc(clientCutTextLen*2); + buffer = malloc(strlen(clientCutText)*2); if (buffer == NULL) return BadAlloc; out = buffer; len = 0; in = clientCutText; - in_len = clientCutTextLen; - while (in_len > 0) { + while (*in != '\0') { if (*in & 0x80) { *out++ = 0xc0 | (*in >> 6); *out++ = 0x80 | (*in & 0x3f); len += 2; in++; - in_len--; } else { *out++ = *in++; len++; - in_len--; } } @@ -426,7 +419,7 @@ static void vncHandleSelection(Atom selection, Atom target, if (filtered == NULL) return; - vncServerCutText(filtered, strlen(filtered)); + vncServerCutText(filtered); vncStrFree(filtered); } else if (target == xaUTF8_STRING) { @@ -484,7 +477,7 @@ static void vncHandleSelection(Atom selection, Atom target, if (filtered == NULL) return; - vncServerCutText(filtered, strlen(filtered)); + vncServerCutText(filtered); vncStrFree(filtered); } diff --git a/unix/xserver/hw/vnc/vncSelection.h b/unix/xserver/hw/vnc/vncSelection.h index 969f8958..e11d4a49 100644 --- a/unix/xserver/hw/vnc/vncSelection.h +++ b/unix/xserver/hw/vnc/vncSelection.h @@ -1,4 +1,4 @@ -/* Copyright 2016 Pierre Ossman for Cendio AB +/* Copyright 2016-2019 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 @@ -24,7 +24,7 @@ extern "C" { void vncSelectionInit(void); -void vncClientCutText(const char* str, int len); +void vncClientCutText(const char* str); #ifdef __cplusplus } diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index b4610e6a..2cc1fe4c 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -377,9 +377,9 @@ void CConn::bell() fl_beep(); } -void CConn::serverCutText(const char* str, rdr::U32 len) +void CConn::serverCutText(const char* str) { - desktop->serverCutText(str, len); + desktop->serverCutText(str); } void CConn::dataRect(const Rect& r, int encoding) diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h index 2e3362ce..38e09c25 100644 --- a/vncviewer/CConn.h +++ b/vncviewer/CConn.h @@ -61,7 +61,7 @@ public: void bell(); - void serverCutText(const char* str, rdr::U32 len); + void serverCutText(const char* str); void framebufferUpdateStart(); void framebufferUpdateEnd(); diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 150c39bc..4429e775 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -276,9 +276,9 @@ void DesktopWindow::resizeFramebuffer(int new_w, int new_h) } -void DesktopWindow::serverCutText(const char* str, rdr::U32 len) +void DesktopWindow::serverCutText(const char* str) { - viewport->serverCutText(str, len); + viewport->serverCutText(str); } diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h index 6ec8e1bd..fe938d9b 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -63,7 +63,7 @@ public: void resizeFramebuffer(int new_w, int new_h); // Incoming clipboard from server - void serverCutText(const char* str, rdr::U32 len); + void serverCutText(const char* str); // New image for the locally rendered cursor void setCursor(int width, int height, const rfb::Point& hotspot, diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 15a3ec42..5e495992 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -232,7 +232,7 @@ void Viewport::updateWindow() damage(FL_DAMAGE_USER1, r.tl.x + x(), r.tl.y + y(), r.width(), r.height()); } -void Viewport::serverCutText(const char* str, rdr::U32 len) +void Viewport::serverCutText(const char* str) { char *buffer; int size, ret; @@ -242,7 +242,7 @@ void Viewport::serverCutText(const char* str, rdr::U32 len) if (!acceptClipboard) return; - size = fl_utf8froma(NULL, 0, str, len); + size = fl_utf8froma(NULL, 0, str, strlen(str)); if (size <= 0) return; @@ -250,7 +250,7 @@ void Viewport::serverCutText(const char* str, rdr::U32 len) buffer = new char[size]; - ret = fl_utf8froma(buffer, size, str, len); + ret = fl_utf8froma(buffer, size, str, strlen(str)); assert(ret < size); vlog.debug("Got clipboard data (%d bytes)", (int)strlen(buffer)); @@ -577,7 +577,7 @@ int Viewport::handle(int event) vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered)); try { - cc->writer()->writeClientCutText(filtered, strlen(filtered)); + cc->writer()->writeClientCutText(filtered); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); exit_vncviewer(e.str()); @@ -768,7 +768,7 @@ void Viewport::flushPendingClipboard() size_t len = strlen(pendingClientCutText); vlog.debug("Sending pending clipboard data (%d bytes)", (int)len); try { - cc->writer()->writeClientCutText(pendingClientCutText, len); + cc->writer()->writeClientCutText(pendingClientCutText); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); exit_vncviewer(e.str()); diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h index be2192b8..c2c9872e 100644 --- a/vncviewer/Viewport.h +++ b/vncviewer/Viewport.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -46,7 +46,7 @@ public: void updateWindow(); // Incoming clipboard from server - void serverCutText(const char* str, rdr::U32 len); + void serverCutText(const char* str); // New image for the locally rendered cursor void setCursor(int width, int height, const rfb::Point& hotspot, diff --git a/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx index fca6c1df..49526956 100644 --- a/win/rfb_win32/Clipboard.cxx +++ b/win/rfb_win32/Clipboard.cxx @@ -113,11 +113,11 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { // Notify clients if (notifier) { if (!clipdata) { - notifier->notifyClipboardChanged(0, 0); + notifier->notifyClipboardChanged(0); } else { CharArray unix_text(convertLF(clipdata, strlen(clipdata))); removeNonISOLatin1Chars(unix_text.buf); - notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf)); + notifier->notifyClipboardChanged(unix_text.buf); } } else { vlog.debug("no clipboard notifier registered"); diff --git a/win/rfb_win32/Clipboard.h b/win/rfb_win32/Clipboard.h index 3da7bfca..c69e981f 100644 --- a/win/rfb_win32/Clipboard.h +++ b/win/rfb_win32/Clipboard.h @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2016-2019 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 @@ -38,7 +39,7 @@ namespace rfb { // -=- Abstract base class for callback recipients class Notifier { public: - virtual void notifyClipboardChanged(const char* text, int len) = 0; + virtual void notifyClipboardChanged(const char* text) = 0; virtual ~Notifier() {}; }; diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx index 2cedc4a8..2c91e3f4 100644 --- a/win/rfb_win32/SDisplay.cxx +++ b/win/rfb_win32/SDisplay.cxx @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 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 @@ -328,19 +329,16 @@ bool SDisplay::checkLedState() { return false; } -void SDisplay::clientCutText(const char* text, int len) { - CharArray clip_sz(len+1); - memcpy(clip_sz.buf, text, len); - clip_sz.buf[len] = 0; - clipboard->setClipText(clip_sz.buf); +void SDisplay::clientCutText(const char* text) { + clipboard->setClipText(text); } void -SDisplay::notifyClipboardChanged(const char* text, int len) { +SDisplay::notifyClipboardChanged(const char* text) { vlog.debug("clipboard text changed"); if (server) - server->serverCutText(text, len); + server->serverCutText(text); } diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h index 6dbfabbc..1773b785 100644 --- a/win/rfb_win32/SDisplay.h +++ b/win/rfb_win32/SDisplay.h @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 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 @@ -77,11 +78,11 @@ namespace rfb { const char* userName); virtual void pointerEvent(const Point& pos, int buttonmask); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); - virtual void clientCutText(const char* str, int len); + virtual void clientCutText(const char* str); // -=- Clipboard - virtual void notifyClipboardChanged(const char* text, int len); + virtual void notifyClipboardChanged(const char* text); // -=- Display events -- cgit v1.2.3 From 56fa7821560a60db39195e8c81d16b46e8f972c2 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 22 Jan 2016 16:40:59 +0100 Subject: Add UTF-8 to/from ISO 8859-1 conversion routines We convert between UTF-8 and ISO 8859-1 (latin 1) in several places so create some common routines for this. --- common/rfb/util.cxx | 166 +++++++++++++++++++++++++++++++++++++ common/rfb/util.h | 9 ++ unix/xserver/hw/vnc/RFBGlue.cc | 18 ++++ unix/xserver/hw/vnc/RFBGlue.h | 4 + unix/xserver/hw/vnc/vncSelection.c | 73 ++-------------- vncviewer/Viewport.cxx | 42 +++------- vncviewer/Viewport.h | 4 +- 7 files changed, 220 insertions(+), 96 deletions(-) (limited to 'unix') diff --git a/common/rfb/util.cxx b/common/rfb/util.cxx index f43a9453..deb68ca1 100644 --- a/common/rfb/util.cxx +++ b/common/rfb/util.cxx @@ -163,6 +163,172 @@ namespace rfb { return buffer; } + size_t ucs4ToUTF8(unsigned src, char* dst) { + if (src < 0x80) { + *dst++ = src; + *dst++ = '\0'; + return 1; + } else if (src < 0x800) { + *dst++ = 0xc0 | (src >> 6); + *dst++ = 0x80 | (src & 0x3f); + *dst++ = '\0'; + return 2; + } else if (src < 0x10000) { + *dst++ = 0xe0 | (src >> 12); + *dst++ = 0x80 | ((src >> 6) & 0x3f); + *dst++ = 0x80 | (src & 0x3f); + *dst++ = '\0'; + return 3; + } else if (src < 0x110000) { + *dst++ = 0xf0 | (src >> 18); + *dst++ = 0x80 | ((src >> 12) & 0x3f); + *dst++ = 0x80 | ((src >> 6) & 0x3f); + *dst++ = 0x80 | (src & 0x3f); + *dst++ = '\0'; + return 4; + } else { + return ucs4ToUTF8(0xfffd, dst); + } + } + + size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst) { + size_t count, consumed; + + *dst = 0xfffd; + + if (max == 0) + return 0; + + consumed = 1; + + if ((*src & 0x80) == 0) { + *dst = *src; + count = 0; + } else if ((*src & 0xe0) == 0xc0) { + *dst = *src & 0x1f; + count = 1; + } else if ((*src & 0xf0) == 0xe0) { + *dst = *src & 0x0f; + count = 2; + } else if ((*src & 0xf8) == 0xf0) { + *dst = *src & 0x07; + count = 3; + } else { + // Invalid sequence, consume all continuation characters + src++; + max--; + while ((max-- > 0) && ((*src++ & 0xc0) == 0x80)) + consumed++; + return consumed; + } + + src++; + max--; + + while (count--) { + // Invalid or truncated sequence? + if ((max == 0) || ((*src & 0xc0) != 0x80)) { + *dst = 0xfffd; + return consumed; + } + + *dst <<= 6; + *dst |= *src & 0x3f; + + src++; + max--; + } + + return consumed; + } + + char* latin1ToUTF8(const char* src, size_t bytes) { + char* buffer; + size_t sz; + + char* out; + const char* in; + size_t in_len; + + // Always include space for a NULL + sz = 1; + + // Compute output size + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + char buf[5]; + sz += ucs4ToUTF8(*in, buf); + in++; + in_len--; + } + + // Alloc + buffer = new char[sz]; + memset(buffer, 0, sz); + + // And convert + out = buffer; + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + out += ucs4ToUTF8(*in, out); + in++; + in_len--; + } + + return buffer; + } + + char* utf8ToLatin1(const char* src, size_t bytes) { + char* buffer; + size_t sz; + + char* out; + const char* in; + size_t in_len; + + // Always include space for a NULL + sz = 1; + + // Compute output size + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + size_t len; + unsigned ucs; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + sz++; + } + + // Alloc + buffer = new char[sz]; + memset(buffer, 0, sz); + + // And convert + out = buffer; + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + size_t len; + unsigned ucs; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + if (ucs > 0xff) + *out++ = '?'; + else + *out++ = (unsigned char)ucs; + } + + return buffer; + } + unsigned msBetween(const struct timeval *first, const struct timeval *second) { diff --git a/common/rfb/util.h b/common/rfb/util.h index de096692..7bd5cc01 100644 --- a/common/rfb/util.h +++ b/common/rfb/util.h @@ -88,6 +88,15 @@ namespace rfb { char* convertLF(const char* src, size_t bytes = (size_t)-1); + // Convertions between various Unicode formats. The returned strings are + // always null terminated and must be freed using strFree(). + + size_t ucs4ToUTF8(unsigned src, char* dst); + size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst); + + char* latin1ToUTF8(const char* src, size_t bytes = (size_t)-1); + char* utf8ToLatin1(const char* src, size_t bytes = (size_t)-1); + // HELPER functions for timeout handling // soonestTimeout() is a function to help work out the soonest of several diff --git a/unix/xserver/hw/vnc/RFBGlue.cc b/unix/xserver/hw/vnc/RFBGlue.cc index d9c456e8..f108fae4 100644 --- a/unix/xserver/hw/vnc/RFBGlue.cc +++ b/unix/xserver/hw/vnc/RFBGlue.cc @@ -220,6 +220,24 @@ char* vncConvertLF(const char* src, size_t bytes) } } +char* vncLatin1ToUTF8(const char* src, size_t bytes) +{ + try { + return latin1ToUTF8(src, bytes); + } catch (...) { + return NULL; + } +} + +char* vncUTF8ToLatin1(const char* src, size_t bytes) +{ + try { + return utf8ToLatin1(src, bytes); + } catch (...) { + return NULL; + } +} + void vncStrFree(char* str) { strFree(str); diff --git a/unix/xserver/hw/vnc/RFBGlue.h b/unix/xserver/hw/vnc/RFBGlue.h index 8e70c680..112405b8 100644 --- a/unix/xserver/hw/vnc/RFBGlue.h +++ b/unix/xserver/hw/vnc/RFBGlue.h @@ -50,6 +50,10 @@ int vncGetSocketPort(int fd); int vncIsTCPPortUsed(int port); char* vncConvertLF(const char* src, size_t bytes); + +char* vncLatin1ToUTF8(const char* src, size_t bytes); +char* vncUTF8ToLatin1(const char* src, size_t bytes); + void vncStrFree(char* str); #ifdef __cplusplus diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 8f4146d9..3438ac86 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -247,36 +247,16 @@ static int vncConvertSelection(ClientPtr client, Atom selection, if (rc != Success) return rc; } else if (target == xaUTF8_STRING) { - unsigned char* buffer; - unsigned char* out; - size_t len; + char* buffer; - const unsigned char* in; - size_t in_len; - - buffer = malloc(strlen(clientCutText)*2); + buffer = vncLatin1ToUTF8(clientCutText, (size_t)-1); if (buffer == NULL) return BadAlloc; - out = buffer; - len = 0; - in = clientCutText; - while (*in != '\0') { - if (*in & 0x80) { - *out++ = 0xc0 | (*in >> 6); - *out++ = 0x80 | (*in & 0x3f); - len += 2; - in++; - } else { - *out++ = *in++; - len++; - } - } - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, xaUTF8_STRING, 8, PropModeReplace, - len, buffer, TRUE); - free(buffer); + strlen(buffer), buffer, TRUE); + vncStrFree(buffer); if (rc != Success) return rc; } else { @@ -424,56 +404,19 @@ static void vncHandleSelection(Atom selection, Atom target, vncStrFree(filtered); } else if (target == xaUTF8_STRING) { char *filtered; - unsigned char* buffer; - unsigned char* out; - size_t len; - - const unsigned char* in; - size_t in_len; + char* buffer; if (prop->format != 8) return; if (prop->type != xaUTF8_STRING) return; - buffer = malloc(prop->size); + buffer = vncUTF8ToLatin1(prop->data, prop->size); if (buffer == NULL) return; - out = buffer; - len = 0; - in = prop->data; - in_len = prop->size; - while (in_len > 0) { - if ((*in & 0x80) == 0x00) { - *out++ = *in++; - len++; - in_len--; - } else if ((*in & 0xe0) == 0xc0) { - unsigned ucs; - ucs = (*in++ & 0x1f) << 6; - in_len--; - if (in_len > 0) { - ucs |= (*in++ & 0x3f); - in_len--; - } - if (ucs <= 0xff) - *out++ = ucs; - else - *out++ = '?'; - len++; - } else { - *out++ = '?'; - len++; - do { - in++; - in_len--; - } while ((in_len > 0) && ((*in & 0xc0) == 0x80)); - } - } - - filtered = vncConvertLF(buffer, len); - free(buffer); + filtered = vncConvertLF(buffer, (size_t)-1); + vncStrFree(buffer); if (filtered == NULL) return; diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 5e495992..151ecb47 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -235,25 +235,17 @@ void Viewport::updateWindow() void Viewport::serverCutText(const char* str) { char *buffer; - int size, ret; + size_t len; clearPendingClipboard(); if (!acceptClipboard) return; - size = fl_utf8froma(NULL, 0, str, strlen(str)); - if (size <= 0) - return; - - size++; - - buffer = new char[size]; + buffer = latin1ToUTF8(str); + len = strlen(buffer); - ret = fl_utf8froma(buffer, size, str, strlen(str)); - assert(ret < size); - - vlog.debug("Got clipboard data (%d bytes)", (int)strlen(buffer)); + vlog.debug("Got clipboard data (%d bytes)", (int)len); if (!hasFocus()) { pendingServerCutText = buffer; @@ -264,11 +256,11 @@ void Viewport::serverCutText(const char* str) // dump the data into both variants. #if !defined(WIN32) && !defined(__APPLE__) if (setPrimary) - Fl::copy(buffer, ret, 0); + Fl::copy(buffer, len, 0); #endif - Fl::copy(buffer, ret, 1); + Fl::copy(buffer, len, 1); - delete [] buffer; + strFree(buffer); } static const char * dotcursor_xpm[] = { @@ -550,27 +542,19 @@ void Viewport::resize(int x, int y, int w, int h) int Viewport::handle(int event) { char *buffer, *filtered; - int ret; int buttonMask, wheelMask; DownMap::const_iterator iter; switch (event) { case FL_PASTE: - buffer = new char[Fl::event_length() + 1]; - clearPendingClipboard(); - // This is documented as to ASCII, but actually does to 8859-1 - ret = fl_utf8toa(Fl::event_text(), Fl::event_length(), buffer, - Fl::event_length() + 1); - assert(ret < (Fl::event_length() + 1)); - filtered = convertLF(buffer, ret); - delete [] buffer; + buffer = utf8ToLatin1(Fl::event_text(), Fl::event_length()); + filtered = convertLF(buffer); + strFree(buffer); if (!hasFocus()) { - pendingClientCutText = new char[strlen(filtered) + 1]; - strcpy((char*)pendingClientCutText, filtered); - strFree(filtered); + pendingClientCutText = filtered; return 1; } @@ -747,9 +731,9 @@ void Viewport::handleClipboardChange(int source, void *data) void Viewport::clearPendingClipboard() { - delete [] pendingServerCutText; + strFree(pendingServerCutText); pendingServerCutText = NULL; - delete [] pendingClientCutText; + strFree(pendingClientCutText); pendingClientCutText = NULL; } diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h index c2c9872e..8b9b469b 100644 --- a/vncviewer/Viewport.h +++ b/vncviewer/Viewport.h @@ -114,8 +114,8 @@ private: bool firstLEDState; - const char* pendingServerCutText; - const char* pendingClientCutText; + char* pendingServerCutText; + char* pendingClientCutText; rdr::U32 menuKeySym; int menuKeyCode, menuKeyFLTK; -- cgit v1.2.3 From 615d16bd5ba11e89262cc5cfe94a35b6d6e7a628 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 3 May 2019 10:53:06 +0200 Subject: Improved clipboard API Change the internal clipboard API to use a request based model in order to be prepared for more advanced clipboard transfers. --- common/rfb/CConnection.cxx | 47 +++++- common/rfb/CConnection.h | 39 ++++- common/rfb/SConnection.cxx | 47 +++++- common/rfb/SConnection.h | 41 +++++- common/rfb/SDesktop.h | 20 +++ common/rfb/VNCSConnectionST.cxx | 78 +++++++--- common/rfb/VNCSConnectionST.h | 10 +- common/rfb/VNCServer.h | 19 ++- common/rfb/VNCServerST.cxx | 76 ++++++++-- common/rfb/VNCServerST.h | 14 +- unix/xserver/hw/vnc/XserverDesktop.cc | 54 +++++-- unix/xserver/hw/vnc/XserverDesktop.h | 10 +- unix/xserver/hw/vnc/vncExtInit.cc | 18 ++- unix/xserver/hw/vnc/vncExtInit.h | 6 +- unix/xserver/hw/vnc/vncSelection.c | 261 +++++++++++++++++++++++++++------- unix/xserver/hw/vnc/vncSelection.h | 4 +- vncviewer/CConn.cxx | 20 ++- vncviewer/CConn.h | 6 +- vncviewer/DesktopWindow.cxx | 22 ++- vncviewer/DesktopWindow.h | 8 +- vncviewer/Viewport.cxx | 143 +++++++++++-------- vncviewer/Viewport.h | 15 +- win/rfb_win32/Clipboard.cxx | 64 +++++---- win/rfb_win32/Clipboard.h | 5 +- win/rfb_win32/SDisplay.cxx | 24 +++- win/rfb_win32/SDisplay.h | 8 +- 26 files changed, 820 insertions(+), 239 deletions(-) (limited to 'unix') diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx index d6960dd2..ce2741e4 100644 --- a/common/rfb/CConnection.cxx +++ b/common/rfb/CConnection.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2017 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -52,7 +52,8 @@ CConnection::CConnection() formatChange(false), encodingChange(false), firstUpdate(true), pendingUpdate(false), continuousUpdates(false), forceNonincremental(true), - framebuffer(NULL), decoder(this) + framebuffer(NULL), decoder(this), + serverClipboard(NULL) { } @@ -65,6 +66,7 @@ CConnection::~CConnection() reader_ = 0; delete writer_; writer_ = 0; + strFree(serverClipboard); } void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_) @@ -463,6 +465,16 @@ void CConnection::dataRect(const Rect& r, int encoding) decoder.decodeRect(r, encoding, framebuffer); } +void CConnection::serverCutText(const char* str) +{ + strFree(serverClipboard); + serverClipboard = NULL; + + serverClipboard = strDup(str); + + handleClipboardAnnounce(true); +} + void CConnection::authSuccess() { } @@ -476,6 +488,37 @@ void CConnection::resizeFramebuffer() assert(false); } +void CConnection::handleClipboardRequest() +{ +} + +void CConnection::handleClipboardAnnounce(bool available) +{ +} + +void CConnection::handleClipboardData(const char* data) +{ +} + +void CConnection::requestClipboard() +{ + if (serverClipboard != NULL) { + handleClipboardData(serverClipboard); + return; + } +} + +void CConnection::announceClipboard(bool available) +{ + if (available) + handleClipboardRequest(); +} + +void CConnection::sendClipboardData(const char* data) +{ + writer()->writeClientCutText(data); +} + void CConnection::refreshFramebuffer() { forceNonincremental = true; diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h index 66a71a24..4106a1e6 100644 --- a/common/rfb/CConnection.h +++ b/common/rfb/CConnection.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2017 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -109,6 +109,8 @@ namespace rfb { virtual void framebufferUpdateEnd(); virtual void dataRect(const Rect& r, int encoding); + virtual void serverCutText(const char* str); + // Methods to be overridden in a derived class @@ -128,9 +130,42 @@ namespace rfb { // sure the pixel buffer has been updated once this call returns. virtual void resizeFramebuffer(); + // handleClipboardRequest() is called whenever the server requests + // the client to send over its clipboard data. It will only be + // called after the client has first announced a clipboard change + // via announceClipboard(). + virtual void handleClipboardRequest(); + + // handleClipboardAnnounce() is called to indicate a change in the + // clipboard on the server. Call requestClipboard() to access the + // actual data. + virtual void handleClipboardAnnounce(bool available); + + // handleClipboardData() is called when the server has sent over + // the clipboard data as a result of a previous call to + // requestClipboard(). Note that this function might never be + // called if the clipboard data was no longer available when the + // server received the request. + virtual void handleClipboardData(const char* data); + // Other methods + // requestClipboard() will result in a request to the server to + // transfer its clipboard data. A call to handleClipboardData() + // will be made once the data is available. + virtual void requestClipboard(); + + // announceClipboard() informs the server of changes to the + // clipboard on the client. The server may later request the + // clipboard data via handleClipboardRequest(). + virtual void announceClipboard(bool available); + + // sendClipboardData() transfers the clipboard data to the server + // and should be called whenever the server has requested the + // clipboard via handleClipboardRequest(). + virtual void sendClipboardData(const char* data); + // refreshFramebuffer() forces a complete refresh of the entire // framebuffer void refreshFramebuffer(); @@ -240,6 +275,8 @@ namespace rfb { ModifiablePixelBuffer* framebuffer; DecodeManager decoder; + + char* serverClipboard; }; } #endif diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index 4e224aa1..1cc330d8 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -52,7 +52,8 @@ SConnection::SConnection() : readyForSetColourMapEntries(false), is(0), os(0), reader_(0), writer_(0), ssecurity(0), state_(RFBSTATE_UNINITIALISED), - preferredEncoding(encodingRaw) + preferredEncoding(encodingRaw), + clientClipboard(NULL) { defaultMajorVersion = 3; defaultMinorVersion = 8; @@ -70,6 +71,7 @@ SConnection::~SConnection() reader_ = 0; delete writer_; writer_ = 0; + strFree(clientClipboard); } void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_) @@ -299,6 +301,16 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings) SMsgHandler::setEncodings(nEncodings, encodings); } +void SConnection::clientCutText(const char* str) +{ + strFree(clientClipboard); + clientClipboard = NULL; + + clientClipboard = strDup(str); + + handleClipboardAnnounce(true); +} + void SConnection::supportsQEMUKeyEvent() { writer()->writeQEMUKeyEvent(); @@ -410,6 +422,37 @@ void SConnection::enableContinuousUpdates(bool enable, { } +void SConnection::handleClipboardRequest() +{ +} + +void SConnection::handleClipboardAnnounce(bool available) +{ +} + +void SConnection::handleClipboardData(const char* data) +{ +} + +void SConnection::requestClipboard() +{ + if (clientClipboard != NULL) { + handleClipboardData(clientClipboard); + return; + } +} + +void SConnection::announceClipboard(bool available) +{ + if (available) + handleClipboardRequest(); +} + +void SConnection::sendClipboardData(const char* data) +{ + writer()->writeServerCutText(data); +} + void SConnection::writeFakeColourMap(void) { int i; diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index 31d1cb2e..6c80569d 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -80,8 +80,11 @@ namespace rfb { virtual void setEncodings(int nEncodings, const rdr::S32* encodings); + virtual void clientCutText(const char* str); + virtual void supportsQEMUKeyEvent(); + // Methods to be overridden in a derived class // versionReceived() indicates that the version number has just been read @@ -129,8 +132,42 @@ namespace rfb { virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h); + // handleClipboardRequest() is called whenever the client requests + // the server to send over its clipboard data. It will only be + // called after the server has first announced a clipboard change + // via announceClipboard(). + virtual void handleClipboardRequest(); + + // handleClipboardAnnounce() is called to indicate a change in the + // clipboard on the client. Call requestClipboard() to access the + // actual data. + virtual void handleClipboardAnnounce(bool available); + + // handleClipboardData() is called when the client has sent over + // the clipboard data as a result of a previous call to + // requestClipboard(). Note that this function might never be + // called if the clipboard data was no longer available when the + // client received the request. + virtual void handleClipboardData(const char* data); + + // Other methods + // requestClipboard() will result in a request to the client to + // transfer its clipboard data. A call to handleClipboardData() + // will be made once the data is available. + virtual void requestClipboard(); + + // announceClipboard() informs the client of changes to the + // clipboard on the server. The client may later request the + // clipboard data via handleClipboardRequest(). + virtual void announceClipboard(bool available); + + // sendClipboardData() transfers the clipboard data to the client + // and should be called whenever the client has requested the + // clipboard via handleClipboardRequest(). + virtual void sendClipboardData(const char* data); + // setAccessRights() allows a security package to limit the access rights // of a SConnection to the server. How the access rights are treated // is up to the derived class. @@ -208,6 +245,8 @@ namespace rfb { stateEnum state_; rdr::S32 preferredEncoding; AccessRights accessRights; + + char* clientClipboard; }; } #endif diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index 0060aa23..b4104ea7 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2019 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 @@ -93,6 +94,25 @@ namespace rfb { // pointerEvent(), keyEvent() and clientCutText() are called in response to // the relevant RFB protocol messages from clients. // See InputHandler for method signatures. + + // handleClipboardRequest() is called whenever a client requests + // the server to send over its clipboard data. It will only be + // called after the server has first announced a clipboard change + // via VNCServer::announceClipboard(). + virtual void handleClipboardRequest() {} + + // handleClipboardAnnounce() is called to indicate a change in the + // clipboard on a client. Call VNCServer::requestClipboard() to + // access the actual data. + virtual void handleClipboardAnnounce(bool __unused_attr available) {} + + // handleClipboardData() is called when a client has sent over + // the clipboard data as a result of a previous call to + // VNCServer::requestClipboard(). Note that this function might + // never be called if the clipboard data was no longer available + // when the client received the request. + virtual void handleClipboardData(const char* __unused_attr data) {} + protected: virtual ~SDesktop() {} }; diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 002ae925..cdd87b13 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2018 Pierre Ossman for Cendio AB + * Copyright 2009-2019 Pierre Ossman for Cendio AB * Copyright 2018 Peter Astrand for Cendio AB * * This is free software; you can redistribute it and/or modify @@ -282,51 +282,71 @@ void VNCSConnectionST::bellOrClose() } } -void VNCSConnectionST::serverCutTextOrClose(const char *str) +void VNCSConnectionST::setDesktopNameOrClose(const char *name) { try { - if (!accessCheck(AccessCutText)) return; - if (!rfb::Server::sendCutText) return; - if (state() == RFBSTATE_NORMAL) - writer()->writeServerCutText(str); + setDesktopName(name); + writeFramebufferUpdate(); } catch(rdr::Exception& e) { close(e.str()); } } - -void VNCSConnectionST::setDesktopNameOrClose(const char *name) +void VNCSConnectionST::setCursorOrClose() { try { - setDesktopName(name); + setCursor(); writeFramebufferUpdate(); } catch(rdr::Exception& e) { close(e.str()); } } - -void VNCSConnectionST::setCursorOrClose() +void VNCSConnectionST::setLEDStateOrClose(unsigned int state) { try { - setCursor(); + setLEDState(state); writeFramebufferUpdate(); } catch(rdr::Exception& e) { close(e.str()); } } +void VNCSConnectionST::requestClipboardOrClose() +{ + try { + if (!accessCheck(AccessCutText)) return; + if (!rfb::Server::acceptCutText) return; + if (state() != RFBSTATE_NORMAL) return; + requestClipboard(); + } catch(rdr::Exception& e) { + close(e.str()); + } +} -void VNCSConnectionST::setLEDStateOrClose(unsigned int state) +void VNCSConnectionST::announceClipboardOrClose(bool available) { try { - setLEDState(state); - writeFramebufferUpdate(); + if (!accessCheck(AccessCutText)) return; + if (!rfb::Server::sendCutText) return; + if (state() != RFBSTATE_NORMAL) return; + announceClipboard(available); } catch(rdr::Exception& e) { close(e.str()); } } +void VNCSConnectionST::sendClipboardDataOrClose(const char* data) +{ + try { + if (!accessCheck(AccessCutText)) return; + if (!rfb::Server::sendCutText) return; + if (state() != RFBSTATE_NORMAL) return; + sendClipboardData(data); + } catch(rdr::Exception& e) { + close(e.str()); + } +} bool VNCSConnectionST::getComparerState() { @@ -596,13 +616,6 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { server->keyEvent(keysym, keycode, down); } -void VNCSConnectionST::clientCutText(const char* str) -{ - if (!accessCheck(AccessCutText)) return; - if (!rfb::Server::acceptCutText) return; - server->clientCutText(str); -} - void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) { Rect safeRect; @@ -719,6 +732,26 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable, } } +void VNCSConnectionST::handleClipboardRequest() +{ + if (!accessCheck(AccessCutText)) return; + server->handleClipboardRequest(this); +} + +void VNCSConnectionST::handleClipboardAnnounce(bool available) +{ + if (!accessCheck(AccessCutText)) return; + if (!rfb::Server::acceptCutText) return; + server->handleClipboardAnnounce(this, available); +} + +void VNCSConnectionST::handleClipboardData(const char* data) +{ + if (!accessCheck(AccessCutText)) return; + if (!rfb::Server::acceptCutText) return; + server->handleClipboardData(this, data); +} + // supportsLocalCursor() is called whenever the status of // client.supportsLocalCursor() has changed. If the client does now support local // cursor, we make sure that the old server-side rendered cursor is cleaned up @@ -756,7 +789,6 @@ void VNCSConnectionST::supportsLEDState() writer()->writeLEDState(); } - bool VNCSConnectionST::handleTimeout(Timer* t) { try { diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 54266e3c..c8f4c24f 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2016 Pierre Ossman for Cendio AB + * Copyright 2009-2019 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 @@ -72,10 +72,12 @@ namespace rfb { void screenLayoutChangeOrClose(rdr::U16 reason); void setCursorOrClose(); void bellOrClose(); - void serverCutTextOrClose(const char *str); void setDesktopNameOrClose(const char *name); void setLEDStateOrClose(unsigned int state); void approveConnectionOrClose(bool accept, const char* reason); + void requestClipboardOrClose(); + void announceClipboardOrClose(bool available); + void sendClipboardDataOrClose(const char* data); // The following methods never throw exceptions @@ -115,13 +117,15 @@ namespace rfb { virtual void setPixelFormat(const PixelFormat& pf); virtual void pointerEvent(const Point& pos, int buttonMask); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); - virtual void clientCutText(const char* str); virtual void framebufferUpdateRequest(const Rect& r, bool incremental); virtual void setDesktopSize(int fb_width, int fb_height, const ScreenSet& layout); virtual void fence(rdr::U32 flags, unsigned len, const char data[]); virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h); + virtual void handleClipboardRequest(); + virtual void handleClipboardAnnounce(bool available); + virtual void handleClipboardData(const char* data); virtual void supportsLocalCursor(); virtual void supportsFence(); virtual void supportsContinuousUpdates(); diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 5398e9fd..5d04da53 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2019 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 @@ -55,9 +56,21 @@ namespace rfb { // getPixelBuffer() returns a pointer to the PixelBuffer object. virtual const PixelBuffer* getPixelBuffer() const = 0; - // serverCutText() tells the server that the cut text has changed. This - // will normally be sent to all clients. - virtual void serverCutText(const char* str) = 0; + // requestClipboard() will result in a request to a client to + // transfer its clipboard data. A call to + // SDesktop::handleClipboardData() will be made once the data is + // available. + virtual void requestClipboard() = 0; + + // announceClipboard() informs all clients of changes to the + // clipboard on the server. A client may later request the + // clipboard data via SDesktop::handleClipboardRequest(). + virtual void announceClipboard(bool available) = 0; + + // sendClipboardData() transfers the clipboard data to a client + // and should be called whenever a client has requested the + // clipboard via SDesktop::handleClipboardRequest(). + virtual void sendClipboardData(const char* data) = 0; // bell() tells the server that it should make all clients make a bell sound. virtual void bell() = 0; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 21af0dac..a3655bca 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-2018 Pierre Ossman for Cendio AB + * Copyright 2009-2019 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 @@ -77,8 +77,8 @@ static LogWriter connectionsLog("Connections"); VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), blockCounter(0), pb(0), ledState(ledUnknown), - name(strDup(name_)), pointerClient(0), comparer(0), - cursor(new Cursor(0, 0, Point(), NULL)), + name(strDup(name_)), pointerClient(0), clipboardClient(0), + comparer(0), cursor(new Cursor(0, 0, Point(), NULL)), renderedCursorInvalid(false), keyRemapper(&KeyRemapper::defInstance), idleTimer(this), disconnectTimer(this), connectTimer(this), @@ -167,9 +167,12 @@ void VNCServerST::removeSocket(network::Socket* sock) { if ((*ci)->getSock() == sock) { clients.remove(*ci); - // - Release the cursor if this client owns it + // - Remove any references to it if (pointerClient == *ci) pointerClient = NULL; + if (clipboardClient == *ci) + clipboardClient = NULL; + clipboardRequestors.remove(*ci); // Adjust the exit timers connectTimer.stop(); @@ -331,23 +334,51 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout) } } -void VNCServerST::bell() +void VNCServerST::requestClipboard() +{ + if (clipboardClient == NULL) + return; + + clipboardClient->requestClipboard(); +} + +void VNCServerST::announceClipboard(bool available) { std::list::iterator ci, ci_next; + + if (available) + clipboardClient = NULL; + + clipboardRequestors.clear(); + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { ci_next = ci; ci_next++; - (*ci)->bellOrClose(); + (*ci)->announceClipboard(available); } } -void VNCServerST::serverCutText(const char* str) +void VNCServerST::sendClipboardData(const char* data) { - if (strchr(str, '\r') != NULL) + std::list::iterator ci, ci_next; + + if (strchr(data, '\r') != NULL) throw Exception("Invalid carriage return in clipboard data"); + + for (ci = clipboardRequestors.begin(); + ci != clipboardRequestors.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->sendClipboardData(data); + } + + clipboardRequestors.clear(); +} + +void VNCServerST::bell() +{ std::list::iterator ci, ci_next; for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { ci_next = ci; ci_next++; - (*ci)->serverCutTextOrClose(str); + (*ci)->bellOrClose(); } } @@ -461,9 +492,32 @@ void VNCServerST::pointerEvent(VNCSConnectionST* client, desktop->pointerEvent(pos, buttonMask); } -void VNCServerST::clientCutText(const char* str) +void VNCServerST::handleClipboardRequest(VNCSConnectionST* client) { - desktop->clientCutText(str); + clipboardRequestors.push_back(client); + if (clipboardRequestors.size() == 1) + desktop->handleClipboardRequest(); +} + +void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, + bool available) +{ + if (available) + clipboardClient = client; + else { + if (client != clipboardClient) + return; + clipboardClient = NULL; + } + desktop->handleClipboardAnnounce(available); +} + +void VNCServerST::handleClipboardData(VNCSConnectionST* client, + const char* data) +{ + if (client != clipboardClient) + return; + desktop->handleClipboardData(data); } unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester, diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 5231977d..fd20cc37 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2016 Pierre Ossman for Cendio AB + * Copyright 2009-2019 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 @@ -85,7 +85,10 @@ namespace rfb { virtual void setPixelBuffer(PixelBuffer* pb); virtual void setScreenLayout(const ScreenSet& layout); virtual const PixelBuffer* getPixelBuffer() const { return pb; } - virtual void serverCutText(const char* str); + + virtual void requestClipboard(); + virtual void announceClipboard(bool available); + virtual void sendClipboardData(const char* data); virtual void approveConnection(network::Socket* sock, bool accept, const char* reason); @@ -115,7 +118,10 @@ namespace rfb { // Event handlers void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); void pointerEvent(VNCSConnectionST* client, const Point& pos, int buttonMask); - void clientCutText(const char* str); + + void handleClipboardRequest(VNCSConnectionST* client); + void handleClipboardAnnounce(VNCSConnectionST* client, bool available); + void handleClipboardData(VNCSConnectionST* client, const char* data); unsigned int setDesktopSize(VNCSConnectionST* requester, int fb_width, int fb_height, @@ -181,6 +187,8 @@ namespace rfb { std::list clients; VNCSConnectionST* pointerClient; + VNCSConnectionST* clipboardClient; + std::list clipboardRequestors; std::list closingSockets; ComparingUpdateTracker* comparer; diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index cd9b1f9b..4edffec7 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-2017 Pierre Ossman for Cendio AB + * Copyright 2009-2019 Pierre Ossman for Cendio AB * Copyright 2014 Brian P. Hinz * * This is free software; you can redistribute it and/or modify @@ -182,25 +182,43 @@ void XserverDesktop::queryConnection(network::Socket* sock, queryConnectTimer.start(queryConnectTimeout * 1000); } -void XserverDesktop::bell() +void XserverDesktop::requestClipboard() { - server->bell(); + try { + server->requestClipboard(); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::requestClipboard: %s",e.str()); + } } -void XserverDesktop::setLEDState(unsigned int state) +void XserverDesktop::announceClipboard(bool available) { - server->setLEDState(state); + try { + server->announceClipboard(available); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::announceClipboard: %s",e.str()); + } } -void XserverDesktop::serverCutText(const char* str) +void XserverDesktop::sendClipboardData(const char* data) { try { - server->serverCutText(str); + server->sendClipboardData(data); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::serverCutText: %s",e.str()); + vlog.error("XserverDesktop::sendClipboardData: %s",e.str()); } } +void XserverDesktop::bell() +{ + server->bell(); +} + +void XserverDesktop::setLEDState(unsigned int state) +{ + server->setLEDState(state); +} + void XserverDesktop::setDesktopName(const char* name) { try { @@ -436,11 +454,6 @@ void XserverDesktop::pointerEvent(const Point& pos, int buttonMask) vncPointerButtonAction(buttonMask); } -void XserverDesktop::clientCutText(const char* str) -{ - vncClientCutText(str); -} - unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout) { @@ -462,6 +475,21 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, return result; } +void XserverDesktop::handleClipboardRequest() +{ + vncHandleClipboardRequest(); +} + +void XserverDesktop::handleClipboardAnnounce(bool available) +{ + vncHandleClipboardAnnounce(available); +} + +void XserverDesktop::handleClipboardData(const char* data_) +{ + vncHandleClipboardData(data_); +} + void XserverDesktop::grabRegion(const rfb::Region& region) { if (directFbptr) diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index c6c4eb10..6c670689 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-2015 Pierre Ossman for Cendio AB + * Copyright 2009-2019 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,9 +59,11 @@ public: void unblockUpdates(); void setFramebuffer(int w, int h, void* fbptr, int stride); void refreshScreenLayout(); + void requestClipboard(); + void announceClipboard(bool available); + void sendClipboardData(const char* data); void bell(); void setLEDState(unsigned int state); - void serverCutText(const char* str); void setDesktopName(const char* name); void setCursor(int width, int height, int hotX, int hotY, const unsigned char *rgbaData); @@ -92,9 +94,11 @@ public: const char* userName); virtual void pointerEvent(const rfb::Point& pos, int buttonMask); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); - virtual void clientCutText(const char* str); virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout); + virtual void handleClipboardRequest(); + virtual void handleClipboardAnnounce(bool available); + virtual void handleClipboardData(const char* data); // rfb::PixelBuffer callbacks virtual void grabRegion(const rfb::Region& r); diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 10143f6f..6ab306b1 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-2015 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -285,10 +285,22 @@ void vncUpdateDesktopName(void) desktop[scr]->setDesktopName(desktopName); } -void vncServerCutText(const char *text) +void vncRequestClipboard(void) { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->serverCutText(text); + desktop[scr]->requestClipboard(); +} + +void vncAnnounceClipboard(int available) +{ + for (int scr = 0; scr < vncGetScreenCount(); scr++) + desktop[scr]->announceClipboard(available); +} + +void vncSendClipboardData(const char* data) +{ + for (int scr = 0; scr < vncGetScreenCount(); scr++) + desktop[scr]->sendClipboardData(data); } int vncConnectClient(const char *addr) diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index bdcce83a..1fb87c19 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-2015 Pierre Ossman for Cendio AB + * Copyright 2011-2019 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 @@ -53,7 +53,9 @@ int vncGetSendPrimary(void); void vncUpdateDesktopName(void); -void vncServerCutText(const char *text); +void vncRequestClipboard(void); +void vncAnnounceClipboard(int available); +void vncSendClipboardData(const char* data); int vncConnectClient(const char *addr); diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 3438ac86..4fac50e2 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -47,14 +47,34 @@ static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING; static WindowPtr pWindow; static Window wid; -static char* clientCutText; +static Bool probing; +static Atom activeSelection = None; + +struct VncDataTarget { + ClientPtr client; + Atom selection; + Atom target; + Atom property; + Window requestor; + CARD32 time; + struct VncDataTarget* next; +}; + +static struct VncDataTarget* vncDataTargetHead; static int vncCreateSelectionWindow(void); static int vncOwnSelection(Atom selection); +static int vncConvertSelection(ClientPtr client, Atom selection, + Atom target, Atom property, + Window requestor, CARD32 time, + const char* data); static int vncProcConvertSelection(ClientPtr client); +static void vncSelectionRequest(Atom selection, Atom target); static int vncProcSendEvent(ClientPtr client); static void vncSelectionCallback(CallbackListPtr *callbacks, void * data, void * args); +static void vncClientStateCallback(CallbackListPtr * l, + void * d, void * p); static int (*origProcConvertSelection)(ClientPtr); static int (*origProcSendEvent)(ClientPtr); @@ -79,31 +99,99 @@ void vncSelectionInit(void) if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0)) FatalError("Add VNC SelectionCallback failed\n"); + if (!AddCallback(&ClientStateCallback, vncClientStateCallback, 0)) + FatalError("Add VNC ClientStateCallback failed\n"); } -void vncClientCutText(const char* str) +void vncHandleClipboardRequest(void) { - int rc; - - if (clientCutText != NULL) - free(clientCutText); - - clientCutText = strdup(str); - if (clientCutText == NULL) { - LOG_ERROR("Could not allocate clipboard buffer"); - DeleteWindowFromAnySelections(pWindow); + if (activeSelection == None) { + LOG_DEBUG("Got request for local clipboard although no clipboard is active"); return; } - if (vncGetSetPrimary()) { - rc = vncOwnSelection(xaPRIMARY); + LOG_DEBUG("Got request for local clipboard, re-probing formats"); + + probing = FALSE; + vncSelectionRequest(activeSelection, xaTARGETS); +} + +void vncHandleClipboardAnnounce(int available) +{ + if (available) { + int rc; + + LOG_DEBUG("Remote clipboard announced, grabbing local ownership"); + + if (vncGetSetPrimary()) { + rc = vncOwnSelection(xaPRIMARY); + if (rc != Success) + LOG_ERROR("Could not set PRIMARY selection"); + } + + rc = vncOwnSelection(xaCLIPBOARD); if (rc != Success) - LOG_ERROR("Could not set PRIMARY selection"); + LOG_ERROR("Could not set CLIPBOARD selection"); + } else { + struct VncDataTarget* next; + + if (pWindow == NULL) + return; + + LOG_DEBUG("Remote clipboard lost, removing local ownership"); + + DeleteWindowFromAnySelections(pWindow); + + /* Abort any pending transfer */ + while (vncDataTargetHead != NULL) { + xEvent event; + + event.u.u.type = SelectionNotify; + event.u.selectionNotify.time = vncDataTargetHead->time; + event.u.selectionNotify.requestor = vncDataTargetHead->requestor; + event.u.selectionNotify.selection = vncDataTargetHead->selection; + event.u.selectionNotify.target = vncDataTargetHead->target; + event.u.selectionNotify.property = None; + WriteEventsToClient(vncDataTargetHead->client, 1, &event); + + next = vncDataTargetHead->next; + free(vncDataTargetHead); + vncDataTargetHead = next; + } } +} - rc = vncOwnSelection(xaCLIPBOARD); - if (rc != Success) - LOG_ERROR("Could not set CLIPBOARD selection"); +void vncHandleClipboardData(const char* data) +{ + struct VncDataTarget* next; + + LOG_DEBUG("Got remote clipboard data, sending to X11 clients"); + + while (vncDataTargetHead != NULL) { + int rc; + xEvent event; + + rc = vncConvertSelection(vncDataTargetHead->client, + vncDataTargetHead->selection, + vncDataTargetHead->target, + vncDataTargetHead->property, + vncDataTargetHead->requestor, + vncDataTargetHead->time, + data); + if (rc != Success) { + event.u.u.type = SelectionNotify; + event.u.selectionNotify.time = vncDataTargetHead->time; + event.u.selectionNotify.requestor = vncDataTargetHead->requestor; + event.u.selectionNotify.selection = vncDataTargetHead->selection; + event.u.selectionNotify.target = vncDataTargetHead->target; + event.u.selectionNotify.property = None; + WriteEventsToClient(vncDataTargetHead->client, 1, &event); + } + + next = vncDataTargetHead->next; + free(vncDataTargetHead); + vncDataTargetHead = next; + } } static int vncCreateSelectionWindow(void) @@ -191,7 +279,8 @@ static int vncOwnSelection(Atom selection) static int vncConvertSelection(ClientPtr client, Atom selection, Atom target, Atom property, - Window requestor, CARD32 time) + Window requestor, CARD32 time, + const char* data) { Selection *pSel; WindowPtr pWin; @@ -201,8 +290,13 @@ static int vncConvertSelection(ClientPtr client, Atom selection, xEvent event; - LOG_DEBUG("Selection request for %s (type %s)", - NameForAtom(selection), NameForAtom(target)); + if (data == NULL) { + LOG_DEBUG("Selection request for %s (type %s)", + NameForAtom(selection), NameForAtom(target)); + } else { + LOG_DEBUG("Sending data for selection request for %s (type %s)", + NameForAtom(selection), NameForAtom(target)); + } rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess); if (rc != Success) @@ -239,28 +333,58 @@ static int vncConvertSelection(ClientPtr client, Atom selection, TRUE); if (rc != Success) return rc; - } else if ((target == xaSTRING) || (target == xaTEXT)) { - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, - XA_STRING, 8, PropModeReplace, - strlen(clientCutText), clientCutText, - TRUE); - if (rc != Success) - return rc; - } else if (target == xaUTF8_STRING) { - char* buffer; - - buffer = vncLatin1ToUTF8(clientCutText, (size_t)-1); - if (buffer == NULL) - return BadAlloc; - - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, - xaUTF8_STRING, 8, PropModeReplace, - strlen(buffer), buffer, TRUE); - vncStrFree(buffer); - if (rc != Success) - return rc; } else { - return BadMatch; + if (data == NULL) { + struct VncDataTarget* vdt; + + if ((target != xaSTRING) && (target != xaTEXT) && + (target != xaUTF8_STRING)) + return BadMatch; + + vdt = calloc(1, sizeof(struct VncDataTarget)); + if (vdt == NULL) + return BadAlloc; + + vdt->client = client; + vdt->selection = selection; + vdt->target = target; + vdt->property = property; + vdt->requestor = requestor; + vdt->time = time; + + vdt->next = vncDataTargetHead; + vncDataTargetHead = vdt; + + LOG_DEBUG("Requesting clipboard data from client"); + + vncRequestClipboard(); + + return Success; + } else { + if ((target == xaSTRING) || (target == xaTEXT)) { + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + XA_STRING, 8, PropModeReplace, + strlen(data), (char*)data, + TRUE); + if (rc != Success) + return rc; + } else if (target == xaUTF8_STRING) { + char* buffer; + + buffer = vncLatin1ToUTF8(data, (size_t)-1); + if (buffer == NULL) + return BadAlloc; + + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + xaUTF8_STRING, 8, PropModeReplace, + strlen(buffer), buffer, TRUE); + vncStrFree(buffer); + if (rc != Success) + return rc; + } else { + return BadMatch; + } + } } event.u.u.type = SelectionNotify; @@ -299,7 +423,7 @@ static int vncProcConvertSelection(ClientPtr client) pSel->window == wid) { rc = vncConvertSelection(client, stuff->selection, stuff->target, stuff->property, - stuff->requestor, stuff->time); + stuff->requestor, stuff->time, NULL); if (rc != Success) { xEvent event; @@ -383,10 +507,19 @@ static void vncHandleSelection(Atom selection, Atom target, if (prop->type != XA_ATOM) return; - if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size)) - vncSelectionRequest(selection, xaSTRING); - else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) - vncSelectionRequest(selection, xaUTF8_STRING); + if (probing) { + if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) || + vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) { + LOG_DEBUG("Compatible format found, notifying clients"); + activeSelection = selection; + vncAnnounceClipboard(TRUE); + } + } else { + if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size)) + vncSelectionRequest(selection, xaSTRING); + else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) + vncSelectionRequest(selection, xaUTF8_STRING); + } } else if (target == xaSTRING) { char* filtered; @@ -399,7 +532,10 @@ static void vncHandleSelection(Atom selection, Atom target, if (filtered == NULL) return; - vncServerCutText(filtered); + LOG_DEBUG("Sending clipboard to clients (%d bytes)", + (int)strlen(filtered)); + + vncSendClipboardData(filtered); vncStrFree(filtered); } else if (target == xaUTF8_STRING) { @@ -420,7 +556,10 @@ static void vncHandleSelection(Atom selection, Atom target, if (filtered == NULL) return; - vncServerCutText(filtered); + LOG_DEBUG("Sending clipboard to clients (%d bytes)", + (int)strlen(filtered)); + + vncSendClipboardData(filtered); vncStrFree(filtered); } @@ -454,6 +593,12 @@ static void vncSelectionCallback(CallbackListPtr *callbacks, { SelectionInfoRec *info = (SelectionInfoRec *) args; + if (info->selection->selection == activeSelection) { + LOG_DEBUG("Local clipboard lost, notifying clients"); + activeSelection = None; + vncAnnounceClipboard(FALSE); + } + if (info->kind != SelectionSetOwner) return; if (info->client == serverClient) @@ -470,5 +615,25 @@ static void vncSelectionCallback(CallbackListPtr *callbacks, !vncGetSendPrimary()) return; + LOG_DEBUG("Got clipboard notification, probing for formats"); + + probing = TRUE; vncSelectionRequest(info->selection->selection, xaTARGETS); } + +static void vncClientStateCallback(CallbackListPtr * l, + void * d, void * p) +{ + ClientPtr client = ((NewClientInfoRec*)p)->client; + if (client->clientState == ClientStateGone) { + struct VncDataTarget** nextPtr = &vncDataTargetHead; + for (struct VncDataTarget* cur = vncDataTargetHead; cur; cur = *nextPtr) { + if (cur->client == client) { + *nextPtr = cur->next; + free(cur); + continue; + } + nextPtr = &cur->next; + } + } +} diff --git a/unix/xserver/hw/vnc/vncSelection.h b/unix/xserver/hw/vnc/vncSelection.h index e11d4a49..ea52bf23 100644 --- a/unix/xserver/hw/vnc/vncSelection.h +++ b/unix/xserver/hw/vnc/vncSelection.h @@ -24,7 +24,9 @@ extern "C" { void vncSelectionInit(void); -void vncClientCutText(const char* str); +void vncHandleClipboardRequest(void); +void vncHandleClipboardAnnounce(int available); +void vncHandleClipboardData(const char* data); #ifdef __cplusplus } diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index 2cc1fe4c..6ba3276b 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -377,11 +377,6 @@ void CConn::bell() fl_beep(); } -void CConn::serverCutText(const char* str) -{ - desktop->serverCutText(str); -} - void CConn::dataRect(const Rect& r, int encoding) { sock->inStream().startTiming(); @@ -422,6 +417,21 @@ void CConn::setLEDState(unsigned int state) desktop->setLEDState(state); } +void CConn::handleClipboardRequest() +{ + desktop->handleClipboardRequest(); +} + +void CConn::handleClipboardAnnounce(bool available) +{ + desktop->handleClipboardAnnounce(available); +} + +void CConn::handleClipboardData(const char* data) +{ + desktop->handleClipboardData(data); +} + ////////////////////// Internal methods ////////////////////// diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h index 38e09c25..4d935c96 100644 --- a/vncviewer/CConn.h +++ b/vncviewer/CConn.h @@ -61,8 +61,6 @@ public: void bell(); - void serverCutText(const char* str); - void framebufferUpdateStart(); void framebufferUpdateEnd(); void dataRect(const rfb::Rect& r, int encoding); @@ -74,6 +72,10 @@ public: void setLEDState(unsigned int state); + virtual void handleClipboardRequest(); + virtual void handleClipboardAnnounce(bool available); + virtual void handleClipboardData(const char* data); + private: void resizeFramebuffer(); diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 4429e775..4860b928 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -276,12 +276,6 @@ void DesktopWindow::resizeFramebuffer(int new_w, int new_h) } -void DesktopWindow::serverCutText(const char* str) -{ - viewport->serverCutText(str); -} - - void DesktopWindow::setCursor(int width, int height, const rfb::Point& hotspot, const rdr::U8* data) @@ -420,6 +414,22 @@ void DesktopWindow::setLEDState(unsigned int state) } +void DesktopWindow::handleClipboardRequest() +{ + viewport->handleClipboardRequest(); +} + +void DesktopWindow::handleClipboardAnnounce(bool available) +{ + viewport->handleClipboardAnnounce(available); +} + +void DesktopWindow::handleClipboardData(const char* data) +{ + viewport->handleClipboardData(data); +} + + void DesktopWindow::resize(int x, int y, int w, int h) { bool resizing; diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h index fe938d9b..6b03325c 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -62,9 +62,6 @@ public: // Resize the current framebuffer, but retain the contents void resizeFramebuffer(int new_w, int new_h); - // Incoming clipboard from server - void serverCutText(const char* str); - // New image for the locally rendered cursor void setCursor(int width, int height, const rfb::Point& hotspot, const rdr::U8* data); @@ -72,6 +69,11 @@ public: // Change client LED state void setLEDState(unsigned int state); + // Clipboard events + void handleClipboardRequest(); + void handleClipboardAnnounce(bool available); + void handleClipboardData(const char* data); + // Fl_Window callback methods void draw(); void resize(int x, int y, int w, int h); diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 151ecb47..713d3648 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -119,7 +119,7 @@ Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_) altGrArmed(false), #endif firstLEDState(true), - pendingServerCutText(NULL), pendingClientCutText(NULL), + pendingServerClipboard(false), pendingClientClipboard(false), menuCtrlKey(false), menuAltKey(false), cursor(NULL) { #if !defined(WIN32) && !defined(__APPLE__) @@ -208,8 +208,6 @@ Viewport::~Viewport() delete cursor; } - clearPendingClipboard(); - // FLTK automatically deletes all child widgets, so we shouldn't touch // them ourselves here } @@ -232,37 +230,6 @@ void Viewport::updateWindow() damage(FL_DAMAGE_USER1, r.tl.x + x(), r.tl.y + y(), r.width(), r.height()); } -void Viewport::serverCutText(const char* str) -{ - char *buffer; - size_t len; - - clearPendingClipboard(); - - if (!acceptClipboard) - return; - - buffer = latin1ToUTF8(str); - len = strlen(buffer); - - vlog.debug("Got clipboard data (%d bytes)", (int)len); - - if (!hasFocus()) { - pendingServerCutText = buffer; - return; - } - - // RFB doesn't have separate selection and clipboard concepts, so we - // dump the data into both variants. -#if !defined(WIN32) && !defined(__APPLE__) - if (setPrimary) - Fl::copy(buffer, len, 0); -#endif - Fl::copy(buffer, len, 1); - - strFree(buffer); -} - static const char * dotcursor_xpm[] = { "5 5 2 1", ". c #000000", @@ -311,6 +278,59 @@ void Viewport::setCursor(int width, int height, const Point& hotspot, window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y); } +void Viewport::handleClipboardRequest() +{ + Fl::paste(*this, clipboardSource); +} + +void Viewport::handleClipboardAnnounce(bool available) +{ + if (!acceptClipboard) + return; + + if (available) + vlog.debug("Got notification of new clipboard on server"); + else + vlog.debug("Clipboard is no longer available on server"); + + if (!available) { + pendingServerClipboard = false; + return; + } + + pendingClientClipboard = false; + + if (!hasFocus()) { + pendingServerClipboard = true; + return; + } + + cc->requestClipboard(); +} + +void Viewport::handleClipboardData(const char* data) +{ + char* buffer; + size_t len; + + if (!hasFocus()) + return; + + buffer = latin1ToUTF8(data); + len = strlen(buffer); + + vlog.debug("Got clipboard data (%d bytes)", (int)len); + + // RFB doesn't have separate selection and clipboard concepts, so we + // dump the data into both variants. +#if !defined(WIN32) && !defined(__APPLE__) + if (setPrimary) + Fl::copy(buffer, len, 0); +#endif + Fl::copy(buffer, len, 1); + + strFree(buffer); +} void Viewport::setLEDState(unsigned int state) { @@ -547,21 +567,14 @@ int Viewport::handle(int event) switch (event) { case FL_PASTE: - clearPendingClipboard(); - buffer = utf8ToLatin1(Fl::event_text(), Fl::event_length()); filtered = convertLF(buffer); strFree(buffer); - if (!hasFocus()) { - pendingClientCutText = filtered; - return 1; - } - vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered)); try { - cc->writer()->writeClientCutText(filtered); + cc->sendClipboardData(filtered); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); exit_vncviewer(e.str()); @@ -725,41 +738,47 @@ void Viewport::handleClipboardChange(int source, void *data) return; #endif - Fl::paste(*self, source); -} + self->clipboardSource = source; + self->pendingServerClipboard = false; -void Viewport::clearPendingClipboard() -{ - strFree(pendingServerCutText); - pendingServerCutText = NULL; - strFree(pendingClientCutText); - pendingClientCutText = NULL; + if (!self->hasFocus()) { + self->pendingClientClipboard = true; + // Clear any older client clipboard from the server + self->cc->announceClipboard(false); + return; + } + + try { + self->cc->announceClipboard(true); + } catch (rdr::Exception& e) { + vlog.error("%s", e.str()); + exit_vncviewer(e.str()); + } } void Viewport::flushPendingClipboard() { - if (pendingServerCutText) { - size_t len = strlen(pendingServerCutText); -#if !defined(WIN32) && !defined(__APPLE__) - if (setPrimary) - Fl::copy(pendingServerCutText, len, 0); -#endif - Fl::copy(pendingServerCutText, len, 1); + if (pendingServerClipboard) { + try { + cc->requestClipboard(); + } catch (rdr::Exception& e) { + vlog.error("%s", e.str()); + exit_vncviewer(e.str()); + } } - if (pendingClientCutText) { - size_t len = strlen(pendingClientCutText); - vlog.debug("Sending pending clipboard data (%d bytes)", (int)len); + if (pendingClientClipboard) { try { - cc->writer()->writeClientCutText(pendingClientCutText); + cc->announceClipboard(true); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); exit_vncviewer(e.str()); } } - clearPendingClipboard(); + pendingServerClipboard = false; + pendingClientClipboard = false; } diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h index 8b9b469b..1fb93c66 100644 --- a/vncviewer/Viewport.h +++ b/vncviewer/Viewport.h @@ -45,9 +45,6 @@ public: // Flush updates to screen void updateWindow(); - // Incoming clipboard from server - void serverCutText(const char* str); - // New image for the locally rendered cursor void setCursor(int width, int height, const rfb::Point& hotspot, const rdr::U8* data); @@ -57,6 +54,11 @@ public: void draw(Surface* dst); + // Clipboard events + void handleClipboardRequest(); + void handleClipboardAnnounce(bool available); + void handleClipboardData(const char* data); + // Fl_Widget callback methods void draw(); @@ -72,7 +74,6 @@ private: static void handleClipboardChange(int source, void *data); - void clearPendingClipboard(); void flushPendingClipboard(); void handlePointerEvent(const rfb::Point& pos, int buttonMask); @@ -114,8 +115,10 @@ private: bool firstLEDState; - char* pendingServerCutText; - char* pendingClientCutText; + bool pendingServerClipboard; + bool pendingClientClipboard; + + int clipboardSource; rdr::U32 menuKeySym; int menuKeyCode, menuKeyFLTK; diff --git a/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx index 49526956..306cfbad 100644 --- a/win/rfb_win32/Clipboard.cxx +++ b/win/rfb_win32/Clipboard.cxx @@ -103,32 +103,10 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { } else { vlog.debug("local clipboard changed by %p", owner); - // Open the clipboard - if (OpenClipboard(getHandle())) { - // Get the clipboard data - HGLOBAL cliphandle = GetClipboardData(CF_TEXT); - if (cliphandle) { - char* clipdata = (char*) GlobalLock(cliphandle); - - // Notify clients - if (notifier) { - if (!clipdata) { - notifier->notifyClipboardChanged(0); - } else { - CharArray unix_text(convertLF(clipdata, strlen(clipdata))); - removeNonISOLatin1Chars(unix_text.buf); - notifier->notifyClipboardChanged(unix_text.buf); - } - } else { - vlog.debug("no clipboard notifier registered"); - } - - // Release the buffer and close the clipboard - GlobalUnlock(cliphandle); - } - - CloseClipboard(); - } + if (notifier == NULL) + vlog.debug("no clipboard notifier registered"); + else + notifier->notifyClipboardChanged(IsClipboardFormatAvailable(CF_TEXT)); } } if (next_window) @@ -139,6 +117,40 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { return MsgWindow::processMessage(msg, wParam, lParam); }; +char* +Clipboard::getClipText() { + HGLOBAL cliphandle; + char* clipdata; + char* filtered; + + // Open the clipboard + if (!OpenClipboard(getHandle())) + return NULL; + + // Get the clipboard data + cliphandle = GetClipboardData(CF_TEXT); + if (!cliphandle) { + CloseClipboard(); + return NULL; + } + + clipdata = (char*) GlobalLock(cliphandle); + if (!clipdata) { + CloseClipboard(); + return NULL; + } + + // Filter out anything unwanted + filtered = convertLF(clipdata, strlen(clipdata)); + removeNonISOLatin1Chars(filtered); + + // Release the buffer and close the clipboard + GlobalUnlock(cliphandle); + CloseClipboard(); + + return filtered; +} + void Clipboard::setClipText(const char* text) { HANDLE clip_handle = 0; diff --git a/win/rfb_win32/Clipboard.h b/win/rfb_win32/Clipboard.h index c69e981f..1dead82e 100644 --- a/win/rfb_win32/Clipboard.h +++ b/win/rfb_win32/Clipboard.h @@ -39,7 +39,7 @@ namespace rfb { // -=- Abstract base class for callback recipients class Notifier { public: - virtual void notifyClipboardChanged(const char* text) = 0; + virtual void notifyClipboardChanged(bool available) = 0; virtual ~Notifier() {}; }; @@ -49,6 +49,9 @@ namespace rfb { // - Set the notifier to use void setNotifier(Notifier* cbn) {notifier = cbn;} + // - Get the clipboard contents + char* getClipText(); + // - Set the clipboard contents void setClipText(const char* text); diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx index 2c91e3f4..be33ff15 100644 --- a/win/rfb_win32/SDisplay.cxx +++ b/win/rfb_win32/SDisplay.cxx @@ -292,6 +292,22 @@ void SDisplay::restartCore() { } +void SDisplay::handleClipboardRequest() { + CharArray data(clipboard->getClipText()); + server->sendClipboardData(data.buf); +} + +void SDisplay::handleClipboardAnnounce(bool available) { + // FIXME: Wait for an application to actually request it + if (available) + server->requestClipboard(); +} + +void SDisplay::handleClipboardData(const char* data) { + clipboard->setClipText(data); +} + + void SDisplay::pointerEvent(const Point& pos, int buttonmask) { if (pb->getRect().contains(pos)) { Point screenPos = pos.translate(screenRect.tl); @@ -329,16 +345,12 @@ bool SDisplay::checkLedState() { return false; } -void SDisplay::clientCutText(const char* text) { - clipboard->setClipText(text); -} - void -SDisplay::notifyClipboardChanged(const char* text) { +SDisplay::notifyClipboardChanged(bool available) { vlog.debug("clipboard text changed"); if (server) - server->serverCutText(text); + server->announceClipboard(available); } diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h index 1773b785..8e38edb3 100644 --- a/win/rfb_win32/SDisplay.h +++ b/win/rfb_win32/SDisplay.h @@ -76,13 +76,15 @@ namespace rfb { virtual void terminate(); virtual void queryConnection(network::Socket* sock, const char* userName); + virtual void handleClipboardRequest(); + virtual void handleClipboardAnnounce(bool available); + virtual void handleClipboardData(const char* data); virtual void pointerEvent(const Point& pos, int buttonmask); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); - virtual void clientCutText(const char* str); - // -=- Clipboard + // -=- Clipboard events - virtual void notifyClipboardChanged(const char* text); + virtual void notifyClipboardChanged(bool available); // -=- Display events -- cgit v1.2.3 From 5fbbe10b6c180ae8c0545695db6ad308cb5caea7 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 10 May 2019 11:44:19 +0200 Subject: Use UTF-8 in clipboard API In prepartion for better clipboard extensions that can send Unicode data between the client and server. --- common/rfb/CConnection.cxx | 6 +- common/rfb/SConnection.cxx | 6 +- common/rfb/util.cxx | 218 +++++++++++++++++++++++++++++++++++++ common/rfb/util.h | 8 ++ unix/xserver/hw/vnc/vncSelection.c | 47 ++++---- vncviewer/Viewport.cxx | 16 +-- win/rfb_win32/Clipboard.cxx | 73 ++++--------- 7 files changed, 282 insertions(+), 92 deletions(-) (limited to 'unix') diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx index ce2741e4..4e8ea4e5 100644 --- a/common/rfb/CConnection.cxx +++ b/common/rfb/CConnection.cxx @@ -470,7 +470,7 @@ void CConnection::serverCutText(const char* str) strFree(serverClipboard); serverClipboard = NULL; - serverClipboard = strDup(str); + serverClipboard = latin1ToUTF8(str); handleClipboardAnnounce(true); } @@ -516,7 +516,9 @@ void CConnection::announceClipboard(bool available) void CConnection::sendClipboardData(const char* data) { - writer()->writeClientCutText(data); + CharArray latin1(utf8ToLatin1(data)); + + writer()->writeClientCutText(latin1.buf); } void CConnection::refreshFramebuffer() diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index 1cc330d8..46f0a850 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -306,7 +306,7 @@ void SConnection::clientCutText(const char* str) strFree(clientClipboard); clientClipboard = NULL; - clientClipboard = strDup(str); + clientClipboard = latin1ToUTF8(str); handleClipboardAnnounce(true); } @@ -450,7 +450,9 @@ void SConnection::announceClipboard(bool available) void SConnection::sendClipboardData(const char* data) { - writer()->writeServerCutText(data); + CharArray latin1(utf8ToLatin1(data)); + + writer()->writeServerCutText(latin1.buf); } void SConnection::writeFakeColourMap(void) diff --git a/common/rfb/util.cxx b/common/rfb/util.cxx index deb68ca1..fc4f4ca4 100644 --- a/common/rfb/util.cxx +++ b/common/rfb/util.cxx @@ -64,6 +64,10 @@ namespace rfb { delete [] s; } + void strFree(wchar_t* s) { + delete [] s; + } + bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) { CharArray out1old, out2old; @@ -163,6 +167,67 @@ namespace rfb { return buffer; } + char* convertCRLF(const char* src, size_t bytes) + { + char* buffer; + size_t sz; + + char* out; + const char* in; + size_t in_len; + + // Always include space for a NULL + sz = 1; + + // Compute output size + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + sz++; + + if (*in == '\r') { + if ((in_len == 0) || (*(in+1) != '\n')) + sz++; + } else if (*in == '\n') { + if ((in == src) || (*(in-1) != '\r')) + sz++; + } + + in++; + in_len--; + } + + // Alloc + buffer = new char[sz]; + memset(buffer, 0, sz); + + // And convert + out = buffer; + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + if (*in == '\n') { + if ((in == src) || (*(in-1) != '\r')) + *out++ = '\r'; + } + + *out = *in; + + if (*in == '\r') { + if ((in_len == 0) || (*(in+1) != '\n')) { + out++; + *out = '\n'; + } + } + + out++; + in++; + in_len--; + } + + return buffer; + } + size_t ucs4ToUTF8(unsigned src, char* dst) { if (src < 0x80) { *dst++ = src; @@ -242,6 +307,61 @@ namespace rfb { return consumed; } + size_t ucs4ToUTF16(unsigned src, wchar_t* dst) { + if ((src < 0xd800) || ((src >= 0xe000) && (src < 0x10000))) { + *dst++ = src; + *dst++ = L'\0'; + return 1; + } else if (src < 0x110000) { + *dst++ = 0xd800 | ((src >> 10) & 0x07ff); + *dst++ = 0xdc00 | (src & 0x07ff); + *dst++ = L'\0'; + return 2; + } else { + return ucs4ToUTF16(0xfffd, dst); + } + } + + size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst) { + *dst = 0xfffd; + + if (max == 0) + return 0; + + if ((*src < 0xd800) || (*src >= 0xe000)) { + *dst = *src; + return 1; + } + + if (*src & 0x0400) { + size_t consumed; + + // Invalid sequence, consume all continuation characters + consumed = 0; + while ((max > 0) && (*src & 0x0400)) { + src++; + max--; + consumed++; + } + + return consumed; + } + + *dst = *src++; + max--; + + // Invalid or truncated sequence? + if ((max == 0) || ((*src & 0xfc00) != 0xdc00)) { + *dst = 0xfffd; + return 1; + } + + *dst = 0x10000 | ((*dst & 0x03ff) << 10); + *dst |= *src & 0x3ff; + + return 2; + } + char* latin1ToUTF8(const char* src, size_t bytes) { char* buffer; size_t sz; @@ -329,6 +449,104 @@ namespace rfb { return buffer; } + char* utf16ToUTF8(const wchar_t* src, size_t units) + { + char* buffer; + size_t sz; + + char* out; + const wchar_t* in; + size_t in_len; + + // Always include space for a NULL + sz = 1; + + // Compute output size + in = src; + in_len = units; + while ((*in != '\0') && (in_len > 0)) { + size_t len; + unsigned ucs; + char buf[5]; + + len = utf16ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + sz += ucs4ToUTF8(ucs, buf); + } + + // Alloc + buffer = new char[sz]; + memset(buffer, 0, sz); + + // And convert + out = buffer; + in = src; + in_len = units; + while ((*in != '\0') && (in_len > 0)) { + size_t len; + unsigned ucs; + + len = utf16ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + out += ucs4ToUTF8(ucs, out); + } + + return buffer; + } + + wchar_t* utf8ToUTF16(const char* src, size_t bytes) + { + wchar_t* buffer; + size_t sz; + + wchar_t* out; + const char* in; + size_t in_len; + + // Always include space for a NULL + sz = 1; + + // Compute output size + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + size_t len; + unsigned ucs; + wchar_t buf[3]; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + sz += ucs4ToUTF16(ucs, buf); + } + + // Alloc + buffer = new wchar_t[sz]; + memset(buffer, 0, sz); + + // And convert + out = buffer; + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + size_t len; + unsigned ucs; + + len = utf8ToUCS4(in, in_len, &ucs); + in += len; + in_len -= len; + + out += ucs4ToUTF16(ucs, out); + } + + return buffer; + } + unsigned msBetween(const struct timeval *first, const struct timeval *second) { diff --git a/common/rfb/util.h b/common/rfb/util.h index 7bd5cc01..8503519d 100644 --- a/common/rfb/util.h +++ b/common/rfb/util.h @@ -68,6 +68,7 @@ namespace rfb { char* strDup(const char* s); void strFree(char* s); + void strFree(wchar_t* s); // Returns true if split successful. Returns false otherwise. // ALWAYS *copies* first part of string to out1 buffer. @@ -87,6 +88,7 @@ namespace rfb { // Makes sure line endings are in a certain format char* convertLF(const char* src, size_t bytes = (size_t)-1); + char* convertCRLF(const char* src, size_t bytes = (size_t)-1); // Convertions between various Unicode formats. The returned strings are // always null terminated and must be freed using strFree(). @@ -94,9 +96,15 @@ namespace rfb { size_t ucs4ToUTF8(unsigned src, char* dst); size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst); + size_t ucs4ToUTF16(unsigned src, wchar_t* dst); + size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst); + char* latin1ToUTF8(const char* src, size_t bytes = (size_t)-1); char* utf8ToLatin1(const char* src, size_t bytes = (size_t)-1); + char* utf16ToUTF8(const wchar_t* src, size_t units = (size_t)-1); + wchar_t* utf8ToUTF16(const char* src, size_t bytes = (size_t)-1); + // HELPER functions for timeout handling // soonestTimeout() is a function to help work out the soonest of several diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 4fac50e2..5191bb94 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -362,23 +362,24 @@ static int vncConvertSelection(ClientPtr client, Atom selection, return Success; } else { if ((target == xaSTRING) || (target == xaTEXT)) { + char* latin1; + + latin1 = vncUTF8ToLatin1(data, (size_t)-1); + if (latin1 == NULL) + return BadAlloc; + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, XA_STRING, 8, PropModeReplace, - strlen(data), (char*)data, - TRUE); + strlen(latin1), latin1, TRUE); + + vncStrFree(latin1); + if (rc != Success) return rc; } else if (target == xaUTF8_STRING) { - char* buffer; - - buffer = vncLatin1ToUTF8(data, (size_t)-1); - if (buffer == NULL) - return BadAlloc; - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, xaUTF8_STRING, 8, PropModeReplace, - strlen(buffer), buffer, TRUE); - vncStrFree(buffer); + strlen(data), data, TRUE); if (rc != Success) return rc; } else { @@ -515,13 +516,14 @@ static void vncHandleSelection(Atom selection, Atom target, vncAnnounceClipboard(TRUE); } } else { - if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size)) - vncSelectionRequest(selection, xaSTRING); - else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) + if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) vncSelectionRequest(selection, xaUTF8_STRING); + else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size)) + vncSelectionRequest(selection, xaSTRING); } } else if (target == xaSTRING) { char* filtered; + char* utf8; if (prop->format != 8) return; @@ -532,27 +534,26 @@ static void vncHandleSelection(Atom selection, Atom target, if (filtered == NULL) return; + utf8 = vncLatin1ToUTF8(filtered, (size_t)-1); + vncStrFree(filtered); + if (utf8 == NULL) + return; + LOG_DEBUG("Sending clipboard to clients (%d bytes)", - (int)strlen(filtered)); + (int)strlen(utf8)); - vncSendClipboardData(filtered); + vncSendClipboardData(utf8); - vncStrFree(filtered); + vncStrFree(utf8); } else if (target == xaUTF8_STRING) { char *filtered; - char* buffer; if (prop->format != 8) return; if (prop->type != xaUTF8_STRING) return; - buffer = vncUTF8ToLatin1(prop->data, prop->size); - if (buffer == NULL) - return; - - filtered = vncConvertLF(buffer, (size_t)-1); - vncStrFree(buffer); + filtered = vncConvertLF(prop->data, prop->size); if (filtered == NULL) return; diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 713d3648..cd613279 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -310,14 +310,12 @@ void Viewport::handleClipboardAnnounce(bool available) void Viewport::handleClipboardData(const char* data) { - char* buffer; size_t len; if (!hasFocus()) return; - buffer = latin1ToUTF8(data); - len = strlen(buffer); + len = strlen(data); vlog.debug("Got clipboard data (%d bytes)", (int)len); @@ -325,11 +323,9 @@ void Viewport::handleClipboardData(const char* data) // dump the data into both variants. #if !defined(WIN32) && !defined(__APPLE__) if (setPrimary) - Fl::copy(buffer, len, 0); + Fl::copy(data, len, 0); #endif - Fl::copy(buffer, len, 1); - - strFree(buffer); + Fl::copy(data, len, 1); } void Viewport::setLEDState(unsigned int state) @@ -561,15 +557,13 @@ void Viewport::resize(int x, int y, int w, int h) int Viewport::handle(int event) { - char *buffer, *filtered; + char *filtered; int buttonMask, wheelMask; DownMap::const_iterator iter; switch (event) { case FL_PASTE: - buffer = utf8ToLatin1(Fl::event_text(), Fl::event_length()); - filtered = convertLF(buffer); - strFree(buffer); + filtered = convertLF(Fl::event_text(), Fl::event_length()); vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered)); diff --git a/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx index 306cfbad..11963675 100644 --- a/win/rfb_win32/Clipboard.cxx +++ b/win/rfb_win32/Clipboard.cxx @@ -30,41 +30,6 @@ using namespace rfb::win32; static LogWriter vlog("Clipboard"); - -// -// -=- CR/LF handlers -// - -char* -unix2dos(const char* text) { - int len = strlen(text)+1; - char* dos = new char[strlen(text)*2+1]; - int i, j=0; - for (i=0; i= 1) && (text[i] <= 127)) || - ((text[i] >= 160) && (text[i] <= 255))) - text[j++] = text[i]; - } - text[j] = 0; -} - // // -=- Clipboard object // @@ -106,7 +71,7 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { if (notifier == NULL) vlog.debug("no clipboard notifier registered"); else - notifier->notifyClipboardChanged(IsClipboardFormatAvailable(CF_TEXT)); + notifier->notifyClipboardChanged(IsClipboardFormatAvailable(CF_UNICODETEXT)); } } if (next_window) @@ -120,35 +85,34 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { char* Clipboard::getClipText() { HGLOBAL cliphandle; - char* clipdata; - char* filtered; + wchar_t* clipdata; + CharArray utf8; // Open the clipboard if (!OpenClipboard(getHandle())) return NULL; // Get the clipboard data - cliphandle = GetClipboardData(CF_TEXT); + cliphandle = GetClipboardData(CF_UNICODETEXT); if (!cliphandle) { CloseClipboard(); return NULL; } - clipdata = (char*) GlobalLock(cliphandle); + clipdata = (wchar_t*) GlobalLock(cliphandle); if (!clipdata) { CloseClipboard(); return NULL; } - // Filter out anything unwanted - filtered = convertLF(clipdata, strlen(clipdata)); - removeNonISOLatin1Chars(filtered); + // Convert it to UTF-8 + utf8.replaceBuf(utf16ToUTF8(clipdata)); // Release the buffer and close the clipboard GlobalUnlock(cliphandle); CloseClipboard(); - return filtered; + return convertLF(utf8.buf); } void @@ -161,26 +125,27 @@ Clipboard::setClipText(const char* text) { if (!OpenClipboard(getHandle())) throw rdr::SystemException("unable to open Win32 clipboard", GetLastError()); - // - Pre-process the supplied clipboard text into DOS format - CharArray dos_text; - dos_text.buf = unix2dos(text); - removeNonISOLatin1Chars(dos_text.buf); - int dos_text_len = strlen(dos_text.buf); + // - Convert the supplied clipboard text into UTF-16 format with CRLF + CharArray filtered(convertCRLF(text)); + wchar_t* utf16; + + utf16 = utf8ToUTF16(filtered.buf); // - Allocate global memory for the data - clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1); + clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, (wcslen(utf16) + 1) * 2); - char* data = (char*) GlobalLock(clip_handle); - memcpy(data, dos_text.buf, dos_text_len+1); - data[dos_text_len] = 0; + wchar_t* data = (wchar_t*) GlobalLock(clip_handle); + wcscpy(data, utf16); GlobalUnlock(clip_handle); + strFree(utf16); + // - Next, we must clear out any existing data if (!EmptyClipboard()) throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError()); // - Set the new clipboard data - if (!SetClipboardData(CF_TEXT, clip_handle)) + if (!SetClipboardData(CF_UNICODETEXT, clip_handle)) throw rdr::SystemException("unable to set Win32 clipboard", GetLastError()); clip_handle = 0; -- cgit v1.2.3