From c62e70c5b34de478127a130384b8958ccf232504 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 12 Jan 2016 17:33:32 +0100 Subject: [PATCH] Move client to server clipboard out of vncconfig Handle the clipboard directly in the server, avoiding the dependency on vncconfig. This commit adds support for clipboard from the client to the server. Handling of the other direction will follow. --- unix/xserver/hw/vnc/Makefile.am | 7 +- unix/xserver/hw/vnc/RFBGlue.cc | 1 + unix/xserver/hw/vnc/XserverDesktop.cc | 1 + unix/xserver/hw/vnc/vncExt.c | 89 -------- unix/xserver/hw/vnc/vncExtInit.cc | 3 + unix/xserver/hw/vnc/vncExtInit.h | 2 - unix/xserver/hw/vnc/vncSelection.c | 291 ++++++++++++++++++++++++++ unix/xserver/hw/vnc/vncSelection.h | 33 +++ 8 files changed, 333 insertions(+), 94 deletions(-) create mode 100644 unix/xserver/hw/vnc/vncSelection.c create mode 100644 unix/xserver/hw/vnc/vncSelection.h diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am index d7ab2595..95b03d2d 100644 --- a/unix/xserver/hw/vnc/Makefile.am +++ b/unix/xserver/hw/vnc/Makefile.am @@ -10,11 +10,12 @@ COMMON_LIBS=$(NETWORK_LIB) $(RFB_LIB) $(RDR_LIB) $(XREGION_LIB) noinst_LTLIBRARIES = libvnccommon.la HDRS = vncExtInit.h vncHooks.h \ - vncBlockHandler.h XorgGlue.h \ - XserverDesktop.h xorg-version.h \ + vncBlockHandler.h vncSelection.h \ + XorgGlue.h XserverDesktop.h xorg-version.h \ Input.h RFBGlue.h -libvnccommon_la_SOURCES = $(HDRS) vncExt.c vncExtInit.cc vncHooks.c \ +libvnccommon_la_SOURCES = $(HDRS) \ + vncExt.c vncExtInit.cc vncHooks.c vncSelection.c \ vncBlockHandler.c XorgGlue.c RFBGlue.cc XserverDesktop.cc \ Input.c InputXKB.c diff --git a/unix/xserver/hw/vnc/RFBGlue.cc b/unix/xserver/hw/vnc/RFBGlue.cc index 9a014f17..dd073e46 100644 --- a/unix/xserver/hw/vnc/RFBGlue.cc +++ b/unix/xserver/hw/vnc/RFBGlue.cc @@ -31,6 +31,7 @@ using namespace rfb; // Loggers used by C code must be created here static LogWriter inputLog("Input"); +static LogWriter selectionLog("Selection"); void vncInitRFB(void) { diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 9b91d9a4..09ba6d75 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -43,6 +43,7 @@ #include "XserverDesktop.h" #include "vncExtInit.h" #include "vncHooks.h" +#include "vncSelection.h" #include "XorgGlue.h" #include "Input.h" diff --git a/unix/xserver/hw/vnc/vncExt.c b/unix/xserver/hw/vnc/vncExt.c index 43794dad..e07ed865 100644 --- a/unix/xserver/hw/vnc/vncExt.c +++ b/unix/xserver/hw/vnc/vncExt.c @@ -52,9 +52,6 @@ static int vncEventBase = 0; int vncNoClipboard = 0; -static char* clientCutText = NULL; -static int clientCutTextLen = 0; - static struct VncInputSelect* vncInputSelectHead = NULL; struct VncInputSelect { @@ -121,47 +118,6 @@ int vncNotifyQueryConnect(void) return count; } -void vncClientCutText(const char* str, int len) -{ - xVncExtClientCutTextNotifyEvent ev; - - if (clientCutText != NULL) - free(clientCutText); - clientCutTextLen = 0; - - clientCutText = malloc(len); - if (clientCutText == NULL) { - ErrorF("Could not allocate clipboard buffer\n"); - return; - } - - memcpy(clientCutText, str, len); - clientCutTextLen = len; - - ev.type = vncEventBase + VncExtClientCutTextNotify; - for (struct VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) { - if (cur->mask & VncExtClientCutTextMask) { - ev.sequenceNumber = cur->client->sequence; - ev.window = cur->window; - ev.time = GetTimeInMillis(); - if (cur->client->swapped) { -#if XORG < 112 - int n; - swaps(&ev.sequenceNumber, n); - swapl(&ev.window, n); - swapl(&ev.time, n); -#else - swaps(&ev.sequenceNumber); - swapl(&ev.window); - swapl(&ev.time); -#endif - } - WriteToClient(cur->client, sizeof(xVncExtClientCutTextNotifyEvent), - (char *)&ev); - } - } -} - static int ProcVncExtSetParam(ClientPtr client) { char *param; @@ -424,47 +380,6 @@ static int SProcVncExtSetServerCutText(ClientPtr client) return ProcVncExtSetServerCutText(client); } -static int ProcVncExtGetClientCutText(ClientPtr client) -{ - xVncExtGetClientCutTextReply rep; - - REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); - - rep.type = X_Reply; - rep.length = (clientCutTextLen + 3) >> 2; - rep.sequenceNumber = client->sequence; - rep.textLen = clientCutTextLen; - if (client->swapped) { -#if XORG < 112 - int n; - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.textLen, n); -#else - swaps(&rep.sequenceNumber); - swapl(&rep.length); - swapl(&rep.textLen); -#endif - } - WriteToClient(client, sizeof(xVncExtGetClientCutTextReply), (char *)&rep); - if (clientCutText) - WriteToClient(client, clientCutTextLen, clientCutText); - return (client->noClientException); -} - -static int SProcVncExtGetClientCutText(ClientPtr client) -{ - REQUEST(xVncExtGetClientCutTextReq); -#if XORG < 112 - register char n; - swaps(&stuff->length, n); -#else - swaps(&stuff->length); -#endif - REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); - return ProcVncExtGetClientCutText(client); -} - static int ProcVncExtSelectInput(ClientPtr client) { struct VncInputSelect** nextPtr; @@ -670,8 +585,6 @@ static int ProcVncExtDispatch(ClientPtr client) return ProcVncExtListParams(client); case X_VncExtSetServerCutText: return ProcVncExtSetServerCutText(client); - case X_VncExtGetClientCutText: - return ProcVncExtGetClientCutText(client); case X_VncExtSelectInput: return ProcVncExtSelectInput(client); case X_VncExtConnect: @@ -699,8 +612,6 @@ static int SProcVncExtDispatch(ClientPtr client) return SProcVncExtListParams(client); case X_VncExtSetServerCutText: return SProcVncExtSetServerCutText(client); - case X_VncExtGetClientCutText: - return SProcVncExtGetClientCutText(client); case X_VncExtSelectInput: return SProcVncExtSelectInput(client); case X_VncExtConnect: diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 863cd36b..32a93f03 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -35,6 +35,7 @@ #include "vncExtInit.h" #include "vncHooks.h" #include "vncBlockHandler.h" +#include "vncSelection.h" #include "XorgGlue.h" using namespace rfb; @@ -123,6 +124,8 @@ void vncExtensionInit(void) if (ret == -1) return; + vncSelectionInit(); + vlog.info("VNC extension running!"); try { diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 6430ac05..1057118a 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -41,8 +41,6 @@ int vncAddExtension(void); int vncNotifyQueryConnect(void); -void vncClientCutText(const char* str, int len); - // vncExtInit.cc extern void* vncFbptr[]; extern int vncFbstride[]; diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c new file mode 100644 index 00000000..2369c5d7 --- /dev/null +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -0,0 +1,291 @@ +/* Copyright 2016 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include + +#include "scrnintstr.h" +#include "selection.h" +#include "windowstr.h" +#include "xace.h" + +#include "xorg-version.h" + +#include "vncSelection.h" +#include "RFBGlue.h" + +#define LOG_NAME "Selection" + +#define LOG_ERROR(...) vncLogError(LOG_NAME, __VA_ARGS__) +#define LOG_STATUS(...) vncLogStatus(LOG_NAME, __VA_ARGS__) +#define LOG_INFO(...) vncLogInfo(LOG_NAME, __VA_ARGS__) +#define LOG_DEBUG(...) vncLogDebug(LOG_NAME, __VA_ARGS__) + +static WindowPtr pWindow; +static Window wid; + +static char* clientCutText; +static int clientCutTextLen; + +static int vncCreateSelectionWindow(void); +static int vncOwnSelection(Atom selection); +static int vncProcConvertSelection(ClientPtr client); + +static int (*origProcConvertSelection)(ClientPtr); + +void vncSelectionInit(void) +{ + origProcConvertSelection = ProcVector[X_ConvertSelection]; + ProcVector[X_ConvertSelection] = vncProcConvertSelection; +} + +void vncClientCutText(const char* str, int len) +{ + int rc; + + if (clientCutText != NULL) + free(clientCutText); + + clientCutText = malloc(len); + if (clientCutText == NULL) { + LOG_ERROR("Could not allocate clipboard buffer"); + DeleteWindowFromAnySelections(pWindow); + return; + } + + memcpy(clientCutText, str, len); + clientCutTextLen = len; + + rc = vncOwnSelection(MakeAtom("PRIMARY", 7, TRUE)); + if (rc != Success) + LOG_ERROR("Could not set PRIMARY selection"); + vncOwnSelection(MakeAtom("CLIPBOARD", 9, TRUE)); + if (rc != Success) + LOG_ERROR("Could not set CLIPBOARD selection"); +} + +static int vncCreateSelectionWindow(void) +{ + ScreenPtr pScreen; + int result; + + if (pWindow != NULL) + return Success; + + pScreen = screenInfo.screens[0]; + + wid = FakeClientID(0); + pWindow = CreateWindow(wid, pScreen->root, + 0, 0, 100, 100, 0, InputOnly, + 0, NULL, 0, serverClient, + CopyFromParent, &result); + if (!pWindow) + return result; + + if (!AddResource(pWindow->drawable.id, RT_WINDOW, pWindow)) + return BadAlloc; + + LOG_DEBUG("Created selection window"); + + return Success; +} + +static int vncOwnSelection(Atom selection) +{ + Selection *pSel; + int rc; + + SelectionInfoRec info; + + rc = vncCreateSelectionWindow(); + if (rc != Success) + return rc; + + rc = dixLookupSelection(&pSel, selection, serverClient, DixSetAttrAccess); + if (rc == Success) { + if (pSel->client && (pSel->client != serverClient)) { + xEvent event = { + .u.selectionClear.time = currentTime.milliseconds, + .u.selectionClear.window = pSel->window, + .u.selectionClear.atom = pSel->selection + }; + event.u.u.type = SelectionClear; + WriteEventsToClient(pSel->client, 1, &event); + } + } else if (rc == BadMatch) { + pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION); + if (!pSel) + return BadAlloc; + + pSel->selection = selection; + + rc = XaceHookSelectionAccess(serverClient, &pSel, + DixCreateAccess | DixSetAttrAccess); + if (rc != Success) { + free(pSel); + return rc; + } + + pSel->next = CurrentSelections; + CurrentSelections = pSel; + } + else + return rc; + + pSel->lastTimeChanged = currentTime; + pSel->window = wid; + pSel->pWin = pWindow; + pSel->client = serverClient; + + LOG_DEBUG("Grabbed %s selection", NameForAtom(selection)); + + info.selection = pSel; + info.client = serverClient; + info.kind = SelectionSetOwner; + CallCallbacks(&SelectionCallback, &info); + + return Success; +} + +static int vncConvertSelection(ClientPtr client, Atom selection, + Atom target, Atom property, + Window requestor, TimeStamp time) +{ + Selection *pSel; + WindowPtr pWin; + int rc; + + Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT; + Atom realProperty; + + xEvent event; + + LOG_DEBUG("Selection request for %s (type %s)", + NameForAtom(selection), NameForAtom(target)); + + rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess); + if (rc != Success) + return rc; + + if (CompareTimeStamps(time, pSel->lastTimeChanged) != LATER) + return BadMatch; + + rc = dixLookupWindow(&pWin, requestor, client, DixSetAttrAccess); + if (rc != Success) + return rc; + + if (property != None) + realProperty = property; + else + realProperty = target; + + /* FIXME: MULTIPLE target */ + + xaTARGETS = MakeAtom("TARGETS", 7, TRUE); + xaTIMESTAMP = MakeAtom("TIMESTAMP", 9, TRUE); + xaSTRING = MakeAtom("STRING", 6, TRUE); + xaTEXT = MakeAtom("TEXT", 4, TRUE); + + if (target == xaTARGETS) { + Atom targets[] = { xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT }; + + rc = ChangeWindowProperty(pWin, realProperty, XA_ATOM, 32, + PropModeReplace, + sizeof(targets)/sizeof(targets[0]), + targets, TRUE); + if (rc != Success) + return rc; + } else if (target == xaTIMESTAMP) { + rc = ChangeWindowProperty(pWin, realProperty, XA_INTEGER, 32, + PropModeReplace, 1, + &pSel->lastTimeChanged.milliseconds, + TRUE); + if (rc != Success) + return rc; + } else if ((target == xaSTRING) || (target == xaTEXT)) { + rc = ChangeWindowProperty(pWin, realProperty, XA_STRING, 8, + PropModeReplace, clientCutTextLen, + clientCutText, TRUE); + if (rc != Success) + return rc; + } else { + return BadMatch; + } + + event.u.u.type = SelectionNotify; + event.u.selectionNotify.time = time.milliseconds; + event.u.selectionNotify.requestor = requestor; + event.u.selectionNotify.selection = selection; + event.u.selectionNotify.target = target; + event.u.selectionNotify.property = property; + WriteEventsToClient(client, 1, &event); + return Success; +} + +/* The original code cannot deal with the selection owner being + * serverClient, so we have to reimplement this. */ +static int vncProcConvertSelection(ClientPtr client) +{ + Bool paramsOkay; + WindowPtr pWin; + Selection *pSel; + int rc; + + REQUEST(xConvertSelectionReq); + REQUEST_SIZE_MATCH(xConvertSelectionReq); + + rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess); + if (rc != Success) + return rc; + + paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target); + paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property); + if (!paramsOkay) { + client->errorValue = stuff->property; + return BadAtom; + } + + rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess); + if (rc == Success && pSel->client == serverClient && + pSel->window == wid) { + TimeStamp time; + time = ClientTimeToServerTime(stuff->time); + rc = vncConvertSelection(client, stuff->selection, + stuff->target, stuff->property, + stuff->requestor, time); + if (rc != Success) { + xEvent event; + + memset(&event, 0, sizeof(xEvent)); + event.u.u.type = SelectionNotify; + event.u.selectionNotify.time = stuff->time; + event.u.selectionNotify.requestor = stuff->requestor; + event.u.selectionNotify.selection = stuff->selection; + event.u.selectionNotify.target = stuff->target; + event.u.selectionNotify.property = None; + WriteEventsToClient(client, 1, &event); + } + + return Success; + } + + return origProcConvertSelection(client); +} diff --git a/unix/xserver/hw/vnc/vncSelection.h b/unix/xserver/hw/vnc/vncSelection.h new file mode 100644 index 00000000..969f8958 --- /dev/null +++ b/unix/xserver/hw/vnc/vncSelection.h @@ -0,0 +1,33 @@ +/* Copyright 2016 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef __SELECTION_H__ +#define __SELECTION_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void vncSelectionInit(void); + +void vncClientCutText(const char* str, int len); + +#ifdef __cplusplus +} +#endif + +#endif -- 2.39.5