[X2Go-Commits] [x2gokdrive] 01/01: support sending and recieving selections on demand. Support reading and writing INCR properties.
git-admin at x2go.org
git-admin at x2go.org
Wed Sep 30 18:36:18 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 aed1bdfa2e43fdf9fb3bdb576e8cb00d7e11aac3
Author: Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
Date: Wed Sep 30 11:36:04 2020 -0500
support sending and recieving selections on demand. Support reading and writing INCR properties.
---
debian/changelog | 1 +
x2gokdriveremote.c | 352 ++++++++++++++++++----
x2gokdriveremote.h | 93 ++++--
x2gokdriveselection.c | 787 +++++++++++++++++++++++++++++++++++++++++---------
x2gokdriveselection.h | 25 +-
5 files changed, 1047 insertions(+), 211 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index 3a442cf..9bfc70b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -14,6 +14,7 @@ x2gokdrive (0.0.0.1-0x2go1) UNRELEASED; urgency=medium
- add xcb-xfixes to deps.
- send a recive feature versions.
- reinit client version on new connection and awaka sending thread when client version recieved.
+ - support sending and recieving selections on demand. Support reading and writing INCR properties.
[ Mihai Moldovan ]
* Initial release:
diff --git a/x2gokdriveremote.c b/x2gokdriveremote.c
index 210de85..0fd828a 100644
--- a/x2gokdriveremote.c
+++ b/x2gokdriveremote.c
@@ -36,6 +36,7 @@
#endif
#include "x2gokdriveremote.h"
#include "x2gokdriveselection.h"
+#include <zlib.h>
/* init it in OsInit() */
static struct _remoteHostVars remoteVars = {0};
@@ -45,6 +46,7 @@ static BOOL remoteInitialized=FALSE;
void remote_selection_init(void)
{
+ remoteVars.selstruct.readingInputBuffer=-1;
selection_init(&remoteVars);
}
@@ -207,6 +209,7 @@ void remote_removeCursor(uint32_t serialNumber)
struct sentCursor* prev = NULL;
struct deletedCursor* dcur = NULL;
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
cur=remoteVars.sentCursorsHead;
@@ -239,6 +242,7 @@ void remote_removeCursor(uint32_t serialNumber)
remoteVars.first_deleted_cursor=remoteVars.last_deleted_cursor=dcur;
}
++remoteVars.deletedcursor_list_size;
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
@@ -253,8 +257,10 @@ void remote_sendCursor(CursorPtr cursor)
cframe->data=0;
cframe->next=0;
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
cursorSent=isCursorSent(cursor->serialNumber);
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
if(!cursorSent)
{
@@ -289,13 +295,17 @@ void remote_sendCursor(CursorPtr cursor)
cframe->forG=cursor->foreGreen*255./65535.0;
cframe->forB=cursor->foreBlue*255./65535.0;
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
addSentCursor(cursor->serialNumber);
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
addCursorToQueue(cframe);
pthread_cond_signal(&remoteVars.have_sendqueue_cond);
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
@@ -305,6 +315,7 @@ void remote_sendVersion(void)
unsigned char buffer[56] = {0};
_X_UNUSED int l;
+
*((uint32_t*)buffer)=SERVERVERSION; //4B
*((uint16_t*)buffer+2)=FEATURE_VERSION;
EPHYR_DBG("Sending server version: %d", FEATURE_VERSION);
@@ -312,6 +323,16 @@ void remote_sendVersion(void)
remoteVars.server_version_sent=TRUE;
}
+void request_selection_from_client(enum SelectionType selection)
+{
+ unsigned char buffer[56] = {0};
+ _X_UNUSED int l;
+
+ *((uint32_t*)buffer)=DEMANDCLIENTSELECTION; //4B
+ *((uint16_t*)buffer+2)=(uint16_t)selection;
+ l=write(remoteVars.clientsock,buffer,56);
+ EPHYR_DBG("requesting selection from client");
+}
static
int32_t send_cursor(struct cursorFrame* cursor)
@@ -542,19 +563,19 @@ int send_deleted_cursors(void)
return sent;
}
-int send_output_selection(outputChunk* chunk)
+int send_output_selection(struct 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);
+ return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, chunk->firstChunk, chunk->lastChunk, chunk->compressed_size, 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)
+ if(!chunk->compressed_size && chunk->firstChunk && chunk->lastChunk)
{
return send_selection_chunk(chunk->selection, chunk->data, chunk->size, chunk->mimeData, TRUE, TRUE, FALSE, chunk->size);
}
@@ -563,7 +584,7 @@ int send_output_selection(outputChunk* 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)
+int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, uint32_t compressed, uint32_t total)
{
unsigned char buffer[56] = {0};
_X_UNUSED int ln = 0;
@@ -578,12 +599,20 @@ int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t
*((uint32_t*)buffer+4)=first; //20
*((uint32_t*)buffer+5)=last; //24
*((uint32_t*)buffer+6)=compressed; //28
- *((uint32_t*)buffer+7)=total; //28
+ *((uint32_t*)buffer+7)=total; //32
// #warning check this
ln=write(remoteVars.clientsock,buffer,56);
+ //if the data is compressed, send "compressed" amount of bytes
+// EPHYR_DBG("sending chunk. total %d, chunk %d, compressed %d", total, length, compressed);
+ if(compressed)
+ {
+ length=compressed;
+ }
+
+
while(sent<length)
{
l=write(remoteVars.clientsock,data+sent,((length-sent)<MAXMSGSIZE)?(length-sent):MAXMSGSIZE);
@@ -1339,6 +1368,7 @@ void sendMainImageFromSendThread(uint32_t width, uint32_t height, int32_t dx ,in
EPHYR_DBG("sending mainImage");
}
+
pthread_mutex_lock(&remoteVars.mainimg_mutex);
for(int j=0;j<9;++j)
@@ -1387,6 +1417,7 @@ void sendMainImageFromSendThread(uint32_t width, uint32_t height, int32_t dx ,in
send_frame(width, height,dx,dy,0,regions);
}
+
pthread_mutex_unlock(&remoteVars.mainimg_mutex);
free(regions[0].compressed_data);
}
@@ -1395,6 +1426,7 @@ static
void *send_frame_thread (void *threadid)
{
long tid;
+ enum SelectionType r;
tid = (long)threadid;
EPHYR_DBG("Started sending thread: #%ld!\n", tid);
@@ -1473,6 +1505,7 @@ void *send_frame_thread (void *threadid)
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
//only accept one client, close server socket
shutdown(remoteVars.serversock, SHUT_RDWR);
@@ -1482,8 +1515,7 @@ void *send_frame_thread (void *threadid)
#endif /* XORG_VERSION_CURRENT */
remoteVars.client_connected=TRUE;
remoteVars.server_version_sent=FALSE;
- remoteVars.client_version=0;
- remoteVars.client_os=0;
+ set_client_version(0,0);
if(remoteVars.checkConnectionTimer)
{
TimerFree(remoteVars.checkConnectionTimer);
@@ -1495,10 +1527,12 @@ void *send_frame_thread (void *threadid)
remoteVars.data_copy=0;
remoteVars.evBufferOffset=0;
setAgentState(RUNNING);
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
while(1)
{
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
if(!remoteVars.client_connected)
{
@@ -1508,6 +1542,7 @@ void *send_frame_thread (void *threadid)
#endif /* XORG_VERSION_CURRENT */
shutdown(remoteVars.clientsock, SHUT_RDWR);
close(remoteVars.clientsock);
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
break;
}
@@ -1535,12 +1570,13 @@ void *send_frame_thread (void *threadid)
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;
+ struct OutputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
+ remoteVars.selstruct.firstOutputChunk=chunk->next;
if(!remoteVars.selstruct.firstOutputChunk)
{
remoteVars.selstruct.lastOutputChunk=NULL;
}
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
//send chunk
send_output_selection(chunk);
@@ -1551,9 +1587,28 @@ void *send_frame_thread (void *threadid)
}
// EPHYR_DBG(" REMOVE CHUNK %p %p %p", remoteVars.selstruct.firstOutputChunk, remoteVars.selstruct.lastOutputChunk, chunk);
free(chunk);
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
}
+ //check if we need to request the selection from client
+ if(remoteVars.selstruct.requestSelection[PRIMARY] || remoteVars.selstruct.requestSelection[CLIPBOARD])
+ {
+ for(r=PRIMARY; r<=CLIPBOARD; ++r)
+ {
+ if(remoteVars.selstruct.requestSelection[r])
+ {
+ remoteVars.selstruct.requestSelection[r]=FALSE;
+
+ pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
+ //send request for selection
+ request_selection_from_client(r);
+
+ 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 */
@@ -1563,6 +1618,7 @@ void *send_frame_thread (void *threadid)
remoteVars.firstCursor=remoteVars.firstCursor->next;
else
remoteVars.firstCursor=remoteVars.lastCursor=0;
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
send_cursor(cframe);
if(cframe->data)
@@ -1572,9 +1628,11 @@ void *send_frame_thread (void *threadid)
}
else
{
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
if(remoteVars.first_sendqueue_element)
{
@@ -1614,16 +1672,19 @@ void *send_frame_thread (void *threadid)
uint32_t frame_height=frame->height;
/* unlock sendqueue for main thread */
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
send_frame(frame_width, frame_height, x, y, crc, frame->regions);
}
else
{
EPHYR_DBG("Sending main image or screen update");
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
sendMainImageFromSendThread(width, height, x, y);
}
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
if(frame)
{
@@ -1665,11 +1726,14 @@ void *send_frame_thread (void *threadid)
send_deleted_cursors();
}
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
remoteVars.framenum++;
}
else
{
+
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
}
@@ -1682,13 +1746,13 @@ void *send_frame_thread (void *threadid)
/* warning! sendqueue_mutex should be locked by thread calling this function! */
void clear_output_selection(void)
{
- outputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
- outputChunk* prev_chunk;
+ struct OutputChunk* chunk=remoteVars.selstruct.firstOutputChunk;
+ struct OutputChunk* prev_chunk;
while(chunk)
{
prev_chunk=chunk;
- chunk=(outputChunk*)chunk->next;
+ chunk=chunk->next;
if(prev_chunk->data)
{
free(prev_chunk->data);
@@ -1852,6 +1916,7 @@ void setAgentState(int state)
void disconnect_client(void)
{
EPHYR_DBG("DISCONNECTING CLIENT, DOING SOME CLEAN UP");
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
remoteVars.client_connected=FALSE;
setAgentState(SUSPENDED);
@@ -1860,44 +1925,113 @@ void disconnect_client(void)
freeCursors();
clear_output_selection();
pthread_cond_signal(&remoteVars.have_sendqueue_cond);
+
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
+void unpack_current_chunk_to_buffer(struct InputBuffer* selbuff)
+{
+ //unpacking the data from current chunk to selbuff
+
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+// EPHYR_DBG("inflate %d bytes to %d", selbuff->currentChunkCompressedSize, selbuff->currentChunkSize);
+
+ stream.avail_in = selbuff->currentChunkCompressedSize;
+ stream.next_in = selbuff->currentChunkCompressedData;
+ stream.avail_out = selbuff->currentChunkSize;
+ stream.next_out = selbuff->data + selbuff->bytesReady;
+
+ inflateInit(&stream);
+ inflate(&stream, Z_NO_FLUSH);
+ inflateEnd(&stream);
+
+ if(stream.total_out != selbuff->currentChunkSize)
+ {
+ //something is wrong with extracting the data
+ EPHYR_DBG("WARNING!!!! extracting the data failed output has %d bytes instead of %d", (uint32_t)stream.total_out, selbuff->currentChunkSize);
+ }
+
+
+// EPHYR_DBG("%s", selbuff->data + selbuff->bytesReady);
+ ///freeing compressed data
+ free(selbuff->currentChunkCompressedData);
+ selbuff->currentChunkCompressedData=NULL;
+
+ selbuff->bytesReady+=selbuff->currentChunkSize;
+ selbuff->currentChunkCompressedSize=0;
+}
+
void readInputSelectionBuffer(char* buff)
{
//read th rest of the chunk data
- inputBuffer* selbuff = &remoteVars.selstruct.inSelection[remoteVars.selstruct.readingInputBuffer];
+ struct InputBuffer* selbuff;
+ int leftToRead, l;
- 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;
+ selbuff = &remoteVars.selstruct.inSelection[remoteVars.selstruct.readingInputBuffer];
+
+ //if the data is not compressed read it directly to the buffer
+ if(!selbuff->currentChunkCompressedSize)
+ {
+ leftToRead=selbuff->currentChunkSize - selbuff->currentChunkBytesReady;
+ l=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
+
+ //copy data to selection
+ memcpy(selbuff->data+selbuff->bytesReady, buff, l);
+ selbuff->bytesReady+=l;
+ selbuff->currentChunkBytesReady+=l;
+ 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;
+ }
+ }
+ else
+ {
+ //copy to the buffer for compressed data
+ leftToRead=selbuff->currentChunkCompressedSize - selbuff->currentChunkBytesReady;
+ l=(leftToRead < EVLENGTH)?leftToRead:EVLENGTH;
+
+ //copy data to selection
+ memcpy(selbuff->currentChunkCompressedData+selbuff->currentChunkBytesReady, buff, l);
+ selbuff->currentChunkBytesReady+=l;
+ if(selbuff->currentChunkBytesReady==selbuff->currentChunkCompressedSize)
+ {
+ //selection chunk received completely, next event will start with event header
+ EPHYR_DBG("READY Selection Chunk, read %d",selbuff->currentChunkSize);
+ remoteVars.selstruct.readingInputBuffer=-1;
+ //unpack data to selection buffer
+ unpack_current_chunk_to_buffer(selbuff);
+ }
+ }
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;
+ //if state is requested we already own this selection after notify
+ if(selbuff->state != REQUESTED)
+ own_selection(remoteVars.selstruct.readingInputBuffer);
+ selbuff->state=COMPLETED;
+ //send notification event to interrupt sleeping selection thread
+ client_sel_data_notify(remoteVars.selstruct.readingInputBuffer);
}
//unlock selection
+
+
pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
}
@@ -1910,9 +2044,9 @@ void readInputSelectionHeader(char* buff)
uint32_t size, totalSize;
uint8_t destination, mime;
- inputBuffer* selbuff = NULL;
+ struct InputBuffer* selbuff = NULL;
BOOL firstChunk=FALSE, lastChunk=FALSE;
- BOOL compressed=FALSE;
+ uint32_t compressedSize=0;
uint32_t headerSize=10;
uint32_t l;
@@ -1923,20 +2057,21 @@ void readInputSelectionHeader(char* buff)
//if client supports ext selection, read extended header
if(remoteVars.selstruct.clientSupportsExetndedSelection)
{
- headerSize=17;
+ headerSize=20;
firstChunk=*((uint8_t*)buff + 10);
lastChunk=*((uint8_t*)buff + 11);
- compressed=*((uint8_t*)buff + 12);
- totalSize=*( (uint32_t*) (buff+13));
+ compressedSize=*((uint32_t*) (buff + 12));
+ totalSize=*( (uint32_t*) (buff+16));
}
else
{
- compressed=FALSE;
+ compressedSize=0;
lastChunk=firstChunk=TRUE;
totalSize=size;
}
-// EPHYR_DBG("HAVE NEW INCOMING SELECTION Chunk: sel %d size %d mime %d",destination, size, mime);
+ EPHYR_DBG("HAVE NEW INCOMING SELECTION Chunk: sel %d size %d mime %d compressed size %d, total %d",destination, size, mime, compressedSize, totalSize);
+
//lock selection
pthread_mutex_lock(&remoteVars.selstruct.inMutex);
@@ -1944,6 +2079,28 @@ void readInputSelectionHeader(char* buff)
remoteVars.selstruct.readingInputBuffer=-1;
selbuff = &remoteVars.selstruct.inSelection[destination];
+
+ //we recieved selection notify from client
+ if(firstChunk && lastChunk && remoteVars.selstruct.clientSupportsExetndedSelection && (totalSize == 0) &&(size == 0))
+ {
+ EPHYR_DBG("Selection notify from client for %d", destination);
+ if(selbuff->size && selbuff->data)
+ {
+ free(selbuff->data);
+ }
+ selbuff->size=0;
+ selbuff->mimeData=mime;
+ selbuff->data=0;
+ selbuff->bytesReady=0;
+ selbuff->state=NOTIFIED;
+// own selection
+ own_selection(destination);
+ //unlock mutex
+
+
+ pthread_mutex_unlock(&remoteVars.selstruct.inMutex);
+ return;
+ }
if(firstChunk)
{
//if it's first chunk, initialize our selection buffer
@@ -1957,30 +2114,73 @@ void readInputSelectionHeader(char* buff)
selbuff->bytesReady=0;
}
- //read the selection data from header
- l=(size < EVLENGTH-headerSize)?size:(EVLENGTH-headerSize);
- memcpy(selbuff->data+selbuff->bytesReady, buff+headerSize, l);
+ if(selbuff->currentChunkCompressedSize && selbuff->currentChunkCompressedData)
+ {
+ free(selbuff->currentChunkCompressedData);
+ }
+ selbuff->currentChunkCompressedData=NULL;
+ selbuff->currentChunkCompressedSize=0;
- selbuff->bytesReady+=l;
+ //if compressed data will be read in buffer for compressed data
+ if(compressedSize)
+ {
+ selbuff->currentChunkCompressedData=malloc(compressedSize);
+ selbuff->currentChunkCompressedSize=compressedSize;
+ l=(compressedSize < EVLENGTH-headerSize)?compressedSize:(EVLENGTH-headerSize);
+ memcpy(selbuff->currentChunkCompressedData, buff+headerSize, l);
+ }
+ else
+ {
+ //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(!compressedSize)
+ {
+ if(selbuff->currentChunkBytesReady != selbuff->currentChunkSize)
+ {
+ // we didn't recieve complete chunk yet, next event will have data
+ remoteVars.selstruct.readingInputBuffer=destination;
+ }
+ }
+ else
+ {
+ if(selbuff->currentChunkBytesReady != selbuff->currentChunkCompressedSize)
+ {
+ // we didn't recieve complete chunk yet, next event will have data
+ remoteVars.selstruct.readingInputBuffer=destination;
+ }
+ else
+ {
+ //we read all compressed chunk data, unpack it to sel buff
+ unpack_current_chunk_to_buffer(selbuff);
+ }
+
+ }
+
if(selbuff->size == selbuff->bytesReady)
{
//Selection is completed
// EPHYR_DBG("READY INCOMING SELECTION for %d",destination);
//own the selection
- own_selection(destination);
+ //if state is requested we already own this selection after notify
+ if(selbuff->state != REQUESTED)
+ own_selection(destination);
+ selbuff->state=COMPLETED;
+ //send notification event to interrupt sleeping selection thread
+ client_sel_data_notify(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);
}
@@ -1994,6 +2194,8 @@ clientReadNotify(int fd, int ready, void *data)
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
con=remoteVars.client_connected;
+
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
if(!con)
return;
@@ -2176,12 +2378,14 @@ clientReadNotify(int fd, int ready, void *data)
if(remoteVars.main_img && x+width <= remoteVars.main_img_width && y+height <= remoteVars.main_img_height )
{
+
pthread_mutex_unlock(&remoteVars.mainimg_mutex);
add_frame(width, height, x, y, 0, 0);
}
else
{
EPHYR_DBG("UPDATE: skip request");
+
pthread_mutex_unlock(&remoteVars.mainimg_mutex);
}
break;
@@ -2195,17 +2399,18 @@ clientReadNotify(int fd, int ready, void *data)
{
int16_t ver=*((uint16_t*)buff+2);
int16_t os=*((uint16_t*)buff+3);
+ set_client_version(ver, os);
EPHYR_DBG("Client information: vesrion %d, os %d", ver, os);
- remoteVars.client_version=ver;
- if(os > OS_DARWIN)
- {
- EPHYR_DBG("Unsupported OS, assuming OS_LINUX");
- }
- else
- remoteVars.client_os=os;
pthread_cond_signal(&remoteVars.have_sendqueue_cond);
break;
}
+ case DEMANDSELECTION:
+ {
+ int16_t sel=*((uint16_t*)buff+2);
+// EPHYR_DBG("Client requesting selection %d", sel);
+ client_sel_request_notify(sel);
+ break;
+ }
default:
{
EPHYR_DBG("UNSUPPORTED EVENT: %d",event_type);
@@ -2227,6 +2432,22 @@ clientReadNotify(int fd, int ready, void *data)
}
+void set_client_version(uint16_t ver, uint16_t os)
+{
+ remoteVars.client_version=ver;
+ if(os > OS_DARWIN)
+ {
+ EPHYR_DBG("Unsupported OS, assuming OS_LINUX");
+ }
+ else
+ remoteVars.client_os=os;
+ //clients version >= 1 supporting extended selection (sending big amount of data aín several chunks)
+ remoteVars.selstruct.clientSupportsExetndedSelection=(ver > 1);
+ //Linux clients supporting sending selection on demand (not sending data if not needed)
+ remoteVars.selstruct.clientSupportsOnDemandSelection=((ver > 1) && (os == OS_LINUX));
+
+}
+
#if XORG_VERSION_CURRENT < 11900000
void pollEvents(void)
{
@@ -2234,8 +2455,10 @@ void pollEvents(void)
struct pollfd fds[2];
int nfds = 1;
BOOL con;
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
con=remoteVars.client_connected;
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
if(!con)
return;
@@ -2254,6 +2477,7 @@ unsigned int checkSocketConnection(OsTimerPtr timer, CARD32 time, void* args)
{
EPHYR_DBG("CHECKING ACCEPTED CONNECTION");
TimerFree(timer);
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
remoteVars.checkConnectionTimer=0;
if(!remoteVars.client_connected)
@@ -2265,6 +2489,7 @@ unsigned int checkSocketConnection(OsTimerPtr timer, CARD32 time, void* args)
{
EPHYR_DBG("CLIENT CONNECTED");
}
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
return 0;
}
@@ -2333,6 +2558,7 @@ void terminateServer(int exitStatus)
if(remoteVars.selstruct.selThreadId)
{
pthread_cancel(remoteVars.selstruct.selThreadId);
+ remove_obsolete_incr_transactions(FALSE);
if(remoteVars.selstruct.xcbConnection)
{
xcb_disconnect(remoteVars.selstruct.xcbConnection);
@@ -2718,6 +2944,7 @@ struct cache_elem* add_cache_element(uint32_t crc, int32_t dx, int32_t dy, uint3
++i;
}
}
+
pthread_mutex_unlock(&remoteVars.mainimg_mutex);
el->rval/=numOfPix;
@@ -2798,6 +3025,7 @@ void initFrameRegions(struct cache_elem* frame)
uint32_t bestm_crc = 0;
struct cache_elem* best_match = NULL;
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
best_match = find_best_match(frame, &match_val);
@@ -2805,6 +3033,7 @@ void initFrameRegions(struct cache_elem* frame)
{
best_match->busy+=1;
}
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
if(best_match && best_match->width>4 && best_match->height>4 && best_match->width * best_match->height > 100 )
@@ -2916,12 +3145,15 @@ void initFrameRegions(struct cache_elem* frame)
}
}
/* if we didn't find any common regions and have best match element, mark it as not busy */
+
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
if(best_match && frame->source != best_match)
{
// EPHYR_DBG("Have best mutch but not common region");
best_match->busy-=1;
}
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
@@ -3002,10 +3234,12 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
struct cache_elem* frame = 0;
struct sendqueue_element* element = NULL;
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
if(! (remoteVars.client_connected && remoteVars.client_initialized))
{
/* don't have any clients connected, return */
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
return;
}
@@ -3013,6 +3247,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
if(crc==0)
{
/* sending main image */
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
}
else
@@ -3031,6 +3266,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
}
frame->busy+=1;
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
/* if element is new find common regions and compress the data */
@@ -3041,6 +3277,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
}
}
+
pthread_mutex_lock(&remoteVars.sendqueue_mutex);
/* add element in the queue for sending */
element=malloc(sizeof(struct sendqueue_element));
@@ -3060,6 +3297,7 @@ void add_frame(uint32_t width, uint32_t height, int32_t x, int32_t y, uint32_t c
remoteVars.first_sendqueue_element=remoteVars.last_sendqueue_element=element;
}
pthread_cond_signal(&remoteVars.have_sendqueue_cond);
+
pthread_mutex_unlock(&remoteVars.sendqueue_mutex);
/* on this point will be sent wakeup single to send mutex */
@@ -3104,6 +3342,7 @@ remote_paint_rect(KdScreenInfo *screen,
* OK, here we assuming that XSERVERBPP is 4. If not, we'll have troubles
* but it should work faster like this
*/
+
pthread_mutex_lock(&remoteVars.mainimg_mutex);
maxdiff=2;
@@ -3169,6 +3408,7 @@ remote_paint_rect(KdScreenInfo *screen,
ind+=XSERVERBPP;
}
}
+
pthread_mutex_unlock(&remoteVars.mainimg_mutex);
@@ -3202,11 +3442,13 @@ uint32_t calculate_crc(uint32_t width, uint32_t height, int32_t dx, int32_t dy)
{
uint32_t crc=adler32(0L, Z_NULL, 0);
+
pthread_mutex_lock(&remoteVars.mainimg_mutex);
for(uint32_t y=0; y< height;++y)
{
crc=adler32(crc,remoteVars.main_img+ ((y+dy)*remoteVars.main_img_width + dx)*XSERVERBPP, width*XSERVERBPP);
}
+
pthread_mutex_unlock(&remoteVars.mainimg_mutex);
return crc;
}
@@ -3249,6 +3491,7 @@ remote_screen_init(KdScreenInfo *screen,
EPHYR_DBG("host_screen=%p x=%d, y=%d, wxh=%dx%d, buffer_height=%d",
screen, x, y, width, height, buffer_height);
+
pthread_mutex_lock(&remoteVars.mainimg_mutex);
@@ -3294,6 +3537,7 @@ remote_screen_init(KdScreenInfo *screen,
*bytes_per_line = width*XSERVERBPP;
*bits_per_pixel = 32;
+
pthread_mutex_unlock(&remoteVars.mainimg_mutex);
return remoteVars.main_img;
diff --git a/x2gokdriveremote.h b/x2gokdriveremote.h
index 4249563..7735d0b 100644
--- a/x2gokdriveremote.h
+++ b/x2gokdriveremote.h
@@ -95,7 +95,8 @@
//FEATURE_VERSION is not cooresponding to actual version of server
//it used to tell server which features are supported by server
//Changes 0 - 1: sending and recieving client and OS version
-#define FEATURE_VERSION 1
+//Changes 1 - 2: supporting extended selection and sending selection on demand
+#define FEATURE_VERSION 2
#define EPHYR_WANT_DEBUG 1
@@ -130,7 +131,7 @@ fprintf(stderr, __FILE__ ":%d,%s() " x "\n", __LINE__, __func__, ##a)
//always 4
#define XSERVERBPP 4
-enum msg_type{FRAME,DELETED, CURSOR, DELETEDCURSOR, SELECTION, SERVERVERSION};
+enum msg_type{FRAME,DELETED, CURSOR, DELETEDCURSOR, SELECTION, SERVERVERSION, DEMANDCLIENTSELECTION};
enum AgentState{STARTING, RUNNING, RESUMING, SUSPENDING, SUSPENDED, TERMINATING, TERMINATED};
enum Compressions{JPEG,PNG};
enum SelectionType{PRIMARY,CLIPBOARD};
@@ -160,6 +161,7 @@ enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
#define UPDATE 8
#define SELECTIONEVENT 9
#define CLIENTVERSION 10
+#define DEMANDSELECTION 11
#define EVLENGTH 41
@@ -267,35 +269,59 @@ struct sendqueue_element
struct sendqueue_element* next;
};
+//chunk of data with output selection
+struct OutputChunk
+{
+ unsigned char* data; //data
+ uint32_t size; //size of chunk in B
+ enum SelectionMime mimeData; //UTF_STRING or PIXMAP (text or image)
+ uint32_t compressed_size; // if chunk is compressed the size of compressed data, otherwise 0
+ 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
+};
+
//input selection
-typedef struct
+struct InputBuffer
{
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;
+ uint32_t currentChunkCompressedSize; //if chunk is compressed, size of compressed data
+ unsigned char* currentChunkCompressedData; //if chunk is compressed, compressed dat will be stored here
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;
+ enum {NOTIFIED, REQUESTED, COMPLETED} state;
+};
+//requests which processing should be delayed till we get data from client
+struct DelayedRequest
+{
+ xcb_selection_request_event_t *request; // request from X-client
+ xcb_selection_notify_event_t* event; // event which we are going to send to X-client
+ struct DelayedRequest* next;
+};
-//chunk of data with output selection
-typedef struct
+//save running INCR transactions in this struct
+struct IncrTransaction
{
- 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;
+ xcb_window_t requestor;
+ xcb_atom_t property;
+ xcb_atom_t target;
+ char* data;
+ uint32_t size;
+ uint32_t sentBytes;
+ xcb_timestamp_t timestamp;
+ struct IncrTransaction* next;
+};
-typedef struct
+
+struct SelectionStructure
{
unsigned long selThreadId; //id of selection thread
enum ClipboardMode selectionMode; //CLIP_NONE, CLIP_CLIENT, CLIP_SERVER, CLIP_BOTH
@@ -306,17 +332,29 @@ typedef struct
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
+ BOOL clientSupportsExetndedSelection; //if client supports extended selection - sending selection in several chunks for big size data
+ BOOL clientSupportsOnDemandSelection; //if client supports selection on demand - sending data only if client requests it
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
+ struct OutputChunk* firstOutputChunk; //the first and last elements of the
+ struct OutputChunk* lastOutputChunk; //queue of selection chunks
+ xcb_atom_t best_atom[2]; //the best mime type for selection to request on demand sel request from client
+ BOOL requestSelection[2]; //selection thread will set it to TRUE if the selection need to be requested
//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
+ struct InputBuffer inSelection[2]; //PRIMARY an CLIPBOARD selection buffers
+
+ //list of delayed requests
+ struct DelayedRequest* firstDelayedRequest;
+ struct DelayedRequest* lastDelayedRequest;
+
+ //list of INCR transactions
+ struct IncrTransaction* firstIncrTransaction;
+ struct IncrTransaction* lastIncrTransaction;
+
pthread_mutex_t inMutex; //mutex for synchronization of incoming selection
-}SelectionStructure;
+};
struct _remoteHostVars
@@ -400,11 +438,13 @@ struct _remoteHostVars
BOOL client_connected;
BOOL client_initialized;
- SelectionStructure selstruct;
+ struct SelectionStructure selstruct;
} RemoteHostVars;
-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);
+int send_selection_chunk(int sel, unsigned char* data, uint32_t length, uint32_t format, BOOL first, BOOL last, uint32_t compressed, uint32_t total);
+int send_output_selection(struct OutputChunk* chunk);
+
+void set_client_version(uint16_t ver, uint16_t os);
void readInputSelectionBuffer(char* buff);
void readInputSelectionHeader(char* buff);
@@ -430,6 +470,7 @@ void setAgentState(int state);
void terminateServer(int exitStatus);
+void unpack_current_chunk_to_buffer(struct InputBuffer* selbuff);
unsigned char* image_compress(uint32_t image_width, uint32_t image_height,
unsigned char* RGBA_buffer, uint32_t* compressed_size, int bpp, char* fname);
@@ -477,4 +518,6 @@ void
remote_paint_rect(KdScreenInfo *screen,
int sx, int sy, int dx, int dy, int width, int height);
+void request_selection_from_client(enum SelectionType selection);
+
#endif /* X2GOKDRIVE_REMOTE_H */
diff --git a/x2gokdriveselection.c b/x2gokdriveselection.c
index e174aff..0a1f8d4 100644
--- a/x2gokdriveselection.c
+++ b/x2gokdriveselection.c
@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <png.h>
-
+#include <zlib.h>
#ifdef HAVE_CONFIG_H
#include <dix-config.h>
@@ -45,9 +45,37 @@
#endif
#include "x2gokdriveselection.h"
+
+#define SELECTION_DELAY 30000 //timeout for selection operation
+#define INCR_SIZE 256*1024 //size of part for incr selection incr selection
+
static struct _remoteHostVars *remoteVars = NULL;
+//internal atoms
+static xcb_atom_t ATOM_ATOM;
static xcb_atom_t ATOM_CLIPBOARD;
+static xcb_atom_t ATOM_TARGETS;
+static xcb_atom_t ATOM_TIMESTAMP;
+static xcb_atom_t ATOM_INCR;
+static xcb_atom_t ATOM_XT_SELECTION;
+static xcb_atom_t ATOM_QT_SELECTION;
+
+
+//text atoms
+static xcb_atom_t ATOM_UTF8_STRING;
+static xcb_atom_t ATOM_TEXT_PLAIN_UTF;
+static xcb_atom_t ATOM_STRING;
+static xcb_atom_t ATOM_TEXT;
+static xcb_atom_t ATOM_TEXT_PLAIN;
+
+//image atoms
+static xcb_atom_t ATOM_IMAGE_PNG;
+static xcb_atom_t ATOM_IMAGE_XPM;
+static xcb_atom_t ATOM_IMAGE_JPG;
+static xcb_atom_t ATOM_IMAGE_JPEG;
+static xcb_atom_t ATOM_PIXMAP;
+static xcb_atom_t ATOM_IMAGE_BMP;
+
uint32_t max_chunk(void)
{
@@ -57,8 +85,248 @@ uint32_t max_chunk(void)
return 10*1024*1024/4; //10MB
}
+void init_atoms(void)
+{
+ //init intern atoms
+
+ ATOM_ATOM=string_to_atom("ATOM");
+ ATOM_CLIPBOARD=string_to_atom("CLIPBOARD");
+ ATOM_TARGETS=string_to_atom("TARGETS");
+ ATOM_TIMESTAMP=string_to_atom("TIMESTAMP");
+ ATOM_INCR=string_to_atom("INCR");
+
+ ATOM_XT_SELECTION=string_to_atom("_XT_SELECTION_0");
+ ATOM_QT_SELECTION=string_to_atom("_QT_SELECTION");
+
+
+ ATOM_UTF8_STRING=string_to_atom("UTF8_STRING");
+ ATOM_TEXT_PLAIN_UTF=string_to_atom("text/plain;charset=utf-8");
+ ATOM_STRING=string_to_atom("STRING");
+ ATOM_TEXT=string_to_atom("TEXT");
+ ATOM_TEXT_PLAIN=string_to_atom("text/plain");
+
+ ATOM_IMAGE_PNG=string_to_atom("image/png");
+ ATOM_IMAGE_XPM=string_to_atom("image/xpm");
+ ATOM_IMAGE_JPG=string_to_atom("image/jpg");
+ ATOM_IMAGE_JPEG=string_to_atom("image/jpeg");
+ ATOM_PIXMAP=string_to_atom("PIXMAP");
+ ATOM_IMAGE_BMP=string_to_atom("image/bmp");
+}
+
+struct DelayedRequest* discard_delayed_request(struct DelayedRequest* d, struct DelayedRequest* prev)
+{
+ //this function finalizing delayed request and destroys XCB request and event
+ //removing the request from list and returning the pointer of the next element for iteration
+ struct DelayedRequest* next=d->next;
+ xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, d->request->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)d->event);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ free(d->event);
+ free((xcb_generic_event_t *)(d->request));
+
+ //remove element from list
+ if(prev)
+ prev->next=next;
+
+ if(d==remoteVars->selstruct.firstDelayedRequest)
+ remoteVars->selstruct.firstDelayedRequest=next;
+
+ if(d==remoteVars->selstruct.lastDelayedRequest)
+ remoteVars->selstruct.lastDelayedRequest=prev;
+
+ free(d);
+
+ return next;
+}
+
+void destroy_incr_transaction(struct IncrTransaction* tr, struct IncrTransaction* prev)
+{
+ //destroy incr transaction
+ struct IncrTransaction* next=tr->next;
+
+ const uint32_t mask[] = { XCB_EVENT_MASK_NO_EVENT };
+ //don't resive property notify events for this window anymore
+ xcb_change_window_attributes(remoteVars->selstruct.xcbConnection, tr->requestor,
+ XCB_CW_EVENT_MASK, mask);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+
+ //free data
+ free(tr->data);
+
+ //remove element from list
+ if(prev)
+ prev->next=next;
+
+ if(tr==remoteVars->selstruct.firstIncrTransaction)
+ remoteVars->selstruct.firstIncrTransaction=next;
+
+ if(tr==remoteVars->selstruct.lastIncrTransaction)
+ remoteVars->selstruct.lastIncrTransaction=prev;
+
+ free(tr);
+}
+
+
+
+BOOL check_req_sanity(xcb_selection_request_event_t* req)
+{
+ //check if the requested mime can be delivered
+ //inmutex should be locked from calling function
+
+ enum SelectionType sel=selection_from_atom(req->selection);
+ 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))
+ {
+ return TRUE;
+ }
+ else
+ {
+ EPHYR_DBG("Unsupported property requested %d", req->target);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if(!is_image_atom(req->target))
+ {
+ EPHYR_DBG("Unsupported property requested %d", req->target);
+ return FALSE;
+ }
+ return TRUE;
+ }
+}
+
+
+void remove_obsolete_incr_transactions( BOOL checkTs)
+{
+ //remove_obsolete_incr_transactions
+ //if checkTS true, check timestamp and destroy only if ts exceed delay
+
+ struct DelayedRequest* prev=NULL;
+ struct DelayedRequest* d=remoteVars->selstruct.firstDelayedRequest;
+ while(d)
+ {
+ if(!checkTs || (currentTime.milliseconds > (d->request->time + SELECTION_DELAY)))
+ {
+ d=discard_delayed_request(d, prev);
+ }
+ else
+ {
+ prev=d;
+ d=d->next;
+ }
+ }
+}
+
+
+void process_delayed_requests(void)
+{
+ //process delayed requests
+
+ enum SelectionType selection;
+
+ struct DelayedRequest* prev=NULL;
+ struct DelayedRequest* d=remoteVars->selstruct.firstDelayedRequest;
+ while(d)
+ {
+ selection = selection_from_atom( d->request->selection);
+ if(currentTime.milliseconds > (d->request->time + SELECTION_DELAY))
+ {
+ EPHYR_DBG("timeout selection: %d",selection);
+ d=discard_delayed_request(d, prev);
+ continue;
+ }
+ pthread_mutex_lock(&remoteVars->selstruct.inMutex);
+ if(!remoteVars->selstruct.inSelection[selection].owner)
+ {
+
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ EPHYR_DBG("we are not owner of requested selection %d",selection);
+ //we are not anymore owners of this selection
+ d=discard_delayed_request(d, prev);
+ continue;
+ }
+ if(remoteVars->selstruct.inSelection[selection].timestamp > d->request->time )
+ {
+
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ EPHYR_DBG("selection request for %d is too old", selection);
+ //requested selection is older than the current one
+ d=discard_delayed_request(d, prev);
+ continue;
+ }
+ if(!check_req_sanity(d->request))
+ {
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+// EPHYR_DBG("can't convert selection %d to requested myme type %d",selection, d->request->property);
+ //our selection don't support requested mime type
+ d=discard_delayed_request(d, prev);
+ continue;
+ }
+ if(remoteVars->selstruct.inSelection[selection].state != COMPLETED)
+ {
+
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ //we don't have the data yet
+ prev=d;
+ d=d->next;
+ continue;
+ }
+ //data is ready, send data to requester and discard the request
+ //inMutex need be locked for sending data
+ d->event->property=send_data(d->request);
+
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ d=discard_delayed_request(d, prev);
+ }
+}
+
+void process_incr_transaction_property(xcb_property_notify_event_t * pn)
+{
+ //process incr transactions
+
+ struct IncrTransaction* prev=NULL;
+ struct IncrTransaction* tr=remoteVars->selstruct.firstIncrTransaction;
+ uint32_t left, sendingBytes;
+ while(tr)
+ {
+ if((tr->requestor == pn->window) && (tr->property == pn->atom ) && ( pn->state == XCB_PROPERTY_DELETE) )
+ {
+ //requestor ready for the new portion of data
+ left=tr->size-tr->sentBytes;
+ if(!left)
+ {
+// EPHYR_DBG("all INCR data sent to %d",tr->requestor);
+ //all data sent, sending NULL data and destroying transaction
+ xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, tr->requestor, tr->property,
+ tr->target, 8, 0, NULL);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ destroy_incr_transaction(tr, prev);
+ return;
+ }
+ sendingBytes=(INCR_SIZE< left)?INCR_SIZE:left;
+
+ xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, tr->requestor, tr->property,
+ tr->target, 8, sendingBytes, tr->data + tr->sentBytes);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ tr->sentBytes+=sendingBytes;
+ tr->timestamp=currentTime.milliseconds;
+ return;
+ }
+ prev=tr;
+ tr=tr->next;
+ }
+ //notify event doesn't belong to any of started incr transactions or it's notification for new property
+ return;
+}
+
+
+
int own_selection(enum SelectionType selection)
{
+ //owning selection
+ //remoteVars.selstruct.inMutex locked in the calling function
xcb_atom_t sel=XCB_ATOM_PRIMARY;
if(remoteVars->selstruct.selectionMode == CLIP_NONE || remoteVars->selstruct.selectionMode == CLIP_SERVER)
{
@@ -72,9 +340,12 @@ int own_selection(enum SelectionType selection)
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;
+ remoteVars->selstruct.inSelection[selection].timestamp=currentTime.milliseconds;
+
+// EPHYR_DBG("own selection %d", currentTime.milliseconds);
return 0;
}
+
void selection_init(struct _remoteHostVars *obj)
{
remoteVars=obj;
@@ -82,24 +353,23 @@ void selection_init(struct _remoteHostVars *obj)
}
-xcb_atom_t atom(const char* name)
+xcb_atom_t string_to_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;
-
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);
}
+ EPHYR_DBG("The %s atom has ID %u", name, a);
return a;
}
-char *atom_name(xcb_atom_t xatom)
+char *atom_to_string(xcb_atom_t xatom)
{
//get name for atom, don't forget to free return value
char* name;
@@ -120,13 +390,10 @@ char *atom_name(xcb_atom_t xatom)
return name;
}
-static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, const char *name)
+static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, xcb_atom_t a)
{
//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;
for (i = 0;i < size;i++)
{
@@ -136,31 +403,24 @@ static xcb_atom_t target_has_atom(xcb_atom_t* list, size_t size, const char *nam
return 0;
}
-static int is_string_atom( xcb_atom_t at)
+int is_string_atom( xcb_atom_t at)
{
//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;
+ if((at==ATOM_UTF8_STRING) || (at==ATOM_TEXT_PLAIN_UTF) ||
+ (at==ATOM_STRING) || (at==ATOM_TEXT) || (at==ATOM_TEXT_PLAIN))
+ return 1;
return 0;
}
-static int is_image_atom( xcb_atom_t at)
+int is_image_atom( xcb_atom_t at)
{
//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"))
+ if( at == ATOM_IMAGE_PNG ||
+ at == ATOM_IMAGE_XPM ||
+ at == ATOM_IMAGE_JPEG ||
+ at == ATOM_IMAGE_JPG ||
+ at == ATOM_PIXMAP ||
+ at == ATOM_IMAGE_BMP)
return 1;
return 0;
}
@@ -170,59 +430,59 @@ static xcb_atom_t best_atom_from_target(xcb_atom_t* list, size_t size)
//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")))
+ if((a=target_has_atom(list, size, ATOM_UTF8_STRING)))
{
// EPHYR_DBG("selecting mime type UTF8_STRING");
return a;
}
- if((a=target_has_atom(list, size, "text/plain;charset=utf-8")))
+ if((a=target_has_atom(list, size, ATOM_TEXT_PLAIN_UTF)))
{
// EPHYR_DBG("selecting mime type text/plain;charset=utf-8");
return a;
}
- if((a=target_has_atom(list, size, "STRING")))
+ if((a=target_has_atom(list, size, ATOM_STRING)))
{
// EPHYR_DBG( "selecting mime type STRING");
return a;
}
- if((a=target_has_atom(list, size, "TEXT")))
+ if((a=target_has_atom(list, size, ATOM_TEXT)))
{
// EPHYR_DBG( "selecting mime type TEXT");
return a;
}
- if((a=target_has_atom(list, size, "text/plain")))
+ if((a=target_has_atom(list, size, ATOM_TEXT_PLAIN)))
{
// EPHYR_DBG( "selecting mime type text/plain");
return a;
}
//selecting loseless formats first
- if((a=target_has_atom(list, size, "image/png")))
+ if((a=target_has_atom(list, size, ATOM_IMAGE_PNG)))
{
// EPHYR_DBG( "selecting mime type image/png");
return a;
}
- if((a=target_has_atom(list, size, "image/xpm")))
+ if((a=target_has_atom(list, size, ATOM_IMAGE_XPM)))
{
// EPHYR_DBG( "selecting mime type image/xpm");
return a;
}
- if((a=target_has_atom(list, size, "PIXMAP")))
+ if((a=target_has_atom(list, size, ATOM_PIXMAP)))
{
// EPHYR_DBG( "selecting mime type PIXMAP");
return a;
}
- if((a=target_has_atom(list, size, "image/bmp")))
+ if((a=target_has_atom(list, size, ATOM_IMAGE_BMP)))
{
// EPHYR_DBG( "selecting mime type image/bmp");
return a;
}
- if((a=target_has_atom(list, size, "image/jpg")))
+ if((a=target_has_atom(list, size, ATOM_IMAGE_JPG)))
{
// EPHYR_DBG( "selecting mime type image/jpg");
return a;
}
- if((a=target_has_atom(list, size, "image/jpeg")))
+ if((a=target_has_atom(list, size, ATOM_IMAGE_JPEG)))
{
// EPHYR_DBG( "selecting mime type image/jpeg");
return a;
@@ -245,13 +505,13 @@ void request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t
void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
{
- 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;
+ struct OutputChunk* chunk;
+ unsigned char* compressed_data;
+ uint32_t compressed_size;
//request property which represents value of selection (data or mime types)
@@ -282,7 +542,7 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
free(sprop);
*/
//need to read property incrementally
- if(reply->type == atom("INCR"))
+ if(reply->type == ATOM_INCR)
{
unsigned int sz=*((unsigned int*) xcb_get_property_value(reply));
// EPHYR_DBG( "have incr property size: %d", sz);
@@ -297,7 +557,7 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
return;
}
//we have supported mime types in reply
- if(reply->type == atom( "ATOM"))
+ if(reply->type == ATOM_ATOM)
{
if(reply->format!=32)
{
@@ -328,7 +588,29 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
xcb_flush(remoteVars->selstruct.xcbConnection);
if(data_atom)
- request_selection_data( selection, data_atom, data_atom, 0);
+ {
+ if(remoteVars->client_os == OS_WINDOWS && selection_from_atom(selection) == PRIMARY)
+ {
+// EPHYR_DBG("client doesn't support PRIMARY selection");
+ }
+ else
+ {
+ if(remoteVars->selstruct.clientSupportsOnDemandSelection)
+ {
+ //don't ask for data yet, only send notification that we have a selection to client
+// EPHYR_DBG("client supports onDemand selection, notify client");
+ send_notify_to_client(selection, data_atom);
+ //save the data atom for possible data demand
+ remoteVars->selstruct.best_atom[selection_from_atom(selection)]=data_atom;
+ }
+ else
+ {
+ //request selection data
+// EPHYR_DBG("client not supports onDemand selection, request data");
+ request_selection_data( selection, data_atom, data_atom, 0);
+ }
+ }
+ }
else
{
EPHYR_DBG( "there are no supported mime types in the target");
@@ -351,9 +633,9 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
fclose(cp);*/
- chunk=(outputChunk*) malloc(sizeof(outputChunk));
+ chunk=malloc(sizeof(struct OutputChunk));
- memset((void*)chunk,0,sizeof(outputChunk));
+ memset((void*)chunk,0,sizeof(struct OutputChunk));
if(xcb_get_property_value_length(reply))
{
@@ -362,9 +644,23 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
memcpy(chunk->data, xcb_get_property_value(reply),chunk->size);
}
- chunk->compressed=FALSE;
+ chunk->compressed_size=0;
if(is_string_atom(property))
+ {
chunk->mimeData=UTF_STRING;
+ //for text chunks > 1K using zlib compression if client supports it
+ if(remoteVars->selstruct.clientSupportsExetndedSelection && chunk->size > 1024)
+ {
+ compressed_data=zcompress(chunk->data, chunk->size, &compressed_size);
+ if(compressed_data && compressed_size)
+ {
+ free(chunk->data);
+ chunk->data=compressed_data;
+ chunk->compressed_size=compressed_size;
+// EPHYR_DBG("compressed chunk from %d to %d", chunk->size, chunk->compressed_size);
+ }
+ }
+ }
else
chunk->mimeData=PIXMAP;
@@ -410,7 +706,8 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
}
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);
+// 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
@@ -420,11 +717,13 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
}
else
{
- remoteVars->selstruct.lastOutputChunk->next=(struct outputChunk*)chunk;
+ remoteVars->selstruct.lastOutputChunk->next=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)
@@ -444,10 +743,7 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
}
else
{
- stype=atom_name(reply->type);
- EPHYR_DBG("Not supported mime type: %s, %d",stype, reply->type);
- if(stype)
- free(stype);
+ EPHYR_DBG("Not supported mime type:%d",reply->type);
}
}
if(reply)
@@ -459,12 +755,65 @@ void read_selection_property(xcb_atom_t selection, xcb_atom_t property)
}
}
+void send_notify_to_client(xcb_atom_t selection, xcb_atom_t mime)
+{
+ //creating the selection chunk with no data, which notifyes client that we have a selection
+ struct OutputChunk* chunk= malloc(sizeof(struct OutputChunk));
+// EPHYR_DBG("send selection notify to client");
+
+
+ memset((void*)chunk,0,sizeof(struct OutputChunk));
+
+ if(is_string_atom(mime))
+ chunk->mimeData=UTF_STRING;
+ else
+ chunk->mimeData=PIXMAP;
+
+ chunk->selection=selection_from_atom(selection);
+ chunk->totalSize=0;
+ chunk->firstChunk=chunk->lastChunk=TRUE;
+
+ 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=chunk;
+ remoteVars->selstruct.lastOutputChunk=chunk;
+ }
+ pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+
+
+ pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
+}
+
void process_selection_notify(xcb_generic_event_t *e)
{
xcb_selection_notify_event_t *sel_event;
+ enum SelectionType selection;
+
// EPHYR_DBG("selection notify");
sel_event=(xcb_selection_notify_event_t *)e;
+ selection=selection_from_atom(sel_event->selection);
+
+ if(sel_event->property== XCB_NONE && sel_event->target==XCB_NONE)
+ {
+ //have data demand from client
+ request_selection_data( sel_event->selection, remoteVars->selstruct.best_atom[selection],remoteVars->selstruct.best_atom[selection], 0);
+ return;
+ }
+
+ if(sel_event->property== sel_event->selection && sel_event->target==sel_event->selection)
+ {
+ //have data ready from server. We don't need to do anything here. This event interrrupted the waiting procedure and the delayed requests are already processed
+// EPHYR_DBG("Have DATA READY event");
+ return;
+ }
//processing the event which is reply for convert selection call
@@ -481,7 +830,7 @@ void process_selection_notify(xcb_generic_event_t *e)
// 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)
{
- EPHYR_DBG( "NO SELECTION");
+// EPHYR_DBG( "NO SELECTION");
}
else
{
@@ -501,7 +850,9 @@ void process_property_notify(xcb_generic_event_t *e)
pn = (xcb_property_notify_event_t *)e;
if (pn->window != remoteVars->selstruct.clipWinId)
{
-// EPHYR_DBG("not our window");
+ //this property doesn't belong to our window;
+ //let's check if it's not the property corresponding to one of incr transactions
+ process_incr_transaction_property(pn);
return;
}
// EPHYR_DBG("property %d, state %d ", pn->atom, pn->state);
@@ -552,10 +903,15 @@ void process_selection_owner_notify(xcb_generic_event_t *e)
selection=selection_from_atom(notify_event->selection);
//we are not owners of this selction anymore
+
+ pthread_mutex_lock(&remoteVars->selstruct.inMutex);
remoteVars->selstruct.inSelection[selection].owner=FALSE;
+
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+
//get supported mime types
- request_selection_data( notify_event->selection, atom( "TARGETS"), atom( "TARGETS"), 0);
+ request_selection_data( notify_event->selection, ATOM_TARGETS, ATOM_TARGETS, 0);
}
@@ -588,7 +944,7 @@ void *selection_thread (void* id)
values[0] = screen->white_pixel;
values[1] = XCB_EVENT_MASK_PROPERTY_CHANGE;
- ATOM_CLIPBOARD=atom("CLIPBOARD");
+ init_atoms();
//create window which will recieve selection events and provide remote selection to X-clients
xcb_create_window (remoteVars->selstruct.xcbConnection,
@@ -627,6 +983,8 @@ void *selection_thread (void* id)
// event loop
while ((e = xcb_wait_for_event(remoteVars->selstruct.xcbConnection)))
{
+ process_delayed_requests();
+ remove_obsolete_incr_transactions(TRUE);
response_type = e->response_type & ~0x80;
//we notified that selection is changed in primary or clipboard
@@ -648,7 +1006,11 @@ void *selection_thread (void* id)
}
else if (response_type == XCB_SELECTION_REQUEST)
{
- process_selection_request(e);
+ if(!process_selection_request(e))
+ {
+ //we delayed this request, not deleteing the event yet
+ continue;
+ }
}
else
{
@@ -682,10 +1044,53 @@ void install_selection_callbacks(void)
return;
}
-void process_selection_request(xcb_generic_event_t *e)
+void client_sel_request_notify(enum SelectionType sel)
{
+ //this function will be used from main thread to send the event which will
+ // notify selection thread that client want us to send data for selection sel
+
+ xcb_selection_notify_event_t* event= (xcb_selection_notify_event_t*)calloc(32, 1);
+ event->response_type = XCB_SELECTION_NOTIFY;
+ event->requestor = remoteVars->selstruct.clipWinId;
+ event->selection = atom_from_selection(sel);
+ event->target = XCB_NONE;
+ event->property = XCB_NONE;
+ event->time = XCB_TIME_CURRENT_TIME;
+
+ xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, remoteVars->selstruct.clipWinId, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ free(event);
+}
+
+void client_sel_data_notify(enum SelectionType sel)
+{
+ //this function will be used from main thread to send the event which will
+ // notify selection thread that client sent us data for selection sel
+
+ xcb_selection_notify_event_t* event= (xcb_selection_notify_event_t*)calloc(32, 1);
+ event->response_type = XCB_SELECTION_NOTIFY;
+ event->requestor = remoteVars->selstruct.clipWinId;
+ event->selection = atom_from_selection(sel);
+ event->target = atom_from_selection(sel);
+ event->property = atom_from_selection(sel);
+ event->time = XCB_TIME_CURRENT_TIME;
+
+ xcb_send_event(remoteVars->selstruct.xcbConnection, FALSE, remoteVars->selstruct.clipWinId, XCB_EVENT_MASK_NO_EVENT, (char*)event);
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ free(event);
+}
+
+
+BOOL process_selection_request(xcb_generic_event_t *e)
+{
+ //processing selection request.
+ //return true if the processing is finishing after return
+ //false if data is not ready and we are delaying processing of this request
+ //in this case calling function SHOULD NOT destroy the request neither event should not be destroyed
+ //we'll free this objects after processing of the request when the data is available
+
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;
@@ -702,45 +1107,35 @@ void process_selection_request(xcb_generic_event_t *e)
if(property == XCB_NONE)
property=target;
-
-/*
- 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");
+// 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;
+ return TRUE;
}
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");
+// 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;
+ return TRUE;
}
- if(req->target==atom("TIMESTAMP"))
+ if(req->target==ATOM_TIMESTAMP)
{
event->property=property;
// EPHYR_DBG("requested TIMESTAMP");
@@ -748,7 +1143,7 @@ void process_selection_request(xcb_generic_event_t *e)
property, XCB_ATOM_INTEGER, 32, 1, &remoteVars->selstruct.inSelection[sel].timestamp);
}
- else if(req->target==atom("TARGETS"))
+ else if(req->target==ATOM_TARGETS)
{
event->property=property;
// EPHYR_DBG("requested TARGETS");
@@ -756,12 +1151,32 @@ void process_selection_request(xcb_generic_event_t *e)
}
else
{
- event->property=send_data(req);
+ if(remoteVars->selstruct.inSelection[sel].state==COMPLETED)
+ event->property=send_data(req);
+ else
+ {
+// EPHYR_DBG("the data for %d is not ready yet",sel);
+ delay_selection_request(req,event);
+
+
+ pthread_mutex_unlock(&remoteVars->selstruct.inMutex);
+ return FALSE;
+ }
}
+
+
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);
+ return TRUE;
+}
+
+xcb_atom_t atom_from_selection(enum SelectionType sel)
+{
+ if(sel==PRIMARY)
+ return XCB_ATOM_PRIMARY;
+ return ATOM_CLIPBOARD;
}
enum SelectionType selection_from_atom(xcb_atom_t selection)
@@ -780,42 +1195,42 @@ void send_mime_types(xcb_selection_request_event_t* req)
//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;
- if((a=atom("TARGETS")))
- targets[mcount++]=a;
- if((a=atom("TIMESTAMP")))
- targets[mcount++]=a;
+ targets[mcount++]=ATOM_TARGETS;
+ targets[mcount++]=ATOM_TIMESTAMP;
if(remoteVars->selstruct.inSelection[sel].mimeData==PIXMAP)
{
+
+ //only supporting PNG here at the moment
+ targets[mcount++]=ATOM_IMAGE_PNG;
+
+/*
//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;
+ EPHYR_DBG("SENDING PNG ATOMS");
}
else
{
+ EPHYR_DBG("SENDING JPG ATOMS");
if((a=atom("image/jpg")))
targets[mcount++]=a;
if((a=atom("image/jpeg")))
targets[mcount++]=a;
- }
+ }*/
}
else
{
- 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;
+// EPHYR_DBG("SENDING STRING ATOMS");
+ targets[mcount++]=ATOM_UTF8_STRING;
+ targets[mcount++]=ATOM_TEXT_PLAIN_UTF;
+ targets[mcount++]=ATOM_STRING;
+ targets[mcount++]=ATOM_TEXT;
+ targets[mcount++]=ATOM_TEXT_PLAIN;
}
xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, XCB_ATOM_ATOM,
@@ -828,11 +1243,7 @@ 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");
if(remoteVars->selstruct.inSelection[sel].mimeData==UTF_STRING)
{
@@ -844,10 +1255,7 @@ xcb_atom_t send_data(xcb_selection_request_event_t* req)
}
else
{
- starget=atom_name(req->target);
- EPHYR_DBG("unsupported property requested: %s",starget);
- if(starget)
- free(starget);
+ EPHYR_DBG("unsupported property requested: %d",req->target);
return XCB_NONE;
}
}
@@ -855,10 +1263,7 @@ xcb_atom_t send_data(xcb_selection_request_event_t* req)
{
if(!is_image_atom(req->target))
{
- starget=atom_name(req->target);
- EPHYR_DBG("unsupported property requested: %s",starget);
- if(starget)
- free(starget);
+ EPHYR_DBG("unsupported property requested: %d",req->target);
return XCB_NONE;
}
/*
@@ -871,23 +1276,17 @@ xcb_atom_t send_data(xcb_selection_request_event_t* req)
//TODO: implement convertion between different image formats
if(is_png(remoteVars->selstruct.inSelection[sel].data, remoteVars->selstruct.inSelection[sel].size))
{
- if(req->target!=png)
+ if(req->target!=ATOM_IMAGE_PNG)
{
- starget=atom_name(req->target);
- EPHYR_DBG("unsupported property requested: %s",starget);
- if(starget)
- free(starget);
+ EPHYR_DBG("unsupported property requested: %d",req->target);
return XCB_NONE;
}
}
else
{
- if((req->target!=jpg)&&(req->target!=jpeg))
+ if((req->target!=ATOM_IMAGE_JPEG)&&(req->target!=ATOM_IMAGE_JPG))
{
- starget=atom_name(req->target);
- EPHYR_DBG("unsupported property requested: %s",starget);
- if(starget)
- free(starget);
+ EPHYR_DBG("unsupported property requested: %d",req->target);
return XCB_NONE;
}
}
@@ -902,36 +1301,166 @@ xcb_atom_t set_data_property(xcb_selection_request_event_t* req, unsigned char*
//set data to window property
//change when implemented
- BOOL support_incr=FALSE;
+ BOOL support_incr=TRUE;
- 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 )
+ if(req->property == ATOM_XT_SELECTION|| req->property == ATOM_QT_SELECTION )
{
EPHYR_DBG("property %d doesn't support INCR",req->property);
support_incr=FALSE;
}
//check if we are sending incr
- if(size < xcb_get_maximum_request_length(remoteVars->selstruct.xcbConnection) * 4 - 24)
+ if(!support_incr)
{
-// EPHYR_DBG( "sending %d bytes, property %d, target %d", size, req->property, req->target);
+ 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);
+ xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
+ 8, size, (const void *)data);
+
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ return req->property;
+ }
+ //the data is to big to sent in one property and requestor doesn't support INCR
+ EPHYR_DBG("data is too big");
+ return XCB_NONE;
+ }
+ if(size < INCR_SIZE)
+ {
+ //if size is < 256K send in one property
xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property, req->target,
8, size, (const void *)data);
xcb_flush(remoteVars->selstruct.xcbConnection);
return req->property;
}
+ //sending INCR atom to let requester know that we are starting data incrementally
+// EPHYR_DBG("starting INCR send of size %d for win ID %d" ,size, req->requestor);
+ xcb_change_property(remoteVars->selstruct.xcbConnection, XCB_PROP_MODE_REPLACE, req->requestor, req->property,
+ ATOM_INCR, 32, 1, (const void *)&size);
- EPHYR_DBG("data is too big");
- return XCB_NONE;
+ start_incr_transaction(req->requestor, req->property, req->target, data, size);
+
+
+ xcb_flush(remoteVars->selstruct.xcbConnection);
+ return req->property;
}
+
+void start_incr_transaction(xcb_window_t requestor, xcb_atom_t property, xcb_atom_t target, unsigned char* data, uint32_t size)
+{
+ //creating INCR transaction
+ //inmutex is locked from parent thread
+
+ const uint32_t mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+
+ struct IncrTransaction* tr=malloc( sizeof(struct IncrTransaction));
+ tr->requestor=requestor;
+ tr->property=property;
+ tr->target=target;
+ tr->sentBytes=0;
+ tr->timestamp=currentTime.milliseconds;
+ tr->data=malloc(size);
+ tr->size=size;
+ tr->next=NULL;
+ memcpy(tr->data, data, size);
+
+ //add new transaction to the list
+ if(!remoteVars->selstruct.firstIncrTransaction)
+ {
+ remoteVars->selstruct.firstIncrTransaction=remoteVars->selstruct.lastIncrTransaction=tr;
+ }
+ else
+ {
+ remoteVars->selstruct.lastIncrTransaction->next=tr;
+ remoteVars->selstruct.lastIncrTransaction=tr;
+ }
+
+
+ //we'll recive property change events for requestor window from now
+ xcb_change_window_attributes(remoteVars->selstruct.xcbConnection, requestor,
+ XCB_CW_EVENT_MASK, mask);
+}
+
+
BOOL is_png(unsigned char* data, uint32_t size)
{
if( size<8)
return FALSE;
return !png_sig_cmp(data, 0, 8);
}
+
+unsigned char* zcompress(unsigned char *inbuf, uint32_t size, uint32_t* compress_size)
+{
+ //compressing the data with zlib
+ //return compressed data, storing the size of compressed data in compress_size
+ //caller function should chek result of compression and free the output buffer
+
+
+ //out buffer at least the size of input buffer
+ unsigned char* out=malloc(size);
+
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ stream.avail_in = size;
+ stream.next_in = inbuf;
+ stream.avail_out = size;
+ stream.next_out = out;
+
+ deflateInit(&stream, Z_BEST_COMPRESSION);
+ deflate(&stream, Z_FINISH);
+ deflateEnd(&stream);
+
+ if(!stream.total_out || stream.total_out >= size)
+ {
+ EPHYR_DBG("zlib compression failed");
+ free(out);
+ *compress_size=0;
+ return NULL;
+ }
+ *compress_size=stream.total_out;
+ return out;
+}
+
+void delay_selection_request( xcb_selection_request_event_t *request, xcb_selection_notify_event_t* event)
+{
+ //delay the request for later processing when data will be ready
+ //inmutex is locked in the caller function
+ enum SelectionType sel=selection_from_atom(request->selection);
+ struct DelayedRequest* dr = malloc( sizeof(struct DelayedRequest));
+ dr->event=event;
+ dr->request=request;
+ dr->next=NULL;
+
+ //add new request to the queue
+ if(!remoteVars->selstruct.firstDelayedRequest)
+ {
+ remoteVars->selstruct.firstDelayedRequest=remoteVars->selstruct.lastDelayedRequest=dr;
+ }
+ else
+ {
+ remoteVars->selstruct.lastDelayedRequest->next=dr;
+ remoteVars->selstruct.lastDelayedRequest=dr;
+ }
+
+ if(remoteVars->selstruct.inSelection[sel].state==NOTIFIED)
+ {
+ //if we didn't request the data yet, let's do it now
+// EPHYR_DBG("requesting data");
+
+ pthread_mutex_lock(&remoteVars->sendqueue_mutex);
+ remoteVars->selstruct.requestSelection[sel] = TRUE;
+ pthread_cond_signal(&remoteVars->have_sendqueue_cond);
+
+
+ pthread_mutex_unlock(&remoteVars->sendqueue_mutex);
+ remoteVars->selstruct.inSelection[sel].state=REQUESTED;
+
+ }
+}
+
diff --git a/x2gokdriveselection.h b/x2gokdriveselection.h
index 7ec8d8a..e144d20 100644
--- a/x2gokdriveselection.h
+++ b/x2gokdriveselection.h
@@ -36,18 +36,37 @@ void selection_init(struct _remoteHostVars *obj);
void install_selection_callbacks(void);
int own_selection(enum SelectionType selection);
-xcb_atom_t atom(const char* name);
-char *atom_name(xcb_atom_t xatom);
+int is_string_atom( xcb_atom_t at);
+int is_image_atom( xcb_atom_t at);
+
+xcb_atom_t string_to_atom(const char* name);
+char *atom_to_string(xcb_atom_t xatom);
+void init_atoms(void);
+
+void send_notify_to_client(xcb_atom_t selection, xcb_atom_t mime);
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);
+BOOL 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 atom_from_selection(enum SelectionType sel);
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);
+void client_sel_request_notify(enum SelectionType sel);
+void client_sel_data_notify(enum SelectionType sel);
BOOL is_png(unsigned char* data, uint32_t size);
+void delay_selection_request( xcb_selection_request_event_t *reqest, xcb_selection_notify_event_t* event);
+void process_delayed_requests(void);
+struct DelayedRequest* discard_delayed_request(struct DelayedRequest* d, struct DelayedRequest* prev);
+BOOL check_req_sanity(xcb_selection_request_event_t* req);
+void start_incr_transaction(xcb_window_t requestor, xcb_atom_t property, xcb_atom_t target, unsigned char* data, uint32_t size);
+void process_incr_transaction_property(xcb_property_notify_event_t * pn);
+void destroy_incr_transaction(struct IncrTransaction* tr, struct IncrTransaction* prev);
+void remove_obsolete_incr_transactions( BOOL checkTs);
+
+unsigned char* zcompress(unsigned char *inbuf, uint32_t size, uint32_t* compress_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