From be0f6bd4d70cb8ca31c6a8ee9be4f630f9e221ee Mon Sep 17 00:00:00 2001 From: Alex Chandel Date: Wed, 17 Sep 2014 15:18:57 -0400 Subject: [PATCH 1/5] Added hello_world example, expanded appkit This commit adds a Hello World program, which creates and displays a minimalist Cocoa app with a Menu Bar, a Quit menu, and a Window. It also wraps the Cocoa methods used in hello_world in static functions in appkit, inside separate modules for different classes. Later on, Cocoa methods could be implemented as traits on `id` if that turns out to be easier. --- examples/hello_world.rs | 58 ++++++++++++++ src/appkit.rs | 168 ++++++++++++++++++++++++++++++++++++++-- src/base.rs | 114 +++++++++++++++++++++++++-- src/lib.rs | 2 +- src/msgsend.m | 22 ++++++ 5 files changed, 349 insertions(+), 15 deletions(-) create mode 100644 examples/hello_world.rs diff --git a/examples/hello_world.rs b/examples/hello_world.rs new file mode 100644 index 0000000..252f5b2 --- /dev/null +++ b/examples/hello_world.rs @@ -0,0 +1,58 @@ +extern crate cocoa; + +use cocoa::base::{NSUInteger, nil, ObjCSelector}; +use cocoa::appkit::{NSApp, NSRect, NSPoint, NSSize, + NSProcessInfo, + NSApplication, NSApplicationActivationPolicyRegular, + NSWindow, NSTitledWindowMask, NSBackingStoreBuffered, + NSString, + NSMenu, NSMenuItem}; + +fn main() { + unsafe { + let app = NSApp(); + NSApplication::setActivationPolicy_(app, NSApplicationActivationPolicyRegular); + + // create Menu Bar + let menubar = NSMenu::new(); + let app_menu_item = NSMenuItem::new(); + NSMenu::addItem_(menubar, app_menu_item); + NSApplication::setMainMenu_(app, menubar); + + // create Application menu + let app_menu = NSMenu::new(); + let app_name = NSProcessInfo::processName(NSProcessInfo::processInfo()); + let quit_prefix = NSString::from_str("Quit \0"); + let quit_title = NSString::stringByAppendingString_( + quit_prefix, + app_name + ); + let quit_action = "terminate:".as_selector(); + let quit_key = NSString::from_str("q\0"); + let quit_item = NSMenuItem::initWithTitle_action_keyEquivalent_( + NSMenuItem::alloc(), + quit_title, + quit_action, + quit_key + ); + NSMenu::addItem_(app_menu, quit_item); + NSMenuItem::setSubmenu_(app_menu_item, app_menu); + + // create Window + let window = NSWindow::initWithContentRect_styleMask_backing_defer_( + NSWindow::alloc(), + NSRect::new(NSPoint::new(0., 0.), NSSize::new(200., 200.)), + NSTitledWindowMask as NSUInteger, + NSBackingStoreBuffered, + false + ); + // segfaults in invoke_msg_NSPoint_NSPoint(): + // NSWindow::cascadeTopLeftFromPoint_(window, NSPoint::new(20., 20.)); + let title = NSString::from_str("Hello World!\0"); + NSWindow::setTitle_(window, title); + NSWindow::makeKeyAndOrderFront_(window, nil); + + NSApplication::activateIgnoringOtherApps_(app, true); + NSApplication::run(app); + } +} diff --git a/src/appkit.rs b/src/appkit.rs index 4af0c96..6b83714 100644 --- a/src/appkit.rs +++ b/src/appkit.rs @@ -11,13 +11,14 @@ use base::{ObjCMethodCall, id}; pub type CGFloat = f32; -#[allow(dead_code)] +#[repr(C)] pub struct NSPoint { - x: f64, - y: f64, + pub x: f64, + pub y: f64, } impl NSPoint { + #[inline] pub fn new(x: f64, y: f64) -> NSPoint { NSPoint { x: x, @@ -26,13 +27,14 @@ impl NSPoint { } } -#[allow(dead_code)] +#[repr(C)] pub struct NSSize { - width: f64, - height: f64, + pub width: f64, + pub height: f64, } impl NSSize { + #[inline] pub fn new(width: f64, height: f64) -> NSSize { NSSize { width: width, @@ -41,11 +43,22 @@ impl NSSize { } } +#[repr(C)] pub struct NSRect { pub origin: NSPoint, pub size: NSSize, } +impl NSRect { + #[inline] + pub fn new(origin: NSPoint, size: NSSize) -> NSRect { + NSRect { + origin: origin, + size: size + } + } +} + #[link(name = "AppKit", kind = "framework")] extern {} @@ -53,3 +66,146 @@ pub unsafe fn NSApp() -> id { "NSApplication".send("sharedApplication", ()) } +#[repr(i64)] +pub enum NSApplicationActivationPolicy { + NSApplicationActivationPolicyRegular = 0, + NSApplicationActivationPolicyERROR = -1 +} + +#[repr(u64)] +pub enum NSWindowMask { + NSBorderlessWindowMask = 0, + NSTitledWindowMask = 1 << 0, + NSClosableWindowMask = 1 << 1, + NSMiniaturizableWindowMask = 1 << 2, + NSResizableWindowMask = 1 << 3, + + NSTexturedBackgroundWindowMask = 1 << 8, + + NSUnifiedTitleAndToolbarWindowMask = 1 << 12, + + NSFullScreenWindowMask = 1 << 14 +} + +#[repr(u64)] +pub enum NSBackingStoreType { + NSBackingStoreRetained = 0, + NSBackingStoreNonretained = 1, + NSBackingStoreBuffered = 2 +} + +pub mod NSProcessInfo { + use base::{id, ObjCMethodCall}; + + pub unsafe fn processInfo() -> id { + "NSProcessInfo".send("processInfo", ()) + } + + pub unsafe fn processName(this: id) -> id { + this.send("processName", ()) + } +} + +pub mod NSApplication { + use base::{id, NSInteger, ObjCMethodCall}; + use appkit::NSApplicationActivationPolicy; + + pub unsafe fn setActivationPolicy_(this: id, policy: NSApplicationActivationPolicy) -> bool { + this.send_bool("setActivationPolicy:", policy as NSInteger) + } + + pub unsafe fn setMainMenu_(this: id, menu: id) { + this.send_void("setMainMenu:", menu) + } + + pub unsafe fn activateIgnoringOtherApps_(this: id, ignore: bool) { + this.send_void("activateIgnoringOtherApps:", ignore); + } + + pub unsafe fn run(this: id) { + this.send_void("run", ()); + } +} + +pub mod NSMenu { + use base::{id, ObjCMethodCall}; + + pub unsafe fn new() -> id { + "NSMenu".send("new", ()) + } + + pub unsafe fn addItem_(this: id, menu_item: id) { + this.send_void("addItem:", menu_item) + } +} + +pub mod NSMenuItem { + use base::{id, ObjCMethodCall, SEL}; + + pub unsafe fn alloc() -> id { + "NSMenuItem".send("alloc", ()) + } + + pub unsafe fn new() -> id { + "NSMenuItem".send("new", ()) + } + + pub unsafe fn initWithTitle_action_keyEquivalent_(this: id, title: id, action: SEL, key: id) -> id { + this.send("initWithTitle:action:keyEquivalent:", (title, action, key)) + } + + pub unsafe fn setSubmenu_(this: id, submenu: id) { + this.send_void("setSubmenu:", submenu) + } +} + +pub mod NSWindow { + use base::{id, NSUInteger, ObjCMethodCall}; + use appkit::{NSRect, NSPoint, NSBackingStoreType}; + + pub unsafe fn alloc() -> id { + "NSWindow".send("alloc", ()) + } + + pub unsafe fn initWithContentRect_styleMask_backing_defer_(this: id, + rect: NSRect, + style: NSUInteger, + backing: NSBackingStoreType, + defer: bool) -> id { + this.send("initWithContentRect:styleMask:backing:defer:", + (rect, style, backing as NSUInteger, defer)) + } + + pub unsafe fn cascadeTopLeftFromPoint_(this: id, top_left: NSPoint) -> NSPoint { + this.send_point("cascadeTopLeftFromPoint:", top_left) + } + + pub unsafe fn setTitle_(this: id, title: id) { + this.send_void("setTitle:", title); + } + + pub unsafe fn makeKeyAndOrderFront_(this: id, sender: id) { + this.send_void("makeKeyAndOrderFront:", sender) + } +} + +pub mod NSString { + use base::{id, ObjCMethodCall}; + + pub unsafe fn alloc() -> id { + "NSString".send("alloc", ()) + } + + pub unsafe fn initWithUTF8String_(this: id, c_string: *const u8) -> id { + this.send("initWithUTF8String:", c_string as id) + } + + pub unsafe fn stringByAppendingString_(this: id, other: id) -> id { + this.send("stringByAppendingString:", other) + } + + pub unsafe fn from_str(string: &str) -> id { + initWithUTF8String_(alloc(), string.as_ptr()) + } +} + diff --git a/src/base.rs b/src/base.rs index c882c34..62c4e7b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -7,9 +7,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use appkit::NSRect; +use appkit::{NSRect, NSPoint}; -use libc::{c_double, c_long}; +use libc::{c_double, c_long, c_ulong, c_char}; use libc; pub type Class = libc::intptr_t; @@ -19,6 +19,16 @@ pub type SEL = libc::intptr_t; #[allow(non_camel_case_types)] pub type id = libc::intptr_t; +#[cfg(target_word_size = "32")] +pub type NSInteger = libc::c_int; +#[cfg(target_word_size = "32")] +pub type NSUInteger = libc::c_uint; + +#[cfg(target_word_size = "64")] +pub type NSInteger = libc::c_long; +#[cfg(target_word_size = "64")] +pub type NSUInteger = libc::c_ulong; + pub static nil: id = 0 as id; extern { @@ -57,50 +67,81 @@ pub fn selector(name: &str) -> SEL { /// A trait that allows syntax like: /// -/// let string = "NSString".send("alloc").send("initWithCString:", "Hello world!"); +/// let string = "NSString".send("alloc").send("initWithUTF8String:", "Hello world!"); pub trait ObjCMethodCall { unsafe fn send(self, selector: S, args: A) -> id; unsafe fn send_double(self, selector: S, args: A) -> c_double; unsafe fn send_long(self, selector: S, args: A) -> c_long; unsafe fn send_void(self, selector: S, args: A); + unsafe fn send_bool(self, selector: S, args: A) -> bool; + unsafe fn send_point(self, selector: S, args: A) -> NSPoint; + } impl ObjCMethodCall for id { + #[inline] unsafe fn send(self, selector: S, args: A) -> id { args.send_args(self, selector.as_selector()) } + #[inline] unsafe fn send_double(self, selector: S, args: A) -> c_double { args.send_double_args(self, selector.as_selector()) } + #[inline] unsafe fn send_long(self, selector: S, args: A) -> c_long { args.send_long_args(self, selector.as_selector()) } + #[inline] unsafe fn send_void(self, selector: S, args: A) { args.send_void_args(self, selector.as_selector()) } + #[inline] + unsafe fn send_bool(self, selector: S, args: A) + -> bool { + args.send_bool_args(self, selector.as_selector()) + } + #[inline] + unsafe fn send_point(self, selector: S, args: A) + -> NSPoint { + args.send_point_args(self, selector.as_selector()) + } } /// A convenience implementation that allows methods on class names to be called directly, as in: -/// +/// /// "NSString".send("alloc") impl<'a> ObjCMethodCall for &'a str { + #[inline] unsafe fn send(self, selector: S, args: A) -> id { args.send_args(class(self), selector.as_selector()) } + #[inline] unsafe fn send_double(self, selector: S, args: A) -> c_double { args.send_double_args(class(self), selector.as_selector()) } + #[inline] unsafe fn send_long(self, selector: S, args: A) -> c_long { args.send_long_args(class(self), selector.as_selector()) } + #[inline] unsafe fn send_void(self, selector: S, args: A) { args.send_void_args(class(self), selector.as_selector()) } + #[inline] + unsafe fn send_bool(self, selector: S, args: A) + -> bool { + args.send_bool_args(class(self), selector.as_selector()) + } + #[inline] + unsafe fn send_point(self, selector: S, args: A) + -> NSPoint { + args.send_point_args(class(self), selector.as_selector()) + } } /// A trait that allows C strings to be used as selectors without having to convert them first. @@ -138,6 +179,12 @@ trait ObjCMethodLongArgs { trait ObjCMethodVoidArgs { unsafe fn send_void_args(self, receiver: id, selector: SEL); } +trait ObjCMethodBoolArgs { + unsafe fn send_bool_args(self, receiver: id, selector: SEL) -> bool; +} +trait ObjCMethodPointArgs { + unsafe fn send_point_args(self, receiver: id, selector: SEL) -> NSPoint; +} impl ObjCMethodArgs for () { #[inline] @@ -146,11 +193,10 @@ impl ObjCMethodArgs for () { } } -impl ObjCMethodArgs for (id, id, id, id, id) { +impl ObjCMethodArgs for id { #[inline] unsafe fn send_args(self, receiver: id, selector: SEL) -> id { - let (first, second, third, fourth, fifth) = self; - invoke_msg_id_id_id_id_id_id(receiver, selector, first, second, third, fourth, fifth) + invoke_msg_id_id(receiver, selector, self) } } @@ -161,6 +207,30 @@ impl ObjCMethodArgs for NSRect { } } +impl ObjCMethodArgs for (id, SEL, id) { + #[inline] + unsafe fn send_args(self, receiver: id, selector: SEL) -> id { + let (first, second, third) = self; + invoke_msg_id_id_SEL_id(receiver, selector, first, second, third) + } +} + +impl ObjCMethodArgs for (NSRect, c_ulong, c_ulong, bool) { + #[inline] + unsafe fn send_args(self, receiver: id, selector: SEL) -> id { + let (first, second, third, fourth) = self; + invoke_msg_id_NSRect_ulong_ulong_bool(receiver, selector, first, second, third, fourth) + } +} + +impl ObjCMethodArgs for (id, id, id, id, id) { + #[inline] + unsafe fn send_args(self, receiver: id, selector: SEL) -> id { + let (first, second, third, fourth, fifth) = self; + invoke_msg_id_id_id_id_id_id(receiver, selector, first, second, third, fourth, fifth) + } +} + impl ObjCMethodDoubleArgs for () { #[inline] unsafe fn send_double_args(self, receiver: id, selector: SEL) -> f64 { @@ -196,6 +266,24 @@ impl ObjCMethodVoidArgs for id { } } +impl ObjCMethodBoolArgs for c_long { + #[inline] + unsafe fn send_bool_args(self, receiver: id, selector: SEL) -> bool { + invoke_msg_bool_long(receiver, selector, self) + } +} + +impl ObjCMethodPointArgs for NSPoint { + #[inline] + unsafe fn send_point_args(self, receiver: id, selector: SEL) -> NSPoint { + println!("before"); + let stret: NSPoint = NSPoint::new(0., 0.); + invoke_msg_NSPoint_NSPoint(&stret, receiver, selector, self); + println!("after"); + stret + } +} + /// A trait that simulates variadic parameters for method calls. #[cfg(test)] @@ -238,6 +326,15 @@ mod test { extern { pub fn invoke_msg_double(theReceiver: id, theSelector: SEL) -> f64; pub fn invoke_msg_id(theReceiver: id, theSelector: SEL) -> id; + pub fn invoke_msg_id_id(theReceiver: id, theSelector: SEL, a: id) -> id; + pub fn invoke_msg_id_NSRect(theReceiver: id, theSelector: SEL, a: &NSRect) -> id; + pub fn invoke_msg_id_id_SEL_id(theReceiver: id, theSelector: SEL, a: id, b: SEL, c: id) -> id; + pub fn invoke_msg_id_NSRect_ulong_ulong_bool(theReceiver: id, + theSelector: SEL, + a: NSRect, + b: c_ulong, + c: c_ulong, + d: bool) -> id; pub fn invoke_msg_id_id_id_id_id_id(theReceiver: id, theSelector: SEL, a: id, @@ -246,10 +343,11 @@ extern { d: id, e: id) -> id; - pub fn invoke_msg_id_NSRect(theReceiver: id, theSelector: SEL, a: &NSRect) -> id; pub fn invoke_msg_long(theReceiver: id, theSelector: SEL) -> c_long; pub fn invoke_msg_void(theReceiver: id, theSelector: SEL); pub fn invoke_msg_void_bool(theReceiver: id, theSelector: SEL, a: bool); pub fn invoke_msg_void_id(theReceiver: id, theSelector: SEL, a: id); + pub fn invoke_msg_bool_long(theReceiver: id, theSelector: SEL, a: c_long) -> bool; + pub fn invoke_msg_NSPoint_NSPoint(stretAddr: &NSPoint, theReceiver: id, theSelector: SEL, a: NSPoint); } diff --git a/src/lib.rs b/src/lib.rs index f1485b5..d6e3ab5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ #![feature(globs)] -#![allow(non_snake_case_functions)] +#![allow(non_snake_case)] extern crate libc; diff --git a/src/msgsend.m b/src/msgsend.m index 413b0aa..69cfaae 100644 --- a/src/msgsend.m +++ b/src/msgsend.m @@ -22,14 +22,26 @@ id invoke_msg_id(id theReceiver, SEL theSelector) { return objc_msgSend(theReceiver, theSelector); } +id invoke_msg_id_id(id theReceiver, SEL theSelector, id a) { + return objc_msgSend(theReceiver, theSelector, a); +} + id invoke_msg_id_NSRect(id theReceiver, SEL theSelector, NSRect *rect) { return objc_msgSend(theReceiver, theSelector, *rect); } +id invoke_msg_id_id_SEL_id(id theReceiver, SEL theSelector, id a, SEL b, id c) { + return objc_msgSend(theReceiver, theSelector, a, b, c); +} + id invoke_msg_id_id_id_id_id_id(id theReceiver, SEL theSelector, id a, id b, id c, id d, id e) { return objc_msgSend(theReceiver, theSelector, a, b, c, d, e); } +id invoke_msg_id_NSRect_ulong_ulong_bool(id theReceiver, SEL theSelector, NSRect a, unsigned long b, unsigned long c, bool d) { + return objc_msgSend(theReceiver, theSelector, a, b, c, d); +} + long invoke_msg_long(id theReceiver, SEL theSelector) { return (long)objc_msgSend(theReceiver, theSelector); } @@ -46,3 +58,13 @@ void invoke_msg_void_id(id theReceiver, SEL theSelector, id id) { objc_msgSend(theReceiver, theSelector, id); } +char invoke_msg_bool_long(id theReceiver, SEL theSelector, long a) { + return objc_msgSend(theReceiver, theSelector, a); +} + +/** segfaults */ +void invoke_msg_NSPoint_NSPoint(NSPoint *stretAddr, id theReceiver, SEL theSelector, NSPoint point) { + void (*s)(void *ret, id self, SEL op, ...) = (void *)objc_msgSend_stret; + return s((void *)stretAddr, theReceiver, theSelector, point); +} + From 1c0bcd8fa631fce193c0479ee552bf20a5513446 Mon Sep 17 00:00:00 2001 From: Alex Chandel Date: Wed, 17 Sep 2014 15:25:59 -0400 Subject: [PATCH 2/5] Provide NSWindow -(void)center This is to demonstrate moving the window in hello_world.rs as an alternative to cascadeTopLeftFromPoint. --- examples/hello_world.rs | 3 ++- src/appkit.rs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 252f5b2..d51c1af 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -46,8 +46,9 @@ fn main() { NSBackingStoreBuffered, false ); - // segfaults in invoke_msg_NSPoint_NSPoint(): + // this segfaults in invoke_msg_NSPoint_NSPoint(): // NSWindow::cascadeTopLeftFromPoint_(window, NSPoint::new(20., 20.)); + NSWindow::center(window); let title = NSString::from_str("Hello World!\0"); NSWindow::setTitle_(window, title); NSWindow::makeKeyAndOrderFront_(window, nil); diff --git a/src/appkit.rs b/src/appkit.rs index 6b83714..6490e3f 100644 --- a/src/appkit.rs +++ b/src/appkit.rs @@ -187,6 +187,10 @@ pub mod NSWindow { pub unsafe fn makeKeyAndOrderFront_(this: id, sender: id) { this.send_void("makeKeyAndOrderFront:", sender) } + + pub unsafe fn center(this: id) { + this.send_void("center", ()) + } } pub mod NSString { From b14fa888cf4564b869f0d5203d2c3ae468cd8bf4 Mon Sep 17 00:00:00 2001 From: Alex Chandel Date: Thu, 18 Sep 2014 02:24:28 -0400 Subject: [PATCH 3/5] Switch class implementation to traits/impls. --- examples/hello_world.rs | 48 ++++++------ src/appkit.rs | 166 +++++++++++++++++++++++++--------------- 2 files changed, 127 insertions(+), 87 deletions(-) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index d51c1af..a65b5d9 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -2,7 +2,7 @@ extern crate cocoa; use cocoa::base::{NSUInteger, nil, ObjCSelector}; use cocoa::appkit::{NSApp, NSRect, NSPoint, NSSize, - NSProcessInfo, + NSAutoreleasePool, NSProcessInfo, NSApplication, NSApplicationActivationPolicyRegular, NSWindow, NSTitledWindowMask, NSBackingStoreBuffered, NSString, @@ -10,37 +10,35 @@ use cocoa::appkit::{NSApp, NSRect, NSPoint, NSSize, fn main() { unsafe { + let pool = NSAutoreleasePool::new(nil); + let app = NSApp(); - NSApplication::setActivationPolicy_(app, NSApplicationActivationPolicyRegular); + app.setActivationPolicy_(NSApplicationActivationPolicyRegular); // create Menu Bar - let menubar = NSMenu::new(); - let app_menu_item = NSMenuItem::new(); - NSMenu::addItem_(menubar, app_menu_item); - NSApplication::setMainMenu_(app, menubar); + let menubar = NSMenu::new(nil); + let app_menu_item = NSMenuItem::new(nil); + menubar.addItem_(app_menu_item); + app.setMainMenu_(menubar); // create Application menu - let app_menu = NSMenu::new(); - let app_name = NSProcessInfo::processName(NSProcessInfo::processInfo()); - let quit_prefix = NSString::from_str("Quit \0"); - let quit_title = NSString::stringByAppendingString_( - quit_prefix, - app_name + let app_menu = NSMenu::new(nil); + let quit_prefix = NSString::alloc(nil).init_str("Quit \0"); + let quit_title = quit_prefix.stringByAppendingString_( + NSProcessInfo::processInfo(nil).processName() ); let quit_action = "terminate:".as_selector(); - let quit_key = NSString::from_str("q\0"); - let quit_item = NSMenuItem::initWithTitle_action_keyEquivalent_( - NSMenuItem::alloc(), + let quit_key = NSString::alloc(nil).init_str("q\0"); + let quit_item = NSMenuItem::alloc(nil).initWithTitle_action_keyEquivalent_( quit_title, quit_action, quit_key ); - NSMenu::addItem_(app_menu, quit_item); - NSMenuItem::setSubmenu_(app_menu_item, app_menu); + app_menu.addItem_(quit_item); + app_menu_item.setSubmenu_(app_menu); // create Window - let window = NSWindow::initWithContentRect_styleMask_backing_defer_( - NSWindow::alloc(), + let window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( NSRect::new(NSPoint::new(0., 0.), NSSize::new(200., 200.)), NSTitledWindowMask as NSUInteger, NSBackingStoreBuffered, @@ -48,12 +46,12 @@ fn main() { ); // this segfaults in invoke_msg_NSPoint_NSPoint(): // NSWindow::cascadeTopLeftFromPoint_(window, NSPoint::new(20., 20.)); - NSWindow::center(window); - let title = NSString::from_str("Hello World!\0"); - NSWindow::setTitle_(window, title); - NSWindow::makeKeyAndOrderFront_(window, nil); + window.center(); + let title = NSString::alloc(nil).init_str("Hello World!\0"); + window.setTitle_(title); + window.makeKeyAndOrderFront_(nil); - NSApplication::activateIgnoringOtherApps_(app, true); - NSApplication::run(app); + app.activateIgnoringOtherApps_(true); + app.run(); } } diff --git a/src/appkit.rs b/src/appkit.rs index 6490e3f..ff613e0 100644 --- a/src/appkit.rs +++ b/src/appkit.rs @@ -7,7 +7,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use base::{ObjCMethodCall, id}; +use base::{ObjCMethodCall, id, SEL, nil, NSInteger, NSUInteger}; pub type CGFloat = f32; @@ -94,122 +94,164 @@ pub enum NSBackingStoreType { NSBackingStoreBuffered = 2 } -pub mod NSProcessInfo { - use base::{id, ObjCMethodCall}; +pub trait NSAutoreleasePool { + unsafe fn new(_: Self) -> id { + "NSAutoreleasePool".send("new", ()) + } - pub unsafe fn processInfo() -> id { + unsafe fn autorelease(self) -> Self; +} + +impl NSAutoreleasePool for id { + unsafe fn autorelease(self) -> id { + self.send("autorelease", ()) + } +} + +pub trait NSProcessInfo { + unsafe fn processInfo(_: Self) -> id { "NSProcessInfo".send("processInfo", ()) } - pub unsafe fn processName(this: id) -> id { - this.send("processName", ()) + unsafe fn processName(self) -> id; +} + +impl NSProcessInfo for id { + unsafe fn processName(self) -> id { + self.send("processName", ()) } } -pub mod NSApplication { - use base::{id, NSInteger, ObjCMethodCall}; - use appkit::NSApplicationActivationPolicy; - - pub unsafe fn setActivationPolicy_(this: id, policy: NSApplicationActivationPolicy) -> bool { - this.send_bool("setActivationPolicy:", policy as NSInteger) +pub trait NSApplication { + unsafe fn sharedApplication(_: Self) -> id { + "NSApplication".send("sharedApplication", ()) } - pub unsafe fn setMainMenu_(this: id, menu: id) { - this.send_void("setMainMenu:", menu) + unsafe fn setActivationPolicy_(self, policy: NSApplicationActivationPolicy) -> bool; + unsafe fn setMainMenu_(self, menu: id); + unsafe fn activateIgnoringOtherApps_(self, ignore: bool); + unsafe fn run(self); +} + +impl NSApplication for id { + unsafe fn setActivationPolicy_(self, policy: NSApplicationActivationPolicy) -> bool { + self.send_bool("setActivationPolicy:", policy as NSInteger) } - pub unsafe fn activateIgnoringOtherApps_(this: id, ignore: bool) { - this.send_void("activateIgnoringOtherApps:", ignore); + unsafe fn setMainMenu_(self, menu: id) { + self.send_void("setMainMenu:", menu) } - pub unsafe fn run(this: id) { - this.send_void("run", ()); + unsafe fn activateIgnoringOtherApps_(self, ignore: bool) { + self.send_void("activateIgnoringOtherApps:", ignore); + } + + unsafe fn run(self) { + self.send_void("run", ()); } } -pub mod NSMenu { - use base::{id, ObjCMethodCall}; - - pub unsafe fn new() -> id { +pub trait NSMenu { + unsafe fn new(_: Self) -> id { "NSMenu".send("new", ()) } - pub unsafe fn addItem_(this: id, menu_item: id) { - this.send_void("addItem:", menu_item) + unsafe fn addItem_(self, menu_item: id); +} + +impl NSMenu for id { + unsafe fn addItem_(self, menu_item: id) { + self.send_void("addItem:", menu_item) } } -pub mod NSMenuItem { - use base::{id, ObjCMethodCall, SEL}; - - pub unsafe fn alloc() -> id { +pub trait NSMenuItem { + unsafe fn alloc(_: Self) -> id { "NSMenuItem".send("alloc", ()) } - pub unsafe fn new() -> id { + unsafe fn new(_: Self) -> id { "NSMenuItem".send("new", ()) } - pub unsafe fn initWithTitle_action_keyEquivalent_(this: id, title: id, action: SEL, key: id) -> id { - this.send("initWithTitle:action:keyEquivalent:", (title, action, key)) + unsafe fn initWithTitle_action_keyEquivalent_(self, title: id, action: SEL, key: id) -> id; + unsafe fn setSubmenu_(self, submenu: id); +} + +impl NSMenuItem for id { + unsafe fn initWithTitle_action_keyEquivalent_(self, title: id, action: SEL, key: id) -> id { + self.send("initWithTitle:action:keyEquivalent:", (title, action, key)) } - pub unsafe fn setSubmenu_(this: id, submenu: id) { - this.send_void("setSubmenu:", submenu) + unsafe fn setSubmenu_(self, submenu: id) { + self.send_void("setSubmenu:", submenu) } } -pub mod NSWindow { - use base::{id, NSUInteger, ObjCMethodCall}; - use appkit::{NSRect, NSPoint, NSBackingStoreType}; - - pub unsafe fn alloc() -> id { +pub trait NSWindow { + unsafe fn alloc(_: Self) -> id { "NSWindow".send("alloc", ()) } - pub unsafe fn initWithContentRect_styleMask_backing_defer_(this: id, - rect: NSRect, - style: NSUInteger, - backing: NSBackingStoreType, - defer: bool) -> id { - this.send("initWithContentRect:styleMask:backing:defer:", + unsafe fn initWithContentRect_styleMask_backing_defer_(self, + rect: NSRect, + style: NSUInteger, + backing: NSBackingStoreType, + defer: bool) -> id; + unsafe fn cascadeTopLeftFromPoint_(self, top_left: NSPoint) -> NSPoint; + unsafe fn setTitle_(self, title: id); + unsafe fn makeKeyAndOrderFront_(self, sender: id); + unsafe fn center(self); +} + +impl NSWindow for id { + unsafe fn initWithContentRect_styleMask_backing_defer_(self, + rect: NSRect, + style: NSUInteger, + backing: NSBackingStoreType, + defer: bool) -> id { + self.send("initWithContentRect:styleMask:backing:defer:", (rect, style, backing as NSUInteger, defer)) } - pub unsafe fn cascadeTopLeftFromPoint_(this: id, top_left: NSPoint) -> NSPoint { - this.send_point("cascadeTopLeftFromPoint:", top_left) + unsafe fn cascadeTopLeftFromPoint_(self, top_left: NSPoint) -> NSPoint { + self.send_point("cascadeTopLeftFromPoint:", top_left) } - pub unsafe fn setTitle_(this: id, title: id) { - this.send_void("setTitle:", title); + unsafe fn setTitle_(self, title: id) { + self.send_void("setTitle:", title); } - pub unsafe fn makeKeyAndOrderFront_(this: id, sender: id) { - this.send_void("makeKeyAndOrderFront:", sender) + unsafe fn makeKeyAndOrderFront_(self, sender: id) { + self.send_void("makeKeyAndOrderFront:", sender) } - pub unsafe fn center(this: id) { - this.send_void("center", ()) + unsafe fn center(self) { + self.send_void("center", ()) } } -pub mod NSString { - use base::{id, ObjCMethodCall}; - - pub unsafe fn alloc() -> id { +pub trait NSString { + unsafe fn alloc(_: Self) -> id { "NSString".send("alloc", ()) } - pub unsafe fn initWithUTF8String_(this: id, c_string: *const u8) -> id { - this.send("initWithUTF8String:", c_string as id) + unsafe fn initWithUTF8String_(self, c_string: *const u8) -> id; + unsafe fn stringByAppendingString_(self, other: id) -> id; + unsafe fn init_str(self, string: &str) -> Self; +} + +impl NSString for id { + unsafe fn initWithUTF8String_(self, c_string: *const u8) -> id { + self.send("initWithUTF8String:", c_string as id) } - pub unsafe fn stringByAppendingString_(this: id, other: id) -> id { - this.send("stringByAppendingString:", other) + unsafe fn stringByAppendingString_(self, other: id) -> id { + self.send("stringByAppendingString:", other) } - pub unsafe fn from_str(string: &str) -> id { - initWithUTF8String_(alloc(), string.as_ptr()) + unsafe fn init_str(self, string: &str) -> id { + self.initWithUTF8String_(string.as_ptr()) } } From 86144041e5793ee58e5b76390d9c02fb2ad55f5c Mon Sep 17 00:00:00 2001 From: Justin Grimes Date: Thu, 18 Sep 2014 20:09:57 -0400 Subject: [PATCH 4/5] Fixed segfaul with invoke_msg_NSPoint_NSPoint. The issue was using objc_msgSend_stret instead of objc_msgSend. --- examples/hello_world.rs | 4 ++-- src/base.rs | 8 ++------ src/msgsend.m | 7 +++---- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index a65b5d9..85ae6b6 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -44,8 +44,8 @@ fn main() { NSBackingStoreBuffered, false ); - // this segfaults in invoke_msg_NSPoint_NSPoint(): - // NSWindow::cascadeTopLeftFromPoint_(window, NSPoint::new(20., 20.)); + + window.cascadeTopLeftFromPoint_(NSPoint::new(20., 20.)); window.center(); let title = NSString::alloc(nil).init_str("Hello World!\0"); window.setTitle_(title); diff --git a/src/base.rs b/src/base.rs index 62c4e7b..8185433 100644 --- a/src/base.rs +++ b/src/base.rs @@ -276,11 +276,7 @@ impl ObjCMethodBoolArgs for c_long { impl ObjCMethodPointArgs for NSPoint { #[inline] unsafe fn send_point_args(self, receiver: id, selector: SEL) -> NSPoint { - println!("before"); - let stret: NSPoint = NSPoint::new(0., 0.); - invoke_msg_NSPoint_NSPoint(&stret, receiver, selector, self); - println!("after"); - stret + invoke_msg_NSPoint_NSPoint(receiver, selector, self) } } @@ -348,6 +344,6 @@ extern { pub fn invoke_msg_void_bool(theReceiver: id, theSelector: SEL, a: bool); pub fn invoke_msg_void_id(theReceiver: id, theSelector: SEL, a: id); pub fn invoke_msg_bool_long(theReceiver: id, theSelector: SEL, a: c_long) -> bool; - pub fn invoke_msg_NSPoint_NSPoint(stretAddr: &NSPoint, theReceiver: id, theSelector: SEL, a: NSPoint); + pub fn invoke_msg_NSPoint_NSPoint(theReceiver: id, theSelector: SEL, a: NSPoint) -> NSPoint; } diff --git a/src/msgsend.m b/src/msgsend.m index 69cfaae..f368c8c 100644 --- a/src/msgsend.m +++ b/src/msgsend.m @@ -62,9 +62,8 @@ char invoke_msg_bool_long(id theReceiver, SEL theSelector, long a) { return objc_msgSend(theReceiver, theSelector, a); } -/** segfaults */ -void invoke_msg_NSPoint_NSPoint(NSPoint *stretAddr, id theReceiver, SEL theSelector, NSPoint point) { - void (*s)(void *ret, id self, SEL op, ...) = (void *)objc_msgSend_stret; - return s((void *)stretAddr, theReceiver, theSelector, point); +NSPoint invoke_msg_NSPoint_NSPoint(id theReceiver, SEL theSelector, NSPoint point) { + NSPoint (*f)(id self, SEL op, NSPoint p) = (NSPoint *)objc_msgSend; + return f(theReceiver, theSelector, point); } From 01d8c72f325d3d532e93412a0e6a786025d9f174 Mon Sep 17 00:00:00 2001 From: Alex Chandel Date: Fri, 19 Sep 2014 03:22:00 -0400 Subject: [PATCH 5/5] Use autorelease in hello_world --- examples/hello_world.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 85ae6b6..165a827 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -16,13 +16,13 @@ fn main() { app.setActivationPolicy_(NSApplicationActivationPolicyRegular); // create Menu Bar - let menubar = NSMenu::new(nil); - let app_menu_item = NSMenuItem::new(nil); + let menubar = NSMenu::new(nil).autorelease(); + let app_menu_item = NSMenuItem::new(nil).autorelease(); menubar.addItem_(app_menu_item); app.setMainMenu_(menubar); // create Application menu - let app_menu = NSMenu::new(nil); + let app_menu = NSMenu::new(nil).autorelease(); let quit_prefix = NSString::alloc(nil).init_str("Quit \0"); let quit_title = quit_prefix.stringByAppendingString_( NSProcessInfo::processInfo(nil).processName() @@ -33,7 +33,7 @@ fn main() { quit_title, quit_action, quit_key - ); + ).autorelease(); app_menu.addItem_(quit_item); app_menu_item.setSubmenu_(app_menu); @@ -43,8 +43,7 @@ fn main() { NSTitledWindowMask as NSUInteger, NSBackingStoreBuffered, false - ); - + ).autorelease(); window.cascadeTopLeftFromPoint_(NSPoint::new(20., 20.)); window.center(); let title = NSString::alloc(nil).init_str("Hello World!\0");