aboutsummaryrefslogtreecommitdiff
path: root/pugl
diff options
context:
space:
mode:
authorGravatar Hanspeter Portner <dev@open-music-kontrollers.ch>2017-03-07 19:53:31 +0100
committerGravatar Hanspeter Portner <dev@open-music-kontrollers.ch>2017-03-07 19:53:31 +0100
commit8806670348ef537346552979fd21ea19b533b9a3 (patch)
treee5163b92067e1c9e3bd89305a1c7cfdc9344449d /pugl
parent12bcc8c7bbf100473dc7a840b2b9da6b2eda21b3 (diff)
parent0406d71d421e141802ca8c085bbd00125a4af961 (diff)
downloadsynthpod-8806670348ef537346552979fd21ea19b533b9a3.zip
synthpod-8806670348ef537346552979fd21ea19b533b9a3.tar.gz
synthpod-8806670348ef537346552979fd21ea19b533b9a3.tar.bz2
synthpod-8806670348ef537346552979fd21ea19b533b9a3.tar.xz
Merge commit '0406d71d421e141802ca8c085bbd00125a4af961' into nk
Diffstat (limited to 'pugl')
-rw-r--r--pugl/pugl/pugl.h12
-rw-r--r--pugl/pugl/pugl.hpp2
-rw-r--r--pugl/pugl/pugl_internal.h32
-rw-r--r--pugl/pugl/pugl_osx.m37
-rw-r--r--pugl/pugl/pugl_win.cpp60
-rw-r--r--pugl/pugl/pugl_x11.c90
6 files changed, 233 insertions, 0 deletions
diff --git a/pugl/pugl/pugl.h b/pugl/pugl/pugl.h
index 1b22260..dbbad90 100644
--- a/pugl/pugl/pugl.h
+++ b/pugl/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/pugl.hpp b/pugl/pugl/pugl.hpp
index 8232887..7d3ea9e 100644
--- a/pugl/pugl/pugl.hpp
+++ b/pugl/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/pugl_internal.h b/pugl/pugl/pugl_internal.h
index 4a3fc0c..45084de 100644
--- a/pugl/pugl/pugl_internal.h
+++ b/pugl/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/pugl_osx.m b/pugl/pugl/pugl_osx.m
index 9f97f20..5f57798 100644
--- a/pugl/pugl/pugl_osx.m
+++ b/pugl/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/pugl_win.cpp b/pugl/pugl/pugl_win.cpp
index 83d4474..6468963 100644
--- a/pugl/pugl/pugl_win.cpp
+++ b/pugl/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/pugl_x11.c b/pugl/pugl/pugl_x11.c
index 6a9135b..3d29ab3 100644
--- a/pugl/pugl/pugl_x11.c
+++ b/pugl/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(