AXElements/accessibility_core

View on GitHub
ext/accessibility/extras/extras.c

Summary

Maintainability
Test Coverage
#include "ruby.h"

#include "../bridge/bridge.h"

#import <IOKit/IOKitLib.h>
#import <IOKit/ps/IOPowerSources.h>
#import <IOKit/ps/IOPSKeys.h>
#import <IOKit/pwr_mgt/IOPMLib.h>
#import <IOKit/hidsystem/IOHIDShared.h>
#import <Cocoa/Cocoa.h>

static VALUE rb_mBattery;

static VALUE battery_not_installed;
static VALUE battery_charged;
static VALUE battery_charging;
static VALUE battery_discharging;

static io_connect_t screen_connection = MACH_PORT_NULL;

static VALUE rb_cRunningApp;
static VALUE rb_cWorkspace;
static VALUE rb_cProcInfo;
static VALUE rb_cHost;
static VALUE rb_cBundle;

static VALUE key_opts;
//static VALUE key_event_params;
//static VALUE key_launch_id;


static
VALUE
wrap_app(NSRunningApplication* const app)
{
    return Data_Wrap_Struct(rb_cRunningApp, NULL, objc_finalizer, (void*)app);
}

static
NSRunningApplication*
unwrap_app(VALUE app)
{
  NSRunningApplication* running_app;
  Data_Get_Struct(app, NSRunningApplication, running_app);
  return running_app;
}

static VALUE wrap_array_apps(NSArray* const ary)
{
  CFArrayRef const array = (CFArrayRef const)ary;
  WRAP_ARRAY(wrap_app);
}


static
VALUE
rb_running_app_with_pid(VALUE self, VALUE num_pid)
{
    const pid_t pid = NUM2PIDT(num_pid);
    NSRunningApplication* const app =
      [NSRunningApplication runningApplicationWithProcessIdentifier:pid];

    if (app) return wrap_app(app);
    return Qnil; // ruby behaviour would be to raise, but we want "drop-in" compat
}

static
VALUE
rb_running_app_with_bundle_id(VALUE self, VALUE bundle_id)
{
  return wrap_array_apps([NSRunningApplication
              runningApplicationsWithBundleIdentifier:unwrap_nsstring(bundle_id)]);
}

static
VALUE
rb_running_app_current_app(VALUE self)
{
    NSRunningApplication* const app = [NSRunningApplication currentApplication];
    if (app) return wrap_app(app);
    return Qnil;
}

static
VALUE
rb_running_app_drop_nuke(VALUE self)
{
  [NSRunningApplication terminateAutomaticallyTerminableApplications];
  return rb_cRunningApp; // return this to be MacRuby compatible
}


static
VALUE
rb_running_app_is_active(VALUE self)
{
  return unwrap_app(self).isActive ? Qtrue : Qfalse;
}

static
VALUE
rb_running_app_activate(VALUE self, VALUE options)
{
  return ([unwrap_app(self) activateWithOptions:FIX2INT(options)] ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_activation_policy(VALUE self)
{
  return INT2FIX(unwrap_app(self).activationPolicy);
}

static
VALUE
rb_running_app_hide(VALUE self)
{
  return ([unwrap_app(self) hide] ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_unhide(VALUE self)
{
  return ([unwrap_app(self) unhide] ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_is_hidden(VALUE self)
{
  return (unwrap_app(self).isHidden ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_localized_name(VALUE self)
{
  NSString* string = [unwrap_app(self) localizedName];
  VALUE        str = Qnil;
  if (string)
    str = wrap_nsstring(string);
  [string release];
  return str;
}

static
VALUE
rb_running_app_bundle_id(VALUE self)
{
  NSString* name = [unwrap_app(self) bundleIdentifier];
  if (name)
    return wrap_nsstring(name);
  return Qnil;
}

static
VALUE
rb_running_app_bundle_url(VALUE self)
{
  NSURL* url  = [unwrap_app(self) bundleURL];
  VALUE rburl = Qnil;
  if (url)
    rburl = wrap_nsurl(url);
  [url release];
  return rburl;
}

static
VALUE
rb_running_app_executable_arch(VALUE self)
{
  return INT2FIX(unwrap_app(self).executableArchitecture);
}

static
VALUE
rb_running_app_executable_url(VALUE self)
{
  return wrap_nsurl(unwrap_app(self).executableURL);
}

static
VALUE
rb_running_app_launch_date(VALUE self)
{
  return wrap_nsdate(unwrap_app(self).launchDate);
}

static
VALUE
rb_running_app_is_launched(VALUE self)
{
  return (unwrap_app(self).isFinishedLaunching ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_pid(VALUE self)
{
  return PIDT2NUM(unwrap_app(self).processIdentifier);
}

static
VALUE
rb_running_app_owns_menu_bar(VALUE self)
{
  return (unwrap_app(self).ownsMenuBar ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_force_terminate(VALUE self)
{
  return ([unwrap_app(self) forceTerminate] ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_terminate(VALUE self)
{
  return ([unwrap_app(self) terminate] ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_is_terminated(VALUE self)
{
  return (unwrap_app(self).isTerminated ? Qtrue : Qfalse);
}

static
VALUE
rb_running_app_equality(VALUE self, VALUE other)
{
  if (CLASS_OF(other) == rb_cRunningApp)
    if ([unwrap_app(self) isEqual:unwrap_app(other)])
      return Qtrue;
  return Qfalse;
}


static
VALUE
rb_workspace_shared(VALUE self)
{
  return self;
}


static
VALUE
rb_workspace_running_apps(VALUE self)
{
  NSArray* apps = [[NSWorkspace sharedWorkspace] runningApplications];
  VALUE rb_apps = wrap_array_apps(apps);
  [apps enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
      [obj retain];
    }];
  [apps release];
  return rb_apps;
}

static
VALUE
rb_workspace_frontmost_app(VALUE self)
{
  return wrap_app([[NSWorkspace sharedWorkspace] frontmostApplication]);
}

static
VALUE
rb_workspace_menu_bar_owner(VALUE self)
{
  return wrap_app([[NSWorkspace sharedWorkspace] menuBarOwningApplication]);
}

static
VALUE
rb_workspace_find(VALUE self, VALUE query)
{
  BOOL result =
    [[NSWorkspace sharedWorkspace] showSearchResultsForQueryString:unwrap_nsstring(query)];
  return (result ? Qtrue : Qfalse);
}

/*
 * @todo One thing we want to look into is using Launch Services instead of
 *       NSWorkspace for app launching. The reason is that we will avoid the
 *       problem launching new apps that have not been registered yet. But the
 *       big thing is that we can get the PSN for the application, which will
 *       allow for more directed input using CGEvents and it also gives us a
 *       data structure to wait on that indicates when the app has properly
 *       started up.
 *
 * The `additonalEventParamDescriptor` option is not supported by the bridge rigt now
 */
static
VALUE
rb_workspace_launch(VALUE self, VALUE bundle_id, VALUE opts)
{
  NSString*             identifier = unwrap_nsstring(bundle_id);
  NSWorkspaceLaunchOptions options = NUM2INT(rb_hash_lookup(opts, key_opts));

  BOOL result = [[NSWorkspace sharedWorkspace]
                 launchAppWithBundleIdentifier:identifier
                                       options:options
                additionalEventParamDescriptor:nil
                                      launchIdentifier:nil];

  [identifier release];
  return result ? Qtrue : Qfalse;
}


static
VALUE
rb_procinfo_self(VALUE self)
{
  return self;
}

static
VALUE
rb_procinfo_os_version(VALUE self)
{
  NSString* value = [[NSProcessInfo processInfo] operatingSystemVersionString];
  VALUE       obj = wrap_nsstring(value);
  [value release];
  return obj;
}

static
VALUE
rb_procinfo_sys_uptime(VALUE self)
{
  return DBL2NUM([[NSProcessInfo processInfo] systemUptime]);
}

static
VALUE
rb_procinfo_cpu_count(VALUE self)
{
  return INT2FIX([[NSProcessInfo processInfo] processorCount]);
}

static
VALUE
rb_procinfo_active_cpus(VALUE self)
{
  return INT2FIX([[NSProcessInfo processInfo] activeProcessorCount]);
}

static
VALUE
rb_procinfo_total_ram(VALUE self)
{
  return ULL2NUM([[NSProcessInfo processInfo] physicalMemory]);
}


static
VALUE
rb_host_self(VALUE self)
{
  return self; // hack
}

static
VALUE
rb_host_names(VALUE self)
{
  NSArray* nsnames = [[NSHost currentHost] names];
  VALUE    rbnames = wrap_array_nsstrings(nsnames);
  [nsnames release];
  return rbnames;
}

static
VALUE
rb_host_addresses(VALUE self)
{
  NSArray* nsaddrs = [[NSHost currentHost] addresses];
  VALUE    rbaddrs = wrap_array_nsstrings(nsaddrs);
  [nsaddrs release];
  return rbaddrs;
}

static
VALUE
rb_host_localized_name(VALUE self)
{
  NSString* name = [[NSHost currentHost] localizedName];
  VALUE  rb_name = wrap_nsstring(name);
  [name release];
  return rb_name;
}


VALUE wrap_bundle(NSBundle* obj) { WRAP_OBJC(rb_cBundle, objc_finalizer); }
NSBundle* unwrap_bundle(VALUE obj) { UNWRAP_OBJC(NSBundle); }

static
VALUE
rb_bundle_with_url(VALUE self, VALUE url)
{
  NSURL*     nsurl = unwrap_nsurl(url);
  NSBundle* bundle = [NSBundle bundleWithURL:nsurl];
  VALUE  rb_bundle = Qnil;

  if (bundle)
    rb_bundle = wrap_bundle(bundle);

  return rb_bundle;
}

static
VALUE
rb_bundle_info_dict(VALUE self)
{
  NSDictionary* dict = [unwrap_bundle(self) infoDictionary];
  VALUE         hash = Qnil;
  if (dict)
    hash = wrap_dictionary(dict);
  return hash;
}

static
VALUE
rb_bundle_object_for_info_dict_key(VALUE self, VALUE key)
{
  NSString* nskey = unwrap_nsstring(key);
  id obj = [unwrap_bundle(self) objectForInfoDictionaryKey:nskey];
  if (obj)
    return to_ruby(obj);
  return Qnil;
}


static
VALUE
rb_load_plist(VALUE self, VALUE plist_data)
{
  NSData* data = [NSData dataWithBytes:(void*)StringValueCStr(plist_data)
                        length:RSTRING_LEN(plist_data)];
  NSError* err = nil;
  id     plist = [NSPropertyListSerialization propertyListWithData:data
                                                            options:0
                                                    format:nil
                                                                error:&err];
  [data release];
  if (plist) {
    VALUE list = to_ruby(plist);
    [plist release];
    return list;
  }

  rb_raise(rb_eArgError, "error loading property list: '%s'",
       [[err localizedDescription] UTF8String]);
  return Qnil; // unreachable
}

VALUE wrap_screen(NSScreen* obj) { WRAP_OBJC(rb_cScreen, NULL); }
VALUE wrap_array_screens(CFArrayRef array) { WRAP_ARRAY(wrap_screen); }
NSScreen* unwrap_screen(VALUE obj) { UNWRAP_OBJC(NSScreen); }

static
VALUE
rb_screen_main(VALUE self)
{
  return wrap_screen([NSScreen mainScreen]);
}

static
VALUE
rb_screen_screens(VALUE self)
{
    return wrap_array_screens((CFArrayRef)[NSScreen screens]);
}

static
VALUE
rb_screen_frame(VALUE self)
{
    return wrap_rect(NSRectToCGRect([unwrap_screen(self) frame]));
}

static
VALUE
rb_screen_wake(VALUE self)
{
  // don't bother if we are awake
  if (!CGDisplayIsAsleep(CGMainDisplayID()))
    return Qtrue;

  if (screen_connection == MACH_PORT_NULL) {
    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
    if (service != MACH_PORT_NULL) {
      IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &screen_connection);
      IOObjectRelease(service);
    }
    else { // give up
      return Qfalse;
    }
  }

  CGPoint      mouse = NSPointToCGPoint([NSEvent mouseLocation]);
  IOGPoint     point = { mouse.x, mouse.y };
  unsigned int flags = (unsigned int)[NSEvent modifierFlags];
  NXEventData   data;

  IOHIDPostEvent(screen_connection, NX_FLAGSCHANGED, point, &data, kNXEventDataVersion, flags, 0);

  // spin rims while we wait for the screen to fully wake up
  spin(1);

  return Qtrue;
}




// Find and return the dictionary that has the battery info
static
CFDictionaryRef
battery_info()
{
  CFTypeRef  psource_info = IOPSCopyPowerSourcesInfo();
  CFArrayRef     psources = IOPSCopyPowerSourcesList(psource_info);

  // constant global strings (like ruby symbols, or lisp atoms, NXAtom, etc)
  // so we do not need to release it later (unless you really want to)
  CFStringRef type_key    = CFSTR(kIOPSTypeKey);
  CFStringRef battery_key  = CFSTR(kIOPSInternalBatteryType);

  CFIndex length = CFArrayGetCount(psources);
  for (CFIndex i = 0; i < length; i++) {

    CFTypeRef           psource = CFArrayGetValueAtIndex(psources, i);
    CFDictionaryRef source_info = IOPSGetPowerSourceDescription(psource_info, psource);
    CFRetain(source_info);

    if (CFEqual(CFDictionaryGetValue(source_info, type_key), battery_key)) {
      CFRelease(psources);
      CFRelease(psource_info);
      return source_info;
    }
    else {
      CFRelease(source_info);
    }
  }

  CFRelease(psources);
  CFRelease(psource_info);
  return NULL;
}

/*
 * Returns the current battery state
 *
 * The state will be one of:
 *
 *  - `:not_installed`
 *  - `:charged`
 *  - `:charging`
 *  - `:discharging`
 *
 * @return [Symbol]
 */
static
VALUE
rb_battery_state(VALUE self)
{
  // constant global strings (like ruby symbols, or lisp atoms, NXAtom, etc)
  // so we do not need to release it later (unless you really want to)
  CFStringRef charged_key  = CFSTR(kIOPSIsChargedKey);
  CFStringRef charging_key = CFSTR(kIOPSIsChargingKey);

  VALUE              state = battery_not_installed;
  CFDictionaryRef     info = battery_info();

  if (info) {
    if (CFDictionaryGetValue(info, charged_key) == kCFBooleanTrue)
      state = battery_charged;
    else if (CFDictionaryGetValue(info, charging_key) == kCFBooleanTrue)
      state = battery_charging;
    else
      state = battery_discharging;

    CFRelease(info);
  }

  return state;
}

/*
 * Returns the batteries charge level as a percentage from 0 to 1
 *
 * A special value of `-1.0` is returned when there is no battery present.
 *
 * @return [Float]
 */
static
VALUE
rb_battery_level(VALUE self)
{
  // constant global strings (like ruby symbols, or lisp atoms, NXAtom, etc)
  // so we do not need to release it later (unless you really want to)
  CFStringRef capacity_key     = CFSTR(kIOPSCurrentCapacityKey);
  CFStringRef max_capacity_key = CFSTR(kIOPSMaxCapacityKey);

  double         level = -1.0;
  CFDictionaryRef info = battery_info();

  if (info) {
    CFNumberRef current_cap = CFDictionaryGetValue(info, capacity_key);
    CFNumberRef     max_cap = CFDictionaryGetValue(info, max_capacity_key);

    if (current_cap && max_cap) {
      int current = 0;
      int     max = 0;

      CFNumberGetValue(current_cap, kCFNumberIntType, &current);
      CFNumberGetValue(max_cap,     kCFNumberIntType, &max);

      level = ((double)current)/((double)max);
    }

    CFRelease(info);
  }

  return DBL2NUM(level);
}


/*
 * Returns the estimated number of minutes until the battery is fully discharged
 *
 * A special value of `-1` indicates that the value is currently being
 * estimated and you should try again later.
 *
 * A special value of `0` indicates that the battery is not discharging,
 * which usually means that the battery does not exist or is in a
 * charging/charged state.
 *
 * @return [Fixnum]
 */
static
VALUE
rb_battery_time_to_empty(VALUE self)
{
  CFStringRef ttempty_key = CFSTR(kIOPSTimeToEmptyKey);
  int                time = -1;
  CFDictionaryRef    info = battery_info();

  if (info) {
    CFNumberRef current_time = CFDictionaryGetValue(info, ttempty_key);
    if (current_time)
      CFNumberGetValue(current_time, kCFNumberIntType, &time);

    CFRelease(info);
  }

  if (time)
    return INT2FIX(time);
  else
    return INT2FIX(0);
}


/*
 * Returns the estimated number of minutes until the battery is fully charged
 *
 * A special value of `-1` indicates that the value is currently being
 * estimated and you should try again later.
 *
 * @return [Fixnum]
*/
static
VALUE
rb_battery_time_full_charge(VALUE self)
{
  CFStringRef ttfull_key = CFSTR(kIOPSTimeToFullChargeKey);
  int                time = -1;
  CFDictionaryRef    info = battery_info();

  if (info) {
    CFNumberRef current_time = CFDictionaryGetValue(info, ttfull_key);
    if (current_time)
      CFNumberGetValue(current_time, kCFNumberIntType, &time);

    CFRelease(info);
  }

  return INT2FIX(time);
}




void
Init_extras()
{
  Init_bridge();

  // force Ruby to be registered as an app with the system
  [NSApplication sharedApplication];

  /*
   * Document-class: NSRunningApplication
   *
   * A 99% drop-in replacement for Cocoa's `NSWorkspace` class on MRI and other
   * non-MacRuby rubies.
   *
   * See [Apple's Developer Reference](https://developer.apple.com/library/mac/#documentation/AppKit/Reference/NSRunningApplication_Class/Reference/Reference.html)
   * for documentation on the methods in this class.
   */
  rb_cRunningApp = rb_define_class("NSRunningApplication", rb_cObject);

  rb_define_singleton_method(rb_cRunningApp, "runningApplicationWithProcessIdentifier",      rb_running_app_with_pid,       1);
  rb_define_singleton_method(rb_cRunningApp, "runningApplicationsWithBundleIdentifier",      rb_running_app_with_bundle_id, 1);
  rb_define_singleton_method(rb_cRunningApp, "currentApplication",                           rb_running_app_current_app,    0);
  rb_define_singleton_method(rb_cRunningApp, "terminateAutomaticallyTerminableApplications", rb_running_app_drop_nuke, 0);

  rb_define_method(rb_cRunningApp, "active?",                rb_running_app_is_active,         0);
  rb_define_method(rb_cRunningApp, "activateWithOptions",    rb_running_app_activate,          1);
  rb_define_method(rb_cRunningApp, "activationPolicy",       rb_running_app_activation_policy, 0);
  rb_define_method(rb_cRunningApp, "hide",                   rb_running_app_hide,              0);
  rb_define_method(rb_cRunningApp, "unhide",                 rb_running_app_unhide,            0);
  rb_define_method(rb_cRunningApp, "hidden?",                rb_running_app_is_hidden,         0);
  rb_define_method(rb_cRunningApp, "localizedName",          rb_running_app_localized_name,    0);
  //rb_define_method(rb_cRunningApp, "icon",                   rb_running_app_icon,              0);
  rb_define_method(rb_cRunningApp, "bundleIdentifier",       rb_running_app_bundle_id,         0);
  rb_define_method(rb_cRunningApp, "bundleURL",              rb_running_app_bundle_url,        0);
  rb_define_method(rb_cRunningApp, "executableArchitecture", rb_running_app_executable_arch,   0);
  rb_define_method(rb_cRunningApp, "executableURL",          rb_running_app_executable_url,    0);
  rb_define_method(rb_cRunningApp, "launchDate",             rb_running_app_launch_date,       0);
  rb_define_method(rb_cRunningApp, "finishedLaunching?",     rb_running_app_is_launched,       0);
  rb_define_method(rb_cRunningApp, "processIdentifier",      rb_running_app_pid,               0);
  rb_define_method(rb_cRunningApp, "ownsMenuBar",            rb_running_app_owns_menu_bar,     0);
  rb_define_alias(rb_cRunningApp, "ownsMenuBar?", "ownsMenuBar");
  rb_define_method(rb_cRunningApp, "forceTerminate",         rb_running_app_force_terminate,   0);
  rb_define_method(rb_cRunningApp, "terminate",              rb_running_app_terminate,         0);
  rb_define_method(rb_cRunningApp, "terminated?",            rb_running_app_is_terminated,     0);
  rb_define_method(rb_cRunningApp, "==",                     rb_running_app_equality,          1);

  // Technically these are global constants, but in MacRuby you can still access them from any
  // namespace. So, we will try by first adding them to the NSRunningApplication namespae only
#define RUNNING_APP_CONST(str, val) rb_define_const(rb_cRunningApp, str, INT2FIX(val));
  if (!rb_const_defined_at(rb_cRunningApp, rb_intern("NSApplicationActivateAllWindows"))) {
    RUNNING_APP_CONST("NSApplicationActivateAllWindows",        NSApplicationActivateAllWindows);
    RUNNING_APP_CONST("NSApplicationActivateIgnoringOtherApps", NSApplicationActivateIgnoringOtherApps);

    RUNNING_APP_CONST("NSBundleExecutableArchitectureI386",     NSBundleExecutableArchitectureI386);
    RUNNING_APP_CONST("NSBundleExecutableArchitecturePPC",      NSBundleExecutableArchitecturePPC);
    RUNNING_APP_CONST("NSBundleExecutableArchitectureX86_64",   NSBundleExecutableArchitectureX86_64);
    RUNNING_APP_CONST("NSBundleExecutableArchitecturePPC64",    NSBundleExecutableArchitecturePPC64);
  }


  /*
   * Document-class: NSWorkspace
   *
   * A subset of Cocoa's `NSWorkspace` class.
   *
   * See [Apple's Developer Reference](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/Reference/Reference.html)
   * for documentation on the methods available in this class.
   */
  rb_cWorkspace = rb_define_class("NSWorkspace", rb_cObject);

  rb_define_singleton_method(rb_cWorkspace, "runningApplications",             rb_workspace_running_apps,   0);
  rb_define_singleton_method(rb_cWorkspace, "sharedWorkspace",                 rb_workspace_shared,         0);
  rb_define_singleton_method(rb_cWorkspace, "frontmostApplication",            rb_workspace_frontmost_app,  0);
  rb_define_singleton_method(rb_cWorkspace, "menuBarOwningApplication",        rb_workspace_menu_bar_owner, 0);
  rb_define_singleton_method(rb_cWorkspace, "showSearchResultsForQueryString", rb_workspace_find,           1);
  rb_define_singleton_method(rb_cWorkspace, "launchAppWithBundleIdentifier",   rb_workspace_launch,         2);

  key_opts         = ID2SYM(rb_intern("options"));
  //  key_event_params = ID2SYM(rb_intern("additionalEventParamDescriptor"));
  //  key_launch_id    = ID2SYM(rb_intern("launchIdentifier"));

#define WORKSPACE_CONST(str, val) rb_define_const(rb_cWorkspace, str, INT2FIX(val));
  if (!rb_const_defined_at(rb_cWorkspace, rb_intern("NSWorkspaceLaunchAndPrint"))) {
    WORKSPACE_CONST("NSWorkspaceLaunchAndPrint",                 NSWorkspaceLaunchAndPrint);
    WORKSPACE_CONST("NSWorkspaceLaunchInhibitingBackgroundOnly", NSWorkspaceLaunchInhibitingBackgroundOnly);
    WORKSPACE_CONST("NSWorkspaceLaunchWithoutAddingToRecents",   NSWorkspaceLaunchWithoutAddingToRecents);
    WORKSPACE_CONST("NSWorkspaceLaunchWithoutActivation",        NSWorkspaceLaunchWithoutActivation);
    WORKSPACE_CONST("NSWorkspaceLaunchAsync",                    NSWorkspaceLaunchAsync);
    //Classic workspace settings deprecated in 10.11
#ifndef MAC_OS_X_VERSION_10_11
    WORKSPACE_CONST("NSWorkspaceLaunchAllowingClassicStartup",   NSWorkspaceLaunchAllowingClassicStartup);
    WORKSPACE_CONST("NSWorkspaceLaunchPreferringClassic",        NSWorkspaceLaunchPreferringClassic);
#endif
    WORKSPACE_CONST("NSWorkspaceLaunchNewInstance",              NSWorkspaceLaunchNewInstance);
    WORKSPACE_CONST("NSWorkspaceLaunchAndHide",                  NSWorkspaceLaunchAndHide);
    WORKSPACE_CONST("NSWorkspaceLaunchAndHideOthers",            NSWorkspaceLaunchAndHideOthers);
    WORKSPACE_CONST("NSWorkspaceLaunchDefault",                  NSWorkspaceLaunchDefault);
  }


  /*
   * Document-class: NSProcessInfo
   *
   * A subset of Cocoa's `NSProcessInfo` class. Methods that might be
   * useful to have been bridged.
   *
   * See [Apple's Developer Reference](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSProcessInfo_Class/Reference/Reference.html)
   * for documentation on the methods available in this class.
   */
  rb_cProcInfo = rb_define_class("NSProcessInfo", rb_cObject);

  rb_define_singleton_method(rb_cProcInfo, "processInfo",                  rb_procinfo_self,        0);
  rb_define_singleton_method(rb_cProcInfo, "operatingSystemVersionString", rb_procinfo_os_version,  0);
  rb_define_singleton_method(rb_cProcInfo, "systemUptime",                 rb_procinfo_sys_uptime,  0);
  rb_define_singleton_method(rb_cProcInfo, "processorCount",               rb_procinfo_cpu_count,   0);
  rb_define_singleton_method(rb_cProcInfo, "activeProcessorCount",         rb_procinfo_active_cpus, 0);
  rb_define_singleton_method(rb_cProcInfo, "physicalMemory",               rb_procinfo_total_ram,   0);


  /*
   * Document-class: NSHost
   *
   * A large subset of Cocoa's `NSHost` class. Methods that might be
   * useful to have have been bridged.
   *
   * See [Apple's Developer Reference](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSHost_Class/Reference/Reference.html)
   * for documentation on the methods available in this class.
   */
  rb_cHost = rb_define_class("NSHost", rb_cObject);

  rb_define_singleton_method(rb_cHost, "currentHost",   rb_host_self,           0);
  rb_define_singleton_method(rb_cHost, "names",         rb_host_names,          0);
  rb_define_singleton_method(rb_cHost, "addresses",     rb_host_addresses,      0);
  rb_define_singleton_method(rb_cHost, "localizedName", rb_host_localized_name, 0);


  /*
   * Document-class: NSBundle
   *
   * A tiny subset of Cocoa's `NSBundle` class. Methods that might be
   * useful to have have been bridged.
   *
   * See [Apple's Developer Reference](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSBundle_Class/Reference/Reference.html)
   * for documentation on the methods available in this class.
   */
  rb_cBundle = rb_define_class("NSBundle", rb_cObject);
  rb_define_singleton_method(rb_cBundle, "bundleWithURL", rb_bundle_with_url, 1);
  rb_define_method(rb_cBundle, "infoDictionary",             rb_bundle_info_dict,                0);
  rb_define_method(rb_cBundle, "objectForInfoDictionaryKey", rb_bundle_object_for_info_dict_key, 1);

  rb_define_method(rb_cObject, "load_plist", rb_load_plist, 1);


  /*
   * Document-class: NSScreen
   *
   * A small subset of Cocoa's `NSScreen` class. Methods that might be
   * useful to have have been bridged.
   *
   * See [Apple's Developer Reference](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSScreen_Class/Reference/Reference.html)
   * for documentation on the methods available in this class.
   */
  rb_cScreen = rb_define_class("NSScreen", rb_cObject);

  rb_define_singleton_method(rb_cScreen, "mainScreen", rb_screen_main,    0);
  rb_define_singleton_method(rb_cScreen, "screens",    rb_screen_screens, 0);
  rb_define_singleton_method(rb_cScreen, "wakeup",     rb_screen_wake,    0); // our custom method
  rb_define_method(          rb_cScreen, "frame",      rb_screen_frame,   0);

  /*
   * Document-module: Battery
   *
   * Utility methods for getting information about the system battery
   * (if present).
   */
  rb_mBattery = rb_define_module("Battery");
  rb_extend_object(rb_mBattery, rb_mBattery);

  battery_not_installed = ID2SYM(rb_intern("not_installed"));
  battery_charged       = ID2SYM(rb_intern("charged"));
  battery_charging      = ID2SYM(rb_intern("charging"));
  battery_discharging   = ID2SYM(rb_intern("discharging"));

  rb_define_method(rb_mBattery, "state",     rb_battery_state, 0);
  rb_define_method(rb_mBattery, "level",     rb_battery_level, 0);
  rb_define_method(rb_mBattery, "time_to_discharged", rb_battery_time_to_empty, 0);
  rb_define_method(rb_mBattery, "time_to_charged", rb_battery_time_full_charge, 0);

  rb_define_alias(rb_mBattery, "charge_level", "level");
  rb_define_alias(rb_mBattery, "time_to_empty", "time_to_discharged");
  rb_define_alias(rb_mBattery, "time_to_full_charge", "time_to_charged");
}