From 21a5cd6d814a913df4cd17b12a7feb5397d65179 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 4 Dec 2016 19:25:17 +0100 Subject: Cocoa for the masses. --- configure.ac | 11 +- plugingui/Makefile.am.plugingui | 13 +- plugingui/nativewindow_cocoa.h | 60 +++++++ plugingui/nativewindow_cocoa.mm | 360 ++++++++++++++++++++++++++++++++++++++++ plugingui/window.cc | 6 + 5 files changed, 442 insertions(+), 8 deletions(-) create mode 100644 plugingui/nativewindow_cocoa.h create mode 100644 plugingui/nativewindow_cocoa.mm diff --git a/configure.ac b/configure.ac index ce2a46f..730b2f2 100644 --- a/configure.ac +++ b/configure.ac @@ -140,7 +140,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="carbon"], + [darwin*], [enable_gui="cocoa"], [linux*|*bsd*], [enable_gui="x11"], [mingw*|windows*|winnt|cygwin], [enable_gui="win32"], @@ -169,10 +169,10 @@ AS_IF( GUI_CPPFLAGS="-DPUGL -I../../pugl" GUI_LIBS="-lGLU -lGL -lglut"], - [test "x$enable_gui" = "xcarbon"], - [AC_MSG_RESULT([Setting gui backend to Carbon]) - GUI_CPPFLAGS="" - GUI_LIBS=""], + [test "x$enable_gui" = "xcocoa"], + [AC_MSG_RESULT([Setting gui backend to Cocoa]) + GUI_CPPFLAGS="-framework Cocoa" + GUI_LIBS="-framework Cocoa"], AC_MSG_ERROR([*** No GUI backend has been selected ***]) ) @@ -183,6 +183,7 @@ AC_SUBST(GUI_LIBS) AM_CONDITIONAL([ENABLE_PUGL], [test "x$enable_gui" = "xpugl"]) AM_CONDITIONAL([ENABLE_WIN32], [test "x$enable_gui" = "xwin32"]) AM_CONDITIONAL([ENABLE_X11], [test "x$enable_gui" = "xx11"]) +AM_CONDITIONAL([ENABLE_COCOA], [test "x$enable_gui" = "xcocoa"]) dnl ====================== dnl Compile unit tests diff --git a/plugingui/Makefile.am.plugingui b/plugingui/Makefile.am.plugingui index d790f64..22ca3d9 100644 --- a/plugingui/Makefile.am.plugingui +++ b/plugingui/Makefile.am.plugingui @@ -58,17 +58,24 @@ PLUGIN_GUI_CPPFLAGS = \ -DLODEPNG_NO_COMPILE_CPP if ENABLE_X11 -PLUGIN_GUI_SOURCES += $(top_srcdir)/plugingui/nativewindow_x11.cc +PLUGIN_GUI_SOURCES += \ + $(top_srcdir)/plugingui/nativewindow_x11.cc endif if ENABLE_WIN32 -PLUGIN_GUI_SOURCES += $(top_srcdir)/plugingui/nativewindow_win32.cc +PLUGIN_GUI_SOURCES += \ + $(top_srcdir)/plugingui/nativewindow_win32.cc endif if ENABLE_PUGL PLUGIN_GUI_SOURCES += \ $(top_srcdir)/plugingui/nativewindow_pugl.cc \ $(top_srcdir)/pugl/pugl/pugl_x11.c - PLUGIN_GUI_CPPFLAGS += -I$(top_srcdir)/pugl/pugl endif + +if ENABLE_COCOA +PLUGIN_GUI_SOURCES += \ + $(top_srcdir)/plugingui/nativewindow_cocoa.m \ + $(top_srcdir)/plugingui/nativewindow_cocoa.cc +endif diff --git a/plugingui/nativewindow_cocoa.h b/plugingui/nativewindow_cocoa.h new file mode 100644 index 0000000..100b3c7 --- /dev/null +++ b/plugingui/nativewindow_cocoa.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * nativewindow_cocoa.h + * + * Sun Dec 4 15:55:14 CET 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +#include "nativewindow.h" + +namespace GUI +{ + +class Window; +class NativeWindowCocoa + : public NativeWindow +{ +public: + NativeWindowCocoa(void* native_window, Window& window); + ~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; + +private: + Window& window; +}; + +} // GUI:: diff --git a/plugingui/nativewindow_cocoa.mm b/plugingui/nativewindow_cocoa.mm new file mode 100644 index 0000000..531c2a8 --- /dev/null +++ b/plugingui/nativewindow_cocoa.mm @@ -0,0 +1,360 @@ +/* -*- Mode: ObjC; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * nativewindow_cocoa.mm + * + * Fri Dec 2 20:31:03 CET 2016 + * Copyright 2016 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* + +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 +#include + +#import + +bool running = true; + +@interface MyWindow : NSWindow +{ +@public + // MyView* my_view; +} + +- (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; +@end + +@implementation MyWindow + +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(unsigned int)aStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)flag +{ + NSWindow* result = [super initWithContentRect:contentRect + styleMask:(NSClosableWindowMask | + NSTitledWindowMask | + NSResizableWindowMask) + backing:NSBackingStoreBuffered defer:NO]; + + [result setAcceptsMouseMovedEvents:YES]; + [result setLevel: CGShieldingWindowLevel() + 1]; + + return result; +} + +//- (void)setMyView:(MyView*)view +//{ +// //my_view = view; +// //[self setContentSize:NSMakeSize(view->width, view->height) ]; +//} + +- (BOOL)windowShouldClose:(id)sender +{ + printf("closing!\n"); + //if (puglview->closeFunc) + // puglview->closeFunc(puglview); + running = false; + return YES; +} + +#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)drawRect:(NSRect)dirtyRect +{ +} +- (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); +} + +@end + + + + +@interface MyView : NSView +{ + int colorBits; + int depthBits; +@public + //PuglView* puglview; + + NSTrackingArea* trackingArea; +} + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits; +- (void) reshape; +- (void) drawRect:(NSRect)rect; + +@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); +} +@end + +#include "window.h" + +namespace GUI +{ + +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]; +} + +NativeWindowCocoa::~NativeWindowCocoa() +{ +} + +void NativeWindowCocoa::setFixedSize(int width, int height) +{ +} + +void NativeWindowCocoa::resize(int width, int height) +{ +} + +void NativeWindowCocoa::move(int x, int y) +{ +} + +void NativeWindowCocoa::show() +{ +} + +void NativeWindowCocoa::hide() +{ +} + +void NativeWindowCocoa::handleBuffer() +{ +} + +void NativeWindowCocoa::redraw() +{ +} + +void NativeWindowCocoa::setCaption(const std::string &caption) +{ +} + +void NativeWindowCocoa::grabMouse(bool grab) +{ +} + +bool NativeWindowCocoa::hasEvent() +{ + return false; +} + +std::shared_ptr NativeWindowCocoa::getNextEvent() +{ + //[window setNeedsDisplay: YES]; + + while(running) { + NSEvent * event; + + do + { + event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefa\ +ultRunLoopMode dequeue:YES]; + + + //Convert the cocoa events to something useful here and add them to your own event queue + + [NSApp sendEvent: event]; + } + while(event != nil); + + //printf("loop\n"); + usleep(10000); + } + + return nullptr; +} + +std::shared_ptr NativeWindowCocoa::peekNextEvent() +{ + return nullptr; +} + +} // GUI:: diff --git a/plugingui/window.cc b/plugingui/window.cc index f8232ad..5d929b2 100644 --- a/plugingui/window.cc +++ b/plugingui/window.cc @@ -35,6 +35,9 @@ #ifdef WIN32 #include "nativewindow_win32.h" #endif/*WIN32*/ +#ifdef COCOA +#include "nativewindow_cocoa.h" +#endif/*COCOA*/ #else #include "nativewindow_pugl.h" #endif @@ -56,6 +59,9 @@ Window::Window(void* native_window) #ifdef WIN32 native = new NativeWindowWin32(native_window, *this); #endif/*WIN32*/ +#ifdef COCOA + native = new NativeWindowCocoa(native_window, *this); +#endif/*COCOA*/ #else/*Use pugl*/ native = new NativeWindowPugl(native_window, *this); #endif -- cgit v1.2.3