From 946aa5ec1454312cf8dda20d7c95e5286fe42a00 Mon Sep 17 00:00:00 2001 From: Daniel Collin Date: Sat, 2 Jan 2016 15:37:50 +0100 Subject: [PATCH] Mac fixes * Removed hard coded global values for width, height, buffer - These are now tracked on a per window basis. * Fixed multi-window support * Various cleanup and fixes --- examples/noise.rs | 3 +- src/lib.rs | 35 +-- src/macos.rs | 414 ++++--------------------- src/native/macosx/MacMiniFB.m | 84 +++-- src/native/macosx/OSXWindow.h | 6 +- src/native/macosx/OSXWindow.m | 30 +- src/native/macosx/OSXWindowFrameView.h | 4 + src/native/macosx/OSXWindowFrameView.m | 13 +- 8 files changed, 148 insertions(+), 441 deletions(-) diff --git a/examples/noise.rs b/examples/noise.rs index fd9c4a7..cecd3c3 100644 --- a/examples/noise.rs +++ b/examples/noise.rs @@ -15,8 +15,7 @@ fn main() { let mut window = Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, - Scale::X1, - Vsync::No) + Scale::X1) .unwrap(); while window.is_open() && !window.is_key_down(Key::Escape) { diff --git a/src/lib.rs b/src/lib.rs index 7881404..82c8bbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,3 @@ -extern crate libc; -#[cfg(target_os = "macos")] -#[macro_use] -extern crate objc; -#[cfg(target_os = "macos")] -extern crate cgl; -#[cfg(target_os = "macos")] -extern crate cocoa; -#[cfg(target_os = "macos")] -extern crate core_foundation; - /// Scale will scale the frame buffer and the window that is being sent in when calling the update /// function. This is useful if you for example want to display a 320 x 256 window on a screen with /// much higher resolution which would result in that the window is very small. @@ -32,20 +21,6 @@ pub enum Scale { X32, } -/// Vsync will allow syncronized rendering with the screen refresh rate. -/// Currently Vsync isn't implemented so nothing will change regardless of given value right now -pub enum Vsync { - /// No vsync - No, - /// Require accurate vsync. Notice that if the library is unable to to setup an accurate - /// syncing the window creation will fail. - Accurate, - /// Setup a best guess syncing with the screen. This will always succesed but may not be - /// accurate. What this means is if the lib is unable to create a accurate syncing approach - /// a 'emulated' one will be used (for example using a timer to approximate syncing) - BestGuess, -} - /// Used for is_key_pressed and get_keys_pressed() to indicated if repeat of presses is wanted #[derive(PartialEq, Clone, Copy)] pub enum KeyRepeat { @@ -172,13 +147,15 @@ pub enum Key { Count = 103, } +extern crate libc; + #[cfg(target_os = "windows")] pub mod windows; #[cfg(target_os = "windows")] pub use windows::*; -//#[cfg(target_os = "macos")] -//pub mod macos; -//#[cfg(target_os = "macos")] -//pub use macos::*; +#[cfg(target_os = "macos")] +pub mod macos; +#[cfg(target_os = "macos")] +pub use macos::*; diff --git a/src/macos.rs b/src/macos.rs index 03d6177..eb34d18 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,377 +1,93 @@ #![cfg(target_os = "macos")] -use Scale; -use Vsync; -use Key; +use {Scale, Key, KeyRepeat}; -use libc; -//use cocoa::appkit; -use cocoa::appkit::*; -//use cocoa::appkit::NSEventSubtype::*; +use libc::{c_void, c_char, c_uchar}; +use std::ffi::{CString}; +use std::ptr; -#[allow(unused_imports)] -use cocoa::base::{id, nil}; -#[allow(unused_imports)] -use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; -use objc::declare::ClassDecl; - -#[allow(unused_imports)] -use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, - NSString, NSUInteger}; - -use std::ops::Deref; +#[link(name = "Cocoa", kind = "framework")] +extern { + fn mfb_open(name: *const c_char, width: u32, height: u32, scale: u32) -> *mut c_void; + fn mfb_close(window: *mut c_void); + fn mfb_update(window: *mut c_void, buffer: *const c_uchar); +} pub struct Window { - view: IdRef, - window: IdRef, - delegate: WindowDelegate, - view_delegate: ViewDelegate, + window_handle: *mut c_void, } -struct DelegateState { - view: IdRef, - window: IdRef, - resize_handler: Option, -} - -struct WindowDelegate { - state: Box, - _this: IdRef, -} - -struct ViewDelegateState { - view: IdRef, -} - -struct ViewDelegate { - state: Box, - _this: IdRef, -} - -impl WindowDelegate { - /// Get the delegate class, initiailizing it neccessary - fn class() -> *const Class { - use std::sync::{Once, ONCE_INIT}; - - extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL { - println!("Should close"); - unsafe { - //let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - //let state = state as *mut DelegateState; - //(*state).pending_events.lock().unwrap().push_back(Closed); - } - YES - } - - extern fn window_did_resize(this: &Object, _: Sel, _: id) { - unsafe { - let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - let state = &mut *(state as *mut DelegateState); - - println!("did_resize"); - - //let _: () = msg_send![*state.context, update]; - - if let Some(handler) = state.resize_handler { - let rect = NSView::frame(*state.view); - let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32; - (handler)((scale_factor * rect.size.width as f32) as u32, - (scale_factor * rect.size.height as f32) as u32); - } - } - } - - extern fn window_did_become_key(this: &Object, _: Sel, _: id) { - unsafe { - // TODO: center the cursor if the window had mouse grab when it - // lost focus - - println!("became key window"); - let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - let state = state as *mut DelegateState; - //(*state).pending_events.lock().unwrap().push_back(Focused(true)); - } - } - - extern fn window_did_resign_key(this: &Object, _: Sel, _: id) { - unsafe { - let state: *mut libc::c_void = *this.get_ivar("minifb_window_state"); - let state = state as *mut DelegateState; - //(*state).pending_events.lock().unwrap().push_back(Focused(false)); - } - } - - static mut delegate_class: *const Class = 0 as *const Class; - static INIT: Once = ONCE_INIT; - - INIT.call_once(|| unsafe { - println!("Create new NSWindowDelegate"); - let superclass = Class::get("NSObject").unwrap(); - let mut decl = ClassDecl::new(superclass, "minifb_window_delegate").unwrap(); - - // Add callback methods - decl.add_method(sel!(windowShouldClose:), - window_should_close as extern fn(&Object, Sel, id) -> BOOL); - decl.add_method(sel!(windowDidResize:), - window_did_resize as extern fn(&Object, Sel, id)); - - decl.add_method(sel!(windowDidBecomeKey:), - window_did_become_key as extern fn(&Object, Sel, id)); - decl.add_method(sel!(windowDidResignKey:), - window_did_resign_key as extern fn(&Object, Sel, id)); - - // Store internal state as user data - decl.add_ivar::<*mut libc::c_void>("minifb_window_state"); - - delegate_class = decl.register(); - }); - - unsafe { - delegate_class - } - } - - fn new(state: DelegateState) -> WindowDelegate { - // Box the state so we can give a pointer to it - let mut state = Box::new(state); - let state_ptr: *mut DelegateState = &mut *state; - unsafe { - let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]); - - (&mut **delegate).set_ivar("minifb_window_state", state_ptr as *mut libc::c_void); - let _: () = msg_send![*state.window, setDelegate:*delegate]; - - println!("Setup delegate"); - - WindowDelegate { state: state, _this: delegate } - } - } -} - -impl Drop for WindowDelegate { - fn drop(&mut self) { - unsafe { - // Nil the window's delegate so it doesn't still reference us - let _: () = msg_send![*self.state.window, setDelegate:nil]; - } - } -} - -impl ViewDelegate { - fn class() -> *const Class { - use std::sync::{Once, ONCE_INIT}; - - extern fn draw_rect(this: &Object, _: Sel, _: id) { - println!("draw_rect"); - } - - static mut delegate_class: *const Class = 0 as *const Class; - static INIT: Once = ONCE_INIT; - - INIT.call_once(|| unsafe { - println!("Create new ViewDelegate"); - let superclass = Class::get("NSObject").unwrap(); - let mut decl = ClassDecl::new(superclass, "minifb_view_delegate").unwrap(); - - // Add callback methods - decl.add_method(sel!(drawRect:), - draw_rect as extern fn(&Object, Sel, id)); - // Store internal state as user data - decl.add_ivar::<*mut libc::c_void>("minifb_view_state"); - - delegate_class = decl.register(); - }); - - unsafe { - delegate_class - } - } - - fn new(state: ViewDelegateState) -> ViewDelegate { - let mut state = Box::new(state); - let state_ptr: *mut ViewDelegateState = &mut *state; - unsafe { - let delegate = IdRef::new(msg_send![ViewDelegate::class(), new]); - - (&mut **delegate).set_ivar("minifb_view_state", state_ptr as *mut libc::c_void); - let _: () = msg_send![*state.view, setDelegate:*delegate]; - - ViewDelegate { state: state, _this: delegate } - } - } -} - -impl Drop for ViewDelegate { - fn drop(&mut self) { - unsafe { - // Nil the views's delegate so it doesn't still reference us - let _: () = msg_send![*self.state.view, setDelegate:nil]; - } - } -} - - - impl Window { - pub fn new(name: &str, width: usize, height: usize, _: Scale, _: Vsync) -> Result { - unsafe { - let app = match Self::create_app() { - Some(app) => app, - None => { - return Err("Couldn't create NSApplication"); - }, - }; - - let window = match Self::create_window(name, width, height) { - Some(window) => window, - None => { - return Err("Unable to create NSWindow"); - } - }; - - let view = match Self::create_view(*window) { - Some(view) => view, - None => { - return Err("Unable to create NSView"); - } - }; - - app.activateIgnoringOtherApps_(YES); - window.makeKeyAndOrderFront_(nil); - - let ds = DelegateState { - view: view.clone(), - window: window.clone(), - resize_handler: None, - }; - - let vs = ViewDelegateState { - view: view.clone(), - }; - - println!("Created window and view"); - - return Ok(Window { - window: window, - view: view, - delegate: WindowDelegate::new(ds), - view_delegate: ViewDelegate::new(vs), - }); - } - } - - unsafe fn create_window(name: &str, width: usize, height: usize) -> Option { - let masks = NSResizableWindowMask as NSUInteger | - NSClosableWindowMask as NSUInteger | - NSTitledWindowMask as NSUInteger; - - let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64)); - - let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( - frame, - masks, - NSBackingStoreBuffered, - NO, - )); - - window.non_nil().map(|window| { - let title = IdRef::new(NSString::alloc(nil).init_str(name)); - window.setLevel_(NSMainMenuWindowLevel as i64 + 1); - window.setTitle_(*title); - window.center(); - window - }) - } - - unsafe fn create_view(window: id) -> Option { - let view = IdRef::new(NSView::alloc(nil).init()); - view.non_nil().map(|view| { - window.setContentView_(*view); - view - }) - } - - pub fn update(&self, _: &[u32]) { - unsafe { - let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( - NSAnyEventMask.bits(), - NSDate::distantPast(nil), - NSDefaultRunLoopMode, - YES); - - if event != nil { - NSApp().sendEvent_(event); + pub fn new(name: &str, + width: usize, + height: usize, + scale: Scale) + -> Result { + let n = match CString::new(name) { + Err(_) => { + println!("Unable to convert {} to c_string", name); + return Err("Unable to set correct name"); } + Ok(n) => n, + }; + + unsafe { + let handle = mfb_open(n.as_ptr(), width as u32, height as u32, scale as u32); + + if handle == ptr::null_mut() { + return Err("Unable to open Window"); + } + + Ok(Window { window_handle: handle }) } } - pub fn get_keys() -> Option> { - return None; + pub fn update(&mut self, buffer: &[u32]) { + unsafe { + mfb_update(self.window_handle, buffer.as_ptr() as *const u8); + } } - pub fn is_esc_pressed() -> bool { + pub fn get_keys(&self) -> Option> { + None + } + + pub fn get_keys_pressed(&self, _: KeyRepeat) -> Option> { + None + } + + #[inline] + pub fn is_key_down(&self, _: Key) -> bool { false } - pub fn close() { - + #[inline] + pub fn set_key_repeat_delay(&mut self, _: f32) { } - fn create_app() -> Option { - unsafe { - let app = NSApp(); - if app == nil { - None - } else { - app.setActivationPolicy_(NSApplicationActivationPolicyRegular); - Some(app) - } - } + #[inline] + pub fn set_key_repeat_rate(&mut self, _: f32) { + } + + pub fn key_pressed(&self, _: usize, _: KeyRepeat) -> bool { + false + } + + pub fn is_key_pressed(&self, _: Key, _: KeyRepeat) -> bool { + false + } + + #[inline] + pub fn is_open(&self) -> bool { + true } } - - -struct IdRef(id); - -impl IdRef { - fn new(i: id) -> IdRef { - IdRef(i) - } - - #[allow(dead_code)] - fn retain(i: id) -> IdRef { - if i != nil { - let _: id = unsafe { msg_send![i, retain] }; - } - IdRef(i) - } - - fn non_nil(self) -> Option { - if self.0 == nil { None } else { Some(self) } - } -} - -impl Drop for IdRef { +impl Drop for Window { fn drop(&mut self) { - if self.0 != nil { - let _: () = unsafe { msg_send![self.0, release] }; + unsafe { + mfb_close(self.window_handle); } } } -impl Deref for IdRef { - type Target = id; - fn deref<'a>(&'a self) -> &'a id { - &self.0 - } -} - -impl Clone for IdRef { - fn clone(&self) -> IdRef { - if self.0 != nil { - let _: id = unsafe { msg_send![self.0, retain] }; - } - IdRef(self.0) - } -} diff --git a/src/native/macosx/MacMiniFB.m b/src/native/macosx/MacMiniFB.m index 83742d1..ae2a60c 100644 --- a/src/native/macosx/MacMiniFB.m +++ b/src/native/macosx/MacMiniFB.m @@ -3,83 +3,74 @@ #include #include -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void* g_updateBuffer = 0; -int g_width = 0; -int g_height = 0; -static NSWindow* window_; +static bool s_init = false; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int mfb_open(const char* name, int width, int height) +void* mfb_open(const char* name, int width, int height, int scale) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - g_width = width; - g_height = height; - - [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + if (!s_init) { + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + s_init = true; + } unsigned int styles = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask; NSRect rectangle = NSMakeRect(0, 0, width, height); - window_ = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO]; + OSXWindow* window = [[OSXWindow alloc] initWithContentRect:rectangle styleMask:styles backing:NSBackingStoreBuffered defer:NO]; - if (!window_) + if (!window) return 0; - [window_ setTitle:[NSString stringWithUTF8String:name]]; - [window_ setReleasedWhenClosed:NO]; - [window_ performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; + window->draw_buffer = malloc(width * height * 4); - [window_ center]; + if (!window->draw_buffer) + return 0; + + window->width = width; + window->height = height; + window->scale = scale; + + [window updateSize]; + + [window setTitle:[NSString stringWithUTF8String:name]]; + [window setReleasedWhenClosed:NO]; + [window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:YES]; + + [window center]; [NSApp activateIgnoringOtherApps:YES]; [pool drain]; - return 1; + return window; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mfb_close() +void mfb_close(void* win) { + NSWindow* window = (NSWindow*)win; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - if (window_) - [window_ close]; + if (window) + [window close]; [pool drain]; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static int updateEvents() +static int update_events() { int state = 0; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event) - { - switch ([event type]) - { - case NSKeyDown: - case NSKeyUp: - { - state = -1; - break; - } - - default : - { - [NSApp sendEvent:event]; - break; - } - } - } + [NSApp sendEvent:event]; [pool release]; return state; @@ -87,10 +78,13 @@ static int updateEvents() /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -int mfb_update(void* buffer) +int mfb_update(void* window, void* buffer) { - g_updateBuffer = buffer; - int state = updateEvents(); - [[window_ contentView] setNeedsDisplay:YES]; + OSXWindow* win = (OSXWindow*)window; + memcpy(win->draw_buffer, buffer, win->width * win->height * 4); + + //g_updateBuffer = buffer; + int state = update_events(); + [[win contentView] setNeedsDisplay:YES]; return state; } diff --git a/src/native/macosx/OSXWindow.h b/src/native/macosx/OSXWindow.h index 5314f9c..2541897 100644 --- a/src/native/macosx/OSXWindow.h +++ b/src/native/macosx/OSXWindow.h @@ -1,10 +1,12 @@ #import -// @class OSXWindowFrameView; - @interface OSXWindow : NSWindow { NSView* childContentView; + @public int width; + @public int height; + @public int scale; + @public void* draw_buffer; } @end diff --git a/src/native/macosx/OSXWindow.m b/src/native/macosx/OSXWindow.m index a910e10..0938eba 100644 --- a/src/native/macosx/OSXWindow.m +++ b/src/native/macosx/OSXWindow.m @@ -57,6 +57,8 @@ NSSize newFrameSize = [frameView bounds].size; newFrameSize.width += sizeDelta.width; newFrameSize.height += sizeDelta.height; + + printf("conten size\n"); [super setContentSize:newFrameSize]; } @@ -79,18 +81,22 @@ NSRect bounds = [self frame]; bounds.origin = NSZeroPoint; - OSXWindowFrameView *frameView = [super contentView]; + printf("view size\n"); + + OSXWindowFrameView* frameView = [super contentView]; if (!frameView) { frameView = [[[OSXWindowFrameView alloc] initWithFrame:bounds] autorelease]; - + frameView->width = width; + frameView->height = height; + frameView->draw_buffer = draw_buffer; + frameView->scale = scale; [super setContentView:frameView]; } if (childContentView) - { [childContentView removeFromSuperview]; - } + childContentView = aView; [childContentView setFrame:[self contentRectForFrameRect:bounds]]; [childContentView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; @@ -133,4 +139,20 @@ return NSInsetRect(windowContentRect, 0, 0); } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)updateSize +{ + OSXWindowFrameView* frameView = [super contentView]; + if (frameView) + { + frameView->width = width; + frameView->height = height; + frameView->draw_buffer = draw_buffer; + frameView->scale = scale; + } + + printf("UpdateSize %d %d - %d\n", width, height, scale); +} + @end diff --git a/src/native/macosx/OSXWindowFrameView.h b/src/native/macosx/OSXWindowFrameView.h index 96408e9..c12bc52 100644 --- a/src/native/macosx/OSXWindowFrameView.h +++ b/src/native/macosx/OSXWindowFrameView.h @@ -2,6 +2,10 @@ @interface OSXWindowFrameView : NSView { + @public int scale; + @public int width; + @public int height; + @public void* draw_buffer; } @end diff --git a/src/native/macosx/OSXWindowFrameView.m b/src/native/macosx/OSXWindowFrameView.m index 1f01a41..0f0d67b 100644 --- a/src/native/macosx/OSXWindowFrameView.m +++ b/src/native/macosx/OSXWindowFrameView.m @@ -2,10 +2,6 @@ @implementation OSXWindowFrameView -extern void* g_updateBuffer; -extern int g_width; -extern int g_height; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (NSRect)resizeRect @@ -27,21 +23,18 @@ extern int g_height; - (void)drawRect:(NSRect)rect { - if (!g_updateBuffer) - return; - CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, g_updateBuffer, g_width * g_height * 4, NULL); + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, draw_buffer, width * height * 4, NULL); - CGImageRef img = CGImageCreate(g_width, g_height, 8, 32, g_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, + CGImageRef img = CGImageCreate(width, height, 8, 32, width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, provider, NULL, false, kCGRenderingIntentDefault); CGColorSpaceRelease(space); CGDataProviderRelease(provider); - CGContextDrawImage(context, CGRectMake(0, 0, g_width, g_height), img); + CGContextDrawImage(context, CGRectMake(0, 0, width, height), img); CGImageRelease(img); }