From abc33d1b000ed7fbebbff9300864420fd6fb8fef Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 31 May 2019 08:18:04 +0200 Subject: Add (working) Cocoa UI support. --- autogen.sh | 10 + configure.ac | 6 +- plugingui/Makefile.am | 16 +- plugingui/guievent.h | 2 +- plugingui/nativewindow_cocoa.h | 39 +- plugingui/nativewindow_cocoa.mm | 897 +++++++++++++++++++++++++++++----------- plugingui/nativewindow_win32.cc | 2 +- plugingui/window.h | 3 +- 8 files changed, 719 insertions(+), 256 deletions(-) diff --git a/autogen.sh b/autogen.sh index 35edb8b..ae4952f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -11,5 +11,15 @@ then exit 1 fi +# Check for support of AC_PROG_OBJCXX macro. If missing emulate it. +mkdir -p actest +cat << EOF > actest/configure.ac +AC_INIT([actest], [1.0.0]) +AC_PROG_OBJCXX +EOF +[ -f acinclude.m4 ] && rm acinclude.m4 +autoreconf -W error actest 2>/dev/null || echo "AC_DEFUN([AC_PROG_OBJCXX],[echo ' - ObjC++ hack - not support by this platform, but not needed either.'])" > acinclude.m4 +rm -Rf actest + # Now run autoreconf ${AUTORECONF:-autoreconf} -fiv --warnings=no-unsupported diff --git a/configure.ac b/configure.ac index 43cef1c..ff226d0 100644 --- a/configure.ac +++ b/configure.ac @@ -5,6 +5,7 @@ AM_INIT_AUTOMAKE AC_PROG_CXX AC_PROG_OBJC +AC_PROG_OBJCXX AM_PROG_CC_C_O AC_PROG_MKDIR_P @@ -39,6 +40,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], ] ) CXXFLAGS="$TMP_CXXFLAGS $CXXFLAGS" +OBJCXXFLAGS="$OBJCXXFLAGS $CXXFLAGS" AC_LANG_POP([C++]) dnl =========================== @@ -144,7 +146,7 @@ AS_IF([test "x$enable_gui" = "xyes"], [enable_gui="auto"]) AS_IF([test "x$enable_gui" = "xauto"], [AC_MSG_RESULT([Auto setting gui based on host: $host_os]) AS_CASE([$host_os], - [darwin*], [enable_gui="pugl-cocoa"], + [darwin*], [enable_gui="cocoa"], [linux*|*bsd*], [enable_gui="x11"], [msys|mingw*|windows*|winnt|cygwin], [enable_gui="win32"], @@ -168,7 +170,7 @@ AS_IF( [test "x$enable_gui" = "xcocoa"], [AC_MSG_RESULT([Setting gui backend to Cocoa]) GUI_CPPFLAGS="-DUI_COCOA" - GUI_LIBS="-framework OpenGL -framework Cocoa" + GUI_LIBS="-framework Cocoa" ], [test "x$enable_gui" = "xpugl-x11"], diff --git a/plugingui/Makefile.am b/plugingui/Makefile.am index c4c2fa0..778d909 100644 --- a/plugingui/Makefile.am +++ b/plugingui/Makefile.am @@ -48,9 +48,9 @@ libdggui_la_CPPFLAGS = \ -DLODEPNG_NO_COMPILE_CPP libdggui_la_CFLAGS = -if ENABLE_PUGL_COCOA + libdggui_la_LIBTOOLFLAGS=--tag=CC -endif + libdggui_la_LIBADD = \ $(GUI_LIBS) $(PTHREAD_LIBS) @@ -125,11 +125,13 @@ nodist_libdggui_la_SOURCES += \ nativewindow_win32.cc endif -#if ENABLE_COCOA -#nodist_libdggui_la_SOURCES += \ -# nativewindow_cocoa.m \ -# nativewindow_cocoa.cc -#endif +if ENABLE_COCOA +nodist_libdggui_la_SOURCES += \ + nativewindow_cocoa.mm + +libdggui_la_OBJCXXFLAGS = \ + -fblocks +endif if ENABLE_PUGL_X11 nodist_libdggui_la_SOURCES += \ diff --git a/plugingui/guievent.h b/plugingui/guievent.h index fe51826..fa42483 100644 --- a/plugingui/guievent.h +++ b/plugingui/guievent.h @@ -103,7 +103,7 @@ public: int x; int y; - int delta; + float delta; }; class RepaintEvent diff --git a/plugingui/nativewindow_cocoa.h b/plugingui/nativewindow_cocoa.h index 100b3c7..3f0518a 100644 --- a/plugingui/nativewindow_cocoa.h +++ b/plugingui/nativewindow_cocoa.h @@ -26,6 +26,8 @@ */ #pragma once +#include + #include "nativewindow.h" namespace GUI @@ -40,21 +42,34 @@ public: ~NativeWindowCocoa(); // From NativeWindow: - void setFixedSize(int width, int height) override; - void resize(int width, int height) override; - void move(int x, int y) override; - void show() override; - void hide() override; - void setCaption(const std::string &caption) override; - void handleBuffer() override; - void redraw() override; - void grabMouse(bool grab) override; - bool hasEvent() override; - std::shared_ptr getNextEvent() override; - std::shared_ptr peekNextEvent() override; + virtual void setFixedSize(std::size_t width, std::size_t height) override; + virtual void resize(std::size_t width, std::size_t height) override; + virtual std::pair getSize() const override; + virtual void move(int x, int y) override; + virtual std::pair getPosition() const override; + virtual void show() override; + virtual void hide() override; + virtual bool visible() const override; + virtual void setCaption(const std::string &caption) override; + virtual void redraw(const Rect& dirty_rect) override; + virtual void grabMouse(bool grab) override; + virtual EventQueue getEvents() override; + virtual void* getNativeWindowHandle() const override; + + // Expose friend members of Window to ObjC++ implementation. + class Window& getWindow(); + class PixelBuffer& getWindowPixbuf(); + void resized(); + void pushBackEvent(std::shared_ptr event); private: + void updateLayerOffset(); + Window& window; + std::unique_ptr priv; + EventQueue event_queue; + void* native_window{nullptr}; + bool first{true}; }; } // GUI:: diff --git a/plugingui/nativewindow_cocoa.mm b/plugingui/nativewindow_cocoa.mm index 531c2a8..aaacd0e 100644 --- a/plugingui/nativewindow_cocoa.mm +++ b/plugingui/nativewindow_cocoa.mm @@ -24,337 +24,770 @@ * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include "nativewindow_cocoa.h" -/* - -loop: -http://stackoverflow.com/questions/6732400/cocoa-integrate-nsapplication-into-an-existing-c-mainloop - -draw pixels: -http://stackoverflow.com/questions/10955913/how-can-i-display-an-array-of-pixels-on-a-nswindow -*/ +#include "guievent.h" #include #include #import -bool running = true; +#include "window.h" -@interface MyWindow : NSWindow +#include + +#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED +#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101300 // Before MacOSX 10.13 (High-Sierra) +#define STYLE_MASK \ + (NSClosableWindowMask | \ + NSTitledWindowMask | \ + NSResizableWindowMask) +#define IMAGE_FLAGS \ + (kCGBitmapByteOrder32Big | \ + kCGImageAlphaPremultipliedLast) +#define EVENT_MASK \ + NSAnyEventMask +#else +#define STYLE_MASK \ + (NSWindowStyleMaskClosable | \ + NSWindowStyleMaskTitled | \ + NSWindowStyleMaskResizable) +#define IMAGE_FLAGS \ + (kCGImageByteOrder32Big | \ + kCGImageAlphaPremultipliedLast) +#define EVENT_MASK \ + NSEventMaskAny +#endif + +#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101400 // Before MacOSX 10.14 (Mojave) +// Nothing here yet... +#endif +#endif + +@interface DGListener : NSWindow { @public - // MyView* my_view; + NSWindow* window; + GUI::NativeWindowCocoa* native; } -- (id) initWithContentRect:(NSRect)contentRect - styleMask:(unsigned int)aStyle - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)flag; -//- (void) setMyView:(MyView*)view; -- (BOOL) windowShouldClose:(id)sender; -- (void) mouseMoved:(NSEvent*)event; -- (void) drawRect:(NSRect)dirtyRect; +- (id) initWithWindow:(NSWindow*)ref + native:(GUI::NativeWindowCocoa*)_native; +- (void) dealloc; +- (void) windowDidResize; +- (void) windowWillResize; +- (void) windowWillClose; +- (void) unbindNative; @end -@implementation MyWindow +@implementation DGListener +- (id) initWithWindow:(NSWindow*)ref + native:(GUI::NativeWindowCocoa*)_native +{ + [super init]; + + native = _native; + window = ref; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(windowDidResize) + name:NSWindowDidResizeNotification + object:ref]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(windowWillResize) + name:NSWindowWillStartLiveResizeNotification + object:ref]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(windowWillClose) + name:NSWindowWillCloseNotification + object:ref]; + + [self windowWillResize]; // trigger to get the initial size as a size change + + return self; +} -- (id)initWithContentRect:(NSRect)contentRect - styleMask:(unsigned int)aStyle - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)flag +- (void) dealloc { - NSWindow* result = [super initWithContentRect:contentRect - styleMask:(NSClosableWindowMask | - NSTitledWindowMask | - NSResizableWindowMask) - backing:NSBackingStoreBuffered defer:NO]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)windowDidResize +{ + if(!native) + { + return; + } + + native->resized(); +} + +- (void)windowWillResize +{ + if(!native) + { + return; + } + + native->resized(); +} - [result setAcceptsMouseMovedEvents:YES]; - [result setLevel: CGShieldingWindowLevel() + 1]; +- (void) windowWillClose +{ + if(!native) + { + return; + } - return result; + auto closeEvent = std::make_shared(); + native->pushBackEvent(closeEvent); } -//- (void)setMyView:(MyView*)view +- (void) unbindNative +{ + native = nullptr; +} +@end + +@interface DGView : NSView +{ + int colorBits; + int depthBits; + +@private + GUI::NativeWindowCocoa* native; + NSTrackingArea* trackingArea; +} + +//- (id) initWithFrame:(NSRect)frame +// colorBits:(int)numColorBits +// depthBits:(int)numDepthBits; +- (void) updateTrackingAreas; + +- (void) mouseEntered:(NSEvent *)event; +- (void) mouseExited:(NSEvent *)event; +- (void) mouseMoved:(NSEvent*)event; +- (void) mouseDown:(NSEvent*)event; +- (void) mouseUp:(NSEvent*)event; +- (void) rightMouseDown:(NSEvent*)event; +- (void) rightMouseUp:(NSEvent*)event; +- (void) otherMouseDown:(NSEvent*)event; +- (void) otherMouseUp:(NSEvent*)event; +- (void) mouseDragged:(NSEvent*)event; +- (void) rightMouseDragged:(NSEvent*)event; +- (void) otherMouseDragged:(NSEvent*)event; +- (void) scrollWheel:(NSEvent*)event; +- (void) keyDown:(NSEvent*)event; +- (void) keyUp:(NSEvent*)event; + +- (void) dealloc; +- (void) bindNative:(GUI::NativeWindowCocoa*)native; +- (void) unbindNative; +@end + +@implementation DGView +//- (id) initWithFrame:(NSRect)frame +// colorBits:(int)numColorBits +// depthBits:(int)numDepthBits //{ -// //my_view = view; -// //[self setContentSize:NSMakeSize(view->width, view->height) ]; +// [super init]; +// [self updateTrackingAreas]; +// return self; //} -- (BOOL)windowShouldClose:(id)sender +- (void) updateTrackingAreas { - printf("closing!\n"); - //if (puglview->closeFunc) - // puglview->closeFunc(puglview); - running = false; - return YES; + if(trackingArea != nil) + { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + int opts = + NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingActiveAlways; + + trackingArea = + [[NSTrackingArea alloc] initWithRect:[self bounds] + options:opts + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; } -#define WIDTH 100 -#define HEIGHT 100 -#define SIZE (WIDTH*HEIGHT) -#define BYTES_PER_PIXEL 2 -#define BITS_PER_COMPONENT 5 -#define BITS_PER_PIXEL 16 +- (void) mouseEntered:(NSEvent *)event +{ + [super mouseEntered:event]; + auto frame = [self frame]; + NSPoint loc = [event locationInWindow]; + auto mouseEnterEvent = std::make_shared(); + mouseEnterEvent->x = loc.x - frame.origin.x; + mouseEnterEvent->y = frame.size.height - loc.y - frame.origin.y; + native->pushBackEvent(mouseEnterEvent); + //[[NSCursor pointingHandCursor] set]; +} -- (void)drawRect:(NSRect)dirtyRect +- (void) mouseExited:(NSEvent *)event { + [super mouseExited:event]; + auto frame = [self frame]; + NSPoint loc = [event locationInWindow]; + auto mouseLeaveEvent = std::make_shared(); + mouseLeaveEvent->x = loc.x - frame.origin.x; + mouseLeaveEvent->y = frame.size.height - loc.y - frame.origin.y; + native->pushBackEvent(mouseLeaveEvent); + //[[NSCursor arrowCursor] set]; } + - (void) mouseMoved:(NSEvent*)event { - /* - if (puglview->motionFunc) { - NSPoint loc = [event locationInWindow]; - puglview->mods = getModifiers(puglview, event); - puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); - } - */ - NSPoint loc = [event locationInWindow]; - printf("mouseMove: %f %f\n", loc.x, loc.y); + auto frame = [self frame]; + NSPoint loc = [event locationInWindow]; + auto mouseMoveEvent = std::make_shared(); + mouseMoveEvent->x = loc.x - frame.origin.x; + mouseMoveEvent->y = frame.size.height - loc.y - frame.origin.y; + native->pushBackEvent(mouseMoveEvent); } -@end +- (void) mouseDown:(NSEvent*)event +{ + auto frame = [self frame]; + NSPoint loc = [event locationInWindow]; + + auto buttonEvent = std::make_shared(); + buttonEvent->x = loc.x - frame.origin.x; + buttonEvent->y = frame.size.height - loc.y - frame.origin.y; + switch((int)[event buttonNumber]) + { + case 0: + buttonEvent->button = GUI::MouseButton::left; + break; + case 1: + buttonEvent->button = GUI::MouseButton::right; + break; + case 2: + buttonEvent->button = GUI::MouseButton::middle; + break; + default: + return; + } + buttonEvent->direction = GUI::Direction::down; + buttonEvent->doubleClick = [event clickCount] == 2; + native->pushBackEvent(buttonEvent); + + [super mouseDown: event]; +} +- (void) mouseUp:(NSEvent*)event +{ + auto frame = [self frame]; + NSPoint loc = [event locationInWindow]; + + auto buttonEvent = std::make_shared(); + buttonEvent->x = loc.x - frame.origin.x; + buttonEvent->y = frame.size.height - loc.y - frame.origin.y; + switch((int)[event buttonNumber]) + { + case 0: + buttonEvent->button = GUI::MouseButton::left; + break; + case 1: + buttonEvent->button = GUI::MouseButton::right; + break; + case 2: + buttonEvent->button = GUI::MouseButton::middle; + break; + default: + return; + } + buttonEvent->direction = GUI::Direction::up; + buttonEvent->doubleClick = false; + native->pushBackEvent(buttonEvent); + + [super mouseUp: event]; +} +- (void) rightMouseDown:(NSEvent*)event +{ + [self mouseDown: event]; + [super rightMouseDown: event]; +} +- (void) rightMouseUp:(NSEvent*)event +{ + [self mouseUp: event]; + [super rightMouseUp: event]; +} -@interface MyView : NSView +- (void) otherMouseDown:(NSEvent*)event { - int colorBits; - int depthBits; -@public - //PuglView* puglview; + [self mouseDown: event]; + [super otherMouseDown: event]; +} - NSTrackingArea* trackingArea; +- (void) otherMouseUp:(NSEvent*)event +{ + [self mouseUp: event]; + [super otherMouseUp: event]; } -- (id) initWithFrame:(NSRect)frame - colorBits:(int)numColorBits - depthBits:(int)numDepthBits; -- (void) reshape; -- (void) drawRect:(NSRect)rect; +- (void) mouseDragged:(NSEvent*)event +{ + [self mouseMoved: event]; + [super mouseDragged: event]; +} -@end -@implementation MyView - -- (id) initWithFrame:(NSRect)frame - colorBits:(int)numColorBits - depthBits:(int)numDepthBits -{ - colorBits = numColorBits; - depthBits = numDepthBits; - - //NSOpenGLPixelFormatAttribute pixelAttribs[16] = { - // NSOpenGLPFADoubleBuffer, - // NSOpenGLPFAAccelerated, - // NSOpenGLPFAColorSize, - // colorBits, - // NSOpenGLPFADepthSize, - // depthBits, - // 0 - //}; - // - //NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] - // initWithAttributes:pixelAttribs]; - // - //if (pixelFormat) { - // self = [super initWithFrame:frame pixelFormat:pixelFormat]; - // [pixelFormat release]; - // if (self) { - // [[self openGLContext] makeCurrentContext]; - // [self reshape]; - // } - //} else { - // self = nil; - //} - - return self; -} -- (void) reshape -{ - //[[self openGLContext] update]; - // - //NSRect bounds = [self bounds]; - //int width = bounds.size.width; - //int height = bounds.size.height; - // - //if (puglview) { - // /* NOTE: Apparently reshape gets called when the GC gets around to - // deleting the view (?), so we must have reset puglview to NULL when - // this comes around. - // */ - // if (puglview->reshapeFunc) { - // puglview->reshapeFunc(puglview, width, height); - // } else { - // puglDefaultReshape(puglview, width, height); - // } - // - // puglview->width = width; - // puglview->height = height; - //} -} -- (void) drawRect:(NSRect)rect -{ - printf("drawRect\n"); - - // Get current context - CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - - // Colorspace RGB - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - - // Pixel Matrix allocation - unsigned short *pixels = calloc(SIZE, sizeof(unsigned short)); - - // Random pixels will give you a non-organized RAINBOW - for (int i = 0; i < WIDTH; i++) { - for (int j = 0; j < HEIGHT; j++) { - pixels[i+ j*HEIGHT] = arc4random() % USHRT_MAX; - } - } - - // Provider - CGDataProviderRef provider = CGDataProviderCreateWithData(nil, pixels, SIZE, nil); - - // CGImage - CGImageRef image = CGImageCreate(WIDTH, - HEIGHT, - BITS_PER_COMPONENT, - BITS_PER_PIXEL, - BYTES_PER_PIXEL*WIDTH, - colorSpace, - kCGImageAlphaNoneSkipFirst, - // xRRRRRGGGGGBBBBB - 16-bits, first bit is ignored! - provider, - nil, //No decode - NO, //No interpolation - kCGRenderingIntentDefault); // Default rendering - - // Draw - CGContextDrawImage(context, CGRectMake(0, 0, WIDTH, HEIGHT), image); - - // Once everything is written on screen we can release everything - CGImageRelease(image); - CGColorSpaceRelease(colorSpace); - CGDataProviderRelease(provider); +- (void) rightMouseDragged:(NSEvent*)event +{ + [self mouseMoved: event]; + [super rightMouseDragged: event]; +} + +- (void) otherMouseDragged:(NSEvent*)event +{ + [self mouseMoved: event]; + [super otherMouseDragged: event]; +} + +- (void) scrollWheel:(NSEvent*)event +{ + auto frame = [self frame]; + NSPoint loc = [event locationInWindow]; + + auto scrollEvent = std::make_shared(); + scrollEvent->x = loc.x - frame.origin.x; + scrollEvent->y = frame.size.height - loc.y - frame.origin.y; + scrollEvent->delta = [event deltaY] * -1.0f; + native->pushBackEvent(scrollEvent); + + [super scrollWheel: event]; +} + +- (void) keyDown:(NSEvent*)event +{ + const NSString* chars = [event characters]; + const char* str = [chars UTF8String]; + + auto keyEvent = std::make_shared(); + + switch([event keyCode]) + { + case 123: keyEvent->keycode = GUI::Key::left; break; + case 124: keyEvent->keycode = GUI::Key::right; break; + case 126: keyEvent->keycode = GUI::Key::up; break; + case 125: keyEvent->keycode = GUI::Key::down; break; + case 117: keyEvent->keycode = GUI::Key::deleteKey; break; + case 51: keyEvent->keycode = GUI::Key::backspace; break; + case 115: keyEvent->keycode = GUI::Key::home; break; + case 119: keyEvent->keycode = GUI::Key::end; break; + case 121: keyEvent->keycode = GUI::Key::pageDown; break; + case 116: keyEvent->keycode = GUI::Key::pageUp; break; + case 36: keyEvent->keycode = GUI::Key::enter; break; + default: keyEvent->keycode = GUI::Key::unknown; break; + } + + if(strlen(str) && keyEvent->keycode == GUI::Key::unknown) + { + keyEvent->keycode = GUI::Key::character; + } + + keyEvent->text = str; // TODO: UTF8 decode + keyEvent->direction = GUI::Direction::down; + + native->pushBackEvent(keyEvent); + [super keyDown: event]; +} + +- (void) keyUp:(NSEvent*)event +{ + const NSString* chars = [event characters]; + const char* str = [chars UTF8String]; + auto keyEvent = std::make_shared(); + + switch([event keyCode]) + { + case 123: keyEvent->keycode = GUI::Key::left; break; + case 124: keyEvent->keycode = GUI::Key::right; break; + case 126: keyEvent->keycode = GUI::Key::up; break; + case 125: keyEvent->keycode = GUI::Key::down; break; + case 117: keyEvent->keycode = GUI::Key::deleteKey; break; + case 51: keyEvent->keycode = GUI::Key::backspace; break; + case 115: keyEvent->keycode = GUI::Key::home; break; + case 119: keyEvent->keycode = GUI::Key::end; break; + case 121: keyEvent->keycode = GUI::Key::pageDown; break; + case 116: keyEvent->keycode = GUI::Key::pageUp; break; + case 36: keyEvent->keycode = GUI::Key::enter; break; + default: keyEvent->keycode = GUI::Key::unknown; break; + } + + if(strlen(str) && keyEvent->keycode == GUI::Key::unknown) + { + keyEvent->keycode = GUI::Key::character; + } + + keyEvent->text = str; // TODO: UTF8 decode + keyEvent->direction = GUI::Direction::up; + + native->pushBackEvent(keyEvent); + [super keyUp: event]; +} + +- (void) dealloc +{ + [super dealloc]; +} + +- (void)bindNative:(GUI::NativeWindowCocoa*)_native +{ + native = _native; +} + +- (void) unbindNative +{ + native = nullptr; } @end -#include "window.h" namespace GUI { +struct priv +{ + NSWindow* window; + DGView* view; + id listener; + id parent_view; + std::uint8_t* pixel_buffer{nullptr}; + std::size_t pixel_buffer_width{0}; + std::size_t pixel_buffer_height{0}; +}; + NativeWindowCocoa::NativeWindowCocoa(void* native_window, Window& window) - : buffer(nullptr) - , window(window) -{ - [NSAutoreleasePool new]; - [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - /* - id menubar = [[NSMenu new] autorelease]; - id appMenuItem = [[NSMenuItem new] autorelease]; - [menubar addItem:appMenuItem]; - [NSApp setMainMenu:menubar]; - id appMenu = [[NSMenu new] autorelease]; - id appName = [[NSProcessInfo processInfo] processName]; - id quitTitle = [@"Quit " stringByAppendingString:appName]; - id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle - action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; - [appMenu addItem:quitMenuItem]; - [appMenuItem setSubmenu:appMenu]; - */ - id window = [[[MyWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200) - styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO] - autorelease]; - [window cascadeTopLeftFromPoint:NSMakePoint(20,20)]; - // [window setTitle:appName]; - [window makeKeyAndOrderFront:nil]; - - id my_view = [MyView new]; - [window setContentView:my_view]; - [NSApp activateIgnoringOtherApps:YES]; - [window makeFirstResponder:my_view]; - - // [NSApp run]; + : window(window) + , priv(new struct priv()) + , native_window(native_window) +{ + [NSAutoreleasePool new]; + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + priv->view = [DGView new]; + + [priv->view bindNative:this]; + + if(native_window) + { + if(sizeof(std::size_t) == sizeof(unsigned int)) // 32 bit machine + { + WindowRef ptr = (WindowRef)native_window; + priv->window = [[[NSWindow alloc] initWithWindowRef:ptr] retain]; + priv->parent_view = [priv->window contentView]; + } + else // 64 bit machine + { + priv->parent_view = (NSView*)native_window; + priv->window = [priv->parent_view window]; + } + + [priv->parent_view addSubview:priv->view]; + [priv->view display]; + [priv->parent_view setNeedsDisplay:YES]; + } + else + { + priv->window = + [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 10, 10) + styleMask:STYLE_MASK + backing:NSBackingStoreBuffered + defer:NO] + retain]; + [priv->window setLevel:NSStatusWindowLevel]; + } + + priv->listener = + [[[DGListener alloc] initWithWindow:priv->window + native:this] + retain]; + + if(native_window) + { + [[priv->window contentView] addSubview:priv->view]; + } + else + { + [priv->window setReleasedWhenClosed:NO]; + [priv->window setContentView:priv->view]; + } + + [priv->view setWantsLayer:YES]; + [priv->view setLayerContentsPlacement:NSViewLayerContentsPlacementTopLeft]; + [priv->view updateTrackingAreas]; + + if(!native_window) + { + hide(); + } } NativeWindowCocoa::~NativeWindowCocoa() { + // Make the garbage collector able to collect the ObjC objects: + if(visible()) + { + hide(); + } + + [priv->listener unbindNative]; + [priv->listener release]; + + [priv->view unbindNative]; + [priv->view release]; + + if(native_window) + { + if(sizeof(std::size_t) == sizeof(unsigned int)) // 32 bit machine + { + [priv->window release]; + } + else + { + // in 64-bit the window was not created by us + } + } + else + { + [priv->window release]; + } +} + +void NativeWindowCocoa::setFixedSize(std::size_t width, std::size_t height) +{ + resize(width, height); + [priv->window setMinSize:NSMakeSize(width, height + 22)]; + [priv->window setMaxSize:NSMakeSize(width, height + 22)]; } -void NativeWindowCocoa::setFixedSize(int width, int height) +void NativeWindowCocoa::resize(std::size_t width, std::size_t height) { + [priv->window setContentSize:NSMakeSize(width, height)]; } -void NativeWindowCocoa::resize(int width, int height) +std::pair NativeWindowCocoa::getSize() const { + if(native_window) + { + auto frame = [priv->parent_view frame]; + return {frame.size.width, frame.size.height - frame.origin.y}; + } + else + { + NSSize size = [priv->view frame].size; + return {size.width, size.height}; + } } void NativeWindowCocoa::move(int x, int y) { + [priv->window setFrameTopLeftPoint:NSMakePoint(x, y)]; +} + +std::pair NativeWindowCocoa::getPosition() const +{ + NSPoint pos = [[priv->window contentView] frame].origin; + return {pos.x, pos.y}; } void NativeWindowCocoa::show() { + if(!native_window) + { + [priv->window makeKeyAndOrderFront:priv->window]; + [NSApp activateIgnoringOtherApps:YES]; + } } void NativeWindowCocoa::hide() { + if(!native_window) + { + [priv->window orderOut:priv->window]; + } } -void NativeWindowCocoa::handleBuffer() +bool NativeWindowCocoa::visible() const { + return [priv->window isVisible]; } -void NativeWindowCocoa::redraw() +void NativeWindowCocoa::redraw(const Rect& dirty_rect) { + NSSize size; + if(native_window) + { + size = [priv->parent_view frame].size; + } + else + { + size = [priv->view frame].size; + } + + std::size_t width = size.width; + std::size_t height = size.height; + + if(priv->pixel_buffer == nullptr || + priv->pixel_buffer_width != width || + priv->pixel_buffer_height != height) + { + if(priv->pixel_buffer) delete[] priv->pixel_buffer; + priv->pixel_buffer = new std::uint8_t[width * height * 4]; + priv->pixel_buffer_width = width; + priv->pixel_buffer_height = height; + } + + CGColorSpaceRef rgb = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGContextRef gc = + CGBitmapContextCreate(priv->pixel_buffer, width, height, + 8, width * 4, rgb, + IMAGE_FLAGS); + CGColorSpaceRelease(rgb); + + size_t pitch = CGBitmapContextGetBytesPerRow(gc); + uint8_t *buffer = (uint8_t *)CGBitmapContextGetData(gc); + + struct Pixel + { + std::uint8_t red; + std::uint8_t green; + std::uint8_t blue; + std::uint8_t alpha; + }; + std::uint8_t* pixels = window.wpixbuf.buf; + for(std::size_t y = dirty_rect.y1; y < std::min(dirty_rect.y2, height); ++y) + { + Pixel *row = (Pixel *)(buffer + y * pitch); + for(std::size_t x = dirty_rect.x1; x < std::min(dirty_rect.x2, width); ++x) + { + row[x] = *(Pixel*)&pixels[(y * width + x) * 3]; + row[x].alpha = 0xff; + } + } + CGImageRef image = CGBitmapContextCreateImage(gc); + CGContextRelease(gc); + + [[priv->view layer] setContents:(__bridge id)image]; + + updateLayerOffset(); } void NativeWindowCocoa::setCaption(const std::string &caption) { + NSString* title = + [NSString stringWithCString:caption.data() + encoding:[NSString defaultCStringEncoding]]; + [priv->window setTitle:title]; } void NativeWindowCocoa::grabMouse(bool grab) { } -bool NativeWindowCocoa::hasEvent() +void NativeWindowCocoa::updateLayerOffset() { - return false; + if(native_window) + { + auto r1 = [priv->parent_view frame]; + auto r2 = [priv->view frame]; + + CATransform3D t = [[priv->view layer] transform]; + if(t.m42 != -r1.origin.y) + { + t.m42 = -r1.origin.y; // y + [[priv->view layer] setTransform:t]; + } + } } -std::shared_ptr NativeWindowCocoa::getNextEvent() +EventQueue NativeWindowCocoa::getEvents() { - //[window setNeedsDisplay: YES]; - - while(running) { - NSEvent * event; - - do - { - event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefa\ -ultRunLoopMode dequeue:YES]; - + if(first) + { + resized(); + first = false; + } + + // If this is the root window, process the events - event processing will + // be handled by the hosting window if the window is embedded. + if(!native_window) + { + NSEvent* event = nil; + do + { + event = [NSApp nextEventMatchingMask:EVENT_MASK + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + [NSApp sendEvent:event]; + } + while(event != nil); + } + + EventQueue events; + std::swap(events, event_queue); + return events; +} - //Convert the cocoa events to something useful here and add them to your own event queue +void* NativeWindowCocoa::getNativeWindowHandle() const +{ + if(sizeof(std::size_t) == sizeof(unsigned int)) // 32 bit machine + { + return [priv->window windowRef]; + } + else // 64 bit machine + { + return [priv->window contentView]; + } +} - [NSApp sendEvent: event]; - } - while(event != nil); +Window& NativeWindowCocoa::getWindow() +{ + return window; +} - //printf("loop\n"); - usleep(10000); - } +PixelBuffer& NativeWindowCocoa::getWindowPixbuf() +{ + window.updateBuffer(); + return window.wpixbuf; +} - return nullptr; +void NativeWindowCocoa::resized() +{ + if(native_window) + { + NSRect frame = [priv->parent_view frame]; + [priv->view setFrame:frame]; + [priv->view updateTrackingAreas]; + updateLayerOffset(); + } + + auto resizeEvent = std::make_shared(); + resizeEvent->width = 42; // size is not actually used + resizeEvent->height = 42; // size is not actually used + pushBackEvent(resizeEvent); } -std::shared_ptr NativeWindowCocoa::peekNextEvent() +void NativeWindowCocoa::pushBackEvent(std::shared_ptr event) { - return nullptr; + event_queue.push_back(event); + redraw({}); } } // GUI:: diff --git a/plugingui/nativewindow_win32.cc b/plugingui/nativewindow_win32.cc index 0dc30de..12d430f 100644 --- a/plugingui/nativewindow_win32.cc +++ b/plugingui/nativewindow_win32.cc @@ -129,7 +129,7 @@ LRESULT CALLBACK NativeWindowWin32::dialogProc(HWND hwnd, UINT msg, scrollEvent->x = p.x; scrollEvent->y = p.y; - scrollEvent->delta = -1 * (short)HIWORD(wp) / 60; + scrollEvent->delta = -1 * (short)HIWORD(wp) / 60.0f; native->event_queue.push_back(scrollEvent); } break; diff --git a/plugingui/window.h b/plugingui/window.h index 218beec..e5bc496 100644 --- a/plugingui/window.h +++ b/plugingui/window.h @@ -90,10 +90,11 @@ protected: // For the Painter friend class Widget; - // For the NativeWindow + // For the NativeWindow implementations: friend class NativeWindowX11; friend class NativeWindowWin32; friend class NativeWindowPugl; + friend class NativeWindowCocoa; PixelBuffer wpixbuf; size_t refcount{0}; -- cgit v1.2.3