summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2019-05-31 08:18:04 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2019-05-31 08:18:04 +0200
commitabc33d1b000ed7fbebbff9300864420fd6fb8fef (patch)
tree2e8c7d8b55c97b05f72e36679f34fb752f78ae73
parent7e7db3fe70ee4e9b7c64eb63843914f2525b8526 (diff)
Add (working) Cocoa UI support.
-rwxr-xr-xautogen.sh10
-rw-r--r--configure.ac6
-rw-r--r--plugingui/Makefile.am16
-rw-r--r--plugingui/guievent.h2
-rw-r--r--plugingui/nativewindow_cocoa.h39
-rw-r--r--plugingui/nativewindow_cocoa.mm897
-rw-r--r--plugingui/nativewindow_win32.cc2
-rw-r--r--plugingui/window.h3
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 <memory>
+
#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<Event> getNextEvent() override;
- std::shared_ptr<Event> 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<std::size_t, std::size_t> getSize() const override;
+ virtual void move(int x, int y) override;
+ virtual std::pair<int, int> 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> event);
private:
+ void updateLayerOffset();
+
Window& window;
+ std::unique_ptr<struct priv> 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 <stdio.h>
#include <unistd.h>
#import <Cocoa/Cocoa.h>
-bool running = true;
+#include "window.h"
-@interface MyWindow : NSWindow
+#include <Availability.h>
+
+#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<GUI::CloseEvent>();
+ 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<GUI::MouseEnterEvent>();
+ 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<GUI::MouseLeaveEvent>();
+ 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<GUI::MouseMoveEvent>();
+ 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<GUI::ButtonEvent>();
+ 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<GUI::ButtonEvent>();
+ 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<GUI::ScrollEvent>();
+ 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<GUI::KeyEvent>();
+
+ 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<GUI::KeyEvent>();
+
+ 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<std::size_t, std::size_t> 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<int, int> 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<Event> 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<GUI::ResizeEvent>();
+ resizeEvent->width = 42; // size is not actually used
+ resizeEvent->height = 42; // size is not actually used
+ pushBackEvent(resizeEvent);
}
-std::shared_ptr<Event> NativeWindowCocoa::peekNextEvent()
+void NativeWindowCocoa::pushBackEvent(std::shared_ptr<Event> 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};