]> source.dussan.org Git - tigervnc.git/commitdiff
Merge branch 'clipboard'
authorPierre Ossman <ossman@cendio.se>
Fri, 8 Jul 2016 12:11:42 +0000 (14:11 +0200)
committerPierre Ossman <ossman@cendio.se>
Fri, 8 Jul 2016 12:11:42 +0000 (14:11 +0200)
1  2 
unix/vncconfig/vncExt.c
unix/vncserver
unix/xserver/hw/vnc/RFBGlue.cc
unix/xserver/hw/vnc/XserverDesktop.cc
unix/xserver/hw/vnc/Xvnc.man
unix/xserver/hw/vnc/vncExt.c
unix/xserver/hw/vnc/vncExtInit.cc
unix/xserver/hw/vnc/vncExtInit.h
unix/xserver/hw/vnc/vncSelection.c

Simple merge
diff --cc unix/vncserver
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 1d374938cba5667914a3e3338c1666f87a945410,fda2562e44c85bdc88e432c71491e7014ed14dec..0ad34a27fc3cab3c102f86eb5a01847d86dc6117
@@@ -81,9 -70,9 +82,14 @@@ rfb::StringParameter interface("interfa
  rfb::BoolParameter avoidShiftNumLock("AvoidShiftNumLock",
                                       "Avoid fake Shift presses for keys affected by NumLock.",
                                       true);
 +rfb::StringParameter allowOverride("AllowOverride",
 +                                   "Comma separated list of parameters that can be modified using VNC extension.",
 +                                   "desktop,AcceptPointerEvents,SendCutText,AcceptCutText");
++rfb::BoolParameter setPrimary("SetPrimary", "Set the PRIMARY as well "
++                              "as the CLIPBOARD selection", true);
+ rfb::BoolParameter sendPrimary("SendPrimary",
+                                "Send the PRIMARY as well as the CLIPBOARD selection",
+                                true);
  
  static PixelFormat vncGetPixelFormat(int scrIdx)
  {
@@@ -274,6 -248,11 +282,16 @@@ int vncGetAvoidShiftNumLock(void
    return (bool)avoidShiftNumLock;
  }
  
++int vncGetSetPrimary(void)
++{
++  return (bool)setPrimary;
++}
++
+ int vncGetSendPrimary(void)
+ {
+   return (bool)sendPrimary;
+ }
  void vncUpdateDesktopName(void)
  {
    for (int scr = 0; scr < vncGetScreenCount(); scr++) {
index be6487c89d63aa2463b028c755032b0cdec67240,4b8568e1795dfe58d8cbecc5a64b35ef56371d7c..a0914ad777fb42e26ca224499c6b08f0f8e2d6eb
@@@ -59,6 -57,8 +57,9 @@@ void vncCallWriteWakeupHandlers(fd_set 
  
  int vncGetAvoidShiftNumLock(void);
  
++int vncGetSetPrimary(void);
+ int vncGetSendPrimary(void);
  void vncUpdateDesktopName(void);
  
  void vncServerCutText(const char *text, size_t len);
index 0000000000000000000000000000000000000000,cea8ecf24c1af93e118ea30de32feba0c17b8895..e50548a42ac5d937b1916f488fb635e30694708b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,518 +1,521 @@@
 -  rc = vncOwnSelection(xaPRIMARY);
 -  if (rc != Success)
 -    LOG_ERROR("Could not set PRIMARY selection");
+ /* 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 <dix-config.h>
+ #endif
+ #include <X11/Xatom.h>
+ #include "propertyst.h"
+ #include "scrnintstr.h"
+ #include "selection.h"
+ #include "windowstr.h"
+ #include "xace.h"
+ #include "xorg-version.h"
+ #include "vncExtInit.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 Atom xaPRIMARY, xaCLIPBOARD;
+ static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING;
+ 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 vncProcSendEvent(ClientPtr client);
+ static void vncSelectionCallback(CallbackListPtr *callbacks,
+                                  void * data, void * args);
+ static int (*origProcConvertSelection)(ClientPtr);
+ static int (*origProcSendEvent)(ClientPtr);
+ void vncSelectionInit(void)
+ {
+   xaPRIMARY = MakeAtom("PRIMARY", 7, TRUE);
+   xaCLIPBOARD = MakeAtom("CLIPBOARD", 9, TRUE);
+   xaTARGETS = MakeAtom("TARGETS", 7, TRUE);
+   xaTIMESTAMP = MakeAtom("TIMESTAMP", 9, TRUE);
+   xaSTRING = MakeAtom("STRING", 6, TRUE);
+   xaTEXT = MakeAtom("TEXT", 4, TRUE);
+   xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE);
+   /* There are no hooks for when these are internal windows, so
+    * override the relevant handlers. */
+   origProcConvertSelection = ProcVector[X_ConvertSelection];
+   ProcVector[X_ConvertSelection] = vncProcConvertSelection;
+   origProcSendEvent = ProcVector[X_SendEvent];
+   ProcVector[X_SendEvent] = vncProcSendEvent;
+   if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0))
+     FatalError("Add VNC SelectionCallback failed\n");
+ }
+ 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;
++  if (vncGetSetPrimary()) {
++    rc = vncOwnSelection(xaPRIMARY);
++    if (rc != Success)
++      LOG_ERROR("Could not set PRIMARY selection");
++  }
++
+   vncOwnSelection(xaCLIPBOARD);
+   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 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 */
+   if (target == xaTARGETS) {
+     Atom targets[] = { xaTARGETS, xaTIMESTAMP,
+                        xaSTRING, xaTEXT, xaUTF8_STRING };
+     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 if (target == xaUTF8_STRING) {
+     unsigned char* buffer;
+     unsigned char* out;
+     size_t len;
+     const unsigned char* in;
+     size_t in_len;
+     buffer = malloc(clientCutTextLen*2);
+     if (buffer == NULL)
+       return BadAlloc;
+     out = buffer;
+     len = 0;
+     in = clientCutText;
+     in_len = clientCutTextLen;
+     while (in_len > 0) {
+       if (*in & 0x80) {
+         *out++ = 0xc0 | (*in >> 6);
+         *out++ = 0x80 | (*in & 0x3f);
+         len += 2;
+         in++;
+         in_len--;
+       } else {
+         *out++ = *in++;
+         len++;
+         in_len--;
+       }
+     }
+     rc = ChangeWindowProperty(pWin, realProperty, xaUTF8_STRING, 8,
+                               PropModeReplace, len, buffer, TRUE);
+     free(buffer);
+     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;
+ }
+ 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);
+ }
+ static void vncSelectionRequest(Atom selection, Atom target)
+ {
+   Selection *pSel;
+   xEvent event;
+   int rc;
+   rc = vncCreateSelectionWindow();
+   if (rc != Success)
+     return;
+   LOG_DEBUG("Requesting %s for %s selection",
+             NameForAtom(target), NameForAtom(selection));
+   rc = dixLookupSelection(&pSel, selection, serverClient, DixGetAttrAccess);
+   if (rc != Success)
+     return;
+   event.u.u.type = SelectionRequest;
+   event.u.selectionRequest.owner = pSel->window;
+   event.u.selectionRequest.time = currentTime.milliseconds;
+   event.u.selectionRequest.requestor = wid;
+   event.u.selectionRequest.selection = selection;
+   event.u.selectionRequest.target = target;
+   event.u.selectionRequest.property = target;
+   WriteEventsToClient(pSel->client, 1, &event);
+ }
+ static Bool vncHasAtom(Atom atom, const Atom list[], size_t size)
+ {
+   size_t i;
+   for (i = 0;i < size;i++) {
+     if (list[i] == atom)
+       return TRUE;
+   }
+   return FALSE;
+ }
+ static void vncHandleSelection(Atom selection, Atom target,
+                                Atom property, Atom requestor,
+                                TimeStamp time)
+ {
+   PropertyPtr prop;
+   int rc;
+   rc = dixLookupProperty(&prop, pWindow, property,
+                          serverClient, DixReadAccess);
+   if (rc != Success)
+     return;
+   LOG_DEBUG("Selection notification for %s (target %s, property %s, type %s)",
+             NameForAtom(selection), NameForAtom(target),
+             NameForAtom(property), NameForAtom(prop->type));
+   if (target != property)
+     return;
+   if (target == xaTARGETS) {
+     if (prop->format != 32)
+       return;
+     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);
+   } else if (target == xaSTRING) {
+     if (prop->format != 8)
+       return;
+     if (prop->type != xaSTRING)
+       return;
+     vncServerCutText(prop->data, prop->size);
+   } else if (target == xaUTF8_STRING) {
+     unsigned char* buffer;
+     unsigned char* out;
+     size_t len;
+     const unsigned char* in;
+     size_t in_len;
+     if (prop->format != 8)
+       return;
+     if (prop->type != xaUTF8_STRING)
+       return;
+     buffer = malloc(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));
+       }
+     }
+     vncServerCutText((const char*)buffer, len);
+     free(buffer);
+   }
+ }
+ #define SEND_EVENT_BIT 0x80
+ static int vncProcSendEvent(ClientPtr client)
+ {
+   REQUEST(xSendEventReq);
+   REQUEST_SIZE_MATCH(xSendEventReq);
+   stuff->event.u.u.type &= ~(SEND_EVENT_BIT);
+   if (stuff->event.u.u.type == SelectionNotify &&
+       stuff->event.u.selectionNotify.requestor == wid) {
+     TimeStamp time;
+     time = ClientTimeToServerTime(stuff->event.u.selectionNotify.time);
+     vncHandleSelection(stuff->event.u.selectionNotify.selection,
+                        stuff->event.u.selectionNotify.target,
+                        stuff->event.u.selectionNotify.property,
+                        stuff->event.u.selectionNotify.requestor,
+                        time);
+   }
+   return origProcSendEvent(client);
+ }
+ static void vncSelectionCallback(CallbackListPtr *callbacks,
+                                  void * data, void * args)
+ {
+   SelectionInfoRec *info = (SelectionInfoRec *) args;
+   if (info->kind != SelectionSetOwner)
+     return;
+   if (info->client == serverClient)
+     return;
+   if ((info->selection->selection != xaPRIMARY) &&
+       (info->selection->selection != xaCLIPBOARD))
+     return;
+   if ((info->selection->selection == xaPRIMARY) &&
+       !vncGetSendPrimary())
+     return;
+   vncSelectionRequest(info->selection->selection, xaTARGETS);
+ }