From 49ccf9a34acea7b4ea74653dda9efd69ae211506 Mon Sep 17 00:00:00 2001 From: Cameron Leger Date: Mon, 22 Oct 2018 18:20:30 -0600 Subject: [PATCH] Add clipboard support --- dep/lglw/lglw_linux.c | 189 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 184 insertions(+), 5 deletions(-) diff --git a/dep/lglw/lglw_linux.c b/dep/lglw/lglw_linux.c index 8eed78cd..a509b584 100644 --- a/dep/lglw/lglw_linux.c +++ b/dep/lglw/lglw_linux.c @@ -130,6 +130,11 @@ typedef struct lglw_int_s { lglw_timer_fxn_t cbk; } timer; + struct { + uint32_t numChars; + char *data; + } clipboard; + struct { lglw_dropfiles_fxn_t cbk; } dropfiles; @@ -525,6 +530,41 @@ static void loc_eventProc(void *_xevent) { break; } break; + + case SelectionClear: + printf("vstgltest: xev SelectionClear\n"); + lglw->clipboard.numChars = 0; + free(lglw->clipboard.data); + break; + + case SelectionRequest: + printf("vstgltest: xev SelectionRequest\n"); + XSelectionRequestEvent *cbReq = (XSelectionRequestEvent*)xev; + XSelectionEvent cbRes; + + Atom utf8 = XInternAtom(lglw->xdsp, "UTF8_STRING", False); + + cbRes.type = SelectionNotify; + cbRes.requestor = cbReq->requestor; + cbRes.selection = cbReq->selection; + cbRes.target = cbReq->target; + cbRes.time = cbReq->time; + + if(cbReq->target == utf8) + { + XChangeProperty(lglw->xdsp, cbReq->requestor, cbReq->property, utf8, 8/*format*/, PropModeReplace, + (unsigned char *)lglw->clipboard.data, lglw->clipboard.numChars); + + cbRes.property = cbReq->property; + } + else + { + cbRes.property = None; + } + + XSendEvent(lglw->xdsp, cbReq->requestor, True, NoEventMask, (XEvent *)&cbRes); + + break; } } } @@ -1569,15 +1609,44 @@ void lglw_clipboard_text_set(lglw_t _lglw, const uint32_t _numChars, const char LGLW(_lglw); (void)_numChars; - // (todo) implement me - if(NULL != _text) { - (void)lglw; + if(NULL != _lglw) + { + if(0 != lglw->win.xwnd) + { + uint32_t numChars = (0u == _numChars) ? ((uint32_t)strlen(_text)+1u) : _numChars; + + if(numChars > 0u) + { + lglw->clipboard.numChars = numChars; + lglw->clipboard.data = malloc(numChars+1); + + uint32_t i; + for(i = 0u; i < numChars; i++) + { + lglw->clipboard.data[i] = _text[i]; + } + lglw->clipboard.data[numChars - 1] = 0; + + printf("xxx lglw_clipboard_text_set(%i): %s\n", lglw->clipboard.numChars, lglw->clipboard.data); + + Atom clipboard = XInternAtom(lglw->xdsp, "CLIPBOARD", False); + XSetSelectionOwner(lglw->xdsp, clipboard, lglw->win.xwnd, CurrentTime); + XSync(lglw->xdsp, False); + } + } + } } } +// ---------------------------------------------------------------------------- loc_is_clipboard_event +static Bool loc_is_clipboard_event(Display *_display, XEvent *_xevent, XPointer _xarg) { + return _xevent->type == SelectionNotify; +} + + // ---------------------------------------------------------------------------- lglw_clipboard_text_get void lglw_clipboard_text_get(lglw_t _lglw, uint32_t _maxChars, uint32_t *_retNumChars, char *_retText) { LGLW(_lglw); @@ -1592,8 +1661,118 @@ void lglw_clipboard_text_get(lglw_t _lglw, uint32_t _maxChars, uint32_t *_retNum { if(NULL != _lglw) { - // (todo) implement me - (void)lglw; + if(0 != lglw->win.xwnd) + { + Window owner; + XEvent xev; + XSelectionEvent *cbReq; + Atom clipboard = XInternAtom(lglw->xdsp, "CLIPBOARD", False); + Atom utf8 = XInternAtom(lglw->xdsp, "UTF8_STRING", False); + Atom target = XInternAtom(lglw->xdsp, "_clipboard_result", False); + + owner = XGetSelectionOwner(lglw->xdsp, clipboard); + if(owner == None) + { + printf("xxx lglw_clipboard_text_get: No Window can provide a clipboard result\n"); + return; + } + + if(owner == lglw->win.xwnd) + { + printf("xxx lglw_clipboard_text_get: We are the owner of the clipboard, skip X interactions\n"); + + uint32_t i = 0u; + for(; i < _maxChars; i++) + { + _retText[i] = lglw->clipboard.data[i]; + if(0 == _retText[i]) + break; + } + _retText[_maxChars - 1u] = 0; + + if(NULL != _retNumChars) + *_retNumChars = i; + + printf("xxx lglw_clipboard_text_get: (result on next line)\n%s\n", _retText); + return; + } + + XConvertSelection(lglw->xdsp, clipboard, utf8, target, lglw->win.xwnd, CurrentTime); + XIfEvent(lglw->xdsp, &xev, &loc_is_clipboard_event, None); + + cbReq = (XSelectionEvent*)&xev; + if(None == cbReq->property) + { + printf("xxx lglw_clipboard_text_get: Clipboard was not converted to UTF-8 string\n"); + return; + } + + Atom returnType; + int returnFormat; + unsigned long size, returnSize, bytesLeft; + unsigned char *propertyValue = NULL; + + XGetWindowProperty(lglw->xdsp, lglw->win.xwnd, target, + 0/*offset*/, + 0/*length*/, + False/*delete*/, + AnyPropertyType/*req_type*/, + &returnType/*actual_type_return*/, + &returnFormat/*actual_format_return*/, + &returnSize/*nitems_return*/, + &size/*bytes_after_return*/, + &propertyValue/*prop_return*/); + XFree(propertyValue); + + if(utf8 != returnType) + { + printf("xxx lglw_clipboard_text_get: Clipboard result is not a UTF-8 string\n"); + return; + } + + if(8u != returnFormat) + { + printf("xxx lglw_clipboard_text_get: Clipboard format is not a char array\n"); + return; + } + + if(_maxChars < size) + size = _maxChars; + size = 1 + ((size - 1) / 4); + + // TODO: Even with the largest current use-case, multiple calls aren't necessary. do it anyway just in case + XGetWindowProperty(lglw->xdsp, lglw->win.xwnd, target, + 0/*offset*/, + size/*length*/, + True/*delete*/, + AnyPropertyType/*req_type*/, + &returnType/*actual_type_return*/, + &returnFormat/*actual_format_return*/, + &returnSize/*nitems_return*/, + &bytesLeft/*bytes_after_return*/, + &propertyValue/*prop_return*/); + + if(returnSize == 0) + { + printf("xxx lglw_clipboard_text_get: No Clipboard result after final request\n"); + return; + } + + uint32_t i = 0u; + for(; i < _maxChars; i++) + { + _retText[i] = propertyValue[i]; + if(0 == _retText[i]) + break; + } + _retText[_maxChars - 1u] = 0; + + if(NULL != _retNumChars) + *_retNumChars = i; + + printf("xxx lglw_clipboard_text_get: (result on next line)\n%s\n", _retText); + XFree(propertyValue); + } } } }