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
This commit is contained in:
Daniel Collin 2016-01-02 15:37:50 +01:00
parent 092965cb49
commit 946aa5ec14
8 changed files with 148 additions and 441 deletions

View file

@ -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) {

View file

@ -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::*;

View file

@ -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<fn(u32, u32)>,
}
struct WindowDelegate {
state: Box<DelegateState>,
_this: IdRef,
}
struct ViewDelegateState {
view: IdRef,
}
struct ViewDelegate {
state: Box<ViewDelegateState>,
_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<Window, &str> {
pub fn new(name: &str,
width: usize,
height: usize,
scale: Scale)
-> Result<Window, &str> {
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 app = match Self::create_app() {
Some(app) => app,
None => {
return Err("Couldn't create NSApplication");
},
};
let handle = mfb_open(n.as_ptr(), width as u32, height as u32, scale as u32);
let window = match Self::create_window(name, width, height) {
Some(window) => window,
None => {
return Err("Unable to create NSWindow");
if handle == ptr::null_mut() {
return Err("Unable to open Window");
}
};
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),
});
Ok(Window { window_handle: handle })
}
}
unsafe fn create_window(name: &str, width: usize, height: usize) -> Option<IdRef> {
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<IdRef> {
let view = IdRef::new(NSView::alloc(nil).init());
view.non_nil().map(|view| {
window.setContentView_(*view);
view
})
}
pub fn update(&self, _: &[u32]) {
pub fn update(&mut self, buffer: &[u32]) {
unsafe {
let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
NSAnyEventMask.bits(),
NSDate::distantPast(nil),
NSDefaultRunLoopMode,
YES);
if event != nil {
NSApp().sendEvent_(event);
}
mfb_update(self.window_handle, buffer.as_ptr() as *const u8);
}
}
pub fn get_keys() -> Option<Vec<Key>> {
return None;
pub fn get_keys(&self) -> Option<Vec<Key>> {
None
}
pub fn is_esc_pressed() -> bool {
pub fn get_keys_pressed(&self, _: KeyRepeat) -> Option<Vec<Key>> {
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<id> {
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<IdRef> {
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)
}
}

View file

@ -3,83 +3,74 @@
#include <Cocoa/Cocoa.h>
#include <unistd.h>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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;
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;
}
}
}
[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;
}

View file

@ -1,10 +1,12 @@
#import <Cocoa/Cocoa.h>
// @class OSXWindowFrameView;
@interface OSXWindow : NSWindow
{
NSView* childContentView;
@public int width;
@public int height;
@public int scale;
@public void* draw_buffer;
}
@end

View file

@ -58,6 +58,8 @@
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;
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

View file

@ -2,6 +2,10 @@
@interface OSXWindowFrameView : NSView
{
@public int scale;
@public int width;
@public int height;
@public void* draw_buffer;
}
@end

View file

@ -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);
}