diff --git a/src/base.rs b/src/base.rs index 5e4b334..6962cd6 100644 --- a/src/base.rs +++ b/src/base.rs @@ -7,13 +7,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use libc::{c_long, c_ulong}; +use libc::{c_long, c_ulong, c_uint}; use std::num::Int; pub type Boolean = u8; pub type CFIndex = c_long; +pub type mach_port_t = c_uint; + pub trait CFIndexConvertible { /// Always use this method to construct a `CFIndex` value. It performs bounds checking to /// ensure the value is in range. diff --git a/src/date.rs b/src/date.rs new file mode 100644 index 0000000..df1c818 --- /dev/null +++ b/src/date.rs @@ -0,0 +1,16 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub type CFTimeInterval = f64; +pub type CFAbsoluteTime = CFTimeInterval; + +#[link(name = "CoreFoundation", kind = "framework")] +extern { + pub fn CFAbsoluteTimeGetCurrent() -> CFAbsoluteTime; +} diff --git a/src/lib.rs b/src/lib.rs index 0f0c117..199d535 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,10 @@ pub mod string; pub mod url; #[cfg(target_os="macos")] pub mod bundle; +#[cfg(target_os="macos")] +pub mod date; +#[cfg(target_os="macos")] +pub mod runloop; #[cfg(all(target_os="macos", test))] pub mod test { diff --git a/src/runloop.rs b/src/runloop.rs new file mode 100644 index 0000000..8287188 --- /dev/null +++ b/src/runloop.rs @@ -0,0 +1,357 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use base::{CFAllocatorRef, CFIndex, CFOptionFlags, CFRelease, CFRetain}; +use base::{CFTypeID, CFTypeRef, TCFType, CFHashCode, mach_port_t}; +use base::{kCFAllocatorDefault}; +use base::{Boolean}; +use array::{CFArrayRef}; +use string::{CFString, CFStringRef}; +use date::{CFAbsoluteTime, CFTimeInterval, CFAbsoluteTimeGetCurrent}; +use libc::c_void; +use std::mem; +use std::ptr; + +/// FIXME(pcwalton): Should be a newtype struct, but that fails due to a Rust compiler bug. +pub struct CFRunLoop { + obj: CFRunLoopRef, +} + +impl Drop for CFRunLoop { + fn drop(&mut self) { + unsafe { + CFRelease(self.as_CFTypeRef()) + } + } +} + +impl TCFType for CFRunLoop { + #[inline] + fn as_concrete_TypeRef(&self) -> CFRunLoopRef { + self.obj + } + + #[inline] + unsafe fn wrap_under_get_rule(reference: CFRunLoopRef) -> CFRunLoop { + let reference: CFRunLoopRef = mem::transmute(CFRetain(mem::transmute(reference))); + TCFType::wrap_under_create_rule(reference) + } + + #[inline] + fn as_CFTypeRef(&self) -> CFTypeRef { + unsafe { + mem::transmute(self.as_concrete_TypeRef()) + } + } + + #[inline] + unsafe fn wrap_under_create_rule(obj: CFRunLoopRef) -> CFRunLoop { + CFRunLoop { + obj: obj, + } + } + + #[inline] + fn type_id(_: Option) -> CFTypeID { + unsafe { + CFRunLoopGetTypeID() + } + } +} + +impl CFRunLoop { + pub fn get_current() -> CFRunLoop { + unsafe { + let run_loop_ref = CFRunLoopGetCurrent(); + TCFType::wrap_under_get_rule(run_loop_ref) + } + } + + pub fn get_main() -> CFRunLoop { + unsafe { + let run_loop_ref = CFRunLoopGetMain(); + TCFType::wrap_under_get_rule(run_loop_ref) + } + } + + pub fn run_current() { + unsafe { + CFRunLoopRun(); + } + } + + pub fn stop(&self) { + unsafe { + CFRunLoopStop(self.obj); + } + } + + pub fn current_mode(&self) -> Option { + unsafe { + let string_ref = CFRunLoopCopyCurrentMode(self.obj); + if string_ref.is_null() { + return None; + } + + let cf_string: CFString = TCFType::wrap_under_create_rule(string_ref); + Some(cf_string.to_string()) + } + } + + pub fn contains_timer(&self, timer: &CFRunLoopTimer, mode: CFStringRef) -> bool { + unsafe { + CFRunLoopContainsTimer(self.obj, timer.obj, mode) != 0 + } + } + + pub fn add_timer(&self, timer: &CFRunLoopTimer, mode: CFStringRef) { + unsafe { + CFRunLoopAddTimer(self.obj, timer.obj, mode); + } + } + +} + +#[repr(C)] +struct __CFRunLoop; + +pub type CFRunLoopRef = *const __CFRunLoop; + +#[repr(C)] +struct __CFRunLoopSource; + +pub type CFRunLoopSourceRef = *const __CFRunLoopSource; + +#[repr(C)] +struct __CFRunLoopObserver; + +pub type CFRunLoopObserverRef = *const __CFRunLoopObserver; + +// Reasons for CFRunLoopRunInMode() to Return +pub const kCFRunLoopRunFinished: i32 = 1; +pub const kCFRunLoopRunStopped: i32 = 2; +pub const kCFRunLoopRunTimedOut: i32 = 3; +pub const kCFRunLoopRunHandledSource: i32 = 4; + +// Run Loop Observer Activities +//typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { +pub type CFRunLoopActivity = CFOptionFlags; +pub const kCFRunLoopEntry: CFOptionFlags = 1 << 0; +pub const kCFRunLoopBeforeTimers: CFOptionFlags = 1 << 1; +pub const kCFRunLoopBeforeSources: CFOptionFlags = 1 << 2; +pub const kCFRunLoopBeforeWaiting: CFOptionFlags = 1 << 5; +pub const kCFRunLoopAfterWaiting: CFOptionFlags = 1 << 6; +pub const kCFRunLoopExit: CFOptionFlags = 1 << 7; +pub const kCFRunLoopAllActivities: CFOptionFlags = 0x0FFFFFFF; + +#[repr(C)] +pub struct CFRunLoopSourceContext { + version: CFIndex, + info: *mut c_void, + retain: extern "C" fn (info: *const c_void) -> *const c_void, + release: extern "C" fn (info: *const c_void), + copyDescription: extern "C" fn (info: *const c_void) -> CFStringRef, + equal: extern "C" fn (info1: *const c_void, info2: *const c_void) -> Boolean, + hash: extern "C" fn (info: *const c_void) -> CFHashCode, + schedule: extern "C" fn (info: *const c_void, rl: CFRunLoopRef, mode: CFStringRef), + cancel: extern "C" fn (info: *const c_void, rl: CFRunLoopRef, mode: CFStringRef), + perform: extern "C" fn (info: *const c_void), +} + +#[repr(C)] +pub struct CFRunLoopSourceContext1 { + version: CFIndex, + info: *mut c_void, + retain: extern "C" fn (info: *const c_void) -> *const c_void, + release: extern "C" fn (info: *const c_void), + copyDescription: extern "C" fn (info: *const c_void) -> CFStringRef, + equal: extern "C" fn (info1: *const c_void, info2: *const c_void) -> Boolean, + hash: extern "C" fn (info: *const c_void) -> CFHashCode, + // note that the following two fields are platform dependent in the C header, the ones here are for OS X + getPort: extern "C" fn (info: *mut c_void) -> mach_port_t, + perform: extern "C" fn (msg: *mut c_void, size: CFIndex, allocator: CFAllocatorRef, info: *mut c_void) -> *mut c_void, +} + +#[repr(C)] +pub struct CFRunLoopObserverContext { + version: CFIndex, + info: *mut c_void, + retain: extern "C" fn (info: *const c_void) -> *const c_void, + release: extern "C" fn (info: *const c_void), + copyDescription: extern "C" fn (info: *const c_void) -> CFStringRef, +} + +pub type CFRunLoopObserverCallBack = extern "C" fn (observer: CFRunLoopObserverRef, activity: CFRunLoopActivity, info: *mut c_void); + +#[repr(C)] +pub struct CFRunLoopTimerContext { + version: CFIndex, + info: *mut c_void, + retain: extern "C" fn (info: *const c_void) -> *const c_void, + release: extern "C" fn (info: *const c_void), + copyDescription: extern "C" fn (info: *const c_void) -> CFStringRef, +} + +pub type CFRunLoopTimerCallBack = extern "C" fn (timer: CFRunLoopTimerRef, info: *mut c_void); + +#[repr(C)] +struct __CFRunLoopTimer; + +pub type CFRunLoopTimerRef = *const __CFRunLoopTimer; + +/// FIXME(pcwalton): Should be a newtype struct, but that fails due to a Rust compiler bug. +pub struct CFRunLoopTimer { + obj: CFRunLoopTimerRef, +} + +impl Drop for CFRunLoopTimer { + fn drop(&mut self) { + unsafe { + CFRelease(self.as_CFTypeRef()) + } + } +} + +impl TCFType for CFRunLoopTimer { + #[inline] + fn as_concrete_TypeRef(&self) -> CFRunLoopTimerRef { + self.obj + } + + #[inline] + unsafe fn wrap_under_get_rule(reference: CFRunLoopTimerRef) -> CFRunLoopTimer { + let reference: CFRunLoopTimerRef = mem::transmute(CFRetain(mem::transmute(reference))); + TCFType::wrap_under_create_rule(reference) + } + + #[inline] + fn as_CFTypeRef(&self) -> CFTypeRef { + unsafe { + mem::transmute(self.as_concrete_TypeRef()) + } + } + + #[inline] + unsafe fn wrap_under_create_rule(obj: CFRunLoopTimerRef) -> CFRunLoopTimer { + CFRunLoopTimer { + obj: obj, + } + } + + #[inline] + fn type_id(_: Option) -> CFTypeID { + unsafe { + CFRunLoopTimerGetTypeID() + } + } +} + +impl CFRunLoopTimer { + pub fn new(fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, callout: CFRunLoopTimerCallBack, context: *mut CFRunLoopTimerContext) -> CFRunLoopTimer { + unsafe { + let timer_ref = CFRunLoopTimerCreate(kCFAllocatorDefault, fireDate, interval, flags, order, callout, context); + TCFType::wrap_under_create_rule(timer_ref) + } + } +} + + +#[allow(dead_code)] +#[link(name = "CoreFoundation", kind = "framework")] +extern { + /* + * CFRunLoop.h + */ + pub static kCFRunLoopDefaultMode: CFStringRef; + pub static kCFRunLoopCommonModes: CFStringRef; + fn CFRunLoopGetTypeID() -> CFTypeID; + fn CFRunLoopGetCurrent() -> CFRunLoopRef; + fn CFRunLoopGetMain() -> CFRunLoopRef; + fn CFRunLoopCopyCurrentMode(rl: CFRunLoopRef) -> CFStringRef; + fn CFRunLoopCopyAllModes(rl: CFRunLoopRef) -> CFArrayRef; + fn CFRunLoopAddCommonMode(rl: CFRunLoopRef, mode: CFStringRef); + fn CFRunLoopGetNextTimerFireDate(rl: CFRunLoopRef, mode: CFStringRef) -> CFAbsoluteTime; + fn CFRunLoopRun(); + fn CFRunLoopRunInMode(mode: CFStringRef, seconds: CFTimeInterval, returnAfterSourceHandled: Boolean) -> i32; + fn CFRunLoopIsWaiting(rl: CFRunLoopRef) -> Boolean; + fn CFRunLoopWakeUp(rl: CFRunLoopRef); + fn CFRunLoopStop(rl: CFRunLoopRef); + // fn CFRunLoopPerformBlock(rl: CFRunLoopRef, mode: CFTypeRef, block: void (^)(void)); + fn CFRunLoopContainsSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFStringRef) -> Boolean; + fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFStringRef); + fn CFRunLoopRemoveSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFStringRef); + fn CFRunLoopContainsObserver(rl: CFRunLoopRef, observer: CFRunLoopObserverRef, mode: CFStringRef) -> Boolean; + fn CFRunLoopAddObserver(rl: CFRunLoopRef, observer: CFRunLoopObserverRef, mode: CFStringRef); + fn CFRunLoopRemoveObserver(rl: CFRunLoopRef, observer: CFRunLoopObserverRef, mode: CFStringRef); + fn CFRunLoopContainsTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFStringRef) -> Boolean; + fn CFRunLoopAddTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFStringRef); + fn CFRunLoopRemoveTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFStringRef); + + fn CFRunLoopSourceGetTypeID() -> CFTypeID; + fn CFRunLoopSourceCreate(allocator: CFAllocatorRef, order: CFIndex, context: *mut CFRunLoopSourceContext) -> CFRunLoopSourceRef; + fn CFRunLoopSourceGetOrder(source: CFRunLoopSourceRef) -> CFIndex; + fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef); + fn CFRunLoopSourceIsValid(source: CFRunLoopSourceRef) -> Boolean; + fn CFRunLoopSourceGetContext(source: CFRunLoopSourceRef, context: *mut CFRunLoopSourceContext); + fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef); + + fn CFRunLoopObserverGetTypeID() -> CFTypeID; + fn CFRunLoopObserverCreate(allocator: CFAllocatorRef, activities: CFOptionFlags, repeats: Boolean, order: CFIndex, callout: CFRunLoopObserverCallBack, context: *mut CFRunLoopObserverContext) -> CFRunLoopObserverRef; + // fn CFRunLoopObserverCreateWithHandler(allocator: CFAllocatorRef, activities: CFOptionFlags, repeats: Boolean, order: CFIndex, block: void (^) (CFRunLoopObserverRef observer, CFRunLoopActivity activity)) -> CFRunLoopObserverRef; + fn CFRunLoopObserverGetActivities(observer: CFRunLoopObserverRef) -> CFOptionFlags; + fn CFRunLoopObserverDoesRepeat(observer: CFRunLoopObserverRef) -> Boolean; + fn CFRunLoopObserverGetOrder(observer: CFRunLoopObserverRef) -> CFIndex; + fn CFRunLoopObserverInvalidate(observer: CFRunLoopObserverRef); + fn CFRunLoopObserverIsValid(observer: CFRunLoopObserverRef) -> Boolean; + fn CFRunLoopObserverGetContext(observer: CFRunLoopObserverRef, context: *mut CFRunLoopObserverContext); + + fn CFRunLoopTimerGetTypeID() -> CFTypeID; + fn CFRunLoopTimerCreate(allocator: CFAllocatorRef, fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, callout: CFRunLoopTimerCallBack, context: *mut CFRunLoopTimerContext) -> CFRunLoopTimerRef; + // fn CFRunLoopTimerCreateWithHandler(allocator: CFAllocatorRef, fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, block: void (^) (CFRunLoopTimerRef timer)) -> CFRunLoopTimerRef; + fn CFRunLoopTimerGetNextFireDate(timer: CFRunLoopTimerRef) -> CFAbsoluteTime; + fn CFRunLoopTimerSetNextFireDate(timer: CFRunLoopTimerRef, fireDate: CFAbsoluteTime); + fn CFRunLoopTimerGetInterval(timer: CFRunLoopTimerRef) -> CFTimeInterval; + fn CFRunLoopTimerDoesRepeat(timer: CFRunLoopTimerRef) -> Boolean; + fn CFRunLoopTimerGetOrder(timer: CFRunLoopTimerRef) -> CFIndex; + fn CFRunLoopTimerInvalidate(timer: CFRunLoopTimerRef); + fn CFRunLoopTimerIsValid(timer: CFRunLoopTimerRef) -> Boolean; + fn CFRunLoopTimerGetContext(timer: CFRunLoopTimerRef, context: *mut CFRunLoopTimerContext); + fn CFRunLoopTimerGetTolerance(timer: CFRunLoopTimerRef) -> CFTimeInterval; + fn CFRunLoopTimerSetTolerance(timer: CFRunLoopTimerRef, tolerance: CFTimeInterval); +} + + +#[test] +fn wait_200_milliseconds() { + let run_loop = CFRunLoop::get_current(); + let mut now = unsafe { CFAbsoluteTimeGetCurrent() }; + let mut context = unsafe { CFRunLoopTimerContext { + version: 0, + info: mem::transmute(&mut now), + retain: mem::zeroed(), + release: mem::zeroed(), + copyDescription: mem::zeroed(), + } }; + + + let run_loop_timer = CFRunLoopTimer::new(now + 0.20f64, 0f64, 0, 0, timer_popped, &mut context); + run_loop.add_timer(&run_loop_timer, kCFRunLoopDefaultMode); + + CFRunLoop::run_current(); +} + +extern "C" fn timer_popped(_timer: CFRunLoopTimerRef, _info: *mut c_void) { + let previous_now_ptr: *const CFAbsoluteTime = unsafe { mem::transmute(_info) }; + let previous_now = unsafe { *previous_now_ptr }; + let now = unsafe { CFAbsoluteTimeGetCurrent() }; + assert!(now - previous_now > 0.19 && now - previous_now < 0.21); + CFRunLoop::get_current().stop(); +} diff --git a/src/url.rs b/src/url.rs index bc80a45..e21696d 100644 --- a/src/url.rs +++ b/src/url.rs @@ -11,7 +11,9 @@ #![allow(non_upper_case_globals)] -use base::{CFOptionFlags, CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType}; +use base::{CFAllocatorRef, CFIndex, CFOptionFlags, CFRelease, CFRetain}; +use base::{Boolean, CFTypeID, CFTypeRef, TCFType}; +use base::{kCFAllocatorDefault}; use string::{CFString, CFStringRef}; use std::fmt; @@ -77,8 +79,31 @@ impl fmt::Debug for CFURL { } } +impl CFURL { + pub fn from_file_system_path(filePath: CFString, pathStyle: CFURLPathStyle, isDirectory: bool) -> CFURL { + unsafe { + let url_ref = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath.as_concrete_TypeRef(), pathStyle, isDirectory as u8); + TCFType::wrap_under_create_rule(url_ref) + } + } + + pub fn get_string(&self) -> CFString { + unsafe { + TCFType::wrap_under_get_rule(CFURLGetString(self.obj)) + } + } +} + type CFURLBookmarkCreationOptions = CFOptionFlags; +pub type CFURLPathStyle = CFIndex; + +/* typedef CF_ENUM(CFIndex, CFURLPathStyle) */ +pub const kCFURLPOSIXPathStyle: CFURLPathStyle = 0; +pub const kCFURLHFSPathStyle: CFURLPathStyle = 1; +pub const kCFURLWindowsPathStyle: CFURLPathStyle = 2; + + // static kCFURLBookmarkCreationPreferFileIDResolutionMask: CFURLBookmarkCreationOptions = // (1 << 8) as u32; // static kCFURLBookmarkCreationMinimalBookmarkMask: CFURLBookmarkCreationOptions = @@ -149,6 +174,7 @@ extern { //fn CFURLCreateFromFSRef //fn CFURLCreateWithBytes //fn CFURLCreateWithFileSystemPath + fn CFURLCreateWithFileSystemPath(allocator: CFAllocatorRef, filePath: CFStringRef, pathStyle: CFURLPathStyle, isDirectory: Boolean) -> CFURLRef; //fn CFURLCreateWithFileSystemPathRelativeToBase //fn CFURLCreateWithString(allocator: CFAllocatorRef, urlString: CFStringRef, // baseURL: CFURLRef) -> CFURLRef; @@ -208,3 +234,11 @@ extern { //fn CFURLStartAccessingSecurityScopedResource //fn CFURLStopAccessingSecurityScopedResource } + +#[test] +fn file_url_from_path() { + let path = "/usr/local/foo/"; + let cfstr_path = CFString::from_static_string(path); + let cfurl = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true); + assert!("file:///usr/local/foo/" == cfurl.get_string().to_string().as_slice()); +}