diff options
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 | 138 | ||||
-rw-r--r-- | pugl/pugl_win.cpp | 60 | ||||
-rw-r--r-- | pugl/pugl_x11.c | 90 |
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( |