aboutsummaryrefslogtreecommitdiff
path: root/pugl
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2017-07-05 22:51:49 +0200
committerHanspeter Portner <dev@open-music-kontrollers.ch>2017-07-05 22:51:49 +0200
commit7081c1c1f06c658fb8edb34344e813b135cf7e2b (patch)
tree7379071879b7eeffb3b207911c3a72ede793eb0b /pugl
parentadaf38d0984087ba9b6281dde46749dfb29a2a38 (diff)
downloadsynthpod-7081c1c1f06c658fb8edb34344e813b135cf7e2b.tar.xz
Squashed 'pugl/' changes from adaf38d..0406d71
0406d71 Implement clipboard c977ef2 Implement special key handling on MacOS git-subtree-dir: pugl git-subtree-split: 0406d71d421e141802ca8c085bbd00125a4af961
Diffstat (limited to 'pugl')
-rw-r--r--pugl/pugl.h12
-rw-r--r--pugl/pugl.hpp2
-rw-r--r--pugl/pugl_internal.h32
-rw-r--r--pugl/pugl_osx.m138
-rw-r--r--pugl/pugl_win.cpp60
-rw-r--r--pugl/pugl_x11.c90
6 files changed, 317 insertions, 17 deletions
diff --git a/pugl/pugl.h b/pugl/pugl.h
index 1b22260d..dbbad901 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 82328878..7d3ea9e3 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 4a3fc0ca..45084de3 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 d2681cb6..5f57798c 100644
--- a/pugl/pugl_osx.m
+++ b/pugl/pugl_osx.m
@@ -33,6 +33,7 @@ struct PuglInternalsImpl {
PuglOpenGLView* glview;
id window;
NSEvent* nextEvent;
+ unsigned mods;
#ifdef PUGL_HAVE_CAIRO
cairo_surface_t* surface;
cairo_t* cr;
@@ -150,6 +151,9 @@ struct PuglInternalsImpl {
NSOpenGLPFAAccelerated,
NSOpenGLPFAColorSize, 32,
NSOpenGLPFADepthSize, 32,
+ NSOpenGLPFAMultisample,
+ NSOpenGLPFASampleBuffers, 1,
+ NSOpenGLPFASamples, 4,
0
};
@@ -166,6 +170,7 @@ struct PuglInternalsImpl {
if (self) {
[[self openGLContext] makeCurrentContext];
[self reshape];
+ [NSOpenGLContext clearCurrentContext];
}
return self;
}
@@ -245,6 +250,39 @@ getModifiers(PuglView* view, NSEvent* ev)
return mods;
}
+static PuglKey
+keySymToSpecial(PuglView* view, NSEvent* ev)
+{
+ NSString* chars = [ev charactersIgnoringModifiers];
+ if([chars length] == 1) {
+ switch ([chars characterAtIndex:0]) {
+ case NSF1FunctionKey: return PUGL_KEY_F1;
+ case NSF2FunctionKey: return PUGL_KEY_F2;
+ case NSF3FunctionKey: return PUGL_KEY_F3;
+ case NSF4FunctionKey: return PUGL_KEY_F4;
+ case NSF5FunctionKey: return PUGL_KEY_F5;
+ case NSF6FunctionKey: return PUGL_KEY_F6;
+ case NSF7FunctionKey: return PUGL_KEY_F7;
+ case NSF8FunctionKey: return PUGL_KEY_F8;
+ case NSF9FunctionKey: return PUGL_KEY_F9;
+ case NSF10FunctionKey: return PUGL_KEY_F10;
+ case NSF11FunctionKey: return PUGL_KEY_F11;
+ case NSF12FunctionKey: return PUGL_KEY_F12;
+ case NSLeftArrowFunctionKey: return PUGL_KEY_LEFT;
+ case NSUpArrowFunctionKey: return PUGL_KEY_UP;
+ case NSRightArrowFunctionKey: return PUGL_KEY_RIGHT;
+ case NSDownArrowFunctionKey: return PUGL_KEY_DOWN;
+ case NSPageUpFunctionKey: return PUGL_KEY_PAGE_UP;
+ case NSPageDownFunctionKey: return PUGL_KEY_PAGE_DOWN;
+ case NSHomeFunctionKey: return PUGL_KEY_HOME;
+ case NSEndFunctionKey: return PUGL_KEY_END;
+ case NSInsertFunctionKey: return PUGL_KEY_INSERT;
+ /* special keys (SHIFT, CTRL, ALT, SUPER) are handled in [flagsChanged] */
+ }
+ }
+ return (PuglKey)0;
+}
+
-(void)updateTrackingAreas
{
if (trackingArea != nil) {
@@ -415,7 +453,7 @@ getModifiers(PuglView* view, NSEvent* ev)
getModifiers(puglview, event),
[event keyCode],
puglDecodeUTF8((const uint8_t*)str),
- 0, // TODO: Special keys?
+ keySymToSpecial(puglview, event),
{ 0, 0, 0, 0, 0, 0, 0, 0 },
false
};
@@ -441,7 +479,7 @@ getModifiers(PuglView* view, NSEvent* ev)
getModifiers(puglview, event),
[event keyCode],
puglDecodeUTF8((const uint8_t*)str),
- 0, // TODO: Special keys?
+ keySymToSpecial(puglview, event),
{ 0, 0, 0, 0, 0, 0, 0, 0 },
false,
};
@@ -451,21 +489,47 @@ getModifiers(PuglView* view, NSEvent* ev)
- (void) flagsChanged:(NSEvent*)event
{
- // TODO: Is this a sensible way to handle special keys?
- /*
- const unsigned mods = getModifiers(puglview, event);
- if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) {
- puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT);
- } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) {
- puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL);
- } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) {
- puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT);
- } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) {
- puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER);
- }
- puglview->mods = mods;
+ const unsigned mods = getModifiers(puglview, event);
+ PuglEventType type = PUGL_NOTHING;
+ PuglKey special = 0;
+
+ if ((mods & PUGL_MOD_SHIFT) != (puglview->impl->mods & PUGL_MOD_SHIFT)) {
+ type = mods & PUGL_MOD_SHIFT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_SHIFT;
+ } else if ((mods & PUGL_MOD_CTRL) != (puglview->impl->mods & PUGL_MOD_CTRL)) {
+ type = mods & PUGL_MOD_CTRL ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_CTRL;
+ } else if ((mods & PUGL_MOD_ALT) != (puglview->impl->mods & PUGL_MOD_ALT)) {
+ type = mods & PUGL_MOD_ALT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_ALT;
+ } else if ((mods & PUGL_MOD_SUPER) != (puglview->impl->mods & PUGL_MOD_SUPER)) {
+ type = mods & PUGL_MOD_SUPER ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_SUPER;
}
- */
+
+ if (special != 0) {
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ PuglEventKey ev = {
+ type,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ mods,
+ [event keyCode],
+ 0,
+ special,
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ false
+ };
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+ }
+
+ puglview->impl->mods = mods;
}
@end
@@ -498,8 +562,11 @@ puglLeaveContext(PuglView* view, bool flush)
#endif
if (flush) {
- [[view->impl->glview openGLContext] flushBuffer];
+ //[[view->impl->glview openGLContext] flushBuffer];
+ glFlush();
+ glSwapAPPLE();
}
+ [NSOpenGLContext clearCurrentContext];
}
int
@@ -575,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 83d4474b..6468963f 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 6a9135b6..3d29ab32 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(