diff --git a/examples/hello_world.rs b/examples/hello_world.rs new file mode 100644 index 0000000..165a827 --- /dev/null +++ b/examples/hello_world.rs @@ -0,0 +1,56 @@ +extern crate cocoa; + +use cocoa::base::{NSUInteger, nil, ObjCSelector}; +use cocoa::appkit::{NSApp, NSRect, NSPoint, NSSize, + NSAutoreleasePool, NSProcessInfo, + NSApplication, NSApplicationActivationPolicyRegular, + NSWindow, NSTitledWindowMask, NSBackingStoreBuffered, + NSString, + NSMenu, NSMenuItem}; + +fn main() { + unsafe { + let pool = NSAutoreleasePool::new(nil); + + let app = NSApp(); + app.setActivationPolicy_(NSApplicationActivationPolicyRegular); + + // create Menu Bar + 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).autorelease(); + 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::alloc(nil).init_str("q\0"); + let quit_item = NSMenuItem::alloc(nil).initWithTitle_action_keyEquivalent_( + quit_title, + quit_action, + quit_key + ).autorelease(); + app_menu.addItem_(quit_item); + app_menu_item.setSubmenu_(app_menu); + + // create Window + let window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( + NSRect::new(NSPoint::new(0., 0.), NSSize::new(200., 200.)), + NSTitledWindowMask as NSUInteger, + NSBackingStoreBuffered, + false + ).autorelease(); + window.cascadeTopLeftFromPoint_(NSPoint::new(20., 20.)); + window.center(); + let title = NSString::alloc(nil).init_str("Hello World!\0"); + window.setTitle_(title); + window.makeKeyAndOrderFront_(nil); + + app.activateIgnoringOtherApps_(true); + app.run(); + } +} diff --git a/src/appkit.rs b/src/appkit.rs index 4af0c96..ff613e0 100644 --- a/src/appkit.rs +++ b/src/appkit.rs @@ -7,17 +7,18 @@ // 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; -#[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,192 @@ 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 trait NSAutoreleasePool { + unsafe fn new(_: Self) -> id { + "NSAutoreleasePool".send("new", ()) + } + + 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", ()) + } + + unsafe fn processName(self) -> id; +} + +impl NSProcessInfo for id { + unsafe fn processName(self) -> id { + self.send("processName", ()) + } +} + +pub trait NSApplication { + unsafe fn sharedApplication(_: Self) -> id { + "NSApplication".send("sharedApplication", ()) + } + + 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) + } + + unsafe fn setMainMenu_(self, menu: id) { + self.send_void("setMainMenu:", menu) + } + + unsafe fn activateIgnoringOtherApps_(self, ignore: bool) { + self.send_void("activateIgnoringOtherApps:", ignore); + } + + unsafe fn run(self) { + self.send_void("run", ()); + } +} + +pub trait NSMenu { + unsafe fn new(_: Self) -> id { + "NSMenu".send("new", ()) + } + + 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 trait NSMenuItem { + unsafe fn alloc(_: Self) -> id { + "NSMenuItem".send("alloc", ()) + } + + unsafe fn new(_: Self) -> id { + "NSMenuItem".send("new", ()) + } + + 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)) + } + + unsafe fn setSubmenu_(self, submenu: id) { + self.send_void("setSubmenu:", submenu) + } +} + +pub trait NSWindow { + unsafe fn alloc(_: Self) -> id { + "NSWindow".send("alloc", ()) + } + + 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)) + } + + unsafe fn cascadeTopLeftFromPoint_(self, top_left: NSPoint) -> NSPoint { + self.send_point("cascadeTopLeftFromPoint:", top_left) + } + + unsafe fn setTitle_(self, title: id) { + self.send_void("setTitle:", title); + } + + unsafe fn makeKeyAndOrderFront_(self, sender: id) { + self.send_void("makeKeyAndOrderFront:", sender) + } + + unsafe fn center(self) { + self.send_void("center", ()) + } +} + +pub trait NSString { + unsafe fn alloc(_: Self) -> id { + "NSString".send("alloc", ()) + } + + 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) + } + + unsafe fn stringByAppendingString_(self, other: id) -> id { + self.send("stringByAppendingString:", other) + } + + unsafe fn init_str(self, string: &str) -> id { + self.initWithUTF8String_(string.as_ptr()) + } +} + diff --git a/src/base.rs b/src/base.rs index 527dd24..948595a 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 @@ pub trait ObjCMethodLongArgs { pub 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,20 @@ 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 { + invoke_msg_NSPoint_NSPoint(receiver, selector, self) + } +} + /// A trait that simulates variadic parameters for method calls. #[cfg(test)] @@ -238,6 +322,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 +339,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(theReceiver: id, theSelector: SEL, a: NSPoint) -> NSPoint; } diff --git a/src/msgsend.m b/src/msgsend.m index 413b0aa..f368c8c 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,12 @@ 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); +} + +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); +} +