~eliasnaur/gio

4408c2a6957093e6cc0ccd4130d47e49a4d54fa1 — Elias Naur 3 years ago 476bf8d
app/internal/window: [macOS] support more than one window

Updates #19

Signed-off-by: Elias Naur <mail@eliasnaur.com>
3 files changed, 73 insertions(+), 70 deletions(-)

M app/internal/window/gl_macos.m
M app/internal/window/os_macos.go
M app/internal/window/os_macos.m
M app/internal/window/gl_macos.m => app/internal/window/gl_macos.m +1 -2
@@ 22,8 22,7 @@ static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFlo
@interface GioView : NSOpenGLView 
@end

@implementation GioView {
}
@implementation GioView
- (instancetype)initWithFrame:(NSRect)frameRect
				  pixelFormat:(NSOpenGLPixelFormat *)format {
	return [super initWithFrame:frameRect pixelFormat:format];

M app/internal/window/os_macos.go => app/internal/window/os_macos.go +52 -41
@@ 31,15 31,15 @@ import (
#define GIO_MOUSE_DOWN 3
#define GIO_MOUSE_SCROLL 4

__attribute__ ((visibility ("hidden"))) void gio_main(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height);
__attribute__ ((visibility ("hidden"))) void gio_main(void);
__attribute__ ((visibility ("hidden"))) CGFloat gio_viewWidth(CFTypeRef viewRef);
__attribute__ ((visibility ("hidden"))) CGFloat gio_viewHeight(CFTypeRef viewRef);
__attribute__ ((visibility ("hidden"))) CGFloat gio_getViewBackingScale(CFTypeRef viewRef);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
__attribute__ ((visibility ("hidden"))) void gio_setNeedsDisplay(CFTypeRef viewRef);
__attribute__ ((visibility ("hidden"))) void gio_makeKeyAndOrderFront(CFTypeRef viewRef);
__attribute__ ((visibility ("hidden"))) void gio_appTerminate(void);
__attribute__ ((visibility ("hidden"))) void gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height);
*/
import "C"



@@ 60,16 60,17 @@ type window struct {
// viewMap is the mapping from Cocoa NSViews to Go windows.
var viewMap = make(map[C.CFTypeRef]*window)

var mainWindow = newWindowRendezvous()

var viewFactory func() C.CFTypeRef

// launched is closed when applicationDidFinishLaunching is called.
var launched = make(chan struct{})

// mustView is like lookoupView, except that it panics
// if the view isn't mapped.
func mustView(view C.CFTypeRef) *window {
	w, ok := lookupView(view)
	if !ok {
		panic("no window view view")
		panic("no window for view")
	}
	return w
}


@@ 246,6 247,8 @@ func gio_onClose(view C.CFTypeRef) {
	w.displayLink.Close()
	deleteView(view)
	w.w.Event(system.DestroyEvent{})
	w.view = 0
	C.CFRelease(view)
	if len(viewMap) == 0 {
		C.gio_appTerminate()
	}


@@ 277,8 280,34 @@ func gio_onAppShow() {
	}
}

//export gio_onCreate
func gio_onCreate(view C.CFTypeRef) {
//export gio_onFinishLaunching
func gio_onFinishLaunching() {
	close(launched)
}

func NewWindow(win Callbacks, opts *Options) error {
	<-launched
	errch := make(chan error)
	var window *window
	runOnMain(func() {
		w, err := newWindow(win, opts)
		window = w
		errch <- err
	})
	if err := <-errch; err != nil {
		return err
	}
	go func() {
		win.SetDriver(window)
	}()
	return nil
}

func newWindow(win Callbacks, opts *Options) (*window, error) {
	view := viewFactory()
	if view == 0 {
		return nil, errors.New("CreateWindow: failed to create view")
	}
	scale := float32(C.gio_getViewBackingScale(view))
	w := &window{
		view:  view,


@@ 286,48 315,30 @@ func gio_onCreate(view C.CFTypeRef) {
	}
	dl, err := NewDisplayLink(func() {
		runOnMain(func() {
			C.gio_setNeedsDisplay(w.view)
			if w.view != 0 {
				C.gio_setNeedsDisplay(w.view)
			}
		})
	})
	if err != nil {
		panic(err)
	}
	w.displayLink = dl
	wopts := <-mainWindow.out
	w.w = wopts.window
	w.w.SetDriver(w)
	insertView(view, w)
	if len(viewMap) == 1 {
		C.CFRetain(view)
		runOnMain(func() {
			defer C.CFRelease(view)
			C.gio_makeKeyAndOrderFront(view)
		})
	}
}

func NewWindow(win Callbacks, opts *Options) error {
	mainWindow.in <- windowAndOptions{win, opts}
	return <-mainWindow.errs
}

func Main() {
	wopts := <-mainWindow.out
	view := viewFactory()
	if view == 0 {
		// TODO: return this error from CreateWindow.
		panic(errors.New("CreateWindow: failed to create view"))
	if err != nil {
		C.CFRelease(view)
		return nil, err
	}
	// Window sizes is in unscaled screen coordinates, not device pixels.
	cfg := configFor(1.0)
	opts := wopts.opts
	w := cfg.Px(opts.Width)
	h := cfg.Px(opts.Height)
	w = int(float32(w))
	h = int(float32(h))
	width := cfg.Px(opts.Width)
	height := cfg.Px(opts.Height)
	title := C.CString(opts.Title)
	defer C.free(unsafe.Pointer(title))
	C.gio_main(view, title, C.CGFloat(w), C.CGFloat(h))
	C.gio_createWindow(view, title, C.CGFloat(width), C.CGFloat(height))
	w.w = win
	insertView(view, w)
	return w, nil
}

func Main() {
	C.gio_main()
}

func convertKey(k rune) (string, bool) {

M app/internal/window/os_macos.m => app/internal/window/os_macos.m +20 -27
@@ 12,18 12,6 @@
@interface GioWindowDelegate : NSObject<NSWindowDelegate>
@end

@implementation GioAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
	[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
}
- (void)applicationDidHide:(NSNotification *)aNotification {
	gio_onAppHide();
}
- (void)applicationWillUnhide:(NSNotification *)notification {
	gio_onAppShow();
}
@end

@implementation GioWindowDelegate
- (void)windowWillMiniaturize:(NSNotification *)notification {
	NSWindow *window = (NSWindow *)[notification object];


@@ 122,7 110,7 @@ void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
	CVDisplayLinkSetCurrentCGDisplay((CVDisplayLinkRef)dl, (CGDirectDisplayID)did);
}

CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height) {
void gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height) {
	@autoreleasepool {
		NSRect rect = NSMakeRect(0, 0, width, height);
		NSUInteger styleMask = NSTitledWindowMask |


@@ 136,29 124,39 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, 
														   defer:NO];
		[window setAcceptsMouseMovedEvents:YES];
		window.title = [NSString stringWithUTF8String: title];
		NSView *view = (NSView *)CFBridgingRelease(viewRef);
		NSView *view = (__bridge NSView *)viewRef;
		[window setContentView:view];
		[window makeFirstResponder:view];
		window.releasedWhenClosed = NO;
		window.delegate = globalWindowDel;
		gio_onCreate((__bridge CFTypeRef)view);
		return (__bridge_retained CFTypeRef)window;
		[window makeKeyAndOrderFront:nil];
	}
}

void gio_makeKeyAndOrderFront(CFTypeRef viewRef) {
	NSView *view = (__bridge NSView *)viewRef;
	[view.window makeKeyAndOrderFront:nil];
}

void gio_appTerminate(void) {
	@autoreleasepool {
		[NSApp terminate:nil];
	}
}

void gio_main(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height) {
@implementation GioAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
	[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
	gio_onFinishLaunching();
}
- (void)applicationDidHide:(NSNotification *)aNotification {
	gio_onAppHide();
}
- (void)applicationWillUnhide:(NSNotification *)notification {
	gio_onAppShow();
}
@end

void gio_main() {
	@autoreleasepool {
		[NSApplication sharedApplication];
		GioAppDelegate *del = [[GioAppDelegate alloc] init];
		[NSApp setDelegate:del];
		[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];

		NSMenuItem *mainMenu = [NSMenuItem new];


@@ 177,12 175,7 @@ void gio_main(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat heigh
		[menuBar addItem:mainMenu];
		[NSApp setMainMenu:menuBar];

		GioAppDelegate *del = [[GioAppDelegate alloc] init];

		globalWindowDel = [[GioWindowDelegate alloc] init];
		NSWindow *window = (__bridge NSWindow *)gio_createWindow(viewRef, title, width, height);

		[NSApp setDelegate:del];

		[NSApp run];
	}