[X2Go-Commits] [x2gokdrive] 01/01: Move selection functionality to separate thread. Use XCB API to manage selections.
git-admin at x2go.org
git-admin at x2go.org
Thu Aug 20 22:45:12 CEST 2020
This is an automated email from the git hooks/post-receive script.
x2go pushed a commit to branch master
in repository x2gokdrive.
commit 3d6295b9f0163e87df733a2490bbbe7d81b66f6d
Author: Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
Date: Thu Aug 20 15:45:02 2020 -0500
Move selection functionality to separate thread. Use XCB API to manage selections.
---
debian/changelog | 2 +
x2gokdriveinit.c | 2 +
x2gokdriveremote.c | 362 ++++++++------
x2gokdriveremote.h | 75 +--
x2gokdriveselection.c | 1252 +++++++++++++++++++++++++++++--------------------
x2gokdriveselection.h | 18 +-
6 files changed, 1052 insertions(+), 659 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index 3ca47a9..152b81b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -9,6 +9,8 @@ x2gokdrive (0.0.0.1-0x2go1) UNRELEASED; urgency=medium
* Calculate screen dimensions (in mm) according to dpi value.
Default DPI (Xorg) is 75. User can set DPI using -dpi command
line option.
+ - Move selection functionality to separate thread. Use XCB API to manage
+ selections.
[ Mihai Moldovan ]
* Initial release:
diff --git a/x2gokdriveinit.c b/x2gokdriveinit.c
index bfad59f..f37e90b 100644
--- a/x2gokdriveinit.c
+++ b/x2gokdriveinit.c
@@ -256,6 +256,8 @@ ddxProcessArgument(int argc, char **argv, int i)
/* compat with nxagent */
return 1;
}
+ else if (argv[i][0] == ':')
+ remote_set_display_name(argv[i]);
return KdProcessArgument(argc, argv, i);
}
diff --git a/x2gokdriveremote.c b/x2gokdriveremote.c
index ac524f0..7732deb 100644
--- a/x2gokdriveremote.c
+++ b/x2gokdriveremote.c
@@ -528,17 +528,44 @@ int send_deleted_cursors(void)
return sent;
}
-int send_selection(int sel, char* data, uint32_t length, uint32_t format)
+int send_output_selection(outputChunk* chunk)
+{
+ //client supports extended selections
+ if(remoteVars.selstruct.clientSupportsExetndedSelection)
+ {
+ //send extended selection
+ return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, chunk->firstChunk, chunk->lastChunk, chunk->compressed, chunk->totalSize);
+ }
+ else
+ {
+ //older client doesn't support only uncompressed datas in single chunk
+ //not sending chunk in other case
+ if(!chunk->compressed && chunk->firstChunk && chunk->lastChunk)
+ {
+ return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, TRUE, TRUE, FALSE, chunk->size);
+ }
+ EPHYR_DBG("Client doesn't support extended selection, not sending this chunk");
+ }
+ return 0;
+}
+
+int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, BOOL compressed, uint32_t total)
{
unsigned char buffer[56] = {0};
_X_UNUSED int ln = 0;
int l = 0;
int sent = 0;
- *((uint32_t*)buffer)=SELECTION;
- *((uint32_t*)buffer+1)=sel;
- *((uint32_t*)buffer+2)=format;
- *((uint32_t*)buffer+3)=length;
+
+ *((uint32_t*)buffer)=SELECTION; //0
+ *((uint32_t*)buffer+1)=sel; //4
+ *((uint32_t*)buffer+2)=format; //8
+ *((uint32_t*)buffer+3)=length; //16
+ *((uint32_t*)buffer+4)=first; //20
+ *((uint32_t*)buffer+5)=last; //24
+ *((uint32_t*)buffer+6)=compressed; //28
+ *((uint32_t*)buffer+7)=total; //28
+
// #warning check this
ln=write(remoteVars.clientsock,buffer,56);
@@ -1470,7 +1497,7 @@ void *send_frame_thread (void *threadid)
remoteVars.client_connected=TRUE;
- if(!remoteVars.first_sendqueue_element && !remoteVars.firstCursor)
+ if(!remoteVars.first_sendqueue_element && !remoteVars.firstCursor && !remoteVars.selstruct.firstOutputChunk)
{
/* sleep if frame queue is empty */
pthread_cond_wait(&remoteVars.have_sendqueue_cond, &remoteVars.sendqueue_mutex);
@@ -1478,6 +1505,30 @@ void *send_frame_thread (void *threadid)
/* mutex is locked on this point */
+ //only send output selection chunks if there are no frames and cursors in the queue
+ //selections can take a lot of bandwidth and have less priority
+ if(remoteVars.selstruct.firstOutputChunk && !remoteVars.first_sendqueue_element && !remoteVars.firstCursor)
+ {
+ //get chunk from queue
+ outputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
+ remoteVars.selstruct.firstOutputChunk=(outputChunk*)chunk->next;
+ if(!remoteVars.selstruct.firstOutputChunk)
+ {
+ remoteVars.selstruct.lastOutputChunk=NULL;
+ }
+ pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+ //send chunk
+ send_output_selection(chunk);
+ //free chunk and it's data
+ if(chunk->data)
+ {
+ free(chunk->data);
+ }
+// EPHYR_DBG(" REMOVE CHUNK %p %p %p", remoteVars.selstruct.firstOutputChunk, remoteVars.selstruct.lastOutputChunk, chunk);
+ free(chunk);
+ pthread_mutex_lock(&remoteVars.sendqueue_mutex);
+ }
+
if(remoteVars.firstCursor)
{
/* get cursor from queue, delete it from queue, unlock mutex and send cursor. After sending free cursor */
@@ -1499,40 +1550,6 @@ void *send_frame_thread (void *threadid)
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
- pthread_mutex_lock(&remoteVars.sendqueue_mutex);
- if(remoteVars.selstruct.clipboard.changed)
- {
- size_t sz=remoteVars.selstruct.clipboard.size;
- char* data=malloc(sz);
- int format = 0;
-
- memcpy(data, remoteVars.selstruct.clipboard.data, sz);
- remoteVars.selstruct.clipboard.changed=FALSE;
- format=remoteVars.selstruct.clipboard.mimeData;
- pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
- send_selection(CLIPBOARD,data,sz, format);
- free(data);
- }
- else
- pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
-
- pthread_mutex_lock(&remoteVars.sendqueue_mutex);
- if(remoteVars.selstruct.selection.changed)
- {
- size_t sz=remoteVars.selstruct.selection.size;
- char* data=malloc(sz);
- int format = 0;
-
- memcpy(data, remoteVars.selstruct.selection.data, sz);
- remoteVars.selstruct.selection.changed=FALSE;
- format=remoteVars.selstruct.selection.mimeData;
- pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
- send_selection(PRIMARY, data, sz, format);
- free(data);
- }
- else
- pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
-
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
if(remoteVars.first_sendqueue_element)
{
@@ -1637,6 +1654,25 @@ void *send_frame_thread (void *threadid)
pthread_exit(0);
}
+/* warning! sendqueue_mutex should be locked by thread calling this function! */
+void clear_output_selection(void)
+{
+ outputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
+ outputChunk* prev_chunk;
+
+ while(chunk)
+ {
+ prev_chunk=chunk;
+ chunk=(outputChunk*)chunk->next;
+ if(prev_chunk->data)
+ {
+ free(prev_chunk->data);
+ }
+ free(prev_chunk);
+ }
+ remoteVars.selstruct.firstOutputChunk=remoteVars.selstruct.lastOutputChunk=NULL;
+}
+
/* warning! sendqueue_mutex should be locked by thread calling this function! */
static
void clear_send_queue(void)
@@ -1797,10 +1833,132 @@ void disconnect_client(void)
clear_send_queue();
clear_frame_cache(0);
freeCursors();
+ clear_output_selection();
pthread_cond_signal(&remoteVars.have_sendqueue_cond);
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
+
+void readInputSelectionBuffer(char* buff)
+{
+ //read th rest of the chunk data
+
+ inputBuffer* selbuff = &remoteVars.selstruct.inSelection[remoteVars.selstruct.readingInputBuffer];
+
+ int leftToRead=selbuff->currentChunkSize - selbuff->currentChunkBytesReady;
+
+
+ int l=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
+
+ //lock input selection
+ pthread_mutex_lock(&remoteVars.selstruct.inMutex);
+
+ //copy data to selection
+ memcpy(selbuff->data+selbuff->bytesReady, buff, l);
+ selbuff->bytesReady+=l;
+ selbuff->currentChunkBytesReady+=l;
+
+ if(selbuff->bytesReady==selbuff->size)
+ {
+ //selection buffer received completely
+// EPHYR_DBG("READY Selection %d, MIME %d, Read %d from %d", remoteVars.selstruct.readingInputBuffer, selbuff->mimeData, selbuff->bytesReady, selbuff->size);
+ //send notify to system that we are using selection
+ own_selection(remoteVars.selstruct.readingInputBuffer);
+
+ }
+ if(selbuff->currentChunkBytesReady==selbuff->currentChunkSize)
+ {
+ //selection chunk received completely, next event will start with event header
+// EPHYR_DBG("READY Selection Chunk, read %d",selbuff->currentChunkSize);
+ remoteVars.selstruct.readingInputBuffer=-1;
+ }
+ //unlock selection
+ pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
+}
+
+void readInputSelectionHeader(char* buff)
+{
+
+ //read the input selection event.
+ //The event represents one chunk of imput selection
+ //it has a header and some data of the chunk
+
+ uint32_t size, totalSize;
+ uint8_t destination, mime;
+ inputBuffer* selbuff = NULL;
+ BOOL firstChunk=FALSE, lastChunk=FALSE;
+ BOOL compressed=FALSE;
+ uint32_t headerSize=10;
+ uint32_t l;
+
+ size=*((uint32_t*)buff+1);
+ destination=*((uint8_t*)buff+8);
+ mime=*((uint8_t*)buff+9);
+
+ //if client supports ext selection, read extended header
+ if(remoteVars.selstruct.clientSupportsExetndedSelection)
+ {
+ headerSize=17;
+ firstChunk=*((uint8_t*)buff + 10);
+ lastChunk=*((uint8_t*)buff + 11);
+ compressed=*((uint8_t*)buff + 12);
+ totalSize=*( (uint32_t*) (buff+13));
+ }
+ else
+ {
+ compressed=FALSE;
+ lastChunk=firstChunk=TRUE;
+ totalSize=size;
+ }
+
+// EPHYR_DBG("HAVE NEW INCOMING SELECTION Chunk: sel %d size %d mime %d",destination, size, mime);
+
+ //lock selection
+ pthread_mutex_lock(&remoteVars.selstruct.inMutex);
+
+ remoteVars.selstruct.readingInputBuffer=-1;
+
+ selbuff = &remoteVars.selstruct.inSelection[destination];
+ if(firstChunk)
+ {
+ //if it's first chunk, initialize our selection buffer
+ if(selbuff->size && selbuff->data)
+ {
+ free(selbuff->data);
+ }
+ selbuff->size=totalSize;
+ selbuff->mimeData=mime;
+ selbuff->data=malloc(totalSize);
+ selbuff->bytesReady=0;
+ }
+
+ //read the selection data from header
+ l=(size < EVLENGTH-headerSize)?size:(EVLENGTH-headerSize);
+ memcpy(selbuff->data+selbuff->bytesReady, buff+headerSize, l);
+
+ selbuff->bytesReady+=l;
+
+ selbuff->currentChunkBytesReady=l;
+ selbuff->currentChunkSize=size;
+
+ if(selbuff->size == selbuff->bytesReady)
+ {
+ //Selection is completed
+// EPHYR_DBG("READY INCOMING SELECTION for %d",destination);
+ //own the selection
+ own_selection(destination);
+ }
+
+ if(selbuff->currentChunkBytesReady != selbuff->currentChunkSize)
+ {
+ // we didn't recieve complete chunk yet, next event will have data
+ remoteVars.selstruct.readingInputBuffer=destination;
+// EPHYR_DBG("READ INCOMING BUFFER %d, read %d from %d", destination, selbuff->bytesReady, selbuff->size);
+ }
+ //unlock selection
+ pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
+}
+
void
clientReadNotify(int fd, int ready, void *data)
{
@@ -1839,38 +1997,9 @@ clientReadNotify(int fd, int ready, void *data)
{
char* buff=remoteVars.eventBuffer+i*EVLENGTH;
- if(remoteVars.selstruct.readingInputBuffer)
+ if(remoteVars.selstruct.readingInputBuffer != -1)
{
- int leftToRead=remoteVars.selstruct.inBuffer.size - remoteVars.selstruct.inBuffer.position;
- int chunk=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
-
- memcpy(remoteVars.selstruct.inBuffer.data+remoteVars.selstruct.inBuffer.position, buff, chunk);
- remoteVars.selstruct.inBuffer.position+=chunk;
- if(! (remoteVars.selstruct.inBuffer.position < remoteVars.selstruct.inBuffer.size))
- {
- inputBuffer* selbuff;
- if(remoteVars.selstruct.inBuffer.target==PRIMARY)
- {
- selbuff=&remoteVars.selstruct.inSelection;
- }
- else
- {
- selbuff=&remoteVars.selstruct.inClipboard;
- }
- if(selbuff->data)
- free(selbuff->data);
- selbuff->target=remoteVars.selstruct.inBuffer.target;
- selbuff->data=remoteVars.selstruct.inBuffer.data;
- remoteVars.selstruct.inBuffer.data=NULL;
- selbuff->size=remoteVars.selstruct.inBuffer.size;
- remoteVars.selstruct.inBuffer.size=0;
- selbuff->mimeData=remoteVars.selstruct.inBuffer.mimeData;
- remoteVars.selstruct.readingInputBuffer=FALSE;
- EPHYR_DBG("READY TARGET %d, MIME %d, Read %d from %d",remoteVars.selstruct.inBuffer.target, selbuff->mimeData,
- remoteVars.selstruct.inBuffer.position, selbuff->size);
- own_selection(selbuff->target);
- }
-// EPHYR_DBG("CHUNK IS DONE %d",remoteVars.selstruct.readingInputBuffer);
+ readInputSelectionBuffer(buff);
}
else
{
@@ -2034,47 +2163,7 @@ clientReadNotify(int fd, int ready, void *data)
}
case SELECTIONEVENT:
{
- uint32_t size;
- uint8_t destination, mime;
- inputBuffer* selbuff = NULL;
-
- size=*((uint32_t*)buff+1);
- destination=*((uint8_t*)buff+8);
- mime=*((uint8_t*)buff+9);
-
- EPHYR_DBG("HAVE NEW INCOMING SELECTION: %d %d %d",size, destination, mime);
-
- if(destination==CLIPBOARD)
- selbuff=&(remoteVars.selstruct.inClipboard);
- else
- selbuff=&(remoteVars.selstruct.inSelection);
- if(size < EVLENGTH-10)
- {
- if(selbuff->data)
- free(selbuff->data);
- selbuff->size=size;
- selbuff->data=malloc(size);
- selbuff->target=destination;
- memcpy(selbuff->data, buff+10, size);
- selbuff->mimeData=mime;
- EPHYR_DBG("READY INCOMING SELECTION for %d",destination);
- own_selection(selbuff->target);
-
- }
- else
- {
- selbuff=&(remoteVars.selstruct.inBuffer);
- if(selbuff->data)
- free(selbuff->data);
- selbuff->data=malloc(size);
- memcpy(selbuff->data, buff+10, EVLENGTH-10);
- selbuff->mimeData=mime;
- selbuff->target=destination;
- selbuff->position=EVLENGTH-10;
- selbuff->size=size;
- remoteVars.selstruct.readingInputBuffer=TRUE;
- EPHYR_DBG("READ INCOMING BUFFER %d from %d",EVLENGTH-10, size);
- }
+ readInputSelectionHeader(buff);
break;
}
default:
@@ -2201,6 +2290,15 @@ void terminateServer(int exitStatus)
{
pthread_cancel(remoteVars.send_thread_id);
}
+ if(remoteVars.selstruct.selThreadId)
+ {
+ pthread_cancel(remoteVars.selstruct.selThreadId);
+ if(remoteVars.selstruct.xcbConnection)
+ {
+ xcb_disconnect(remoteVars.selstruct.xcbConnection);
+ }
+ pthread_mutex_destroy(&remoteVars.selstruct.inMutex);
+ }
pthread_mutex_destroy(&remoteVars.mainimg_mutex);
pthread_mutex_destroy(&remoteVars.sendqueue_mutex);
@@ -2212,27 +2310,14 @@ void terminateServer(int exitStatus)
free(remoteVars.second_buffer);
}
- if(remoteVars.selstruct.clipboard.data)
- {
- free(remoteVars.selstruct.clipboard.data);
- }
-
- if(remoteVars.selstruct.selection.data)
- {
- free(remoteVars.selstruct.selection.data);
- }
- if(remoteVars.selstruct.inClipboard.data)
+ if(remoteVars.selstruct.inSelection[0].data)
{
- free(remoteVars.selstruct.inClipboard.data);
+ free(remoteVars.selstruct.inSelection[0].data);
}
- if(remoteVars.selstruct.inSelection.data)
+ if(remoteVars.selstruct.inSelection[1].data)
{
- free(remoteVars.selstruct.inSelection.data);
- }
- if(remoteVars.selstruct.inBuffer.data)
- {
- free(remoteVars.selstruct.inBuffer.data);
+ free(remoteVars.selstruct.inSelection[1].data);
}
setAgentState(TERMINATED);
EPHYR_DBG("exit program with status %d", exitStatus);
@@ -2372,6 +2457,7 @@ remote_init(void)
remoteVars.jpegQuality=JPG_QUALITY;
remoteVars.compression=DEFAULT_COMPRESSION;
+ remoteVars.selstruct.selectionMode = CLIP_BOTH;
pthread_mutex_init(&remoteVars.mainimg_mutex, NULL);
pthread_mutex_init(&remoteVars.sendqueue_mutex,NULL);
@@ -3085,6 +3171,18 @@ uint32_t calculate_crc(uint32_t width, uint32_t height, int32_t dx, int32_t dy)
return crc;
}
+void remote_set_display_name(const char* name)
+{
+ int max_len=256;
+ if(strlen(name)<max_len)
+ {
+ max_len=strlen(name);
+ }
+ strncpy(RemoteHostVars.displayName, name, max_len);
+ RemoteHostVars.displayName[max_len]='\0';
+ EPHYR_DBG("DISPLAY name: %s",RemoteHostVars.displayName);
+}
+
void *
remote_screen_init(KdScreenInfo *screen,
int x, int y,
@@ -3097,10 +3195,10 @@ remote_screen_init(KdScreenInfo *screen,
//but we need to reinstall it by the next screen init.
EPHYR_DBG("REMOTE SCREEN INIT!!!!!!!!!!!!!!!!!!");
- if(remoteVars.selstruct.callBackInstalled)
+ if(remoteVars.selstruct.threadStarted)
{
- EPHYR_DBG("SKIPPING CALLBACK INSTALL");
- remoteVars.selstruct.callBackInstalled=FALSE;
+ EPHYR_DBG("SKIPPING Selection CALLBACK INSTALL");
+// remoteVars.selstruct.callBackInstalled=FALSE;
}
else
{
diff --git a/x2gokdriveremote.h b/x2gokdriveremote.h
index c574035..a06e692 100644
--- a/x2gokdriveremote.h
+++ b/x2gokdriveremote.h
@@ -259,40 +259,55 @@ struct sendqueue_element
struct sendqueue_element* next;
};
+//input selection
typedef struct
{
- unsigned char* data;
- uint32_t size;
- int mimeData;
- uint32_t position;
- int target;
+ unsigned char* data; //data
+ uint32_t size; //total size of selection
+ uint32_t bytesReady; //how many bytes already read
+ uint32_t currentChunkSize; //size of chunk we reading now;
+ uint32_t currentChunkBytesReady; //how many bytes of current chunk are ready;
+ enum SelectionMime mimeData; //UTF_STRING or PIXMAP
+ xcb_timestamp_t timestamp; //ts when we own selection
+ BOOL owner; //if we are the owners of selection
}inputBuffer;
+
+//chunk of data with output selection
typedef struct
{
- unsigned char* data;
- uint32_t size;
- int mimeData;
- BOOL changed;
-
-}outputBuffer;
+ unsigned char* data; //data
+ uint32_t size; //size of chunk in B
+ enum SelectionMime mimeData; //UTF_STRING or PIXMAP (text or image)
+ BOOL compressed; // if chunk is compressed
+ BOOL firstChunk; // if it's a first chunk in selection
+ BOOL lastChunk; // if it's a last chunk in selection
+ enum SelectionType selection; //PRIMARY or CLIPBOARD
+ uint32_t totalSize; //the total size of the selection data
+ struct outputChunk* next; //next chunk in the queue
+}outputChunk;
typedef struct
{
- BOOL readingIncremental;
- uint32_t incrementalPosition;
- Window clipWinId;
- WindowPtr clipWinPtr;
- BOOL callBackInstalled;
- unsigned char selectionMode;
-//Output selection
- outputBuffer clipboard;
- outputBuffer selection;
-//Input selection
- BOOL readingInputBuffer;
- inputBuffer inBuffer;
- inputBuffer inSelection;
- inputBuffer inClipboard;
+ unsigned long selThreadId; //id of selection thread
+ enum ClipboardMode selectionMode; //CLIP_NONE, CLIP_CLIENT, CLIP_SERVER, CLIP_BOTH
+
+ //output selection members
+ uint32_t incrementalSize; //the total size of INCR selection we are currently reading
+ uint32_t incrementalSizeRead; //bytes already read
+ xcb_window_t clipWinId; // win id of clipboard window
+ xcb_connection_t* xcbConnection; //XCB connection
+ BOOL threadStarted; //if selection thread already started
+ BOOL clientSupportsExetndedSelection; //if client supports extended selection
+ xcb_atom_t incrAtom; //mime type of the incr selection we are reading
+ xcb_atom_t currentSelection; //selection we are currently reading
+ outputChunk* firstOutputChunk; //the first and last elements of the
+ outputChunk* lastOutputChunk; //queue of selection chunks
+
+ //Input selection members
+ int readingInputBuffer; //which selection are reading input buffer at the moments: PRIMARY, CLIPBOARD or -1 if none
+ inputBuffer inSelection[2]; //PRIMARY an CLIPBOARD selection buffers
+ pthread_mutex_t inMutex; //mutex for synchronization of incoming selection
}SelectionStructure;
@@ -306,6 +321,7 @@ struct _remoteHostVars
char stateFile[256];
char acceptAddr[256];
char cookie[33];
+ char displayName[256];
int listenPort;
int jpegQuality;
uint32_t framenum;
@@ -374,7 +390,11 @@ struct _remoteHostVars
SelectionStructure selstruct;
} RemoteHostVars;
-int send_selection(int sel, char* data, uint32_t length, uint32_t mimeData);
+int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, BOOL compressed, uint32_t total);
+int send_output_selection(outputChunk* chunk);
+
+void readInputSelectionBuffer(char* buff);
+void readInputSelectionHeader(char* buff);
#if XORG_VERSION_CURRENT < 11900000
void pollEvents(void);
@@ -414,7 +434,7 @@ void clientReadNotify(int fd, int ready, void *data);
void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t crc, uint32_t size);
-
+void clear_output_selection(void);
void disconnect_client(void);
@@ -432,6 +452,7 @@ int remote_init(void);
void remote_selection_init(void);
+void remote_set_display_name(const char* name);
void *remote_screen_init(KdScreenInfo *screen,
int x, int y,
diff --git a/x2gokdriveselection.c b/x2gokdriveselection.c
index 39129c6..e174aff 100644
--- a/x2gokdriveselection.c
+++ b/x2gokdriveselection.c
@@ -25,659 +25,913 @@
*
*/
+#include <xcb/xcb.h>
+#include <xcb/xfixes.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <png.h>
+
+
#ifdef HAVE_CONFIG_H
#include <dix-config.h>
#if XORG_VERSION_CURRENT < 11999901
#include <kdrive-config.h>
-#endif /* XORG_VERSION_CURRENT */
+#endif // XORG_VERSION_CURRENT
#endif
-
#include "x2gokdriveselection.h"
-
-#include "selection.h"
-#include <X11/Xatom.h>
-#include "propertyst.h"
-#include "xace.h"
-
static struct _remoteHostVars *remoteVars = NULL;
-static Atom atomPrimary, atomClipboard, atomTargets, atomString, atomUTFString, atomTimestamp = {0};
-static Atom imageAtom = 0;
-static Atom atomJPEG, atomJPG = {0};
-
-static int (*proc_send_event_orig)(ClientPtr);
-static int (*proc_convert_selection_orig)(ClientPtr);
-static int (*proc_change_property_orig)(ClientPtr);
+static xcb_atom_t ATOM_CLIPBOARD;
-static int create_selection_window(void);
-
-int own_selection(int target)
+uint32_t max_chunk(void)
{
- Selection *pSel = NULL;
- SelectionInfoRec info = {0};
- Atom selection=atomPrimary;
- int rc;
+ if(remoteVars->selstruct.clientSupportsExetndedSelection)
+ return 1024*100/4; //100KB
+ else
+ return 10*1024*1024/4; //10MB
+}
- if(remoteVars->selstruct.selectionMode == CLIP_SERVER || remoteVars->selstruct.selectionMode == CLIP_NONE)
+int own_selection(enum SelectionType selection)
+{
+ xcb_atom_t sel=XCB_ATOM_PRIMARY;
+ if(remoteVars->selstruct.selectionMode == CLIP_NONE || remoteVars->selstruct.selectionMode == CLIP_SERVER)
{
- EPHYR_DBG("CLIENT selection disabled, not accepting selection event");
- return Success;
+ EPHYR_DBG("Client selection is disabled");
+ return 0;
}
-
- if(target==CLIPBOARD)
- selection=atomClipboard;
-
-
- rc = create_selection_window();
-
- if (rc != Success)
- return rc;
-
- rc = dixLookupSelection(&pSel, selection, serverClient, DixSetAttrAccess);
- if (rc == Success)
+ if(selection!=PRIMARY)
{
- 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);
- }
+ sel=ATOM_CLIPBOARD;
}
- else if (rc == BadMatch)
- {
- pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION);
- if (!pSel)
- return BadAlloc;
+ xcb_set_selection_owner(remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, sel, XCB_CURRENT_TIME);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ remoteVars->selstruct.inSelection[selection].owner=TRUE;
+ remoteVars->selstruct.inSelection[selection].timestamp=XCB_CURRENT_TIME;
+ return 0;
+}
+void selection_init(struct _remoteHostVars *obj)
+{
+ remoteVars=obj;
+ return;
+}
- pSel->selection = selection;
- rc = XaceHookSelectionAccess(serverClient, &pSel,
- DixCreateAccess | DixSetAttrAccess);
- if (rc != Success)
- {
- free(pSel);
- return rc;
- }
+xcb_atom_t atom(const char* name)
+{
+ //get atom for the name, return 0 if not found
+ xcb_intern_atom_cookie_t cookie;
+ xcb_intern_atom_reply_t *reply;
+ xcb_atom_t a=0;
- pSel->next = CurrentSelections;
- CurrentSelections = pSel;
+ cookie = xcb_intern_atom(remoteVars->selstruct.xcbConnection, 0, strlen(name), name);
+ if ((reply = xcb_intern_atom_reply(remoteVars->selstruct.xcbConnection, cookie, NULL)))
+ {
+ a=reply->atom;
+// EPHYR_DBG("The %s atom has ID %u", name, a);
+ free(reply);
}
- else
- return rc;
-
- pSel->lastTimeChanged = currentTime;
- pSel->window = remoteVars->selstruct.clipWinId;
- pSel->pWin = remoteVars->selstruct.clipWinPtr;
- pSel->client = serverClient;
-
- EPHYR_DBG("OWN selection: %s", NameForAtom(selection));
-
- info.selection = pSel;
- info.client = serverClient;
- info.kind = SelectionSetOwner;
- CallCallbacks(&SelectionCallback, &info);
-
- return Success;
+ return a;
}
-
-static int create_selection_window(void)
+char *atom_name(xcb_atom_t xatom)
{
- int result = -1;
-
- if(remoteVars->selstruct.clipWinPtr)
- return Success;
-
-
- remoteVars->selstruct.clipWinId = FakeClientID(0);
-
- remoteVars->selstruct.clipWinPtr = CreateWindow(remoteVars->selstruct.clipWinId, remoteVars->ephyrScreen->pScreen->root,
- 0, 0, 100, 100, 0, InputOnly,
- 0, NULL, 0, serverClient,
- CopyFromParent, &result);
- if (!remoteVars->selstruct.clipWinPtr)
+ //get name for atom, don't forget to free return value
+ char* name;
+ xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(remoteVars->selstruct.xcbConnection, xatom);
+ xcb_get_atom_name_reply_t *reply=xcb_get_atom_name_reply(remoteVars->selstruct.xcbConnection, cookie, NULL);
+
+ if(!reply)
+ return NULL;
+ if(!reply->name_len)
{
- EPHYR_DBG("Can't create selection window");
- return result;
+ free(reply);
+ return NULL;
}
-
- if (!AddResource(remoteVars->selstruct.clipWinPtr->drawable.id, RT_WINDOW, remoteVars->selstruct.clipWinPtr))
- return BadAlloc;
-
- EPHYR_DBG("Selection window created %d, %p",remoteVars->selstruct.clipWinId, remoteVars->selstruct.clipWinPtr);
- return Success;
+ name=malloc( xcb_get_atom_name_name_length(reply)+1);
+ strncpy(name, xcb_get_atom_name_name(reply),xcb_get_atom_name_name_length(reply));
+ name [xcb_get_atom_name_name_length(reply)]='\0';
+ free(reply);
+ return name;
}
-
-static void request_selection(Atom selection, Atom rtype)
+static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, const char *name)
{
- xEvent ev = {{{0}}};
- Selection *selPtr = NULL;
- int rc = -1;
+ //check if the atom which represents "name" is in the list of supported mime types
+ size_t i = 0;
+ xcb_atom_t a=atom(name);
+ if(!a)
+ return 0;
- rc = create_selection_window();
- if (rc != Success)
+ for (i = 0;i < size;i++)
{
- EPHYR_DBG("ERROR! Can't create selection Window");
- return;
+ if (list[i]==a)
+ return a;
}
-
- /*EPHYR_DBG("Request: %s, %s",
- * NameForAtom(selection), NameForAtom(rtype));*/
-
- rc = dixLookupSelection(&selPtr, selection, serverClient, DixGetAttrAccess);
- if (rc != Success)
- return;
- ev.u.u.type = SelectionRequest;
- ev.u.selectionRequest.owner = selPtr->window;
- ev.u.selectionRequest.time = currentTime.milliseconds;
- ev.u.selectionRequest.requestor = remoteVars->selstruct.clipWinId;
- ev.u.selectionRequest.selection = selection;
- ev.u.selectionRequest.target = rtype;
- ev.u.selectionRequest.property = rtype;
- WriteEventsToClient(selPtr->client, 1, &ev);
+ return 0;
}
-
-
-static void selection_callback(CallbackListPtr *callbacks,
- void * data, void * args)
+static int is_string_atom( xcb_atom_t at)
{
- SelectionInfoRec *info = (SelectionInfoRec *) args;
-
- if (info->kind != SelectionSetOwner)
- return;
- if (info->client == serverClient)
- return;
-
- /*
- * EPHYR_DBG("Selection owner changed: %s",
- * NameForAtom(info->selection->selection));*/
-
- if ((info->selection->selection != atomPrimary) &&
- (info->selection->selection != atomClipboard))
- return;
-
- if(remoteVars->selstruct.selectionMode == CLIP_BOTH || remoteVars->selstruct.selectionMode == CLIP_SERVER)
- request_selection(info->selection->selection, atomTargets);
+ //check if selection data is string/text
+ if(!at)
+ return 0;
+ if( at == atom("UTF8_STRING") ||
+ at == atom("text/plain;charset=utf-8") ||
+ at == atom("STRING") ||
+ at == atom("TEXT") ||
+ at == atom("text/plain"))
+ return 1;
+ return 0;
}
-static Atom find_atom_by_name(const char* name, const Atom list[], size_t size)
+static int is_image_atom( xcb_atom_t at)
{
- for (int i = 0;i < size;i++)
- {
- if(!strcmp(name, NameForAtom (list[i])))
- {
- EPHYR_DBG("Found IMAGE ATOM %s:%d", NameForAtom (list[i]), list[i]);
- return list [i];
- }
- }
+ //check if selection data is image
+ if(!at)
+ return 0;
+ if( at == atom("image/png") ||
+ at == atom("image/xpm") ||
+ at == atom("image/jpg") ||
+ at == atom("image/jpeg") ||
+ at == atom("PIXMAP") ||
+ at == atom("image/bmp"))
+ return 1;
return 0;
}
-static BOOL find_image_atom(const Atom list[], size_t size)
+static xcb_atom_t best_atom_from_target(xcb_atom_t* list, size_t size)
{
- Atom at = {0};
- at=find_atom_by_name("image/jpg",list,size);
- if(at)
+ //here we chose the best of supported formats for selection
+ xcb_atom_t a;
+ //selecting utf formats first
+ if((a=target_has_atom(list, size, "UTF8_STRING")))
+ {
+// EPHYR_DBG("selecting mime type UTF8_STRING");
+ return a;
+ }
+ if((a=target_has_atom(list, size, "text/plain;charset=utf-8")))
{
- imageAtom=at;
- return TRUE;
+// EPHYR_DBG("selecting mime type text/plain;charset=utf-8");
+ return a;
}
- at=find_atom_by_name("image/jpeg",list,size);
- if(at)
+ if((a=target_has_atom(list, size, "STRING")))
{
- imageAtom=at;
- return TRUE;
+// EPHYR_DBG( "selecting mime type STRING");
+ return a;
}
- at=find_atom_by_name("image/png",list,size);
- if(at)
+ if((a=target_has_atom(list, size, "TEXT")))
{
- imageAtom=at;
- return TRUE;
+// EPHYR_DBG( "selecting mime type TEXT");
+ return a;
}
- at=find_atom_by_name("image/bmp",list,size);
- if(at)
+ if((a=target_has_atom(list, size, "text/plain")))
{
- imageAtom=at;
- return TRUE;
+// EPHYR_DBG( "selecting mime type text/plain");
+ return a;
}
- at=find_atom_by_name("image/xpm",list,size);
- if(at)
+
+ //selecting loseless formats first
+ if((a=target_has_atom(list, size, "image/png")))
{
- imageAtom=at;
- return TRUE;
+// EPHYR_DBG( "selecting mime type image/png");
+ return a;
}
- at=find_atom_by_name("PIXMAP",list,size);
- if(at)
+ if((a=target_has_atom(list, size, "image/xpm")))
{
- imageAtom=at;
- return TRUE;
+// EPHYR_DBG( "selecting mime type image/xpm");
+ return a;
}
- at=find_atom_by_name("image/ico",list,size);
- if(at)
+ if((a=target_has_atom(list, size, "PIXMAP")))
{
- imageAtom=at;
- return TRUE;
+// EPHYR_DBG( "selecting mime type PIXMAP");
+ return a;
}
-
- imageAtom=0;
- return FALSE;
-
+ if((a=target_has_atom(list, size, "image/bmp")))
+ {
+// EPHYR_DBG( "selecting mime type image/bmp");
+ return a;
+ }
+ if((a=target_has_atom(list, size, "image/jpg")))
+ {
+// EPHYR_DBG( "selecting mime type image/jpg");
+ return a;
+ }
+ if((a=target_has_atom(list, size, "image/jpeg")))
+ {
+// EPHYR_DBG( "selecting mime type image/jpeg");
+ return a;
+ }
+ return 0;
}
-//static void listAtoms(const Atom list[], size_t size)
-//{
-// size_t i;
-//
-// for (i = 0;i < size;i++)
-// {
-// EPHYR_DBG("%d:%s", list[i], NameForAtom( list[i]));
-// }
-//}
-
-static Bool prop_has_atom(Atom atom, const Atom list[], size_t size)
+void request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t)
{
- size_t i = 0;
-
- for (i = 0;i < size;i++) {
- if (list[i] == atom)
- return TRUE;
+ //execute convert selection for primary or clipboard to get mimetypes or data (depends on target atom)
+ if(!t)
+ t=XCB_CURRENT_TIME;
+ if(property)
+ {
+ xcb_delete_property(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId,property);
}
-
- return FALSE;
+ xcb_convert_selection(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId,selection, target, property, t);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
}
-static void process_selection(Atom selection, Atom target,
- Atom property, Atom requestor,
- TimeStamp time)
+void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
{
- PropertyPtr prop;
- int rc = -1;
-
- rc = dixLookupProperty(&prop, remoteVars->selstruct.clipWinPtr, property,
- serverClient, DixReadAccess);
-
- if(rc== BadMatch)
+ xcb_atom_t* tg;
+ char* stype, *sprop;
+ xcb_atom_t data_atom;
+ unsigned int bytes_left, bytes_read=0;
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+ outputChunk* chunk;
+
+
+ //request property which represents value of selection (data or mime types)
+ //get max 100K of data, we don't need to send more than that over network for perfomance reasons
+ cookie= xcb_get_property(remoteVars->selstruct.xcbConnection,FALSE, remoteVars->selstruct.clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, 0, max_chunk());
+ reply=xcb_get_property_reply(remoteVars->selstruct.xcbConnection, cookie, NULL);
+ if(!reply)
{
- EPHYR_DBG("BAD MATCH!!!");
+// EPHYR_DBG( "NULL reply");
}
-
- if (rc != Success)
- return;
-
- EPHYR_DBG("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 == atomTargets)
+ else
{
- if (prop->format != 32)
- return;
- if (prop->type != XA_ATOM)
- return;
-
-// listAtoms((const Atom*)prop->data, prop->size);
-
-
- if(prop_has_atom(atomUTFString, (const Atom*)prop->data, prop->size))
- {
- request_selection(selection, atomUTFString);
- }
- else if(prop_has_atom(atomString, (const Atom*)prop->data, prop->size))
+ if(reply->type==XCB_NONE)
{
- request_selection(selection, atomString);
+// EPHYR_DBG( "NONE reply");
}
else
{
- /* requesting pixmap only for clipboard */
- if((selection == atomClipboard) && find_image_atom((const Atom*)prop->data, prop->size) && imageAtom)
+ //here we have type of data
+
+/*
+ stype=atom_name(reply->type);
+ sprop=atom_name(property);
+ EPHYR_DBG( "Property %s type %s, format %d, length %d", sprop, stype, reply->format, reply->length);
+ if(stype)
+ free(stype);
+ if(sprop)
+ free(sprop);
+*/
+ //need to read property incrementally
+ if(reply->type == atom("INCR"))
+ {
+ unsigned int sz=*((unsigned int*) xcb_get_property_value(reply));
+// EPHYR_DBG( "have incr property size: %d", sz);
+ remoteVars->selstruct.incrAtom=property;
+ remoteVars->selstruct.incrementalSize=sz;
+ remoteVars->selstruct.incrementalSizeRead=0;
+
+ //deleteing property should tell the selection owner that we are ready for incremental reading of data
+ xcb_delete_property(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId, property);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ free(reply);
+ return;
+ }
+ //we have supported mime types in reply
+ if(reply->type == atom( "ATOM"))
{
- request_selection(selection, imageAtom);
+ if(reply->format!=32)
+ {
+ EPHYR_DBG( "wrong format for TARGETS");
+ }
+ else
+ {
+// EPHYR_DBG( "target supports %lu mime types",xcb_get_property_value_length(reply)/sizeof(xcb_atom_t));
+
+ /*
+ #warning debug
+ tg=(xcb_atom_t*) xcb_get_property_value(reply);
+ for(int i=0;i<xcb_get_property_value_length(reply)/sizeof(xcb_atom_t);++i)
+ {
+ stype=atom_name(tg[i]);
+ EPHYR_DBG("selection has target: %s, %d",stype, tg[i]);
+ if(stype)
+ free(stype);
+ }
+ //#debug
+ */
+
+ data_atom=0;
+ //get the best of supported mime types and request the selection in this format
+ data_atom=best_atom_from_target(xcb_get_property_value(reply), xcb_get_property_value_length(reply)/sizeof(xcb_atom_t));
+
+ xcb_delete_property( remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, property);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+
+ if(data_atom)
+ request_selection_data( selection, data_atom, data_atom, 0);
+ else
+ {
+ EPHYR_DBG( "there are no supported mime types in the target");
+ }
+ }
}
+ else
+ {
+ //here we have selection as string or image
+ if(is_image_atom( reply->type) || is_string_atom( reply->type))
+ {
+ //read property data in loop in the chunks with size (100KB)
+ do
+ {
+ bytes_left=reply->bytes_after;
+ //now we can access property data
+
+ /*FILE* cp=fopen("/tmp/clip", "a");
+ fwrite(xcb_get_property_value(reply),1, xcb_get_property_value_length(reply),cp);
+ fclose(cp);*/
+
+
+ chunk=(outputChunk*) malloc(sizeof(outputChunk));
+
+ memset((void*)chunk,0,sizeof(outputChunk));
+
+ if(xcb_get_property_value_length(reply))
+ {
+ chunk->size=xcb_get_property_value_length(reply);
+ chunk->data=(unsigned char*)malloc(chunk->size);
+ memcpy(chunk->data, xcb_get_property_value(reply),chunk->size);
+ }
+
+ chunk->compressed=FALSE;
+ if(is_string_atom(property))
+ chunk->mimeData=UTF_STRING;
+ else
+ chunk->mimeData=PIXMAP;
+
+ chunk->selection=selection_from_atom(selection);
+
+
+ if(remoteVars->selstruct.incrementalSize && (remoteVars->selstruct.incrAtom==property))
+ {
+ //this is the total size of our selection
+ chunk->totalSize=remoteVars->selstruct.incrementalSize;
+ //we are doing incremental reading
+ if(remoteVars->selstruct.incrementalSizeRead == 0)
+ {
+ //it's the first chunk
+ chunk->firstChunk=TRUE;
+ }
+ remoteVars->selstruct.incrementalSizeRead+=xcb_get_property_value_length(reply);
+ if(!bytes_left && ! bytes_read && !xcb_get_property_value_length(reply))
+ {
+ //we got the property with 0 size it means that we recieved all data of incr property
+// EPHYR_DBG("INCR Property done, read %d", remoteVars->selstruct.incrementalSizeRead);
+ remoteVars->selstruct.incrAtom=0;
+ remoteVars->selstruct.incrementalSize=0;
+ //it's the last chunk
+ chunk->lastChunk=TRUE;
+ }
+ }
+ else
+ {
+ //we are doing simple read
+ if(bytes_read==0)
+ {
+ //it's the first chunk
+ chunk->firstChunk=TRUE;
+ }
+ if(bytes_left==0)
+ {
+ //the last chunk
+ chunk->lastChunk=TRUE;
+ }
+ //total size of the selection
+ chunk->totalSize=xcb_get_property_value_length(reply)+bytes_left;
+ }
+
+ bytes_read+=xcb_get_property_value_length(reply);
+// EPHYR_DBG( "read chunk of selection - size %d, total read %d, left %d, first:%d, last:%d", xcb_get_property_value_length(reply), bytes_read, bytes_left, chunk->firstChunk, chunk->lastChunk);
+
+ pthread_mutex_lock(&remoteVars->sendqueue_mutex);
+ //attach chunk to the end of output chunk queue
+ if(!remoteVars->selstruct.lastOutputChunk)
+ {
+ remoteVars->selstruct.lastOutputChunk=remoteVars->selstruct.firstOutputChunk=chunk;
+ }
+ else
+ {
+ remoteVars->selstruct.lastOutputChunk->next=(struct outputChunk*)chunk;
+ remoteVars->selstruct.lastOutputChunk=chunk;
+ }
+// EPHYR_DBG(" ADD CHUNK %p %p %p", remoteVars->selstruct.firstOutputChunk, remoteVars->selstruct.lastOutputChunk, chunk);
+ pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+ pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
+
+ if(bytes_left)
+ {
+ free(reply);
+ cookie= xcb_get_property(remoteVars->selstruct.xcbConnection, 0, remoteVars->selstruct.clipWinId, property, XCB_GET_PROPERTY_TYPE_ANY, bytes_read/4, max_chunk());
+ reply=xcb_get_property_reply(remoteVars->selstruct.xcbConnection, cookie, NULL);
+ if(!reply)
+ {
+ //something is wrong
+ EPHYR_DBG("NULL reply");
+ break;
+ }
+ }
+ //read in loop till no data left
+ }while(bytes_left);
+ }
+ else
+ {
+ stype=atom_name(reply->type);
+ EPHYR_DBG("Not supported mime type: %s, %d",stype, reply->type);
+ if(stype)
+ free(stype);
+ }
+ }
+ if(reply)
+ free(reply);
+ //if reading incr property this will say sel owner that we are ready for the next chunk of data
+ xcb_delete_property(remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, property);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
}
}
- else if (target == atomString || target == atomUTFString || (target==imageAtom && imageAtom) )
+}
+
+void process_selection_notify(xcb_generic_event_t *e)
+{
+ xcb_selection_notify_event_t *sel_event;
+
+// EPHYR_DBG("selection notify");
+ sel_event=(xcb_selection_notify_event_t *)e;
+
+ //processing the event which is reply for convert selection call
+
+ remoteVars->selstruct.incrAtom=0;
+ remoteVars->selstruct.incrementalSize=0;
+
+ if (sel_event->requestor != remoteVars->selstruct.clipWinId)
{
- int format=STRING;
- if(target==atomUTFString)
- {
- format=UTF_STRING;
- }else if(target == imageAtom)
+// EPHYR_DBG("not our window");
+ return;
+ }
+ else
+ {
+// EPHYR_DBG("selection notify sel %d, target %d, property %d", sel_event->selection, sel_event->target, sel_event->property);
+ if(sel_event->property==XCB_NONE)
{
- format=PIXMAP;
+ EPHYR_DBG( "NO SELECTION");
}
- /* read incrementinal data only for clipboard */
- if(!strcmp( NameForAtom(prop->type), "INCR") && selection==atomClipboard)
+ else
{
- EPHYR_DBG("GOT INCR PROPERTY: %d",*((int*)prop->data));
- remoteVars->selstruct.readingIncremental=TRUE;
- pthread_mutex_lock(&remoteVars->sendqueue_mutex);
-
- remoteVars->selstruct.clipboard.size=*((int*)prop->data);
- remoteVars->selstruct.clipboard.data=malloc(remoteVars->selstruct.clipboard.size);
- remoteVars->selstruct.clipboard.mimeData=format;
- remoteVars->selstruct.incrementalPosition=0;
- pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
-
- DeleteProperty(serverClient,remoteVars->selstruct.clipWinPtr, property);
- return;
+ remoteVars->selstruct.currentSelection=sel_event->selection;
+ //read property
+ read_selection_property(remoteVars->selstruct.currentSelection, sel_event->property);
}
+ }
+}
- if (prop->format != 8)
- return;
- if (prop->type != atomString && prop->type != atomUTFString && prop->type!=imageAtom)
- return;
+void process_property_notify(xcb_generic_event_t *e)
+{
+ xcb_property_notify_event_t *pn;
- pthread_mutex_lock(&remoteVars->sendqueue_mutex);
+// EPHYR_DBG("property notify");
- remoteVars->selstruct.readingIncremental=FALSE;
- if(selection==atomClipboard || selection ==atomPrimary)
+ pn = (xcb_property_notify_event_t *)e;
+ if (pn->window != remoteVars->selstruct.clipWinId)
+ {
+// EPHYR_DBG("not our window");
+ return;
+ }
+// EPHYR_DBG("property %d, state %d ", pn->atom, pn->state);
+ if(pn->state==XCB_PROPERTY_NEW_VALUE)
+ {
+// EPHYR_DBG( "NEW VALUE");
+ if(remoteVars->selstruct.incrAtom==pn->atom && remoteVars->selstruct.incrementalSize)
{
- outputBuffer* buff;
- if(selection==atomClipboard)
- buff=&remoteVars->selstruct.clipboard;
- else
- buff=&remoteVars->selstruct.selection;
- if(buff->data)
- {
- free(buff->data);
- }
- buff->size=prop->size;
- buff->data=malloc(buff->size);
- memcpy(buff->data, prop->data, prop->size);
- buff->changed=TRUE;
- buff->mimeData=format;
-// EPHYR_DBG("Have new Clipboard %s %d",remoteVars->selstruct.clipboard, remoteVars->selstruct.clipboardSize);
+ //we recieveing the selection data incrementally, let's read a next chunk
+// EPHYR_DBG("reading incr property %d", pn->atom);
+ read_selection_property(remoteVars->selstruct.currentSelection, pn->atom);
}
- pthread_cond_signal(&remoteVars->have_sendqueue_cond);
- pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
}
- DeleteProperty(serverClient,remoteVars->selstruct.clipWinPtr, property);
+ if(pn->state==XCB_PROPERTY_DELETE)
+ {
+// EPHYR_DBG( "DELETE");
+ }
+ if(pn->state==XCB_PROPERTY_NOTIFY)
+ {
+// EPHYR_DBG( "NOTIFY");
+ }
}
-
-
-#define SEND_EVENT_BIT 0x80
-
-static int send_event(ClientPtr client)
+void process_selection_owner_notify(xcb_generic_event_t *e)
{
- REQUEST(xSendEventReq);
- REQUEST_SIZE_MATCH(xSendEventReq);
-
- stuff->event.u.u.type &= ~(SEND_EVENT_BIT);
+ enum SelectionType selection;
+ xcb_xfixes_selection_notify_event_t *notify_event=(xcb_xfixes_selection_notify_event_t *)e;
-
- if (stuff->event.u.u.type == SelectionNotify &&
- stuff->event.u.selectionNotify.requestor == remoteVars->selstruct.clipWinId)
+// EPHYR_DBG("SEL OWNER notify, selection: %d, window: %d, owner: %d",notify_event->selection, notify_event->window, notify_event->owner);
+ if(notify_event->owner == remoteVars->selstruct.clipWinId)
{
- TimeStamp time;
- time = ClientTimeToServerTime(stuff->event.u.selectionNotify.time);
-
- process_selection(stuff->event.u.selectionNotify.selection,
- stuff->event.u.selectionNotify.target,
- stuff->event.u.selectionNotify.property,
- stuff->event.u.selectionNotify.requestor,
- time);
+// EPHYR_DBG("It's our own selection, ignoring");
+ return;
}
- return proc_send_event_orig(client);
-}
-
-static int convert_selection(ClientPtr client, Atom selection,
- Atom target, Atom property,
- Window requestor, CARD32 time)
-{
- Selection *pSel = NULL;
- WindowPtr pWin = {0};
- int rc = -1;
-
- Atom realProperty = {0};
- xEvent event = {{{0}}};
+ if(remoteVars->selstruct.selectionMode == CLIP_NONE || remoteVars->selstruct.selectionMode == CLIP_CLIENT)
+ {
+ EPHYR_DBG("Server selection is disabled");
+ return;
+ }
- inputBuffer* buff=&remoteVars->selstruct.inSelection;
- if(selection==atomClipboard)
- buff=&remoteVars->selstruct.inClipboard;
+ //cancel all previous incr reading
+ remoteVars->selstruct.incrementalSize=remoteVars->selstruct.incrementalSizeRead=0;
+ remoteVars->selstruct.incrAtom=0;
-// EPHYR_DBG("Selection request for %s (type %s)", NameForAtom(selection), NameForAtom(target));
- rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
- if (rc != Success)
- return rc;
+ selection=selection_from_atom(notify_event->selection);
- rc = dixLookupWindow(&pWin, requestor, client, DixSetAttrAccess);
- if (rc != Success)
- return rc;
+ //we are not owners of this selction anymore
+ remoteVars->selstruct.inSelection[selection].owner=FALSE;
- if (property != None)
- realProperty = property;
- else
- realProperty = target;
+ //get supported mime types
+ request_selection_data( notify_event->selection, atom( "TARGETS"), atom( "TARGETS"), 0);
+}
- if (target == atomTargets)
+static
+void *selection_thread (void* id)
+{
+ xcb_screen_t *screen;
+ uint response_type;
+ xcb_generic_event_t *e=NULL;
+ uint32_t mask = 0;
+ uint32_t values[2];
+ xcb_generic_error_t *error = 0;
+ const xcb_query_extension_reply_t *reply;
+ xcb_xfixes_query_version_cookie_t xfixes_query_cookie;
+ xcb_xfixes_query_version_reply_t *xfixes_query;
+
+
+ /* Create the window */
+ remoteVars->selstruct.xcbConnection = xcb_connect (RemoteHostVars.displayName, NULL);
+ if(xcb_connection_has_error(remoteVars->selstruct.xcbConnection))
+ {
+ EPHYR_DBG("Warning! can't create XCB connection to display %s, selections exchange between client and server will be disabled", RemoteHostVars.displayName);
+ remoteVars->selstruct.xcbConnection=0;
+ pthread_exit(0);
+ return NULL;
+ }
+ screen = xcb_setup_roots_iterator (xcb_get_setup (remoteVars->selstruct.xcbConnection)).data;
+ remoteVars->selstruct.clipWinId = xcb_generate_id (remoteVars->selstruct.xcbConnection);
+ mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+ values[0] = screen->white_pixel;
+ values[1] = XCB_EVENT_MASK_PROPERTY_CHANGE;
+
+ ATOM_CLIPBOARD=atom("CLIPBOARD");
+
+ //create window which will recieve selection events and provide remote selection to X-clients
+ xcb_create_window (remoteVars->selstruct.xcbConnection,
+ XCB_COPY_FROM_PARENT,
+ remoteVars->selstruct.clipWinId,
+ screen->root,
+ 0, 0,
+ 1, 1,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ screen->root_visual,
+ mask, values);
+
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ //check if we have xfixes, we need it to recieve selection owner events
+ reply = xcb_get_extension_data(remoteVars->selstruct.xcbConnection, &xcb_xfixes_id);
+ if (reply && reply->present)
{
- Atom string_targets[] = { atomTargets, atomTimestamp, atomUTFString };
- Atom pixmap_targets[] = { atomTargets, atomTimestamp, atomJPEG, atomJPG };
- if(buff->mimeData == PIXMAP)
+ xfixes_query_cookie = xcb_xfixes_query_version(remoteVars->selstruct.xcbConnection, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
+ xfixes_query = xcb_xfixes_query_version_reply (remoteVars->selstruct.xcbConnection, xfixes_query_cookie, &error);
+ if (!xfixes_query || error || xfixes_query->major_version < 2)
{
- rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
- XA_ATOM, 32, PropModeReplace,
- sizeof(pixmap_targets)/sizeof(pixmap_targets[0]),
- pixmap_targets, TRUE);
+ free(error);
}
else
{
- rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
- XA_ATOM, 32, PropModeReplace,
- sizeof(string_targets)/sizeof(string_targets[0]),
- string_targets, TRUE);
+ //we'll recieve sel owner events for primary amd clipboard
+ mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER;
+ xcb_xfixes_select_selection_input_checked(remoteVars->selstruct.xcbConnection,remoteVars->selstruct.clipWinId, XCB_ATOM_PRIMARY, mask);
+ xcb_xfixes_select_selection_input_checked(remoteVars->selstruct.xcbConnection, remoteVars->selstruct.clipWinId, ATOM_CLIPBOARD, mask);
}
- if (rc != Success)
- return rc;
+ free(xfixes_query);
}
- else if (target == atomTimestamp)
- {
- rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
- XA_INTEGER, 32, PropModeReplace, 1,
- &pSel->lastTimeChanged.milliseconds,
- TRUE);
- if (rc != Success)
- return rc;
- }
- else if (target == atomUTFString || target == atomJPEG || target == atomJPG )
- {
- rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
- target, 8, PropModeReplace,
- buff->size, buff->data, TRUE);
- if (rc != Success)
- return rc;
- }
- else
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+
+ // event loop
+ while ((e = xcb_wait_for_event(remoteVars->selstruct.xcbConnection)))
{
- return BadMatch;
+ response_type = e->response_type & ~0x80;
+
+ //we notified that selection is changed in primary or clipboard
+ if (response_type == reply->first_event + XCB_XFIXES_SELECTION_NOTIFY)
+ {
+ process_selection_owner_notify(e);
+ }
+ else
+ {
+ //we notified that property is changed
+ if (response_type == XCB_PROPERTY_NOTIFY)
+ {
+ process_property_notify(e);
+ }
+ //we got reply to our selection request (mime types or data)
+ else if (response_type == XCB_SELECTION_NOTIFY)
+ {
+ process_selection_notify(e);
+ }
+ else if (response_type == XCB_SELECTION_REQUEST)
+ {
+ process_selection_request(e);
+ }
+ else
+ {
+// EPHYR_DBG("not processing this event %d ", response_type);
+ }
+ }
+ free(e);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
}
- event.u.u.type = SelectionNotify;
- event.u.selectionNotify.time = time;
- 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;
+ pthread_exit(0);
+ return NULL;
}
-static int proc_convert_selection(ClientPtr client)
+void install_selection_callbacks(void)
{
- Bool paramsOkay;
- WindowPtr pWin = {0};
- Selection *pSel = NULL;
- int rc = -1;
-
- 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 == remoteVars->selstruct.clipWinId)
- {
- rc = convert_selection(client, stuff->selection,
- stuff->target, stuff->property,
- stuff->requestor, stuff->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);
- }
+ int ret;
+ if(remoteVars->selstruct.threadStarted)
+ return;
+ remoteVars->selstruct.threadStarted=TRUE;
- return Success;
+ ret = pthread_create(&(remoteVars->selstruct.selThreadId), NULL, selection_thread, (void *)remoteVars->selstruct.selThreadId);
+ if (ret)
+ {
+ EPHYR_DBG("ERROR; return code from pthread_create() is %d", ret);
+ remoteVars->selstruct.selThreadId=0;
}
-
- return proc_convert_selection_orig(client);
+ else
+ pthread_mutex_init(&remoteVars->selstruct.inMutex, NULL);
+ return;
}
-
-static int proc_change_property(ClientPtr client)
+void process_selection_request(xcb_generic_event_t *e)
{
- int rc = -1;
- BOOL incRead;
- PropertyPtr prop = {0};
+ xcb_selection_request_event_t *req=(xcb_selection_request_event_t*)e;
+ char *asel, *atar, *aprop;
+ enum SelectionType sel=selection_from_atom(req->selection);
+ xcb_atom_t property=req->property;
+ xcb_atom_t target=req->target;
- REQUEST(xChangePropertyReq);
+ xcb_selection_notify_event_t* event= (xcb_selection_notify_event_t*)calloc(32, 1);
+ event->response_type = XCB_SELECTION_NOTIFY;
+ event->requestor = req->requestor;
+ event->selection = req->selection;
+ event->target = req->target;
+ event->property = XCB_NONE;
+ event->time = req->time;
- REQUEST_AT_LEAST_SIZE(xChangePropertyReq);
- rc=proc_change_property_orig(client);
+ if(property == XCB_NONE)
+ property=target;
- if(rc!=Success)
- return rc;
+/*
+ asel= atom_name(req->selection);
+ atar= atom_name(req->target);
+ aprop=atom_name(req->property);
+
+ EPHYR_DBG("selection request for %s %s %s ", asel, atar, aprop);
+ if(asel)
+ free(asel);
+ if(aprop)
+ free(aprop);
+ if(atar)
+ free(atar);
+*/
+
+ //synchronize with main thread
+ pthread_mutex_lock(&remoteVars->selstruct.inMutex);
+ if(! remoteVars->selstruct.inSelection[sel].owner)
+ {
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ //we don't own this selection
+ EPHYR_DBG("not our selection");
+ xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ free(event);
+ return;
+ }
- pthread_mutex_lock(&remoteVars->sendqueue_mutex);
- incRead=remoteVars->selstruct.readingIncremental;
- pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
-
+ if(remoteVars->selstruct.inSelection[sel].timestamp > req->time)
+ {
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ //selection changed after request
+ EPHYR_DBG("requested selection doesn't exist anymore");
+ xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ free(event);
+ return;
+ }
+ if(req->target==atom("TIMESTAMP"))
+ {
+ event->property=property;
+// EPHYR_DBG("requested TIMESTAMP");
+ xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor,
+ property, XCB_ATOM_INTEGER, 32, 1, &remoteVars->selstruct.inSelection[sel].timestamp);
- if(stuff->window == remoteVars->selstruct.clipWinId && incRead &&
- ((imageAtom && imageAtom==stuff->type)||(stuff->type == atomUTFString) || (stuff->type == atomString)) )
- EPHYR_DBG("HAVE NEW DATA for %d: %s %s", stuff->window, NameForAtom(stuff->property), NameForAtom(stuff->type));
+ }
+ else if(req->target==atom("TARGETS"))
+ {
+ event->property=property;
+// EPHYR_DBG("requested TARGETS");
+ send_mime_types(req);
+ }
else
- return rc;
-
+ {
+ event->property=send_data(req);
+ }
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, req->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ free(event);
+}
+enum SelectionType selection_from_atom(xcb_atom_t selection)
+{
+ if(selection == XCB_ATOM_PRIMARY)
+ return PRIMARY;
+ return CLIPBOARD;
- rc = dixLookupProperty(&prop, remoteVars->selstruct.clipWinPtr, stuff->property,
- serverClient, DixReadAccess);
+}
- if(rc== BadMatch)
- {
- EPHYR_DBG("BAD MATCH!!!");
- return Success;
- }
+void send_mime_types(xcb_selection_request_event_t* req)
+{
+ //inmutex is locked in the caller function
+ //send supported targets
+ enum SelectionType sel=selection_from_atom(req->selection);
- if (rc != Success)
- return rc;
+ //we'll have max 7 mimetypes, don't forget to change this dimension if adding new mimetypes
+ xcb_atom_t targets[7];
+ xcb_atom_t a;
+ uint32_t mcount=0;
- EPHYR_DBG("Have %d bytes for %s ",
- prop->size, NameForAtom(stuff->property));
+ if((a=atom("TARGETS")))
+ targets[mcount++]=a;
+ if((a=atom("TIMESTAMP")))
+ targets[mcount++]=a;
- pthread_mutex_lock(&remoteVars->sendqueue_mutex);
- if(prop->size==0)
+ if(remoteVars->selstruct.inSelection[sel].mimeData==PIXMAP)
{
- EPHYR_DBG("READ %d FROM %d", remoteVars->selstruct.incrementalPosition, remoteVars->selstruct.clipboard.size);
- remoteVars->selstruct.readingIncremental=FALSE;
- if(remoteVars->selstruct.incrementalPosition == remoteVars->selstruct.clipboard.size)
+ //return PNG mime, if our data in PNG format, otherwise return JPEG
+ if((a=atom("image/png")) && is_png(remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size))
+ {
+ //our buffer is PNG file
+ targets[mcount++]=a;
+ }
+ else
{
- remoteVars->selstruct.clipboard.changed=TRUE;
- pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+ if((a=atom("image/jpg")))
+ targets[mcount++]=a;
+ if((a=atom("image/jpeg")))
+ targets[mcount++]=a;
}
}
else
{
- memcpy(remoteVars->selstruct.clipboard.data+remoteVars->selstruct.incrementalPosition, prop->data, prop->size);
- remoteVars->selstruct.incrementalPosition+=prop->size;
+ if((a=atom("UTF8_STRING")))
+ targets[mcount++]=a;
+ if((a=atom("text/plain;charset=utf-8")))
+ targets[mcount++]=a;
+ if((a=atom("STRING")))
+ targets[mcount++]=a;
+ if((a=atom("TEXT")))
+ targets[mcount++]=a;
+ if((a=atom("text/plain")))
+ targets[mcount++]=a;
}
- pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
-
+ xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, XCB_ATOM_ATOM,
+ 32, mcount, (const void *)targets);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+}
- DeleteProperty(serverClient,remoteVars->selstruct.clipWinPtr, stuff->property);
+xcb_atom_t send_data(xcb_selection_request_event_t* req)
+{
+ //inmutex is locked in the caller function
+ //send data
+ enum SelectionType sel=selection_from_atom(req->selection);
+ char *starget;
+ xcb_atom_t png=atom("image/png");
+ xcb_atom_t jpg=atom("image/jpg");
+ xcb_atom_t jpeg=atom("image/jpeg");
- return rc;
+ if(remoteVars->selstruct.inSelection[sel].mimeData==UTF_STRING)
+ {
+ //if it's one of supported text formats send without convertion
+ if(is_string_atom(req->target))
+ {
+// EPHYR_DBG("sending UTF text");
+ return set_data_property(req, remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size);
+ }
+ else
+ {
+ starget=atom_name(req->target);
+ EPHYR_DBG("unsupported property requested: %s",starget);
+ if(starget)
+ free(starget);
+ return XCB_NONE;
+ }
+ }
+ else
+ {
+ if(!is_image_atom(req->target))
+ {
+ starget=atom_name(req->target);
+ EPHYR_DBG("unsupported property requested: %s",starget);
+ if(starget)
+ free(starget);
+ return XCB_NONE;
+ }
+/*
+ starget=atom_name(req->target);
+ EPHYR_DBG("requested %s",starget);
+ if(starget)
+ free(starget);
+*/
+
+ //TODO: implement convertion between different image formats
+ if(is_png(remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size))
+ {
+ if(req->target!=png)
+ {
+ starget=atom_name(req->target);
+ EPHYR_DBG("unsupported property requested: %s",starget);
+ if(starget)
+ free(starget);
+ return XCB_NONE;
+ }
+ }
+ else
+ {
+ if((req->target!=jpg)&&(req->target!=jpeg))
+ {
+ starget=atom_name(req->target);
+ EPHYR_DBG("unsupported property requested: %s",starget);
+ if(starget)
+ free(starget);
+ return XCB_NONE;
+ }
+ }
+ return set_data_property(req, remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size);
+ }
+ return XCB_NONE;
}
-void selection_init(struct _remoteHostVars *obj)
+xcb_atom_t set_data_property(xcb_selection_request_event_t* req, unsigned char* data, uint32_t size)
{
- EPHYR_DBG("INITIALIZING selections");
- remoteVars=obj;
- proc_convert_selection_orig = ProcVector[X_ConvertSelection];
- proc_send_event_orig = ProcVector[X_SendEvent];
- proc_change_property_orig = ProcVector[X_ChangeProperty];
- ProcVector[X_ConvertSelection] = proc_convert_selection;
- ProcVector[X_SendEvent] = send_event;
- ProcVector[X_ChangeProperty] = proc_change_property;
- remoteVars->selstruct.callBackInstalled=TRUE;
- install_selection_callbacks();
-}
+ //inmutex locked in parent thread
+ //set data to window property
+ //change when implemented
+ BOOL support_incr=FALSE;
-void install_selection_callbacks(void)
-{
- if(remoteVars->selstruct.selectionMode == CLIP_CLIENT || remoteVars->selstruct.selectionMode == CLIP_NONE)
+ xcb_atom_t xtsel=atom("_XT_SELECTION_0");
+ xcb_atom_t qtsel=atom("_QT_SELECTION");
+ //this types of application not supporting incr selection
+ if(req->property == xtsel|| req->property == qtsel )
{
- EPHYR_DBG("SERVER CLIPBOARD disabled, not installing callbacks");
- return;
+ EPHYR_DBG("property %d doesn't support INCR",req->property);
+ support_incr=FALSE;
}
- atomPrimary = MakeAtom("PRIMARY", 7, TRUE);
- atomClipboard = MakeAtom("CLIPBOARD", 9, TRUE);
-
- atomTargets = MakeAtom("TARGETS", 7, TRUE);
- atomTimestamp = MakeAtom("TIMESTAMP", 9, TRUE);
- atomString = MakeAtom("STRING", 6, TRUE);
- atomUTFString = MakeAtom("UTF8_STRING", 11, TRUE);
-
- atomJPEG = MakeAtom("image/jpeg",487,TRUE);
- atomJPG = MakeAtom("image/jpg",488,TRUE);
+ //check if we are sending incr
+ if(size < xcb_get_maximum_request_length(remoteVars->selstruct.xcbConnection) * 4 - 24)
+ {
+// EPHYR_DBG( "sending %d bytes, property %d, target %d", size, req->property, req->target);
- remoteVars->selstruct.clipWinPtr=0;
+ xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
+ 8, size, (const void *)data);
- /* try to dele callback to avaid double call */
- DeleteCallback(&SelectionCallback, selection_callback, 0);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ return req->property;
+ }
- if (!AddCallback(&SelectionCallback, selection_callback, 0))
- FatalError("Failed to install callback\n");
- else
- EPHYR_DBG("Selection callback installed");
+ EPHYR_DBG("data is too big");
+ return XCB_NONE;
+}
+BOOL is_png(unsigned char* data, uint32_t size)
+{
+ if( size<8)
+ return FALSE;
+ return !png_sig_cmp(data, 0, 8);
}
diff --git a/x2gokdriveselection.h b/x2gokdriveselection.h
index 3827ffa..7ec8d8a 100644
--- a/x2gokdriveselection.h
+++ b/x2gokdriveselection.h
@@ -30,8 +30,24 @@
#include "x2gokdriveremote.h"
+uint32_t max_chunk(void);
+
void selection_init(struct _remoteHostVars *obj);
void install_selection_callbacks(void);
-int own_selection(int target);
+int own_selection(enum SelectionType selection);
+
+xcb_atom_t atom(const char* name);
+char *atom_name(xcb_atom_t xatom);
+void request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t);
+void read_selection_property(xcb_atom_t selection, xcb_atom_t property);
+void process_selection_notify(xcb_generic_event_t *e);
+void process_property_notify(xcb_generic_event_t *e);
+void process_selection_owner_notify(xcb_generic_event_t *e);
+void process_selection_request(xcb_generic_event_t *e);
+void send_mime_types(xcb_selection_request_event_t* req);
+enum SelectionType selection_from_atom(xcb_atom_t selection);
+xcb_atom_t send_data(xcb_selection_request_event_t* req);
+xcb_atom_t set_data_property(xcb_selection_request_event_t* req, unsigned char* data, uint32_t size);
+BOOL is_png(unsigned char* data, uint32_t size);
#endif /* X2GOKDRIVESELECTION_H */
--
Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gokdrive.git
More information about the x2go-commits
mailing list