aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2017-07-05 22:52:10 +0200
committerHanspeter Portner <dev@open-music-kontrollers.ch>2017-07-05 22:52:10 +0200
commit9e9b0b23755c95235296b8ec44a9ec7260f3d6ab (patch)
tree7379071879b7eeffb3b207911c3a72ede793eb0b
parenta7a6fb766cf807adf4f49ba0ed66f4048f73abbc (diff)
downloadnuk.lv2-9e9b0b23755c95235296b8ec44a9ec7260f3d6ab.tar.xz
Squashed 'pugl/' changes from a7a6fb7..0406d71
0406d71 Implement clipboard c977ef2 Implement special key handling on MacOS adaf38d Fix cairo inclusion 0361919 Properly reset view->redisplay on windows 38fab74 Support many mouse buttons on OSX be0df74 Fix mouse button numbers on OSX 5afaf31 Fix OSX build 7da29d7 Fix mouse click printing git-subtree-dir: pugl git-subtree-split: 0406d71d421e141802ca8c085bbd00125a4af961
-rw-r--r--pugl/cairo_gl.h2
-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.m203
-rw-r--r--pugl/pugl_win.cpp61
-rw-r--r--pugl/pugl_x11.c90
-rw-r--r--pugl_test.c1
8 files changed, 366 insertions, 37 deletions
diff --git a/pugl/cairo_gl.h b/pugl/cairo_gl.h
index fe296bb..5c0f1f9 100644
--- a/pugl/cairo_gl.h
+++ b/pugl/cairo_gl.h
@@ -16,7 +16,7 @@
#if defined(PUGL_HAVE_GL) && defined(PUGL_HAVE_CAIRO)
-#include <cairo.h>
+#include <cairo/cairo.h>
#include <stdint.h>
#include "pugl/gl.h"
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 f495811..5f57798 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;
@@ -109,14 +110,6 @@ struct PuglInternalsImpl {
@end
-static void
-puglDisplay(PuglView* view)
-{
- if (view->displayFunc) {
- view->displayFunc(view);
- }
-}
-
@interface PuglOpenGLView : NSOpenGLView
{
@public
@@ -139,6 +132,9 @@ puglDisplay(PuglView* view)
- (void) rightMouseDragged:(NSEvent*)event;
- (void) rightMouseDown:(NSEvent*)event;
- (void) rightMouseUp:(NSEvent*)event;
+- (void) otherMouseDragged:(NSEvent*)event;
+- (void) otherMouseDown:(NSEvent*)event;
+- (void) otherMouseUp:(NSEvent*)event;
- (void) scrollWheel:(NSEvent*)event;
- (void) keyDown:(NSEvent*)event;
- (void) keyUp:(NSEvent*)event;
@@ -155,6 +151,9 @@ puglDisplay(PuglView* view)
NSOpenGLPFAAccelerated,
NSOpenGLPFAColorSize, 32,
NSOpenGLPFADepthSize, 32,
+ NSOpenGLPFAMultisample,
+ NSOpenGLPFASampleBuffers, 1,
+ NSOpenGLPFASamples, 4,
0
};
@@ -171,6 +170,7 @@ puglDisplay(PuglView* view)
if (self) {
[[self openGLContext] makeCurrentContext];
[self reshape];
+ [NSOpenGLContext clearCurrentContext];
}
return self;
}
@@ -211,7 +211,18 @@ puglDisplay(PuglView* view)
- (void) drawRect:(NSRect)rect
{
- puglDisplay(puglview);
+ const PuglEventExpose ev = {
+ PUGL_EXPOSE,
+ puglview,
+ 0,
+ rect.origin.x,
+ rect.origin.y,
+ rect.size.width,
+ rect.size.height,
+ 0
+ };
+
+ puglDispatchEvent(puglview, (const PuglEvent*)&ev);
#ifdef PUGL_HAVE_CAIRO
if (puglview->ctx_type & PUGL_CAIRO) {
@@ -219,8 +230,6 @@ puglDisplay(PuglView* view)
&puglview->impl->cairo_gl, puglview->width, puglview->height);
}
#endif
-
- [[self openGLContext] flushBuffer];
}
- (BOOL) acceptsFirstResponder
@@ -233,8 +242,6 @@ getModifiers(PuglView* view, NSEvent* ev)
{
const unsigned modifierFlags = [ev modifierFlags];
- view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX);
-
unsigned mods = 0;
mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0;
mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0;
@@ -243,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) {
@@ -304,6 +344,11 @@ getModifiers(PuglView* view, NSEvent* ev)
[self mouseMoved: event];
}
+- (void) otherMouseDragged:(NSEvent*)event
+{
+ [self mouseMoved: event];
+}
+
- (void) mouseDown:(NSEvent*)event
{
const NSPoint wloc = [self eventLocation:event];
@@ -318,7 +363,7 @@ getModifiers(PuglView* view, NSEvent* ev)
rloc.x,
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(puglview, event),
- [event buttonNumber]
+ [event buttonNumber] + 1
};
puglDispatchEvent(puglview, (PuglEvent*)&ev);
}
@@ -337,7 +382,7 @@ getModifiers(PuglView* view, NSEvent* ev)
rloc.x,
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(puglview, event),
- [event buttonNumber]
+ [event buttonNumber] + 1
};
puglDispatchEvent(puglview, (PuglEvent*)&ev);
[self updateTrackingAreas];
@@ -353,6 +398,16 @@ getModifiers(PuglView* view, NSEvent* ev)
[self mouseUp: event];
}
+- (void) otherMouseDown:(NSEvent*)event
+{
+ [self mouseDown: event];
+}
+
+- (void) otherMouseUp:(NSEvent*)event
+{
+ [self mouseUp: event];
+}
+
- (void) scrollWheel:(NSEvent*)event
{
[self updateTrackingAreas];
@@ -398,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
};
@@ -424,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,
};
@@ -434,19 +489,47 @@ getModifiers(PuglView* view, NSEvent* ev)
- (void) flagsChanged:(NSEvent*)event
{
- if (puglview->specialFunc) {
- 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
@@ -479,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
@@ -556,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
@@ -585,13 +708,21 @@ puglWaitForEvent(PuglView* view)
PuglStatus
puglProcessEvents(PuglView* view)
{
- if (!view->impl->nextEvent) {
- view->impl->nextEvent = [view->impl->window
- nextEventMatchingMask: NSAnyEventMask];
- }
+ while (true) {
+ // Get the next event, or use the cached one from puglWaitForEvent
+ if (!view->impl->nextEvent) {
+ view->impl->nextEvent = [view->impl->window
+ nextEventMatchingMask: NSAnyEventMask];
+ }
- [view->impl->app sendEvent: view->impl->nextEvent];
- view->impl->nextEvent = NULL;
+ if (!view->impl->nextEvent) {
+ break; // No events to process, done
+ }
+
+ // Dispatch event
+ [view->impl->app sendEvent: view->impl->nextEvent];
+ view->impl->nextEvent = NULL;
+ }
return PUGL_SUCCESS;
}
diff --git a/pugl/pugl_win.cpp b/pugl/pugl_win.cpp
index 874abba..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
@@ -598,6 +658,7 @@ puglProcessEvents(PuglView* view)
if (view->redisplay) {
InvalidateRect(view->impl->hwnd, NULL, FALSE);
+ view->redisplay = false;
}
return PUGL_SUCCESS;
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(
diff --git a/pugl_test.c b/pugl_test.c
index 4a3d066..367e7a4 100644
--- a/pugl_test.c
+++ b/pugl_test.c
@@ -182,6 +182,7 @@ onEvent(PuglView* view, const PuglEvent* event)
(event->type == PUGL_BUTTON_PRESS) ? "down" : "up",
event->button.x,
event->button.y);
+ printModifiers(view, event->scroll.state);
break;
case PUGL_SCROLL:
fprintf(stderr, "Scroll %f %f %f %f ",