diff options
author | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2017-03-07 19:49:51 +0100 |
---|---|---|
committer | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2017-03-07 19:49:51 +0100 |
commit | 0406d71d421e141802ca8c085bbd00125a4af961 (patch) | |
tree | 7379071879b7eeffb3b207911c3a72ede793eb0b /pugl | |
parent | c977ef2968f22d0379a857aa2fe1668547bb50c0 (diff) | |
download | sherlock.lv2-0406d71d421e141802ca8c085bbd00125a4af961.tar.xz |
Implement clipboard
Diffstat (limited to 'pugl')
-rw-r--r-- | pugl/pugl.h | 12 | ||||
-rw-r--r-- | pugl/pugl.hpp | 2 | ||||
-rw-r--r-- | pugl/pugl_internal.h | 32 | ||||
-rw-r--r-- | pugl/pugl_osx.m | 37 | ||||
-rw-r--r-- | pugl/pugl_win.cpp | 60 | ||||
-rw-r--r-- | pugl/pugl_x11.c | 90 |
6 files changed, 233 insertions, 0 deletions
diff --git a/pugl/pugl.h b/pugl/pugl.h index 1b22260..dbbad90 100644 --- a/pugl/pugl.h +++ b/pugl/pugl.h @@ -569,6 +569,18 @@ PUGL_API void puglIgnoreKeyRepeat(PuglView* view, bool ignore); /** + Copy selection to clipboard. +*/ +PUGL_API void +puglCopyToClipboard(PuglView* view, const char* selection, size_t len); + +/** + Paste selection from clipboard. +*/ +PUGL_API const char* +puglPasteFromClipboard(PuglView* view, size_t* len); + +/** Grab the input focus. */ PUGL_API void diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp index 8232887..7d3ea9e 100644 --- a/pugl/pugl.hpp +++ b/pugl/pugl.hpp @@ -83,6 +83,8 @@ public: virtual void* getContext() { return puglGetContext(_view); } virtual void ignoreKeyRepeat(bool ignore) { puglIgnoreKeyRepeat(_view, ignore); } virtual void grabFocus() { puglGrabFocus(_view); } + virtual void copyToClipboard(char* selection) { puglCopyToClipboard(_view, selection); } + virtual const char* pasteFromClipboard() { return puglPasteFromClipboard(_view); } virtual PuglStatus waitForEvent() { return puglWaitForEvent(_view); } virtual PuglStatus processEvents() { return puglProcessEvents(_view); } virtual void postRedisplay() { puglPostRedisplay(_view); } diff --git a/pugl/pugl_internal.h b/pugl/pugl_internal.h index 4a3fc0c..45084de 100644 --- a/pugl/pugl_internal.h +++ b/pugl/pugl_internal.h @@ -40,6 +40,7 @@ struct PuglViewImpl { PuglEventFunc eventFunc; PuglInternals* impl; + char* selection; char* windowClass; PuglNativeWindow parent; @@ -239,3 +240,34 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event) view->eventFunc(view, event); } } + +static void +puglClearSelection(PuglView* view) +{ + if(view->selection) { + free(view->selection); + view->selection = NULL; + } +} + +static void +puglSetSelection(PuglView* view, const char *selection, size_t len) +{ + puglClearSelection(view); + + if(selection) { + view->selection = (char*)malloc(len + 1); + if(view->selection) { + memcpy(view->selection, selection, len); + view->selection[len] = 0; + } + } +} + +static const char* +puglGetSelection(PuglView* view, size_t* len) +{ + if(len) + *len = view->selection ? strlen(view->selection) : 0; + return view->selection; +} diff --git a/pugl/pugl_osx.m b/pugl/pugl_osx.m index 9f97f20..5f57798 100644 --- a/pugl/pugl_osx.m +++ b/pugl/pugl_osx.m @@ -642,12 +642,49 @@ puglDestroy(PuglView* view) if (view->impl->window) { [view->impl->window release]; } + puglClearSelection(view); free(view->windowClass); free(view->impl); free(view); } void +puglCopyToClipboard(PuglView* view, const char* selection, size_t len) +{ + PuglInternals* const impl = view->impl; + + puglSetSelection(view, selection, len); + + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + if(pasteboard) { + [pasteboard + declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] + owner:nil]; + [pasteboard + setString:[NSString stringWithUTF8String:puglGetSelection(view, NULL)] + forType:NSStringPboardType]; + } +} + +const char* +puglPasteFromClipboard(PuglView* view, size_t* len) +{ + PuglInternals* const impl = view->impl; + + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + if(pasteboard && [ [pasteboard types] containsObject:NSStringPboardType] ) { + const NSString* mem = [pasteboard stringForType:NSStringPboardType]; + if(mem) { + const char *utf8 = [mem UTF8String]; + if(utf8) + puglSetSelection(view, utf8, strlen(utf8)); + } + } + + return puglGetSelection(view, len); +} + +void puglGrabFocus(PuglView* view) { // TODO diff --git a/pugl/pugl_win.cpp b/pugl/pugl_win.cpp index 83d4474..6468963 100644 --- a/pugl/pugl_win.cpp +++ b/pugl/pugl_win.cpp @@ -229,6 +229,7 @@ puglDestroy(PuglView* view) ReleaseDC(view->impl->hwnd, view->impl->hdc); DestroyWindow(view->impl->hwnd); UnregisterClass(view->impl->wc.lpszClassName, NULL); + puglClearSelection(view); free(view->windowClass); free(view->impl); free(view); @@ -575,6 +576,65 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) } void +puglCopyToClipboard(PuglView* view, const char* selection, size_t len) +{ + PuglInternals* const impl = view->impl; + + puglSetSelection(view, selection, len); + + if(OpenClipboard(impl->hwnd)) { + const int wsize = MultiByteToWideChar(CP_UTF8, 0, selection, len, NULL, 0); + if(wsize) { + HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t)); + if(mem) { + wchar_t* wstr = (wchar_t*)GlobalLock(mem); + if(wstr) { + MultiByteToWideChar(CP_UTF8, 0, selection, len, wstr, wsize); + wstr[wsize] = 0; + GlobalUnlock(mem); + SetClipboardData(CF_UNICODETEXT, mem); + } + else { + GlobalFree(mem); + } + } + } + CloseClipboard(); + } +} + +const char* +puglPasteFromClipboard(PuglView* view, size_t* len) +{ + PuglInternals* const impl = view->impl; + + if(IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(impl->hwnd)) { + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if(mem) { + const int wsize = GlobalSize(mem) / sizeof(wchar_t) - 1; + if(wsize) { + wchar_t* wstr = (wchar_t*)GlobalLock(mem); + if(wstr) { + const int utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, wsize, NULL, 0, NULL, NULL); + if(utf8size) { + char* utf8 = (char*)malloc(utf8size); + if(utf8) { + WideCharToMultiByte(CP_UTF8, 0, wstr, wsize, utf8, utf8size, NULL, NULL); + puglSetSelection(view, utf8, utf8size); + free(utf8); + } + } + GlobalUnlock(mem); + } + } + } + CloseClipboard(); + } + + return puglGetSelection(view, len); +} + +void puglGrabFocus(PuglView* view) { // TODO diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c index 6a9135b..3d29ab3 100644 --- a/pugl/pugl_x11.c +++ b/pugl/pugl_x11.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <limits.h> #include <X11/Xatom.h> #include <X11/Xlib.h> @@ -100,6 +101,8 @@ struct PuglInternalsImpl { #if defined(PUGL_HAVE_CAIRO) && defined(PUGL_HAVE_GL) PuglCairoGL cairo_gl; #endif + Atom clipboard; + Atom utf8_string; }; PuglInternals* @@ -268,6 +271,9 @@ puglCreateWindow(PuglView* view, const char* title) Colormap cmap = XCreateColormap( impl->display, xParent, vi->visual, AllocNone); + impl->clipboard = XInternAtom(impl->display, "CLIPBOARD", 0); + impl->utf8_string = XInternAtom(impl->display, "UTF8_STRING", 0); + XSetWindowAttributes attr; memset(&attr, 0, sizeof(XSetWindowAttributes)); attr.colormap = cmap; @@ -283,6 +289,7 @@ puglCreateWindow(PuglView* view, const char* title) CWColormap | CWEventMask, &attr); if (!createContext(view, vi)) { + XFree(vi); return 2; } @@ -369,6 +376,7 @@ puglDestroy(PuglView* view) destroyContext(view); XDestroyWindow(view->impl->display, view->impl->win); XCloseDisplay(view->impl->display); + puglClearSelection(view); free(view->windowClass); free(view->impl); free(view); @@ -572,6 +580,39 @@ translateEvent(PuglView* view, XEvent xevent) event.focus.grab = (xevent.xfocus.mode != NotifyNormal); break; + case SelectionClear: + puglClearSelection(view); + break; + case SelectionRequest: + { + XSelectionEvent xev = { + .type = SelectionNotify, + .requestor = xevent.xselectionrequest.requestor, + .selection = xevent.xselectionrequest.selection, + .target = xevent.xselectionrequest.target, + .time = xevent.xselectionrequest.time, + }; + + size_t len; + const char *selection = puglGetSelection(view, &len); + if(selection + && (xevent.xselectionrequest.selection == view->impl->clipboard) + && (xevent.xselectionrequest.target == view->impl->utf8_string) ) + { + xev.property = xevent.xselectionrequest.property; + XChangeProperty(view->impl->display, xev.requestor, + xev.property, xev.target, 8, PropModeReplace, + (const uint8_t*)selection, len); + } + else // conversion failed + { + xev.property = None; + } + XSendEvent(view->impl->display, xev.requestor, True, 0, (XEvent*)&xev); + + break; + } + default: break; } @@ -580,6 +621,55 @@ translateEvent(PuglView* view, XEvent xevent) } void +puglCopyToClipboard(PuglView* view, const char* selection, size_t len) +{ + PuglInternals* const impl = view->impl; + + puglSetSelection(view, selection, len); + + XSetSelectionOwner(impl->display, impl->clipboard, impl->win, CurrentTime); +} + +const char* +puglPasteFromClipboard(PuglView* view, size_t* len) +{ + PuglInternals* const impl = view->impl; + + if(XGetSelectionOwner(impl->display, impl->clipboard) != impl->win) { + XConvertSelection(impl->display, impl->clipboard, impl->utf8_string, + XA_PRIMARY, impl->win, CurrentTime); + + XEvent xevent; + while(!XCheckTypedWindowEvent(impl->display, impl->win, SelectionNotify, + &xevent)) { + // wait for answer + } + + if( (xevent.xselection.selection == impl->clipboard) + && (xevent.xselection.target == impl->utf8_string) + && (xevent.xselection.property == XA_PRIMARY) ) { + ulong nitems, rem; + int format; + uint8_t* data; + Atom type; + + XGetWindowProperty(impl->display, impl->win, XA_PRIMARY, + 0, LONG_MAX/4, False, AnyPropertyType, // request 32*32=1024 bytes + &type, &format, &nitems, &rem, &data); + if(data) { + if( (format == 8) && (type == impl->utf8_string) && (rem == 0) ) { + puglSetSelection(view, (const char*)data, nitems); + } + + XFree(data); + } + } + } + + return puglGetSelection(view, len); +} + +void puglGrabFocus(PuglView* view) { XSetInputFocus( |