summaryrefslogtreecommitdiffstats
path: root/unix
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2016-07-08 14:11:42 +0200
committerPierre Ossman <ossman@cendio.se>2016-07-08 14:11:42 +0200
commit1db99eb9ec1868620d1fb49d51f3a2caa8386f77 (patch)
tree17bd8ddada4ab126e6a18cac93752f665d28166b /unix
parent024cee5d203c45ba803c30057ae77b442d23112d (diff)
parent3f2bb341c9734c41a3f0123e44fd27ae50deb18c (diff)
downloadtigervnc-1db99eb9ec1868620d1fb49d51f3a2caa8386f77.tar.gz
tigervnc-1db99eb9ec1868620d1fb49d51f3a2caa8386f77.zip
Merge branch 'clipboard'
Diffstat (limited to 'unix')
-rw-r--r--unix/vncconfig/vncExt.c84
-rw-r--r--unix/vncconfig/vncExt.h85
-rw-r--r--unix/vncconfig/vncconfig.cxx149
-rw-r--r--unix/vncconfig/vncconfig.man13
-rwxr-xr-xunix/vncserver2
-rw-r--r--unix/xserver/hw/vnc/Makefile.am7
-rw-r--r--unix/xserver/hw/vnc/RFBGlue.cc1
-rw-r--r--unix/xserver/hw/vnc/XserverDesktop.cc1
-rw-r--r--unix/xserver/hw/vnc/Xvnc.man17
-rw-r--r--unix/xserver/hw/vnc/vncExt.c163
-rw-r--r--unix/xserver/hw/vnc/vncExtInit.cc18
-rw-r--r--unix/xserver/hw/vnc/vncExtInit.h5
-rw-r--r--unix/xserver/hw/vnc/vncSelection.c521
-rw-r--r--unix/xserver/hw/vnc/vncSelection.h33
14 files changed, 600 insertions, 499 deletions
diff --git a/unix/vncconfig/vncExt.c b/unix/vncconfig/vncExt.c
index e9b948d3..6256d3bf 100644
--- a/unix/vncconfig/vncExt.c
+++ b/unix/vncconfig/vncExt.c
@@ -24,10 +24,6 @@
#define _VNCEXT_PROTO_
#include "vncExt.h"
-static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
- xEvent* w);
-static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
- xEvent* w);
static Bool XVncExtQueryConnectNotifyWireToEvent(Display* dpy, XEvent* e,
xEvent* w);
@@ -40,10 +36,6 @@ static Bool checkExtension(Display* dpy)
extensionInited = True;
codes = XInitExtension(dpy, VNCEXTNAME);
if (!codes) return False;
- XESetWireToEvent(dpy, codes->first_event + VncExtClientCutTextNotify,
- XVncExtClientCutTextNotifyWireToEvent);
- XESetWireToEvent(dpy, codes->first_event + VncExtSelectionChangeNotify,
- XVncExtSelectionChangeNotifyWireToEvent);
XESetWireToEvent(dpy, codes->first_event + VncExtQueryConnectNotify,
XVncExtQueryConnectNotifyWireToEvent);
}
@@ -214,53 +206,6 @@ void XVncExtFreeParamList(char** list)
}
}
-Bool XVncExtSetServerCutText(Display* dpy, const char* str, int len)
-{
- xVncExtSetServerCutTextReq* req;
-
- if (!checkExtension(dpy)) return False;
-
- LockDisplay(dpy);
- GetReq(VncExtSetServerCutText, req);
- req->reqType = codes->major_opcode;
- req->vncExtReqType = X_VncExtSetServerCutText;
- req->length += (len + 3) >> 2;
- req->textLen = len;
- Data(dpy, str, len);
- UnlockDisplay(dpy);
- SyncHandle();
- return True;
-}
-
-Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len)
-{
- xVncExtGetClientCutTextReq* req;
- xVncExtGetClientCutTextReply rep;
-
- if (!checkExtension(dpy)) return False;
-
- LockDisplay(dpy);
- GetReq(VncExtGetClientCutText, req);
- req->reqType = codes->major_opcode;
- req->vncExtReqType = X_VncExtGetClientCutText;
- if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
- UnlockDisplay(dpy);
- SyncHandle();
- return False;
- }
- UnlockDisplay(dpy);
- SyncHandle();
- *len = rep.textLen;
- *str = (char*) Xmalloc (*len+1);
- if (!*str) {
- _XEatData(dpy, (*len+1)&~1);
- return False;
- }
- _XReadPad(dpy, *str, *len);
- (*str)[*len] = 0;
- return True;
-}
-
Bool XVncExtSelectInput(Display* dpy, Window w, int mask)
{
xVncExtSelectInputReq* req;
@@ -359,35 +304,6 @@ Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve)
}
-static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
- xEvent* w)
-{
- XVncExtClientCutTextEvent* ev = (XVncExtClientCutTextEvent*)e;
- xVncExtClientCutTextNotifyEvent* wire = (xVncExtClientCutTextNotifyEvent*)w;
- ev->type = wire->type & 0x7f;
- ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
- ev->send_event = (wire->type & 0x80) != 0;
- ev->display = dpy;
- ev->window = wire->window;
- ev->time = wire->time;
- return True;
-}
-
-static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
- xEvent* w)
-{
- XVncExtSelectionChangeEvent* ev = (XVncExtSelectionChangeEvent*)e;
- xVncExtSelectionChangeNotifyEvent* wire
- = (xVncExtSelectionChangeNotifyEvent*)w;
- ev->type = wire->type & 0x7f;
- ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
- ev->send_event = (wire->type & 0x80) != 0;
- ev->display = dpy;
- ev->window = wire->window;
- ev->selection = wire->selection;
- return True;
-}
-
static Bool XVncExtQueryConnectNotifyWireToEvent(Display* dpy, XEvent* e,
xEvent* w)
{
diff --git a/unix/vncconfig/vncExt.h b/unix/vncconfig/vncExt.h
index e41e2e5b..2b24469e 100644
--- a/unix/vncconfig/vncExt.h
+++ b/unix/vncconfig/vncExt.h
@@ -26,18 +26,12 @@ extern "C" {
#define X_VncExtGetParam 1
#define X_VncExtGetParamDesc 2
#define X_VncExtListParams 3
-#define X_VncExtSetServerCutText 4
-#define X_VncExtGetClientCutText 5
#define X_VncExtSelectInput 6
#define X_VncExtConnect 7
#define X_VncExtGetQueryConnect 8
#define X_VncExtApproveConnect 9
-#define VncExtClientCutTextNotify 0
-#define VncExtSelectionChangeNotify 1
#define VncExtQueryConnectNotify 2
-#define VncExtClientCutTextMask (1 << VncExtClientCutTextNotify)
-#define VncExtSelectionChangeMask (1 << VncExtSelectionChangeNotify)
#define VncExtQueryConnectMask (1 << VncExtQueryConnectNotify)
#define VncExtNumberEvents 3
@@ -51,8 +45,6 @@ Bool XVncExtGetParam(Display* dpy, const char* param, char** value, int* len);
char* XVncExtGetParamDesc(Display* dpy, const char* param);
char** XVncExtListParams(Display* dpy, int* nParams);
void XVncExtFreeParamList(char** list);
-Bool XVncExtSetServerCutText(Display* dpy, const char* str, int len);
-Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len);
Bool XVncExtSelectInput(Display* dpy, Window w, int mask);
Bool XVncExtConnect(Display* dpy, const char* hostAndPort);
Bool XVncExtGetQueryConnect(Display* dpy, char** addr,
@@ -66,24 +58,6 @@ typedef struct {
Bool send_event;
Display *display;
Window window;
- Time time;
-} XVncExtClientCutTextEvent;
-
-typedef struct {
- int type;
- unsigned long serial;
- Bool send_event;
- Display *display;
- Window window;
- Atom selection;
-} XVncExtSelectionChangeEvent;
-
-typedef struct {
- int type;
- unsigned long serial;
- Bool send_event;
- Display *display;
- Window window;
} XVncExtQueryConnectEvent;
#endif
@@ -194,37 +168,6 @@ typedef struct {
typedef struct {
CARD8 reqType; /* always VncExtReqCode */
- CARD8 vncExtReqType; /* always VncExtSetServerCutText */
- CARD16 length B16;
- CARD32 textLen B32;
-} xVncExtSetServerCutTextReq;
-#define sz_xVncExtSetServerCutTextReq 8
-
-
-typedef struct {
- CARD8 reqType; /* always VncExtReqCode */
- CARD8 vncExtReqType; /* always VncExtGetClientCutText */
- CARD16 length B16;
-} xVncExtGetClientCutTextReq;
-#define sz_xVncExtGetClientCutTextReq 4
-
-typedef struct {
- BYTE type; /* X_Reply */
- BYTE pad0;
- CARD16 sequenceNumber B16;
- CARD32 length B32;
- CARD32 textLen B32;
- CARD32 pad1 B32;
- CARD32 pad2 B32;
- CARD32 pad3 B32;
- CARD32 pad4 B32;
- CARD32 pad5 B32;
-} xVncExtGetClientCutTextReply;
-#define sz_xVncExtGetClientCutTextReply 32
-
-
-typedef struct {
- CARD8 reqType; /* always VncExtReqCode */
CARD8 vncExtReqType; /* always VncExtSelectInput */
CARD16 length B16;
CARD32 window B32;
@@ -293,34 +236,6 @@ typedef struct {
typedef struct {
- BYTE type; /* always eventBase + VncExtClientCutTextNotify */
- BYTE pad0;
- CARD16 sequenceNumber B16;
- CARD32 window B32;
- CARD32 time B32;
- CARD32 pad1 B32;
- CARD32 pad2 B32;
- CARD32 pad3 B32;
- CARD32 pad4 B32;
- CARD32 pad5 B32;
-} xVncExtClientCutTextNotifyEvent;
-#define sz_xVncExtClientCutTextNotifyEvent 32
-
-typedef struct {
- BYTE type; /* always eventBase + VncExtSelectionChangeNotify */
- BYTE pad0;
- CARD16 sequenceNumber B16;
- CARD32 window B32;
- CARD32 selection B32;
- CARD32 pad1 B32;
- CARD32 pad2 B32;
- CARD32 pad3 B32;
- CARD32 pad4 B32;
- CARD32 pad5 B32;
-} xVncExtSelectionChangeNotifyEvent;
-#define sz_xVncExtSelectionChangeNotifyEvent 32
-
-typedef struct {
BYTE type; /* always eventBase + VncExtQueryConnectNotify */
BYTE pad0;
CARD16 sequenceNumber B16;
diff --git a/unix/vncconfig/vncconfig.cxx b/unix/vncconfig/vncconfig.cxx
index 2d6adf57..188ebfb8 100644
--- a/unix/vncconfig/vncconfig.cxx
+++ b/unix/vncconfig/vncconfig.cxx
@@ -38,7 +38,6 @@
#include <rfb/Configuration.h>
#include <rfb/Logger_stdio.h>
#include <rfb/LogWriter.h>
-#include <rfb/Timer.h>
#include "TXWindow.h"
#include "TXCheckbox.h"
#include "TXLabel.h"
@@ -51,18 +50,6 @@ LogWriter vlog("vncconfig");
StringParameter displayname("display", "The X display", "");
BoolParameter noWindow("nowin", "Don't display a window", 0);
BoolParameter iconic("iconic", "Start with window iconified", 0);
-BoolParameter setPrimary("SetPrimary", "Set the PRIMARY as well "
- "as the CLIPBOARD selection", true);
-BoolParameter sendPrimary("SendPrimary", "Send the PRIMARY as well as the "
- "CLIPBOARD selection", true);
-IntParameter pollTime("poll",
- "How often to poll for clipboard changes in ms", 0);
-
-inline const char* selectionName(Atom sel) {
- if (sel == xaCLIPBOARD) return "CLIPBOARD";
- if (sel == XA_PRIMARY) return "PRIMARY";
- return "unknown";
-}
#define ACCEPT_CUT_TEXT "AcceptCutText"
#define SEND_CUT_TEXT "SendCutText"
@@ -83,90 +70,29 @@ static bool getBoolParam(Display* dpy, const char* param) {
class VncConfigWindow : public TXWindow, public TXEventHandler,
public TXDeleteWindowCallback,
public TXCheckboxCallback,
- public rfb::Timer::Callback,
public QueryResultCallback {
public:
VncConfigWindow(Display* dpy)
- : TXWindow(dpy, 300, 100), cutText(0), cutTextLen(0),
+ : TXWindow(dpy, 300, 100),
acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
- setPrimaryCB(dpy, "Also set primary selection",
- this, false, this),
sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
- sendPrimaryCB(dpy, "Send primary selection to viewers", this,false,this),
- pollTimer(this),
queryConnectDialog(0)
{
- selection[0] = selection[1] = 0;
- selectionLen[0] = selectionLen[1] = 0;
int y = yPad;
acceptClipboard.move(xPad, y);
acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
y += acceptClipboard.height();
- setPrimaryCB.move(xPad + 10, y);
- setPrimaryCB.checked(setPrimary);
- setPrimaryCB.disabled(!acceptClipboard.checked());
- y += setPrimaryCB.height();
sendClipboard.move(xPad, y);
sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
y += sendClipboard.height();
- sendPrimaryCB.move(xPad, y);
- sendPrimaryCB.checked(sendPrimary);
- sendPrimaryCB.disabled(!sendClipboard.checked());
- y += sendPrimaryCB.height();
setEventHandler(this);
toplevel("VNC config", this, 0, 0, 0, iconic);
- XVncExtSelectInput(dpy, win(),
- VncExtClientCutTextMask|
- VncExtSelectionChangeMask|
- VncExtQueryConnectMask);
- XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
- XA_PRIMARY, win(), CurrentTime);
- XConvertSelection(dpy, xaCLIPBOARD, XA_STRING,
- xaCLIPBOARD, win(), CurrentTime);
- if (pollTime != 0)
- pollTimer.start(pollTime);
+ XVncExtSelectInput(dpy, win(), VncExtQueryConnectMask);
}
- // handleEvent(). If we get a ClientCutTextNotify event from Xvnc, set the
- // primary and clipboard selections to the clientCutText. If we get a
- // SelectionChangeNotify event from Xvnc, set the serverCutText to the value
- // of the new selection.
+ // handleEvent()
virtual void handleEvent(TXWindow* w, XEvent* ev) {
- if (acceptClipboard.checked()) {
- if (ev->type == vncExtEventBase + VncExtClientCutTextNotify) {
- XVncExtClientCutTextEvent* cutEv = (XVncExtClientCutTextEvent*)ev;
- if (cutText)
- XFree(cutText);
- cutText = 0;
- if (XVncExtGetClientCutText(dpy, &cutText, &cutTextLen)) {
- vlog.debug("Got client cut text: '%.*s%s'",
- cutTextLen<9?cutTextLen:8, cutText,
- cutTextLen<9?"":"...");
- XStoreBytes(dpy, cutText, cutTextLen);
- if (setPrimaryCB.checked()) {
- ownSelection(XA_PRIMARY, cutEv->time);
- }
- ownSelection(xaCLIPBOARD, cutEv->time);
- delete [] selection[0];
- delete [] selection[1];
- selection[0] = selection[1] = 0;
- selectionLen[0] = selectionLen[1] = 0;
- }
- }
- }
- if (sendClipboard.checked()) {
- if (ev->type == vncExtEventBase + VncExtSelectionChangeNotify) {
- vlog.debug("selection change event");
- XVncExtSelectionChangeEvent* selEv = (XVncExtSelectionChangeEvent*)ev;
- if (selEv->selection == xaCLIPBOARD ||
- (selEv->selection == XA_PRIMARY && sendPrimaryCB.checked())) {
- if (!selectionOwner(selEv->selection))
- XConvertSelection(dpy, selEv->selection, XA_STRING,
- selEv->selection, win(), CurrentTime);
- }
- }
- }
if (ev->type == vncExtEventBase + VncExtQueryConnectNotify) {
vlog.debug("query connection event");
if (queryConnectDialog)
@@ -188,55 +114,6 @@ public:
}
}
}
-
-
- // selectionRequest() is called when we are the selection owner and another X
- // client has requested the selection. We simply put the server's cut text
- // into the requested property. TXWindow will handle the rest.
- bool selectionRequest(Window requestor, Atom selection, Atom property)
- {
- if (cutText)
- XChangeProperty(dpy, requestor, property, XA_STRING, 8,
- PropModeReplace, (unsigned char*)cutText,
- cutTextLen);
- return cutText;
- }
-
- // selectionNotify() is called when we have requested the selection from the
- // selection owner.
- void selectionNotify(XSelectionEvent* ev, Atom type, int format,
- int nitems, void* data)
- {
- if (ev->requestor != win() || ev->target != XA_STRING)
- return;
-
- if (data && format == 8) {
- int i = (ev->selection == XA_PRIMARY ? 0 : 1);
- if (selectionLen[i] == nitems && memcmp(selection[i], data, nitems) == 0)
- return;
- delete [] selection[i];
- selection[i] = new char[nitems];
- memcpy(selection[i], data, nitems);
- selectionLen[i] = nitems;
- if (cutTextLen == nitems && memcmp(cutText, data, nitems) == 0) {
- vlog.debug("ignoring duplicate cut text");
- return;
- }
- if (cutText)
- XFree(cutText);
- cutText = (char*)malloc(nitems); // assuming XFree() same as free()
- if (!cutText) {
- vlog.error("unable to allocate selection buffer");
- return;
- }
- memcpy(cutText, data, nitems);
- cutTextLen = nitems;
- vlog.debug("sending %s selection as server cut text: '%.*s%s'",
- selectionName(ev->selection),cutTextLen<9?cutTextLen:8,
- cutText, cutTextLen<9?"":"...");
- XVncExtSetServerCutText(dpy, cutText, cutTextLen);
- }
- }
// TXDeleteWindowCallback method
virtual void deleteWindow(TXWindow* w) {
@@ -248,25 +125,12 @@ public:
if (checkbox == &acceptClipboard) {
XVncExtSetParam(dpy, (acceptClipboard.checked()
? ACCEPT_CUT_TEXT "=1" : ACCEPT_CUT_TEXT "=0"));
- setPrimaryCB.disabled(!acceptClipboard.checked());
} else if (checkbox == &sendClipboard) {
XVncExtSetParam(dpy, (sendClipboard.checked()
? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
- sendPrimaryCB.disabled(!sendClipboard.checked());
}
}
- // rfb::Timer::Callback interface
- virtual bool handleTimeout(rfb::Timer* timer) {
- if (sendPrimaryCB.checked() && !selectionOwner(XA_PRIMARY))
- XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
- XA_PRIMARY, win(), CurrentTime);
- if (!selectionOwner(xaCLIPBOARD))
- XConvertSelection(dpy, xaCLIPBOARD, XA_STRING,
- xaCLIPBOARD, win(), CurrentTime);
- return true;
- }
-
// QueryResultCallback interface
virtual void queryApproved() {
XVncExtApproveConnect(dpy, queryConnectId, 1);
@@ -276,12 +140,7 @@ public:
}
private:
- char* cutText;
- int cutTextLen;
- char* selection[2];
- int selectionLen[2];
- TXCheckbox acceptClipboard, setPrimaryCB, sendClipboard, sendPrimaryCB;
- rfb::Timer pollTimer;
+ TXCheckbox acceptClipboard, sendClipboard;
QueryConnectDialog* queryConnectDialog;
void* queryConnectId;
diff --git a/unix/vncconfig/vncconfig.man b/unix/vncconfig/vncconfig.man
index 6695be3d..06f9ca97 100644
--- a/unix/vncconfig/vncconfig.man
+++ b/unix/vncconfig/vncconfig.man
@@ -37,14 +37,11 @@ server with the VNC extension. Note that it cannot be used to control VNC
servers prior to version 4.
When run with no options, it runs as a kind of "helper" application for Xvnc.
-Its main purpose when run in this mode is to support clipboard transfer to and
-from the VNC viewer(s). Note that without a running instance of
-\fBvncconfig\fP there will be no clipboard support. It puts up a window with
-some checkboxes which can be used to disable clipboard transfers if required
-(in the future there may be more functions available from this window). The
-\fB-nowin\fP flag can be used if you always want clipboard support but don't
-wish to clutter the desktop with this window - alternatively the \fB-iconic\fP
-option can be used to make it iconified by default.
+Its main purpose when run in this mode is to query the user how new
+connections should be handled (provided this feature is enabled). The
+\fB-nowin\fP flag can be used if you always want the query support but don't
+wish to clutter the desktop with the settings window - alternatively the
+\fB-iconic\fP option can be used to make it iconified by default.
When run in any other mode, \fBvncconfig\fP is a one-shot program used to
configure or control Xvnc as appropriate. It can be used to tell Xvnc to
diff --git a/unix/vncserver b/unix/vncserver
index 8f1f4b7c..674eadad 100755
--- a/unix/vncserver
+++ b/unix/vncserver
@@ -387,8 +387,6 @@ if (-e "/tmp/.X11-unix/X$displayNumber" ||
}
$ENV{VNCDESKTOP}= $desktopName;
-system($exedir."vncconfig -nowin >> " . &quotedString($desktopLog) . " 2>&1 &");
-
if ($opt{'-fg'}) {
if (! $skipxstartup) {
system("$xstartup >> " . &quotedString($desktopLog) . " 2>&1");
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 d6c3ef6a..c9153364 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 f1c9b747..4eaa41f3 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/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man
index a4d9f8d3..f0f90f8f 100644
--- a/unix/xserver/hw/vnc/Xvnc.man
+++ b/unix/xserver/hw/vnc/Xvnc.man
@@ -126,14 +126,8 @@ screen is updated. Otherwise the delay is from the first update. Default
is off.
.
.TP
-.B \-SendCutText
-Send clipboard changes to clients (default is on). Note that you must also run
-\fBvncconfig\fP(1) to get the clipboard to work.
-.
-.TP
.B \-AcceptCutText
-Accept clipboard updates from clients (default is on). Note that you must also
-run \fBvncconfig\fP(1) to get the clipboard to work.
+Accept clipboard updates from clients (default is on).
.
.TP
.B \-MaxCutText \fIbytes\fP
@@ -141,6 +135,15 @@ The maximum size of a clipboard update that will be accepted from a client.
Default is \fB262144\fP.
.
.TP
+.B \-SendCutText
+Send clipboard changes to clients (default is on).
+.
+.TP
+.B \-SendPrimary
+Send the primary selection and cut buffer to the server as well as the
+clipboard selection. Default is on.
+.
+.TP
.B \-AcceptPointerEvents
Accept pointer press and release events from clients (default is on).
.
diff --git a/unix/xserver/hw/vnc/vncExt.c b/unix/xserver/hw/vnc/vncExt.c
index b27115f6..0ee32101 100644
--- a/unix/xserver/hw/vnc/vncExt.c
+++ b/unix/xserver/hw/vnc/vncExt.c
@@ -27,7 +27,6 @@
#include "dixstruct.h"
#include "extnsionst.h"
#include "scrnintstr.h"
-#include "selection.h"
#define _VNCEXT_SERVER_
#define _VNCEXT_PROTO_
@@ -44,17 +43,11 @@ static void vncResetProc(ExtensionEntry* extEntry);
static void vncClientStateChange(CallbackListPtr*, void *, void *);
-static void vncSelectionCallback(CallbackListPtr *callbacks,
- void * data, void * args);
-
static int vncErrorBase = 0;
static int vncEventBase = 0;
int vncNoClipboard = 0;
-static char* clientCutText = NULL;
-static int clientCutTextLen = 0;
-
static struct VncInputSelect* vncInputSelectHead = NULL;
struct VncInputSelect {
@@ -83,10 +76,6 @@ int vncAddExtension(void)
FatalError("Add ClientStateCallback failed\n");
}
- if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0)) {
- FatalError("Add SelectionCallback failed\n");
- }
-
return 0;
}
@@ -121,47 +110,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;
@@ -397,73 +345,6 @@ static int SProcVncExtListParams(ClientPtr client)
return ProcVncExtListParams(client);
}
-static int ProcVncExtSetServerCutText(ClientPtr client)
-{
- REQUEST(xVncExtSetServerCutTextReq);
- REQUEST_FIXED_SIZE(xVncExtSetServerCutTextReq, stuff->textLen);
- vncServerCutText((const char*)&stuff[1], stuff->textLen);
- return (client->noClientException);
-}
-
-static int SProcVncExtSetServerCutText(ClientPtr client)
-{
- REQUEST(xVncExtSetServerCutTextReq);
-#if XORG < 112
- register char n;
- swaps(&stuff->length, n);
-#else
- swaps(&stuff->length);
-#endif
- REQUEST_AT_LEAST_SIZE(xVncExtSetServerCutTextReq);
-#if XORG < 112
- swapl(&stuff->textLen, n);
-#else
- swapl(&stuff->textLen);
-#endif
- 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;
@@ -667,10 +548,6 @@ static int ProcVncExtDispatch(ClientPtr client)
return ProcVncExtGetParamDesc(client);
case X_VncExtListParams:
return ProcVncExtListParams(client);
- case X_VncExtSetServerCutText:
- return ProcVncExtSetServerCutText(client);
- case X_VncExtGetClientCutText:
- return ProcVncExtGetClientCutText(client);
case X_VncExtSelectInput:
return ProcVncExtSelectInput(client);
case X_VncExtConnect:
@@ -696,10 +573,6 @@ static int SProcVncExtDispatch(ClientPtr client)
return SProcVncExtGetParamDesc(client);
case X_VncExtListParams:
return SProcVncExtListParams(client);
- case X_VncExtSetServerCutText:
- return SProcVncExtSetServerCutText(client);
- case X_VncExtGetClientCutText:
- return SProcVncExtGetClientCutText(client);
case X_VncExtSelectInput:
return SProcVncExtSelectInput(client);
case X_VncExtConnect:
@@ -732,39 +605,3 @@ static void vncClientStateChange(CallbackListPtr * l, void * d, void * p)
}
}
}
-
-static void SendSelectionChangeEvent(Atom selection)
-{
- xVncExtSelectionChangeNotifyEvent ev;
- ev.type = vncEventBase + VncExtSelectionChangeNotify;
- for (struct VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
- if (cur->mask & VncExtSelectionChangeMask) {
- ev.sequenceNumber = cur->client->sequence;
- ev.window = cur->window;
- ev.selection = selection;
- if (cur->client->swapped) {
-#if XORG < 112
- int n;
- swaps(&ev.sequenceNumber, n);
- swapl(&ev.window, n);
- swapl(&ev.selection, n);
-#else
- swaps(&ev.sequenceNumber);
- swapl(&ev.window);
- swapl(&ev.selection);
-#endif
- }
- WriteToClient(cur->client, sizeof(xVncExtSelectionChangeNotifyEvent),
- (char *)&ev);
- }
- }
-}
-
-static void vncSelectionCallback(CallbackListPtr *callbacks, void * data, void * args)
-{
- SelectionInfoRec *info = (SelectionInfoRec *) args;
- Selection *selection = info->selection;
-
- SendSelectionChangeEvent(selection->selection);
-}
-
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index 1d374938..0ad34a27 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -38,6 +38,7 @@
#include "vncExtInit.h"
#include "vncHooks.h"
#include "vncBlockHandler.h"
+#include "vncSelection.h"
#include "XorgGlue.h"
using namespace rfb;
@@ -84,6 +85,11 @@ rfb::BoolParameter avoidShiftNumLock("AvoidShiftNumLock",
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)
{
@@ -151,6 +157,8 @@ void vncExtensionInit(void)
if (ret == -1)
return;
+ vncSelectionInit();
+
vlog.info("VNC extension running!");
try {
@@ -274,6 +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++) {
diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h
index be6487c8..a0914ad7 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[];
@@ -59,6 +57,9 @@ void vncCallWriteWakeupHandlers(fd_set * fds, int nfds);
int vncGetAvoidShiftNumLock(void);
+int vncGetSetPrimary(void);
+int vncGetSendPrimary(void);
+
void vncUpdateDesktopName(void);
void vncServerCutText(const char *text, size_t len);
diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c
new file mode 100644
index 00000000..e50548a4
--- /dev/null
+++ b/unix/xserver/hw/vnc/vncSelection.c
@@ -0,0 +1,521 @@
+/* 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);
+}
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