├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── examples └── hello_world.rs └── src ├── classes ├── mod.rs ├── ns_application.rs ├── ns_menu.rs ├── ns_menu_item.rs ├── ns_object.rs ├── ns_responder.rs ├── ns_window.rs └── ns_window_controller.rs ├── conversions.rs ├── lib.rs ├── macros.rs ├── protocols ├── mod.rs └── ns_application_delegate.rs └── wrappers ├── mod.rs ├── srb_application_delegate.rs ├── srb_object.rs └── srb_window_controller.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sorbet-cocoa" 3 | version = "0.1.0" 4 | authors = ["Kyle Lacy "] 5 | 6 | [lib] 7 | name = "sorbet_cocoa" 8 | crate-type = ["rlib"] 9 | 10 | [dependencies] 11 | objc = "0.2.1" 12 | objc_id = "0.1.0" 13 | objc_exception = "0.1.1" 14 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016-2018 Kyle Lacy 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2018 Kyle Lacy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | #![feature(specialization)] 2 | 3 | // Ported from: 4 | // https://gist.github.com/lucamarrocco/2b06c92e4e6df01de04b 5 | 6 | #[macro_use] extern crate objc; 7 | #[macro_use] extern crate sorbet_cocoa as cocoa; 8 | 9 | use cocoa::{Duck, Id, ShareId, 10 | IsNSApplication, IsNSWindowController, IsNSWindow, IsNSMenu}; 11 | 12 | struct AppDelegate { 13 | super_: Id, 14 | app: ShareId, 15 | controller: ShareId 16 | } 17 | 18 | impl AppDelegate { 19 | fn new(app: ShareId) -> Self { 20 | let controller = NiblessWindowController::new(); 21 | let controller: Id = controller.duck(); 22 | 23 | AppDelegate { 24 | super_: cocoa::NSObject::new(), 25 | app: app, 26 | controller: controller.share() 27 | } 28 | } 29 | } 30 | 31 | objc_inherit! { 32 | impl Object for AppDelegate { 33 | type Super = NSObject; 34 | 35 | let super_ = self.super_; 36 | } 37 | } 38 | 39 | impl cocoa::IsNSApplicationDelegate for AppDelegate { 40 | fn application_did_finish_launching(&self, _: ShareId) { 41 | println!("application started!"); 42 | self.controller.show_window(None); 43 | self.app.activate_ignoring_other_apps(true); 44 | } 45 | 46 | fn application_will_terminate(&self, _: ShareId) { 47 | println!("application terminated!"); 48 | } 49 | 50 | fn application_should_terminate_after_last_window_closed(&self, _: ShareId) -> bool { 51 | true 52 | } 53 | } 54 | 55 | fn set_main_menu(app: &cocoa::NSApplication) { 56 | let tree = vec![ 57 | ("Apple", vec![ 58 | cocoa::NSMenuItem::new("Quit", Some(sel!(terminate:)), "q") 59 | ]) 60 | ]; 61 | 62 | let main_menu = cocoa::NSMenu::new("MainMenu"); 63 | for (title, items) in tree { 64 | let submenu = cocoa::NSMenu::new(title).share(); 65 | if let Some(item) = main_menu.add_item_with_title_action_key_equivalent(title, None, "") { 66 | main_menu.set_submenu_for_item(Some(submenu.clone()), item); 67 | for item in items { 68 | submenu.add_item(item.share()) 69 | } 70 | } 71 | } 72 | 73 | app.set_main_menu(Some(main_menu.share())); 74 | } 75 | 76 | struct NiblessWindowController { 77 | super_: Id 78 | } 79 | 80 | objc_inherit! { 81 | impl Object for NiblessWindowController { 82 | type Super = NSWindowController; 83 | 84 | let super_ = self.super_; 85 | } 86 | } 87 | 88 | impl NiblessWindowController { 89 | fn new() -> Self { 90 | let rect = cocoa::ns_make_rect(0.0, 0.0, 480.0, 320.0); 91 | let style = (1 << 0) | (1 << 1) | (1 << 3); // cocoa::NSTitledWindowMask | cocoa::NSClosableWindowMask | cocoa::NSResizableWindowMask; 92 | let backing = cocoa::NSBackingStoreType::Buffered; 93 | let window = cocoa::NSWindow::new(rect, style, backing, false); 94 | window.set_title("App"); 95 | 96 | let super_ = cocoa::NSWindowController::new(Some(window.share())); 97 | 98 | NiblessWindowController { 99 | super_: super_ 100 | } 101 | } 102 | } 103 | 104 | fn main() { 105 | let app = cocoa::NSApplication::shared_application(); 106 | let delegate = AppDelegate::new(app.clone()).duck(); 107 | unsafe { app.set_delegate(Some(delegate)); } 108 | 109 | set_main_menu(&app); 110 | 111 | app.set_activation_policy(cocoa::NSApplicationActivationPolicy::Regular); 112 | app.run(); 113 | } 114 | -------------------------------------------------------------------------------- /src/classes/mod.rs: -------------------------------------------------------------------------------- 1 | mod ns_object; 2 | mod ns_responder; 3 | mod ns_window; 4 | mod ns_window_controller; 5 | mod ns_application; 6 | mod ns_menu; 7 | mod ns_menu_item; 8 | 9 | pub use self::ns_object::*; 10 | pub use self::ns_responder::*; 11 | pub use self::ns_window::*; 12 | pub use self::ns_window_controller::*; 13 | pub use self::ns_application::*; 14 | pub use self::ns_menu::*; 15 | pub use self::ns_menu_item::*; 16 | 17 | pub type NSEvent = NSObject; 18 | pub type NSViewController = NSObject; 19 | pub type NSStoryboard = NSObject; 20 | pub type NSNotification = NSObject; 21 | -------------------------------------------------------------------------------- /src/classes/ns_application.rs: -------------------------------------------------------------------------------- 1 | use objc; 2 | use objc::runtime as rt; 3 | use {AnyObject, Object, ShareId, RawObjCObject, NSResponder, IsNSResponder, 4 | NSMenu, NSApplicationDelegate, NSApplicationActivationPolicy}; 5 | 6 | #[repr(C)] 7 | pub struct NSApplication { 8 | super_: NSResponder 9 | } 10 | 11 | unsafe impl objc::Message for NSApplication { } 12 | 13 | unsafe impl RawObjCObject for NSApplication { } 14 | 15 | impl Object for NSApplication { 16 | type Super = NSResponder; 17 | 18 | fn super_ref(&self) -> &Self::Super { 19 | &self.super_ 20 | } 21 | 22 | fn super_mut(&mut self) -> &mut Self::Super { 23 | &mut self.super_ 24 | } 25 | } 26 | 27 | pub trait IsNSApplication: IsNSResponder { 28 | unsafe fn delegate(&self) -> Option>; 29 | unsafe fn set_delegate(&self, delegate: Option>); 30 | fn main_menu(&self) -> Option>; 31 | fn set_main_menu(&self, main_menu: Option>); 32 | fn activate_ignoring_other_apps(&self, flag: bool); 33 | fn set_activation_policy(&self, activation_policy: NSApplicationActivationPolicy) -> bool; 34 | fn run(&self); 35 | } 36 | 37 | impl NSApplication { 38 | pub fn shared_application() -> ShareId { 39 | unsafe { 40 | let ns_application = rt::Class::get("NSApplication").unwrap(); 41 | ShareId::from_retained_ptr(msg_send![ns_application, sharedApplication]) 42 | } 43 | } 44 | } 45 | 46 | objc_trait! { 47 | pub unsafe objc trait IsNSApplication: IsNSResponder { 48 | type Base = NSApplication; 49 | trait Sub = SubNSApplication; 50 | 51 | unsafe fn delegate(&self) -> Option> 52 | => [self, delegate] -> *mut AnyObject; 53 | unsafe fn set_delegate(&self, delegate: Option>) 54 | => [self, setDelegate:(delegate: *mut AnyObject)]; 55 | fn main_menu(&self) -> Option> 56 | => [self, mainMenu] -> *mut AnyObject; 57 | fn set_main_menu(&self, main_menu: Option>) 58 | => [self, setMainMenu:(menu: *mut AnyObject)]; 59 | fn activate_ignoring_other_apps(&self, flag: bool) 60 | => [self, activateIgnoringOtherApps:(flag: rt::BOOL)]; 61 | fn set_activation_policy(&self, activation_policy: NSApplicationActivationPolicy) -> bool 62 | => [self, setActivationPolicy:(policy: usize)] -> rt::BOOL; 63 | fn run(&self) 64 | => [self, run]; 65 | } 66 | } 67 | 68 | #[macro_export] 69 | macro_rules! NSApplication { 70 | ($args:tt) => { 71 | __objc_inheritance_for! { 72 | $crate::NSApplication => $crate::SubNSApplication: NSResponder!; 73 | $args 74 | } 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /src/classes/ns_menu.rs: -------------------------------------------------------------------------------- 1 | use objc; 2 | use objc::runtime as rt; 3 | use {Id, ShareId, Object, AnyObject, RawObjCObject, rust_to_objc_id, 4 | OptionSel, NSObject, IsNSObject, NSMenuItem}; 5 | 6 | #[repr(C)] 7 | pub struct NSMenu { 8 | super_: NSObject 9 | } 10 | 11 | unsafe impl objc::Message for NSMenu { } 12 | 13 | unsafe impl RawObjCObject for NSMenu { } 14 | 15 | impl Object for NSMenu { 16 | type Super = NSObject; 17 | 18 | fn super_ref(&self) -> &Self::Super { 19 | &self.super_ 20 | } 21 | 22 | fn super_mut(&mut self) -> &mut Self::Super { 23 | &mut self.super_ 24 | } 25 | } 26 | 27 | impl NSMenu { 28 | pub fn new(title: &str) -> Id { 29 | unsafe { 30 | let title = rust_to_objc_id(title); 31 | 32 | let ns_menu = rt::Class::get("NSMenu").unwrap(); 33 | let self_: *mut AnyObject = msg_send![ns_menu, alloc]; 34 | let self_: *mut AnyObject = msg_send![self_, initWithTitle:title]; 35 | let self_ = self_ as *mut NSMenu; 36 | Id::from_retained_ptr(self_) 37 | } 38 | } 39 | } 40 | 41 | pub trait IsNSMenu: IsNSObject { 42 | fn add_item(&self, item: ShareId); 43 | 44 | fn add_item_with_title_action_key_equivalent(&self, 45 | title: &str, 46 | action: Option, 47 | key_equivalent: &str) 48 | -> Option>; 49 | 50 | fn set_submenu_for_item(&self, 51 | submenu: Option>, 52 | item: ShareId); 53 | } 54 | 55 | objc_trait! { 56 | pub unsafe objc trait IsNSMenu: IsNSObject { 57 | type Base = NSMenu; 58 | trait Sub = SubNSMenu; 59 | 60 | fn add_item(&self, item: ShareId) 61 | => [self, addItem:(item: *mut AnyObject)]; 62 | 63 | fn add_item_with_title_action_key_equivalent(&self, 64 | title: &str, 65 | action: Option, 66 | key_equivalent: &str) 67 | -> Option> 68 | => [self, addItemWithTitle:(title: *mut AnyObject) 69 | action:(action: OptionSel) 70 | keyEquivalent:(key: *mut AnyObject)] 71 | -> *mut AnyObject; 72 | 73 | fn set_submenu_for_item(&self, 74 | submenu: Option>, 75 | item: ShareId) 76 | => [self, setSubmenu:(submenu: *mut AnyObject) 77 | forItem:(item: *mut AnyObject)]; 78 | } 79 | } 80 | 81 | #[macro_export] 82 | macro_rules! NSMenu { 83 | ($args:tt) => { 84 | __objc_inheritance_for! { 85 | $crate::NSMenu => $crate::SubNSMenu: NSObject!; 86 | $args 87 | } 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /src/classes/ns_menu_item.rs: -------------------------------------------------------------------------------- 1 | use objc; 2 | use objc::runtime as rt; 3 | use {Id, AnyObject, rust_to_objc, rust_to_objc_id, 4 | Object, OptionSel, RawObjCObject, NSObject, IsNSObject}; 5 | 6 | #[repr(C)] 7 | pub struct NSMenuItem { 8 | super_: NSObject 9 | } 10 | 11 | unsafe impl objc::Message for NSMenuItem { } 12 | 13 | unsafe impl RawObjCObject for NSMenuItem { } 14 | 15 | impl Object for NSMenuItem { 16 | type Super = NSObject; 17 | 18 | fn super_ref(&self) -> &Self::Super { 19 | &self.super_ 20 | } 21 | 22 | fn super_mut(&mut self) -> &mut Self::Super { 23 | &mut self.super_ 24 | } 25 | } 26 | 27 | impl NSMenuItem { 28 | pub fn new(title: &str, action: Option, key_equivalent: &str) -> Id { 29 | unsafe { 30 | let title = rust_to_objc_id(title); 31 | let action: OptionSel = rust_to_objc(action); 32 | let key_equivalent = rust_to_objc_id(key_equivalent); 33 | 34 | let ns_menu_item = rt::Class::get("NSMenuItem").unwrap(); 35 | let self_: *mut AnyObject = msg_send![ns_menu_item, alloc]; 36 | let self_: *mut AnyObject = msg_send![self_, initWithTitle:title action:action keyEquivalent:key_equivalent]; 37 | let self_ = self_ as *mut NSMenuItem; 38 | Id::from_retained_ptr(self_) 39 | } 40 | } 41 | } 42 | 43 | pub trait IsNSMenuItem: IsNSObject { 44 | 45 | } 46 | 47 | objc_trait! { 48 | pub unsafe objc trait IsNSMenuItem: IsNSObject { 49 | type Base = NSMenuItem; 50 | trait Sub = SubNSMenuItem; 51 | } 52 | } 53 | 54 | #[macro_export] 55 | macro_rules! NSMenuItem { 56 | ($args:tt) => { 57 | __objc_inheritance_for! { 58 | $crate::NSMenuItem => $crate::SubNSMenuItem: NSObject!; 59 | $args 60 | } 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /src/classes/ns_object.rs: -------------------------------------------------------------------------------- 1 | use objc; 2 | use objc::runtime as rt; 3 | use {AnyObject, Id, AsAnyObject, RawObjCObject, Object}; 4 | 5 | #[repr(C)] 6 | pub struct NSObject { 7 | super_: AnyObject 8 | } 9 | 10 | unsafe impl objc::Message for NSObject { } 11 | 12 | unsafe impl RawObjCObject for NSObject { } 13 | 14 | impl Object for NSObject { 15 | type Super = AnyObject; 16 | 17 | fn super_ref(&self) -> &Self::Super { 18 | &self.super_ 19 | } 20 | 21 | fn super_mut(&mut self) -> &mut Self::Super { 22 | &mut self.super_ 23 | } 24 | } 25 | 26 | impl AsAnyObject for NSObject { 27 | fn any_ref(&self) -> &AnyObject { 28 | &self.super_ 29 | } 30 | 31 | fn any_mut(&mut self) -> &mut AnyObject { 32 | &mut self.super_ 33 | } 34 | } 35 | 36 | impl NSObject { 37 | pub fn new() -> Id { 38 | unsafe { 39 | let ns_object = rt::Class::get("NSObject").unwrap(); 40 | let self_: *mut NSObject = msg_send![ns_object, alloc]; 41 | let self_: *mut NSObject = msg_send![self_, init]; 42 | Id::from_retained_ptr(self_) 43 | } 44 | } 45 | } 46 | 47 | pub trait IsNSObject { 48 | fn instance_class(&self) -> &rt::Class; 49 | fn instance_superclass(&self) -> Option<&rt::Class>; 50 | fn is_equal(&self, other: Option<&AnyObject>) -> bool; 51 | fn hash(&self) -> usize; 52 | fn is_kind_of_class(&self, class: &rt::Class) -> bool; 53 | fn is_member_of_class(&self, class: &rt::Class) -> bool; 54 | fn responds_to_selector(&self, sel: rt::Sel) -> bool; 55 | fn description(&self) -> String; 56 | fn debug_description(&self) -> String; 57 | } 58 | 59 | objc_trait! { 60 | pub unsafe objc trait IsNSObject { 61 | type Base = NSObject; 62 | trait Sub = SubNSObject; 63 | 64 | fn instance_class(&self) -> &rt::Class 65 | => [self, class] -> *const rt::Class; 66 | fn instance_superclass(&self) -> Option<&rt::Class> 67 | => [self, superclass] -> *const rt::Class; 68 | fn is_equal(&self, other: Option<&AnyObject>) -> bool 69 | => [self, isEqual:(other: *const AnyObject)] -> rt::BOOL; 70 | fn hash(&self) -> usize 71 | => [self, hash] -> usize; 72 | fn is_kind_of_class(&self, class: &rt::Class) -> bool 73 | => [self, isKindOfClass:(class: *const rt::Class)] -> rt::BOOL; 74 | fn is_member_of_class(&self, class: &rt::Class) -> bool 75 | => [self, isMemberOfClass:(class: *const rt::Class)] -> rt::BOOL; 76 | fn responds_to_selector(&self, sel: rt::Sel) -> bool 77 | => [self, respondsToSelector:(sel: rt::Sel)] -> rt::BOOL; 78 | fn description(&self) -> String 79 | => [self, description] -> *mut AnyObject; 80 | fn debug_description(&self) -> String 81 | => [self, debugDescription] -> *mut AnyObject; 82 | } 83 | } 84 | 85 | // TODO: Generate this macro automatically in `objc_trait!` (see: 86 | // https://github.com/rust-lang/rust/issues/6994) 87 | #[macro_export] 88 | macro_rules! NSObject { 89 | ($args:tt) => { 90 | __objc_inheritance_for! { 91 | $crate::NSObject => $crate::SubNSObject; 92 | $args 93 | } 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /src/classes/ns_responder.rs: -------------------------------------------------------------------------------- 1 | use objc; 2 | use objc::runtime as rt; 3 | use {ShareId, Object, AnyObject, RawObjCObject, NSObject, IsNSObject, NSEvent}; 4 | 5 | #[repr(C)] 6 | pub struct NSResponder { 7 | super_: NSObject 8 | } 9 | 10 | unsafe impl objc::Message for NSResponder { } 11 | 12 | unsafe impl RawObjCObject for NSResponder { } 13 | 14 | impl Object for NSResponder { 15 | type Super = NSObject; 16 | 17 | fn super_ref(&self) -> &Self::Super { 18 | &self.super_ 19 | } 20 | 21 | fn super_mut(&mut self) -> &mut Self::Super { 22 | &mut self.super_ 23 | } 24 | } 25 | 26 | pub trait IsNSResponder: IsNSObject { 27 | fn accepts_first_responder(&self) -> bool; 28 | fn become_first_responder(&self) -> bool; 29 | fn resign_first_responder(&self) -> bool; 30 | fn validate_proposed_first_responder_for_event(&self, responder: ShareId, event: Option>) -> bool; 31 | 32 | unsafe fn next_responder(&self) -> Option>; 33 | unsafe fn set_next_responder(&self, next_responder: Option>); 34 | 35 | fn mouse_down(&self, event: ShareId); 36 | fn mouse_dragged(&self, event: ShareId); 37 | fn mouse_up(&self, event: ShareId); 38 | fn mouse_moved(&self, event: ShareId); 39 | fn mouse_entered(&self, event: ShareId); 40 | fn mouse_exited(&self, event: ShareId); 41 | fn right_mouse_down(&self, event: ShareId); 42 | fn right_mouse_dragged(&self, event: ShareId); 43 | fn right_mouse_up(&self, event: ShareId); 44 | fn other_mouse_down(&self, event: ShareId); 45 | fn other_mouse_dragged(&self, event: ShareId); 46 | fn other_mouse_up(&self, event: ShareId); 47 | 48 | // fn key_down(&self, event: ShareId); 49 | // fn key_up(&self, event: ShareId); 50 | // fn interpret_key_events(&self, event: Vec>); 51 | // fn perform_key_equivalent(&self, event: ShareId) -> bool; 52 | // fn flush_buffered_key_events(&self); 53 | // 54 | // fn pressure_change_with_event(&self, event: ShareId); 55 | // fn cursor_update(&self, event: ShareId); 56 | // fn flags_changed(&self, event: ShareId); 57 | // fn tablet_point(&self, event: ShareId); 58 | // fn tablet_proximity(&self, event: ShareId); 59 | // fn help_requested(&self, event: ShareId); 60 | // fn scroll_wheel(&self, event: ShareId); 61 | // fn quick_look_with_event(&self, event: ShareId); 62 | // 63 | // fn cancel_operation(&self, sender: Option>); 64 | // fn capitalize_word(&self, sender: Option>); 65 | // fn center_selection_in_visible_area(&self, sender: Option>); 66 | // fn change_case_of_letter(&self, sender: Option>); 67 | // fn complete(&self, sender: Option>); 68 | // fn delete_backward(&self, sender: Option>); 69 | // fn delete_backward_by_decomposing_previous_character(&self, sender: Option>); 70 | // fn delete_forward(&self, sender: Option>); 71 | // fn delete_to_beginning_of_line(&self, sender: Option>); 72 | // fn delete_to_beginning_of_paragraph(&self, sender: Option>); 73 | // fn delete_to_end_of_line(&self, sender: Option>); 74 | // fn delete_to_end_of_paragraph(&self, sender: Option>); 75 | // fn delete_to_mark(&self, sender: Option>); 76 | // fn delete_word_backward(&self, sender: Option>); 77 | // fn delete_word_forward(&self, sender: Option>); 78 | } 79 | 80 | objc_trait! { 81 | pub unsafe objc trait IsNSResponder: IsNSObject { 82 | type Base = NSResponder; 83 | trait Sub = SubNSResponder; 84 | 85 | fn accepts_first_responder(&self) -> bool 86 | => [self, acceptsFirstResponder] -> rt::BOOL; 87 | fn become_first_responder(&self) -> bool 88 | => [self, becomeFirstResponder] -> rt::BOOL; 89 | fn resign_first_responder(&self) -> bool 90 | => [self, resignFirstResponder] -> rt::BOOL; 91 | fn validate_proposed_first_responder_for_event( 92 | &self, 93 | responder: ShareId, 94 | event: Option> 95 | ) 96 | -> bool 97 | => [self, validateProposedFirstResponder:(responder: *mut AnyObject) 98 | forEvent:(event: *mut AnyObject)] 99 | -> rt::BOOL; 100 | 101 | unsafe fn next_responder(&self) -> Option> 102 | => [self, nextResponder] -> *mut AnyObject; 103 | unsafe fn set_next_responder( 104 | &self, 105 | next_responder: Option> 106 | ) 107 | => [self, setNextResponder:(responder:*mut AnyObject)]; 108 | 109 | fn mouse_down(&self, event: ShareId) 110 | => [self, mouseDown:(event: *mut AnyObject)]; 111 | fn mouse_dragged(&self, event: ShareId) 112 | => [self, mouseDragged:(event: *mut AnyObject)]; 113 | fn mouse_up(&self, event: ShareId) 114 | => [self, mouseUp:(event: *mut AnyObject)]; 115 | fn mouse_moved(&self, event: ShareId) 116 | => [self, mouseMoved:(event: *mut AnyObject)]; 117 | fn mouse_entered(&self, event: ShareId) 118 | => [self, mouseEntered:(event: *mut AnyObject)]; 119 | fn mouse_exited(&self, event: ShareId) 120 | => [self, mouseExited:(event: *mut AnyObject)]; 121 | fn right_mouse_down(&self, event: ShareId) 122 | => [self, rightMouseDown:(event: *mut AnyObject)]; 123 | fn right_mouse_dragged(&self, event: ShareId) 124 | => [self, rightMouseDragged:(event: *mut AnyObject)]; 125 | fn right_mouse_up(&self, event: ShareId) 126 | => [self, rightMouseUp:(event: *mut AnyObject)]; 127 | fn other_mouse_down(&self, event: ShareId) 128 | => [self, otherMouseDown:(event: *mut AnyObject)]; 129 | fn other_mouse_dragged(&self, event: ShareId) 130 | => [self, otherMouseDragged:(event: *mut AnyObject)]; 131 | fn other_mouse_up(&self, event: ShareId) 132 | => [self, otherMouseUp:(event: *mut AnyObject)]; 133 | } 134 | } 135 | 136 | #[macro_export] 137 | macro_rules! NSResponder { 138 | ($args:tt) => { 139 | __objc_inheritance_for! { 140 | $crate::NSResponder => $crate::SubNSResponder: NSObject!; 141 | $args 142 | } 143 | }; 144 | } 145 | -------------------------------------------------------------------------------- /src/classes/ns_window.rs: -------------------------------------------------------------------------------- 1 | use objc; 2 | use objc::runtime as rt; 3 | use {Id, ShareId, Object, AnyObject, 4 | RawObjCObject, NSObject, NSResponder, IsNSResponder}; 5 | 6 | // NOTE: CGFloat can either be an f32 or an f64 7 | pub type CGFloat = f64; 8 | 9 | #[repr(C)] 10 | pub struct NSPoint { 11 | pub x: CGFloat, 12 | pub y: CGFloat 13 | } 14 | 15 | unsafe impl objc::Encode for NSPoint { 16 | fn encode() -> objc::Encoding { 17 | let x = CGFloat::encode(); 18 | let y = CGFloat::encode(); 19 | let encoding = format!("{{_NSPoint={}{}}}", x.as_str(), y.as_str()); 20 | unsafe { objc::Encoding::from_str(&encoding) } 21 | } 22 | } 23 | 24 | #[repr(C)] 25 | pub struct NSSize { 26 | pub width: CGFloat, 27 | pub height: CGFloat 28 | } 29 | 30 | unsafe impl objc::Encode for NSSize { 31 | fn encode() -> objc::Encoding { 32 | let width = CGFloat::encode(); 33 | let height = CGFloat::encode(); 34 | let encoding = format!("{{_NSSize={}{}}}", width.as_str(), height.as_str()); 35 | unsafe { objc::Encoding::from_str(&encoding) } 36 | } 37 | } 38 | 39 | #[repr(C)] 40 | pub struct NSRect { 41 | pub origin: NSPoint, 42 | pub size: NSSize 43 | } 44 | 45 | unsafe impl objc::Encode for NSRect { 46 | fn encode() -> objc::Encoding { 47 | let origin = NSPoint::encode(); 48 | let size = NSSize::encode(); 49 | let encoding = format!("{{_NSRect={}{}}}", origin.as_str(), size.as_str()); 50 | unsafe { objc::Encoding::from_str(&encoding) } 51 | } 52 | } 53 | 54 | pub fn ns_make_rect(x: f64, y: f64, width: f64, height: f64) -> NSRect { 55 | NSRect { 56 | origin: NSPoint { x: x, y: y }, 57 | size: NSSize { width: width, height: height } 58 | } 59 | } 60 | 61 | #[repr(usize)] 62 | pub enum NSBackingStoreType { 63 | Retained = 0, 64 | Nonretained = 1, 65 | Buffered = 2 66 | } 67 | 68 | #[repr(C)] 69 | pub struct NSWindow { 70 | super_: NSResponder 71 | } 72 | 73 | unsafe impl objc::Message for NSWindow { } 74 | 75 | unsafe impl RawObjCObject for NSWindow { } 76 | 77 | impl Object for NSWindow { 78 | type Super = NSResponder; 79 | 80 | fn super_ref(&self) -> &Self::Super { 81 | &self.super_ 82 | } 83 | 84 | fn super_mut(&mut self) -> &mut Self::Super { 85 | &mut self.super_ 86 | } 87 | } 88 | 89 | impl NSWindow { 90 | pub fn new(content_rect: NSRect, style_mask: usize, backing: NSBackingStoreType, defer: bool) -> Id { 91 | unsafe { 92 | let defer = match defer { 93 | true => rt::YES, 94 | false => rt::NO 95 | }; 96 | let ns_window = rt::Class::get("NSWindow").unwrap(); 97 | let self_: *mut AnyObject = msg_send![ns_window, alloc]; 98 | let self_: *mut AnyObject = msg_send![self_, initWithContentRect:content_rect styleMask:style_mask backing:backing as usize defer:defer]; 99 | let self_ = self_ as *mut NSWindow; 100 | Id::from_retained_ptr(self_) 101 | } 102 | } 103 | } 104 | 105 | pub trait IsNSWindow: IsNSResponder { 106 | unsafe fn delegate(&self) -> Option>; 107 | unsafe fn set_delegate(&self, delegate: Option>); 108 | fn title(&self) -> String; 109 | fn set_title(&self, title: &str); 110 | } 111 | 112 | objc_trait! { 113 | pub unsafe objc trait IsNSWindow: IsNSResponder { 114 | type Base = NSWindow; 115 | trait Sub = SubNSWindow; 116 | 117 | unsafe fn delegate(&self) -> Option> 118 | => [self, delegate] -> *mut AnyObject; 119 | unsafe fn set_delegate(&self, delegate: Option>) 120 | => [self, setDelegate:(delegate: *mut AnyObject)]; 121 | fn title(&self) -> String 122 | => [self, title] -> *mut AnyObject; 123 | fn set_title(&self, title: &str) 124 | => [self, setTitle:(title: *mut AnyObject)]; 125 | } 126 | } 127 | 128 | #[macro_export] 129 | macro_rules! NSWindow { 130 | ($args:tt) => { 131 | __objc_inheritance_for! { 132 | $crate::NSWindow => $crate::SubNSWindow: NSResponder!; 133 | $args 134 | } 135 | }; 136 | } 137 | -------------------------------------------------------------------------------- /src/classes/ns_window_controller.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use objc; 3 | use objc::runtime as rt; 4 | use {Id, ShareId, Object, AnyObject, RawObjCObject, 5 | NSObject, NSResponder, IsNSResponder, NSWindow}; 6 | 7 | #[repr(C)] 8 | pub struct NSWindowController { 9 | super_: NSResponder 10 | } 11 | 12 | impl Object for NSWindowController { 13 | type Super = NSResponder; 14 | 15 | fn super_ref(&self) -> &Self::Super { 16 | &self.super_ 17 | } 18 | 19 | fn super_mut(&mut self) -> &mut Self::Super { 20 | &mut self.super_ 21 | } 22 | } 23 | 24 | unsafe impl objc::Message for NSWindowController { } 25 | 26 | unsafe impl RawObjCObject for NSWindowController { } 27 | 28 | impl NSWindowController { 29 | pub fn new(window: Option>) -> Id { 30 | let window_ptr: *const NSWindow = match window { 31 | Some(window) => &*window, 32 | None => ptr::null() 33 | }; 34 | let window_ptr = window_ptr as *const AnyObject; 35 | 36 | let ns_window_controller = rt::Class::get("NSWindowController").unwrap(); 37 | let id: *mut NSWindowController = unsafe { 38 | msg_send![ns_window_controller, alloc] 39 | }; 40 | let id: *mut NSWindowController = unsafe { 41 | msg_send![id, initWithWindow:window_ptr] 42 | }; 43 | 44 | unsafe { Id::from_retained_ptr(id) } 45 | } 46 | 47 | pub fn new_with_nib_name(_name: &str) -> Id { 48 | unimplemented!(); 49 | } 50 | 51 | pub fn new_with_nib_name_owner(_name: &str, _owner: ShareId) -> Id { 52 | unimplemented!(); 53 | } 54 | 55 | pub fn new_with_nib_path_owner(_path: &str, _owner: ShareId) -> Id { 56 | unimplemented!(); 57 | } 58 | } 59 | 60 | pub trait IsNSWindowController: IsNSResponder { 61 | // fn new(window: Option>) -> Id 62 | // where Self: Sized; 63 | // fn new_with_nib_name(name: &str) -> Id 64 | // where Self: Sized; 65 | // fn new_with_nib_name_owner(name: &str, owner: ShareId) -> Id 66 | // where Self: Sized; 67 | // fn new_with_nib_path_owner(path: &str, owner: ShareId) -> Id 68 | // where Self: Sized; 69 | 70 | fn load_window(&self); 71 | fn show_window(&self, sender: Option>); 72 | fn is_window_loaded(&self) -> bool; 73 | fn window(&self) -> Option>; 74 | fn set_window(&self, window: Option>); 75 | fn window_did_load(&self); 76 | fn window_will_load(&self); 77 | 78 | // unsafe fn document(&self) -> Option>; 79 | // fn set_document_edited(&self, flag: bool); 80 | // 81 | // fn close(&self); 82 | // fn should_close_document(&self) -> bool; 83 | // fn set_should_close_document(&self, should_close_document: bool); 84 | // 85 | // unsafe fn owner(&self) -> WeakId; 86 | // fn storyboard(&self) -> Option>; 87 | // fn window_nib_name(&self) -> Option; 88 | // fn window_nib_path(&self) -> Option; 89 | // 90 | // fn should_cascade_windows(&self) -> bool; 91 | // fn set_should_cascade_windows(&self, should_cascade_windows: bool); 92 | // fn window_frame_autosave_name(&self) -> Option; 93 | // fn set_window_frame_autosave_name(&self, window_frame_autosave_name: Option); 94 | // fn synchronize_window_title_with_document_name(&self); 95 | // fn window_title_for_document_display_name(display_name: String) -> String; 96 | // fn content_view_controller(&self) -> Option>; 97 | // fn set_content_view_controller(&self, content_view_controller: Option>); 98 | // fn dismiss_controller(sender: Option>); 99 | } 100 | 101 | objc_trait! { 102 | pub unsafe objc trait IsNSWindowController: IsNSResponder { 103 | type Base = NSWindowController; 104 | trait Sub = SubNSWindowController; 105 | 106 | fn load_window(&self) => [self, loadWindow]; 107 | fn show_window(&self, sender: Option>) 108 | => [self, showWindow:(sender: *mut AnyObject)]; 109 | fn is_window_loaded(&self) -> bool 110 | => [self, isWindowLoaded] -> rt::BOOL; 111 | fn window(&self) -> Option> 112 | => [self, window] -> *mut AnyObject; 113 | fn set_window(&self, window: Option>) 114 | => [self, setWindow:(window: *mut AnyObject)]; 115 | fn window_did_load(&self) => [self, windowDidLoad]; 116 | fn window_will_load(&self) => [self, windowWillLoad]; 117 | } 118 | } 119 | 120 | #[macro_export] 121 | macro_rules! NSWindowController { 122 | ($args:tt) => { 123 | __objc_inheritance_for! { 124 | $crate::NSWindowController => 125 | $crate::SubNSWindowController: NSResponder!; 126 | $args 127 | } 128 | }; 129 | } 130 | -------------------------------------------------------------------------------- /src/conversions.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::mem; 3 | use std::os::raw::{c_void, c_char}; 4 | use std::ffi::{CStr, CString}; 5 | use objc::runtime as rt; 6 | use {AnyObject, ShareId, Object, OptionSel}; 7 | 8 | pub unsafe fn objc_to_rust(x: T) -> U 9 | where T: ObjCInto 10 | { 11 | x.objc_into() 12 | } 13 | 14 | pub unsafe fn objc_id_to_rust(id: *mut AnyObject) -> U 15 | where *mut AnyObject: ObjCInto 16 | { 17 | objc_to_rust(id) 18 | } 19 | 20 | pub unsafe fn objc_bool_to_rust(x: rt::BOOL) -> bool { 21 | objc_to_rust(x) 22 | } 23 | 24 | pub fn rust_to_objc(x: T) -> U 25 | where T: IntoObjC 26 | { 27 | x.into_objc() 28 | } 29 | 30 | pub fn rust_to_objc_id(x: T) -> *mut AnyObject 31 | where T: IntoObjC<*mut AnyObject> 32 | { 33 | rust_to_objc(x) 34 | } 35 | 36 | pub fn rust_to_objc_bool(x: bool) -> rt::BOOL { 37 | rust_to_objc(x) 38 | } 39 | 40 | 41 | 42 | pub trait ObjCInto { 43 | unsafe fn objc_into(self) -> Out; 44 | } 45 | 46 | impl ObjCInto for T { 47 | unsafe fn objc_into(self) -> T { self } 48 | } 49 | 50 | impl<'a, T> ObjCInto<&'a T> for *const T { 51 | unsafe fn objc_into(self) -> &'a T { 52 | &*self 53 | } 54 | } 55 | 56 | impl<'a, T> ObjCInto> for *const T { 57 | unsafe fn objc_into(self) -> Option<&'a T> { 58 | self.as_ref() 59 | } 60 | } 61 | 62 | impl<'a, T> ObjCInto<&'a T> for *mut T { 63 | unsafe fn objc_into(self) -> &'a T { 64 | &*self 65 | } 66 | } 67 | 68 | impl<'a, T> ObjCInto> for *mut T { 69 | unsafe fn objc_into(self) -> Option<&'a T> { 70 | self.as_ref() 71 | } 72 | } 73 | 74 | impl<'a, T> ObjCInto<&'a mut T> for *mut T { 75 | unsafe fn objc_into(self) -> &'a mut T { 76 | &mut *self 77 | } 78 | } 79 | 80 | impl<'a, T> ObjCInto> for *mut T { 81 | unsafe fn objc_into(self) -> Option<&'a mut T> { 82 | self.as_mut() 83 | } 84 | } 85 | 86 | impl ObjCInto> for *mut AnyObject 87 | where T: FromAnyObject 88 | { 89 | unsafe fn objc_into(self) -> ShareId { 90 | T::from_any(self) 91 | } 92 | } 93 | 94 | impl ObjCInto>> for *mut AnyObject 95 | where T: FromAnyObject 96 | { 97 | unsafe fn objc_into(self) -> Option> { 98 | if self.is_null() { 99 | None 100 | } 101 | else { 102 | Some(T::from_any(self)) 103 | } 104 | } 105 | } 106 | 107 | impl ObjCInto for *mut AnyObject { 108 | unsafe fn objc_into(self) -> String { 109 | let c_str: *const c_void = msg_send![self, UTF8String]; 110 | let c_str = CStr::from_ptr(c_str as *const c_char); 111 | c_str.to_str().unwrap().into() 112 | } 113 | } 114 | 115 | impl ObjCInto for rt::BOOL { 116 | unsafe fn objc_into(self) -> bool { 117 | self == rt::YES 118 | } 119 | } 120 | 121 | impl ObjCInto> for OptionSel { 122 | unsafe fn objc_into(self) -> Option { 123 | if self == OptionSel::none() { 124 | None 125 | } 126 | else { 127 | Some(self.sel) 128 | } 129 | } 130 | } 131 | 132 | impl ObjCInto for T 133 | where T: Into 134 | { 135 | default unsafe fn objc_into(self) -> U { self.into() } 136 | } 137 | 138 | 139 | 140 | pub trait IntoObjC { 141 | fn into_objc(self) -> Out; 142 | } 143 | 144 | impl IntoObjC for T { 145 | fn into_objc(self) -> T { self } 146 | } 147 | 148 | impl<'a, T> IntoObjC<*const T> for Option<&'a T> { 149 | fn into_objc(self) -> *const T { 150 | match self { 151 | Some(x) => x, 152 | None => ptr::null() 153 | } 154 | } 155 | } 156 | 157 | impl<'a, T> IntoObjC<*const T> for Option<&'a mut T> { 158 | fn into_objc(self) -> *const T { 159 | match self { 160 | Some(x) => x, 161 | None => ptr::null() 162 | } 163 | } 164 | } 165 | 166 | impl<'a, T> IntoObjC<*mut T> for Option<&'a mut T> { 167 | fn into_objc(self) -> *mut T { 168 | match self { 169 | Some(x) => x, 170 | None => ptr::null_mut() 171 | } 172 | } 173 | } 174 | 175 | impl<'a, T> IntoObjC<*mut AnyObject> for &'a T 176 | where T: AsAnyObject 177 | { 178 | fn into_objc(self) -> *mut AnyObject { 179 | self.any_ref() as *const _ as *mut _ 180 | } 181 | } 182 | 183 | impl<'a, T> IntoObjC<*mut AnyObject> for Option<&'a T> 184 | where T: AsAnyObject 185 | { 186 | fn into_objc(self) -> *mut AnyObject { 187 | match self { 188 | Some(x) => x.any_ref() as *const _ as *mut _, 189 | None => ptr::null_mut() 190 | } 191 | } 192 | } 193 | 194 | impl<'a, T> IntoObjC<*mut AnyObject> for &'a mut T 195 | where T: AsAnyObject 196 | { 197 | fn into_objc(self) -> *mut AnyObject { 198 | self.any_mut() 199 | } 200 | } 201 | 202 | impl<'a, T> IntoObjC<*mut AnyObject> for Option<&'a mut T> 203 | where T: AsAnyObject 204 | { 205 | fn into_objc(self) -> *mut AnyObject { 206 | match self { 207 | Some(x) => x.any_mut(), 208 | None => ptr::null_mut() 209 | } 210 | } 211 | } 212 | 213 | impl IntoObjC<*mut AnyObject> for ShareId 214 | where T: AsAnyObject 215 | { 216 | fn into_objc(self) -> *mut AnyObject { 217 | // TODO: Do this cleanly 218 | let ptr = self.any_ref() as *const _ as *mut _; 219 | mem::forget(self); 220 | ptr 221 | } 222 | } 223 | 224 | impl IntoObjC<*mut AnyObject> for Option> 225 | where T: AsAnyObject 226 | { 227 | fn into_objc(self) -> *mut AnyObject { 228 | match self { 229 | Some(x) => { 230 | let ptr = x.any_ref() as *const _ as *mut _; 231 | mem::forget(x); 232 | ptr 233 | }, 234 | None => { 235 | ptr::null_mut() 236 | } 237 | } 238 | } 239 | } 240 | 241 | impl IntoObjC for Option { 242 | fn into_objc(self) -> OptionSel { 243 | match self { 244 | Some(sel) => OptionSel::from_sel(sel), 245 | None => OptionSel::none() 246 | } 247 | } 248 | } 249 | 250 | impl IntoObjC for bool { 251 | fn into_objc(self) -> rt::BOOL { 252 | match self { 253 | true => rt::YES, 254 | false => rt::NO 255 | } 256 | } 257 | } 258 | 259 | impl<'a> IntoObjC<*mut AnyObject> for &'a str { 260 | fn into_objc(self) -> *mut AnyObject { 261 | let c_string = CString::new(self).unwrap(); 262 | let c_string = c_string.as_ptr(); 263 | 264 | let ns_string = rt::Class::get("NSString").unwrap(); 265 | unsafe { msg_send![ns_string, stringWithUTF8String:c_string] } 266 | } 267 | } 268 | 269 | impl<'a, T> IntoObjC<*const T> for &'a T { 270 | fn into_objc(self) -> *const T { self } 271 | } 272 | 273 | impl IntoObjC for T 274 | where T: Into 275 | { 276 | default fn into_objc(self) -> U { self.into() } 277 | } 278 | 279 | 280 | 281 | pub trait AsAnyObject { 282 | fn any_ref(&self) -> &AnyObject; 283 | fn any_mut(&mut self) -> &mut AnyObject; 284 | } 285 | 286 | impl AsAnyObject for T 287 | where T: Object, T::Super: AsAnyObject 288 | { 289 | fn any_ref(&self) -> &AnyObject { 290 | self.super_ref().any_ref() 291 | } 292 | 293 | fn any_mut(&mut self) -> &mut AnyObject { 294 | self.super_mut().any_mut() 295 | } 296 | } 297 | 298 | pub trait SubAnyObject { 299 | type AnySuper: AsAnyObject; 300 | 301 | fn any_super_ref(&self) -> &Self::AnySuper; 302 | fn any_super_mut(&mut self) -> &mut Self::AnySuper; 303 | } 304 | 305 | impl SubAnyObject for T 306 | where T: Object, T::Super: AsAnyObject 307 | { 308 | type AnySuper = T::Super; 309 | 310 | fn any_super_ref(&self) -> &Self::AnySuper { 311 | self.super_ref() 312 | } 313 | 314 | fn any_super_mut(&mut self) -> &mut Self::AnySuper { 315 | self.super_mut() 316 | } 317 | } 318 | 319 | impl AsAnyObject for T 320 | where T: SubAnyObject 321 | { 322 | default fn any_ref(&self) -> &AnyObject { 323 | self.any_super_ref().any_ref() 324 | } 325 | 326 | default fn any_mut(&mut self) -> &mut AnyObject { 327 | self.any_super_mut().any_mut() 328 | } 329 | } 330 | 331 | pub trait FromAnyObject: Sized { 332 | unsafe fn from_any(any: *mut AnyObject) -> ShareId; 333 | } 334 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(specialization)] 2 | 3 | #[macro_use] extern crate objc; 4 | extern crate objc_id; 5 | extern crate objc_exception; 6 | 7 | use std::mem; 8 | use objc::runtime as rt; 9 | 10 | pub use objc_id::{Id, WeakId, ShareId, Ownership, Owned, Shared}; 11 | 12 | #[macro_use] mod macros; 13 | mod conversions; 14 | mod classes; 15 | mod protocols; 16 | mod wrappers; 17 | 18 | pub use conversions::{IntoObjC, rust_to_objc, rust_to_objc_id, rust_to_objc_bool, 19 | ObjCInto, objc_to_rust, objc_id_to_rust, objc_bool_to_rust, 20 | AsAnyObject, SubAnyObject, FromAnyObject}; 21 | pub use classes::*; 22 | pub use protocols::*; 23 | pub use wrappers::*; 24 | 25 | // Forces rustc to link to Obj-C frameworks 26 | #[link(name = "Foundation", kind = "framework")] 27 | extern { } 28 | 29 | #[link(name = "AppKit", kind = "framework")] 30 | extern { } 31 | 32 | 33 | 34 | // NOTE: The type of `OptionSel` is used for Objective-C `SEL`s that 35 | // can be null. The safety of its implementation *depends* on 36 | // the `objc::runtime::Sel` type containing a single pointer! 37 | #[derive(PartialEq)] 38 | pub struct OptionSel { 39 | sel: rt::Sel 40 | } 41 | 42 | impl OptionSel { 43 | fn from_sel(sel: rt::Sel) -> OptionSel { 44 | OptionSel { 45 | sel: sel 46 | } 47 | } 48 | 49 | fn none() -> OptionSel { 50 | OptionSel { 51 | sel: unsafe { mem::zeroed() } 52 | } 53 | } 54 | } 55 | 56 | unsafe impl objc::Encode for OptionSel { 57 | fn encode() -> objc::Encoding { 58 | rt::Sel::encode() 59 | } 60 | } 61 | 62 | 63 | 64 | pub type AnyObject = rt::Object; 65 | 66 | pub trait Object { 67 | type Super; 68 | 69 | fn super_ref(&self) -> &Self::Super; 70 | fn super_mut(&mut self) -> &mut Self::Super; 71 | } 72 | 73 | pub unsafe trait RawObjCObject: objc::Message { } 74 | 75 | impl FromAnyObject for T 76 | where T: RawObjCObject + Sized 77 | { 78 | unsafe fn from_any(any: *mut AnyObject) -> ShareId { 79 | let self_ = any as *mut Self; 80 | ShareId::from_ptr(self_) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! objc_trait { 3 | ( 4 | pub unsafe objc trait $name:ident $(: $parent:ident),* { $($body:tt)+ } 5 | ) => { 6 | __objc_trait! { 7 | @name: $name; 8 | @parent: [$($parent),*]; 9 | @vis: [pub]; 10 | body: { $($body)* }; 11 | } 12 | }; 13 | ( 14 | unsafe objc trait $name:ident $(: $parent:ident),* { $($body:tt)+ } 15 | ) => { 16 | __objc_trait! { 17 | @name: $name; 18 | @parent: [$($parent),*]; 19 | @vis: []; 20 | body: { $($body)* }; 21 | } 22 | }; 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! __objc_ty { 27 | () => { () }; 28 | ($t:ty) => { $t }; 29 | } 30 | 31 | #[macro_export] 32 | macro_rules! __objc_msg_args { 33 | () => { 34 | () 35 | }; 36 | ($fn_arg_ty:ty, $msg_arg_ty:ty, $fn_arg:expr) => { 37 | ($crate::rust_to_objc::<$fn_arg_ty, $msg_arg_ty>($fn_arg),) 38 | }; 39 | ($($fn_arg_ty:ty, $msg_arg_ty:ty, $fn_arg:expr),+) => { 40 | ($($crate::rust_to_objc::<$fn_arg_ty, $msg_arg_ty>($fn_arg)),*) 41 | }; 42 | } 43 | 44 | #[macro_export] 45 | macro_rules! __objc_default_impl { 46 | ($method_name:ident, [], [$($args:expr),*]) => { 47 | () 48 | }; 49 | ($method_name:ident, [$default:ty], [$($args:expr),*]) => { 50 | { 51 | let default_impl: $default = ::std::default::Default::default(); 52 | default_impl.$method_name($($args),*) 53 | } 54 | } 55 | } 56 | 57 | #[macro_export] 58 | macro_rules! __objc_expand_method { 59 | { 60 | @pass: $pass:ident! { @_ $($pass_args:tt)* }; 61 | @body: { 62 | $(#[$meta:ident])* 63 | fn $fn_name:ident(&self) $(-> $fn_ret:ty),* 64 | => [self, $msg_sel:ident] $(-> $msg_ret:ty),*; 65 | }; 66 | } => { 67 | $pass! { 68 | @fn { 69 | @name: $fn_name; 70 | @args: []; 71 | @ret: [$($fn_ret),*]; 72 | @meta: [$(#[$meta])*]; 73 | @msg_args: []; 74 | @msg_ret: [$($msg_ret),*]; 75 | @msg_sel: sel!($msg_sel); 76 | @qualifiers: []; 77 | } 78 | $($pass_args)* 79 | } 80 | }; 81 | 82 | { 83 | @pass: $pass:ident! { @_ $($pass_args:tt)* }; 84 | @body: { 85 | $(#[$meta:ident])* 86 | unsafe fn $fn_name:ident(&self) $(-> $fn_ret:ty),* 87 | => [self, $msg_sel:ident] $(-> $msg_ret:ty),*; 88 | }; 89 | } => { 90 | $pass! { 91 | @fn { 92 | @name: $fn_name; 93 | @args: []; 94 | @ret: [$($fn_ret),*]; 95 | @meta: [$(#[$meta])*]; 96 | @msg_args: []; 97 | @msg_ret: [$($msg_ret),*]; 98 | @msg_sel: sel!($msg_sel); 99 | @qualifiers: [unsafe ]; 100 | } 101 | $($pass_args)* 102 | } 103 | }; 104 | 105 | { 106 | @pass: $pass:ident! { @_ $($pass_args:tt)* }; 107 | @body: { 108 | $(#[$meta:ident])* 109 | fn $fn_name:ident(&self $(, $fn_arg:ident: $fn_arg_ty:ty)+) $(-> $fn_ret:ty),* 110 | => [self, $($msg_sel:ident: ($msg_arg:ident: $msg_arg_ty:ty))+] $(-> $msg_ret:ty),*; 111 | }; 112 | } => { 113 | $pass! { 114 | @fn { 115 | @name: $fn_name; 116 | @args: [$($fn_arg: $fn_arg_ty),+]; 117 | @ret: [$($fn_ret),*]; 118 | @meta: [$(#[$meta])*]; 119 | @msg_args: [$($msg_arg: $msg_arg_ty),+]; 120 | @msg_ret: [$($msg_ret),*]; 121 | @msg_sel: sel!($($msg_sel:)+); 122 | @qualifiers: []; 123 | } 124 | $($pass_args)* 125 | } 126 | }; 127 | 128 | { 129 | @pass: $pass:ident! { @_ $($pass_args:tt)* }; 130 | @body: { 131 | $(#[$meta:ident])* 132 | unsafe fn $fn_name:ident(&self $(, $fn_arg:ident: $fn_arg_ty:ty)+) $(-> $fn_ret:ty),* 133 | => [self, $($msg_sel:ident: ($msg_arg:ident: $msg_arg_ty:ty))+] $(-> $msg_ret:ty),*; 134 | }; 135 | } => { 136 | $pass! { 137 | @fn { 138 | @name: $fn_name; 139 | @args: [$($fn_arg: $fn_arg_ty),+]; 140 | @ret: [$($fn_ret),*]; 141 | @meta: [$(#[$meta])*]; 142 | @msg_args: [$($msg_arg: $msg_arg_ty),+]; 143 | @msg_ret: [$($msg_ret),*]; 144 | @msg_sel: sel!($($msg_sel:)+); 145 | @qualifiers: [unsafe]; 146 | } 147 | $($pass_args)* 148 | } 149 | }; 150 | } 151 | 152 | #[macro_export] 153 | macro_rules! __objc_trait_add_fn { 154 | { 155 | @fn { 156 | @name: $fn_name:ident; 157 | @args: [$($fn_args:tt)*]; 158 | @ret: [$($fn_ret:tt)*]; 159 | @meta: []; 160 | @msg_args: [$($fn_msg_args:tt)*]; 161 | @msg_ret: [$($fn_msg_ret:tt)*]; 162 | @msg_sel: $fn_msg_sel:expr; 163 | @qualifiers: [$($fn_qualifiers:tt)*]; 164 | }; 165 | @objc_trait: { 166 | @base: $base:ty; 167 | @name: $name:ident; 168 | @default: [$($default:ty),*]; 169 | @fns: { 170 | @methods: [$($methods:tt)*]; 171 | @optional: [$($optional:tt)*]; 172 | }; 173 | @parent: [$($parent:ident),*]; 174 | @sub: $sub:ident; 175 | @vis: [$($vis:ident),*]; 176 | body: { $($body:tt)* }; 177 | }; 178 | } => { 179 | __objc_trait! { 180 | @base: $base; 181 | @name: $name; 182 | @default: [$($default),*]; 183 | @fns: { 184 | @methods: [ 185 | $($methods)* 186 | @fn { 187 | @name: $fn_name; 188 | @args: [$($fn_args)*]; 189 | @ret: [$($fn_ret)*]; 190 | @meta: []; 191 | @msg_args: [$($fn_msg_args)*]; 192 | @msg_ret: [$($fn_msg_ret)*]; 193 | @msg_sel: $fn_msg_sel; 194 | @qualifiers: [$($fn_qualifiers)*]; 195 | }; 196 | ]; 197 | @optional: [$($optional)*]; 198 | }; 199 | @parent: [$($parent),*]; 200 | @sub: $sub; 201 | @vis: [$($vis),*]; 202 | body: { $($body)* }; 203 | } 204 | }; 205 | 206 | { 207 | @fn { 208 | @name: $fn_name:ident; 209 | @args: [$($fn_args:tt)*]; 210 | @ret: [$($fn_ret:tt)*]; 211 | @meta: [#[optional]]; 212 | @msg_args: [$($fn_msg_args:tt)*]; 213 | @msg_ret: [$($fn_msg_ret:tt)*]; 214 | @msg_sel: $fn_msg_sel:expr; 215 | @qualifiers: [$($fn_qualifiers:tt)*]; 216 | }; 217 | @objc_trait: { 218 | @base: $base:ty; 219 | @name: $name:ident; 220 | @default: [$($default:ty),*]; 221 | @fns: { 222 | @methods: [$($methods:tt)*]; 223 | @optional: [$($optional:tt)*]; 224 | }; 225 | @parent: [$($parent:ident),*]; 226 | @sub: $sub:ident; 227 | @vis: [$($vis:ident),*]; 228 | body: { $($body:tt)* }; 229 | }; 230 | } => { 231 | __objc_trait! { 232 | @base: $base; 233 | @name: $name; 234 | @default: [$($default),*]; 235 | @fns: { 236 | @methods: [$($methods)*]; 237 | @optional: [ 238 | $($optional)* 239 | @fn { 240 | @name: $fn_name; 241 | @args: [$($fn_args)*]; 242 | @ret: [$($fn_ret)*]; 243 | @optional_default: [$($default),*]; 244 | @meta: [#[optional]]; 245 | @msg_args: [$($fn_msg_args)*]; 246 | @msg_ret: [$($fn_msg_ret)*]; 247 | @msg_sel: $fn_msg_sel; 248 | @qualifiers: [$($fn_qualifiers)*]; 249 | }; 250 | ]; 251 | }; 252 | @parent: [$($parent),*]; 253 | @sub: $sub; 254 | @vis: [$($vis),*]; 255 | body: { $($body)* }; 256 | } 257 | }; 258 | } 259 | 260 | #[macro_export] 261 | macro_rules! __objc_trait { 262 | { 263 | @name: $name:ident; 264 | @parent: [$($parent:ident),*]; 265 | @vis: [$($vis:ident),*]; 266 | body: { 267 | type Base = $base:ty; 268 | trait Sub = $sub:ident; 269 | 270 | $($body_rest:tt)* 271 | }; 272 | } => { 273 | __objc_trait! { 274 | @base: $base; 275 | @name: $name; 276 | @default: []; 277 | @fns: { 278 | @methods: []; 279 | @optional: []; 280 | }; 281 | @parent: [$($parent),*]; 282 | @sub: $sub; 283 | @vis: [$($vis),*]; 284 | body: { 285 | $($body_rest)* 286 | }; 287 | } 288 | }; 289 | 290 | 291 | 292 | { 293 | @base: $base:ty; 294 | @name: $name:ident; 295 | @default: []; 296 | @fns: { $($fns:tt)* }; 297 | @parent: [$($parent:ident),*]; 298 | @sub: $sub:ident; 299 | @vis: [$($vis:ident),*]; 300 | body: { 301 | type DefaultImpl = $default:ty; 302 | $($body_rest:tt)* 303 | }; 304 | } => { 305 | __objc_trait! { 306 | @base: $base; 307 | @name: $name; 308 | @default: [$default]; 309 | @fns: { $($fns)* }; 310 | @parent: [$($parent),*]; 311 | @sub: $sub; 312 | @vis: [$($vis),*]; 313 | body: { 314 | $($body_rest)* 315 | }; 316 | } 317 | }; 318 | 319 | { 320 | @base: $base:ty; 321 | @name: $name:ident; 322 | @default: [$($default:ty),*]; 323 | @fns: { $($fns:tt)* }; 324 | @parent: [$($parent:ident),*]; 325 | @sub: $sub:ident; 326 | @vis: [$($vis:ident),*]; 327 | body: { 328 | $(#[$meta:ident])* 329 | fn $fn_name:ident($($args:tt)*) $(-> $fn_ret:ty),* 330 | => [$($msg:tt)*] $(-> $msg_ret:ty),*; 331 | $($body_rest:tt)* 332 | }; 333 | } => { 334 | __objc_expand_method! { 335 | @pass: __objc_trait_add_fn! { 336 | @_; 337 | @objc_trait: { 338 | @base: $base; 339 | @name: $name; 340 | @default: [$($default),*]; 341 | @fns: { $($fns)* }; 342 | @parent: [$($parent),*]; 343 | @sub: $sub; 344 | @vis: [$($vis),*]; 345 | body: { $($body_rest)* }; 346 | }; 347 | }; 348 | @body: { 349 | $(#[$meta])* 350 | fn $fn_name($($args)*) $(-> $fn_ret),* 351 | => [$($msg)*] $(-> $msg_ret),*; 352 | }; 353 | } 354 | }; 355 | 356 | { 357 | @base: $base:ty; 358 | @name: $name:ident; 359 | @default: [$($default:ty),*]; 360 | @fns: { $($fns:tt)* }; 361 | @parent: [$($parent:ident),*]; 362 | @sub: $sub:ident; 363 | @vis: [$($vis:ident),*]; 364 | body: { 365 | $(#[$meta:ident])* 366 | unsafe fn $fn_name:ident($($args:tt)*) $(-> $fn_ret:ty),* 367 | => [$($msg:tt)*] $(-> $msg_ret:ty),*; 368 | $($body_rest:tt)* 369 | }; 370 | } => { 371 | __objc_expand_method! { 372 | @pass: __objc_trait_add_fn! { 373 | @_; 374 | @objc_trait: { 375 | @base: $base; 376 | @name: $name; 377 | @default: [$($default),*]; 378 | @fns: { $($fns)* }; 379 | @parent: [$($parent),*]; 380 | @sub: $sub; 381 | @vis: [$($vis),*]; 382 | body: { $($body_rest)* }; 383 | }; 384 | }; 385 | @body: { 386 | $(#[$meta])* 387 | unsafe fn $fn_name($($args)*) $(-> $fn_ret),* 388 | => [$($msg)*] $(-> $msg_ret),*; 389 | }; 390 | } 391 | }; 392 | 393 | 394 | 395 | { 396 | @base: $base:ty; 397 | @name: $name:ident; 398 | @default: [$($default:ty),*]; 399 | @fns: { $($fns:tt)* }; 400 | @parent: [$($parent:ident),*]; 401 | @sub: $sub:ident; 402 | @vis: [$($vis:ident),*]; 403 | body: { }; 404 | } => { 405 | __objc_trait! { 406 | @base: $base; 407 | @name: $name; 408 | @default: [$($default),*]; 409 | @fns: { $($fns)* }; 410 | @parent: [$($parent),*]; 411 | @sub: $sub; 412 | @vis: [$($vis),*]; 413 | } 414 | }; 415 | 416 | { 417 | @base: $base:ty; 418 | @name: $name:ident; 419 | @default: [$($default:ty),*]; 420 | @fns: { 421 | @methods: [ 422 | $( 423 | @fn { 424 | @name: $method_name:ident; 425 | @args: [$($method_arg:ident: $method_arg_ty:ty),*]; 426 | @ret: [$($method_ret:ty),*]; 427 | @meta: []; 428 | @msg_args: [$($method_msg_arg:ident: $method_msg_arg_ty:ty),*]; 429 | @msg_ret: [$($method_msg_ret:ty),*]; 430 | @msg_sel: $method_msg_sel:expr; 431 | @qualifiers: [$($method_qualifiers:ident),*]; 432 | }; 433 | )* 434 | ]; 435 | @optional: [ 436 | $( 437 | @fn { 438 | @name: $optional_name:ident; 439 | @args: [$($optional_arg:ident: $optional_arg_ty:ty),*]; 440 | @ret: [$($optional_ret:ty),*]; 441 | @optional_default: [$($optional_default:ty),*]; 442 | @meta: [#[optional]]; 443 | @msg_args: [$($optional_msg_arg:ident: $optional_msg_arg_ty:ty),*]; 444 | @msg_ret: [$($optional_msg_ret:ty),*]; 445 | @msg_sel: $optional_msg_sel:expr; 446 | @qualifiers: [$($optional_qualifiers:ident),*]; 447 | }; 448 | )* 449 | ]; 450 | }; 451 | @parent: [$($parent:ident),*]; 452 | @sub: $sub:ident; 453 | @vis: [$($vis:ident),*]; 454 | } => { 455 | #[allow(unused_unsafe)] 456 | impl $name for $base { 457 | $( 458 | $($method_qualifiers)* fn $method_name(&self, $($method_arg: $method_arg_ty),*) $(-> $method_ret),* { 459 | unsafe { 460 | let msg_args = __objc_msg_args!($($method_arg_ty, $method_msg_arg_ty, $method_arg),*); 461 | let result: __objc_ty!($($method_msg_ret),*) = ::objc::Message::send_message(self, $method_msg_sel, msg_args).unwrap(); 462 | $crate::objc_to_rust::<__objc_ty!($($method_msg_ret),*), __objc_ty!($($method_ret),*)>(result) 463 | } 464 | } 465 | )* 466 | 467 | $( 468 | $($optional_qualifiers)* fn $optional_name(&self, $($optional_arg: $optional_arg_ty),*) $(-> $optional_ret),* { 469 | unsafe { 470 | let sel = $optional_msg_sel; 471 | if $crate::objc_bool_to_rust(msg_send![self, respondsToSelector:sel]) { 472 | let msg_args = __objc_msg_args!($($optional_arg_ty, $optional_msg_arg_ty, $optional_arg),*); 473 | let result: __objc_ty!($($optional_msg_ret),*) = ::objc::Message::send_message(self, $optional_msg_sel, msg_args).unwrap(); 474 | $crate::objc_to_rust::<__objc_ty!($($optional_msg_ret),*), __objc_ty!($($optional_ret),*)>(result) 475 | } 476 | else { 477 | __objc_default_impl!($optional_name, [$($optional_default),*], [$($optional_arg),*]) 478 | } 479 | } 480 | } 481 | )* 482 | } 483 | 484 | impl $name for T 485 | where T: $sub + $($parent),* 486 | { 487 | $( 488 | default $($method_qualifiers)* fn $method_name(&self, $($method_arg: $method_arg_ty),*) $(-> $method_ret),* { 489 | ::class_super_ref(self).$method_name($($method_arg),*) 490 | } 491 | )* 492 | 493 | $( 494 | default $($optional_qualifiers)* fn $optional_name(&self, $($optional_arg: $optional_arg_ty),*) $(-> $optional_ret),* { 495 | ::class_super_ref(self).$optional_name($($optional_arg),*) 496 | } 497 | )* 498 | } 499 | 500 | $($vis),* trait $sub { 501 | type ClassSuper: $name; 502 | 503 | fn class_super_ref(&self) -> &Self::ClassSuper; 504 | fn class_super_mut(&mut self) -> &mut Self::ClassSuper; 505 | } 506 | 507 | impl $sub for T 508 | where T: $crate::Object, ::Super: $name 509 | { 510 | type ClassSuper = ::Super; 511 | 512 | fn class_super_ref(&self) -> &Self::ClassSuper { 513 | self.super_ref() 514 | } 515 | 516 | fn class_super_mut(&mut self) -> &mut Self::ClassSuper { 517 | self.super_mut() 518 | } 519 | } 520 | } 521 | } 522 | 523 | // HACK: This macro is a workaround for the issue that "subclassing" 524 | // with the `Object` trait in external crates doesn't compile (see: 525 | // https://github.com/rust-lang/rust/issues/31844#issuecomment-221131352) 526 | #[macro_export] 527 | macro_rules! objc_inherit { 528 | { 529 | impl Object for $class:ty { 530 | type Super = $super_:ident; 531 | 532 | let super_ = self.$super_ref:ident; 533 | } 534 | } => { 535 | $super_!({ 536 | @class: $class; 537 | @super_ref: $super_ref; 538 | }); 539 | } 540 | } 541 | 542 | #[macro_export] 543 | macro_rules! __objc_inheritance_for { 544 | { 545 | $base:ty => $sub_trait:ty $(: $parent:ident!)*; 546 | { 547 | @class: $class:ty; 548 | @super_ref: $super_ref:ident; 549 | } 550 | } => { 551 | __objc_inheritance_for! { 552 | $base => $sub_trait $(: $parent!)*; 553 | { 554 | @class: $class; 555 | @super_ref: $super_ref; 556 | @super_: $base; 557 | } 558 | } 559 | }; 560 | 561 | { 562 | $base:ty => $sub_trait:ty $(: $parent:ident!)*; 563 | { 564 | @class: $class:ty; 565 | @super_ref: $super_ref:ident; 566 | @super_: $super_:ty; 567 | } 568 | } => { 569 | impl $sub_trait for $class { 570 | type ClassSuper = $super_; 571 | 572 | fn class_super_ref(&self) -> &Self::ClassSuper { 573 | &self.$super_ref 574 | } 575 | 576 | fn class_super_mut(&mut self) -> &mut Self::ClassSuper { 577 | &mut self.$super_ref 578 | } 579 | } 580 | 581 | $( 582 | $parent!({ 583 | @class: $class; 584 | @super_ref: $super_ref; 585 | @super_: $super_; 586 | }); 587 | )* 588 | }; 589 | } 590 | 591 | // TODO: Write a proper test suite! This just tests the macro expansion 592 | // itself (and not inputs/outputs) 593 | #[cfg(test)] 594 | mod tests { 595 | use objc; 596 | use objc::runtime as rt; 597 | use {Object, AnyObject, NSObject, IsNSObject}; 598 | 599 | struct MyObject { 600 | super_: NSObject 601 | } 602 | 603 | unsafe impl objc::Message for MyObject { } 604 | 605 | impl Object for MyObject { 606 | type Super = NSObject; 607 | 608 | fn super_ref(&self) -> &Self::Super { &self.super_ } 609 | 610 | fn super_mut(&mut self) -> &mut Self::Super { &mut self.super_ } 611 | } 612 | 613 | trait IsMyObject: IsNSObject { 614 | fn foo(&self); 615 | 616 | fn bar(&self, _a: bool) { } 617 | 618 | fn baz(&self, a: bool, b: &AnyObject) -> bool; 619 | 620 | unsafe fn qux(&self, _a: bool, _b: &AnyObject, _c: &AnyObject) 621 | -> *mut AnyObject 622 | { 623 | unimplemented!() 624 | } 625 | } 626 | 627 | #[derive(Default)] 628 | struct DefaultImplMyObject; 629 | 630 | impl DefaultImplMyObject { 631 | fn bar(&self, _a: bool) { } 632 | 633 | fn qux(&self, _a: bool, _b: &AnyObject, _c: &AnyObject) -> *mut AnyObject { 634 | unimplemented!(); 635 | } 636 | } 637 | 638 | objc_trait! { 639 | unsafe objc trait IsMyObject: IsNSObject { 640 | type Base = MyObject; 641 | trait Sub = SubMyObject; 642 | type DefaultImpl = DefaultImplMyObject; 643 | 644 | fn foo(&self) => [self, foo]; 645 | 646 | #[optional] 647 | fn bar(&self, a: bool) => [self, barWithA:(a: rt::BOOL)]; 648 | 649 | fn baz(&self, a: bool, b: &AnyObject) -> bool 650 | => [self, bazWithA:(a: rt::BOOL) B:(b: *const AnyObject)] 651 | -> rt::BOOL; 652 | 653 | #[optional] 654 | unsafe fn qux(&self, a: bool, b: &AnyObject, c: &AnyObject) 655 | -> *mut AnyObject 656 | => [self, quxWithA:(a: rt::BOOL) 657 | B:(b: *const AnyObject) 658 | C:(c: *const AnyObject)] 659 | -> *mut AnyObject; 660 | } 661 | } 662 | 663 | #[test] 664 | fn test_objc_macro_makes_object_safe_traits() { 665 | let _my_object: MyObject; 666 | let _is_my_object: Box; 667 | let _sub_my_object: Box>; 668 | } 669 | } 670 | -------------------------------------------------------------------------------- /src/protocols/mod.rs: -------------------------------------------------------------------------------- 1 | mod ns_application_delegate; 2 | 3 | pub use self::ns_application_delegate::*; 4 | -------------------------------------------------------------------------------- /src/protocols/ns_application_delegate.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use objc; 3 | use objc::runtime as rt; 4 | use {Object, AnyObject, ShareId, IsNSObject, NSObject, NSApplication, 5 | NSNotification, IntoObjC, ObjCInto, RawObjCObject}; 6 | 7 | #[repr(usize)] 8 | pub enum NSApplicationActivationPolicy { 9 | Regular = 0, 10 | Accessory = 1, 11 | Prohibited = 2 12 | } 13 | 14 | impl IntoObjC for NSApplicationActivationPolicy { 15 | fn into_objc(self) -> usize { 16 | self as usize 17 | } 18 | } 19 | 20 | impl ObjCInto for usize { 21 | unsafe fn objc_into(self) -> NSApplicationActivationPolicy { 22 | // TODO: Do this safely! 23 | mem::transmute(self) 24 | } 25 | } 26 | 27 | #[repr(usize)] 28 | pub enum NSApplicationTerminateReply { 29 | TerminateCancel = 0, 30 | TerminateNow = 1, 31 | TerminateLater = 2 32 | } 33 | 34 | impl IntoObjC for NSApplicationTerminateReply { 35 | fn into_objc(self) -> usize { 36 | self as usize 37 | } 38 | } 39 | 40 | impl ObjCInto for usize { 41 | unsafe fn objc_into(self) -> NSApplicationTerminateReply { 42 | // TODO: Do this safely! 43 | mem::transmute(self) 44 | } 45 | } 46 | 47 | 48 | 49 | #[repr(C)] 50 | pub struct NSApplicationDelegate { 51 | super_: NSObject 52 | } 53 | 54 | unsafe impl objc::Message for NSApplicationDelegate { } 55 | 56 | unsafe impl RawObjCObject for NSApplicationDelegate { } 57 | 58 | impl Object for NSApplicationDelegate { 59 | type Super = NSObject; 60 | 61 | fn super_ref(&self) -> &Self::Super { 62 | &self.super_ 63 | } 64 | 65 | fn super_mut(&mut self) -> &mut Self::Super { 66 | &mut self.super_ 67 | } 68 | } 69 | 70 | pub trait IsNSApplicationDelegate: IsNSObject { 71 | fn application_will_finish_launching(&self, notification: ShareId) { 72 | NSApplicationDelegateDefaultImpl.application_will_finish_launching(notification) 73 | } 74 | 75 | fn application_did_finish_launching(&self, notification: ShareId) { 76 | NSApplicationDelegateDefaultImpl.application_did_finish_launching(notification) 77 | } 78 | 79 | fn application_should_terminate(&self, sender: ShareId) 80 | -> NSApplicationTerminateReply 81 | { 82 | NSApplicationDelegateDefaultImpl.application_should_terminate(sender) 83 | } 84 | 85 | fn application_should_terminate_after_last_window_closed(&self, sender: ShareId) 86 | -> bool 87 | { 88 | NSApplicationDelegateDefaultImpl.application_should_terminate_after_last_window_closed(sender) 89 | } 90 | 91 | fn application_will_terminate(&self, notification: ShareId) { 92 | NSApplicationDelegateDefaultImpl.application_will_terminate(notification) 93 | } 94 | } 95 | 96 | #[derive(Default)] 97 | struct NSApplicationDelegateDefaultImpl; 98 | 99 | impl NSApplicationDelegateDefaultImpl { 100 | fn application_will_finish_launching(&self, _notification: ShareId) { 101 | 102 | } 103 | 104 | fn application_did_finish_launching(&self, _notification: ShareId) { 105 | 106 | } 107 | 108 | fn application_should_terminate(&self, _sender: ShareId) 109 | -> NSApplicationTerminateReply 110 | { 111 | NSApplicationTerminateReply::TerminateNow 112 | } 113 | 114 | fn application_should_terminate_after_last_window_closed(&self, _sender: ShareId) 115 | -> bool 116 | { 117 | false 118 | } 119 | 120 | fn application_will_terminate(&self, _notification: ShareId) { 121 | 122 | } 123 | } 124 | 125 | objc_trait! { 126 | pub unsafe objc trait IsNSApplicationDelegate: IsNSObject { 127 | type Base = NSApplicationDelegate; 128 | trait Sub = SubNSApplicationDelegate; 129 | type DefaultImpl = NSApplicationDelegateDefaultImpl; 130 | 131 | #[optional] 132 | fn application_will_finish_launching(&self, notification: ShareId) 133 | => [self, applicationWillFinishLaunching:(notification: *mut AnyObject)]; 134 | 135 | #[optional] 136 | fn application_did_finish_launching(&self, notification: ShareId) 137 | => [self, applicationDidFinishLaunching:(notification: *mut AnyObject)]; 138 | 139 | #[optional] 140 | fn application_should_terminate(&self, sender: ShareId) 141 | -> NSApplicationTerminateReply 142 | => [self, applicationShouldTerminate:(sender: *mut AnyObject)] 143 | -> usize; 144 | 145 | #[optional] 146 | fn application_should_terminate_after_last_window_closed(&self, sender: ShareId) 147 | -> bool 148 | => [self, applicationShouldTerminateAfterLastWindowClosed:(sender: *mut AnyObject)] 149 | -> rt::BOOL; 150 | 151 | #[optional] 152 | fn application_will_terminate(&self, notification: ShareId) 153 | => [self, applicationWillTerminate:(notification: *mut AnyObject)]; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/wrappers/mod.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | use std::marker::PhantomData; 4 | use std::os::raw::c_void; 5 | use std::sync::Once; 6 | use objc; 7 | use objc::runtime as rt; 8 | use objc::declare as decl; 9 | use {AnyObject, Id, ShareId, IsNSObject}; 10 | 11 | mod srb_object; 12 | mod srb_application_delegate; 13 | mod srb_window_controller; 14 | 15 | pub use self::srb_object::*; 16 | pub use self::srb_application_delegate::*; 17 | pub use self::srb_window_controller::*; 18 | 19 | pub unsafe fn take_boxed(self_: &mut W) -> Box 20 | where W: SRBWrapper + objc::Message 21 | { 22 | let self_: &mut AnyObject = mem::transmute(self_); 23 | let boxed_ptr_ref = self_.get_mut_ivar::<*mut c_void>("_boxed"); 24 | let boxed_ptr = mem::replace(boxed_ptr_ref, ptr::null_mut()); 25 | let boxed_ptr = boxed_ptr as *mut W::Boxed; 26 | Box::from_raw(boxed_ptr) 27 | } 28 | 29 | pub unsafe fn get_boxed_ref(self_: &W) -> &W::Boxed 30 | where W: SRBWrapper + objc::Message 31 | { 32 | let self_: &AnyObject = mem::transmute(self_); 33 | let boxed_ptr = *self_.get_ivar::<*mut c_void>("_boxed") as *const W::Boxed; 34 | &*boxed_ptr 35 | } 36 | 37 | pub unsafe fn get_boxed_mut(self_: &mut W) -> &mut W::Boxed 38 | where W: SRBWrapper + objc::Message 39 | { 40 | let self_: &AnyObject = mem::transmute(self_); 41 | let boxed_ptr = *self_.get_ivar::<*mut c_void>("_boxed") as *mut W::Boxed; 42 | &mut *boxed_ptr 43 | } 44 | 45 | pub unsafe fn new_wrapper_with_boxed(boxed: Box) -> *mut W 46 | where W: SRBWrapper + objc::Message 47 | { 48 | let class = W::class(); 49 | let self_: *mut AnyObject = msg_send![class, alloc]; 50 | let self_: *mut AnyObject = msg_send![self_, init]; 51 | 52 | if let Some(self_) = self_.as_mut() { 53 | let boxed_ptr = Box::into_raw(boxed); 54 | let boxed_ptr = boxed_ptr as *mut c_void; 55 | self_.set_ivar::<*mut c_void>("_boxed", boxed_ptr); 56 | } 57 | 58 | self_ as *mut W 59 | } 60 | 61 | struct Deallocator { 62 | _phantom: PhantomData<*mut W> 63 | } 64 | 65 | impl Deallocator { 66 | extern "C" fn dealloc(self_: &mut AnyObject, _sel: rt::Sel) { 67 | unsafe { 68 | let self_: &mut W = mem::transmute(self_); 69 | let boxed = take_boxed(self_); 70 | drop(boxed); 71 | 72 | let superclass: &rt::Class = msg_send![self_, superclass]; 73 | msg_send![super(self_, superclass), dealloc]; 74 | } 75 | } 76 | 77 | unsafe fn dealloc_ptr() -> extern "C" fn(&mut AnyObject, rt::Sel) { 78 | Self::dealloc as _ 79 | } 80 | } 81 | 82 | pub trait Duck { 83 | fn duck(self) -> T; 84 | } 85 | 86 | impl Duck> for Id { 87 | fn duck(self) -> Id { 88 | self 89 | } 90 | } 91 | 92 | impl Duck> for T 93 | where T: Duck>, U: objc::Message 94 | { 95 | fn duck(self) -> ShareId { 96 | let id: Id = self.duck(); 97 | id.share() 98 | } 99 | } 100 | 101 | pub trait SRBWrapper: IsNSObject + objc::Message + Sized { 102 | type Boxed; 103 | 104 | fn class_initializer() -> &'static Once; 105 | 106 | fn superclass() -> &'static rt::Class; 107 | 108 | fn class_name() -> &'static str; 109 | 110 | fn create_class(class_decl: &mut decl::ClassDecl); 111 | 112 | fn class() -> &'static rt::Class { 113 | let name = Self::class_name(); 114 | 115 | Self::class_initializer().call_once(|| { 116 | let dealloc = unsafe { Deallocator::::dealloc_ptr() }; 117 | 118 | let superclass = Self::superclass(); 119 | let mut decl = decl::ClassDecl::new(name, superclass).unwrap(); 120 | decl.add_ivar::<*mut c_void>("_boxed"); 121 | unsafe { decl.add_method(sel!(dealloc), dealloc) }; 122 | 123 | Self::create_class(&mut decl); 124 | 125 | decl.register(); 126 | }); 127 | 128 | rt::Class::get(name).unwrap() 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/wrappers/srb_application_delegate.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::sync::{Once, ONCE_INIT}; 3 | use objc; 4 | use objc::runtime as rt; 5 | use objc::declare as decl; 6 | use {Duck, Object, AnyObject, RawObjCObject, Id, NSObject, 7 | NSApplicationDelegate, IsNSApplicationDelegate, 8 | NSApplication, NSNotification, SRBWrapper}; 9 | use super::{get_boxed_ref, new_wrapper_with_boxed}; 10 | 11 | #[repr(C)] 12 | pub struct SRBApplicationDelegate { 13 | super_: NSObject 14 | } 15 | 16 | unsafe impl objc::Message for SRBApplicationDelegate { } 17 | 18 | unsafe impl RawObjCObject for SRBApplicationDelegate { } 19 | 20 | impl Object for SRBApplicationDelegate { 21 | type Super = NSObject; 22 | 23 | fn super_ref(&self) -> &Self::Super { 24 | &self.super_ 25 | } 26 | 27 | fn super_mut(&mut self) -> &mut Self::Super { 28 | &mut self.super_ 29 | } 30 | } 31 | 32 | impl SRBWrapper for SRBApplicationDelegate { 33 | type Boxed = Box; 34 | 35 | fn class_initializer() -> &'static Once { 36 | static CLASS_INIT: Once = ONCE_INIT; 37 | &CLASS_INIT 38 | } 39 | 40 | fn superclass() -> &'static rt::Class { 41 | rt::Class::get("NSObject").unwrap() 42 | } 43 | 44 | fn class_name() -> &'static str { 45 | "SRBApplicationDelegate" 46 | } 47 | 48 | fn create_class(class_decl: &mut decl::ClassDecl) { 49 | extern "C" fn application_will_finish_launching(self_: &AnyObject, _sel: rt::Sel, notification: *mut AnyObject) { 50 | unsafe { 51 | let self_: &SRBApplicationDelegate = mem::transmute(self_); 52 | let boxed = get_boxed_ref(self_); 53 | 54 | let notification = notification as *mut NSNotification; 55 | let notification = Id::from_ptr(notification); 56 | boxed.application_will_finish_launching(notification); 57 | } 58 | } 59 | 60 | extern "C" fn application_did_finish_launching(self_: &AnyObject, _sel: rt::Sel, notification: *mut AnyObject) { 61 | unsafe { 62 | let self_: &SRBApplicationDelegate = mem::transmute(self_); 63 | let boxed = get_boxed_ref(self_); 64 | 65 | let notification = notification as *mut NSNotification; 66 | let notification = Id::from_ptr(notification); 67 | boxed.application_did_finish_launching(notification); 68 | } 69 | } 70 | 71 | extern "C" fn application_should_terminate(self_: &AnyObject, _sel: rt::Sel, application: *mut AnyObject) -> usize { 72 | unsafe { 73 | let self_: &SRBApplicationDelegate = mem::transmute(self_); 74 | let boxed = get_boxed_ref(self_); 75 | 76 | let application = application as *mut NSApplication; 77 | let application = Id::from_ptr(application); 78 | boxed.application_should_terminate(application) as usize 79 | } 80 | } 81 | 82 | extern "C" fn application_should_terminate_after_last_window_closed(self_: &AnyObject, _sel: rt::Sel, application: *mut AnyObject) -> rt::BOOL { 83 | unsafe { 84 | let self_: &SRBApplicationDelegate = mem::transmute(self_); 85 | let boxed = get_boxed_ref(self_); 86 | 87 | let application = application as *mut NSApplication; 88 | let application = Id::from_ptr(application); 89 | match boxed.application_should_terminate_after_last_window_closed(application) { 90 | true => rt::YES, 91 | false => rt::NO 92 | } 93 | } 94 | } 95 | 96 | extern "C" fn application_will_terminate(self_: &AnyObject, _sel: rt::Sel, notification: *mut AnyObject) { 97 | unsafe { 98 | let self_: &SRBApplicationDelegate = mem::transmute(self_); 99 | let boxed = get_boxed_ref(self_); 100 | 101 | let notification = notification as *mut NSNotification; 102 | let notification = Id::from_ptr(notification); 103 | boxed.application_will_terminate(notification); 104 | } 105 | } 106 | 107 | let application_will_finish_launching = application_will_finish_launching as extern fn(&AnyObject, rt::Sel, *mut AnyObject); 108 | let application_did_finish_launching = application_did_finish_launching as extern fn(&AnyObject, rt::Sel, *mut AnyObject); 109 | let applicaiton_should_terminate = application_should_terminate as extern fn(&AnyObject, rt::Sel, *mut AnyObject) -> usize; 110 | let application_should_terminate_after_last_window_closed = application_should_terminate_after_last_window_closed as extern fn(&AnyObject, rt::Sel, *mut AnyObject) -> rt::BOOL; 111 | let application_will_terminate = application_will_terminate as extern fn(&AnyObject, rt::Sel, *mut AnyObject); 112 | 113 | unsafe { 114 | class_decl.add_method(sel!(applicationWillFinishLaunching:), application_will_finish_launching); 115 | class_decl.add_method(sel!(applicationDidFinishLaunching:), application_did_finish_launching); 116 | class_decl.add_method(sel!(applicationShouldTerminate:), applicaiton_should_terminate); 117 | class_decl.add_method(sel!(applicationShouldTerminateAfterLastWindowClosed:), application_should_terminate_after_last_window_closed); 118 | class_decl.add_method(sel!(applicationWillTerminate:), application_will_terminate); 119 | } 120 | // let NSApplicationDelegate = rt::Protocol::get("NSApplicationDelegate").unwrap(); 121 | // class_decl.add_protocol(NSApplicationDelegate); 122 | } 123 | } 124 | 125 | impl SRBApplicationDelegate { 126 | pub fn new(delegate: Box) -> Id { 127 | unsafe { 128 | let self_: *mut SRBApplicationDelegate = new_wrapper_with_boxed(Box::new(delegate)); 129 | let self_ = self_ as *mut NSApplicationDelegate; 130 | Id::from_retained_ptr(self_) 131 | } 132 | } 133 | } 134 | 135 | 136 | 137 | impl Duck> for T 138 | where T: IsNSApplicationDelegate + 'static 139 | { 140 | default fn duck(self) -> Id { 141 | SRBApplicationDelegate::new(Box::new(self)) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/wrappers/srb_object.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::sync::{Once, ONCE_INIT}; 3 | use objc; 4 | use objc::runtime as rt; 5 | use objc::declare as decl; 6 | use {Duck, Object, AnyObject, RawObjCObject, Id, 7 | NSObject, IsNSObject, SRBWrapper}; 8 | use super::{get_boxed_ref, new_wrapper_with_boxed}; 9 | 10 | #[repr(C)] 11 | pub struct SRBObject { 12 | super_: NSObject 13 | } 14 | 15 | unsafe impl objc::Message for SRBObject { } 16 | 17 | unsafe impl RawObjCObject for SRBObject { } 18 | 19 | impl Object for SRBObject { 20 | type Super = NSObject; 21 | 22 | fn super_ref(&self) -> &Self::Super { 23 | &self.super_ 24 | } 25 | 26 | fn super_mut(&mut self) -> &mut Self::Super { 27 | &mut self.super_ 28 | } 29 | } 30 | 31 | impl SRBWrapper for SRBObject { 32 | type Boxed = Box; 33 | 34 | fn class_initializer() -> &'static Once { 35 | static CLASS_INIT: Once = ONCE_INIT; 36 | &CLASS_INIT 37 | } 38 | 39 | fn class_name() -> &'static str { 40 | "SRBObject" 41 | } 42 | 43 | fn superclass() -> &'static rt::Class { 44 | rt::Class::get("NSObject").unwrap() 45 | } 46 | 47 | fn create_class(class_decl: &mut decl::ClassDecl) { 48 | extern "C" fn hash(self_: &AnyObject, _sel: rt::Sel) -> usize { 49 | unsafe { 50 | let self_: &SRBObject = mem::transmute(self_); 51 | let boxed = get_boxed_ref(self_); 52 | boxed.hash() 53 | } 54 | } 55 | 56 | let hash = hash as extern "C" fn(&AnyObject, rt::Sel) -> usize; 57 | 58 | unsafe { class_decl.add_method(sel!(hash), hash); } 59 | } 60 | } 61 | 62 | impl SRBObject { 63 | pub fn new(object: Box) -> Id { 64 | unsafe { 65 | let self_: *mut SRBObject = new_wrapper_with_boxed(Box::new(object)); 66 | let self_ = self_ as *mut NSObject; 67 | Id::from_retained_ptr(self_) 68 | } 69 | } 70 | } 71 | 72 | 73 | 74 | impl Duck> for T 75 | where T: IsNSObject + 'static 76 | { 77 | default fn duck(self) -> Id { 78 | SRBObject::new(Box::new(self)) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/wrappers/srb_window_controller.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | use std::sync::{Once, ONCE_INIT}; 4 | use objc; 5 | use objc::runtime as rt; 6 | use objc::declare as decl; 7 | use {Duck, Object, AnyObject, RawObjCObject, Id, ShareId, NSObject, NSWindow, 8 | IsNSWindowController, NSWindowController, SRBWrapper}; 9 | use super::{get_boxed_ref, new_wrapper_with_boxed}; 10 | 11 | #[repr(C)] 12 | pub struct SRBWindowController { 13 | super_: NSWindowController 14 | } 15 | 16 | unsafe impl objc::Message for SRBWindowController { } 17 | 18 | unsafe impl RawObjCObject for SRBWindowController { } 19 | 20 | impl Object for SRBWindowController { 21 | type Super = NSWindowController; 22 | 23 | fn super_ref(&self) -> &Self::Super { 24 | &self.super_ 25 | } 26 | 27 | fn super_mut(&mut self) -> &mut Self::Super { 28 | &mut self.super_ 29 | } 30 | } 31 | 32 | impl SRBWrapper for SRBWindowController { 33 | type Boxed = Box; 34 | 35 | fn class_initializer() -> &'static Once { 36 | static CLASS_INIT: Once = ONCE_INIT; 37 | &CLASS_INIT 38 | } 39 | 40 | fn superclass() -> &'static rt::Class { 41 | rt::Class::get("NSWindowController").unwrap() 42 | } 43 | 44 | fn class_name() -> &'static str { 45 | "SRBWindowController" 46 | } 47 | 48 | fn create_class(class_decl: &mut decl::ClassDecl) { 49 | extern "C" fn load_window(self_: &AnyObject, _sel: rt::Sel) { 50 | unsafe { 51 | let self_: &SRBWindowController = mem::transmute(self_); 52 | let boxed = get_boxed_ref(self_); 53 | boxed.load_window(); 54 | } 55 | } 56 | 57 | extern "C" fn show_window(self_: &AnyObject, _sel: rt::Sel, sender: *mut AnyObject) { 58 | unsafe { 59 | let self_: &SRBWindowController = mem::transmute(self_); 60 | let boxed = get_boxed_ref(self_); 61 | let sender = sender as *mut NSObject; 62 | let sender = if sender.is_null() { 63 | None 64 | } 65 | else { 66 | Some(ShareId::from_ptr(sender)) 67 | }; 68 | boxed.show_window(sender); 69 | } 70 | } 71 | 72 | extern "C" fn is_window_loaded(self_: &AnyObject, _sel: rt::Sel) -> rt::BOOL { 73 | unsafe { 74 | let self_: &SRBWindowController = mem::transmute(self_); 75 | let boxed = get_boxed_ref(self_); 76 | match boxed.is_window_loaded() { 77 | true => rt::YES, 78 | false => rt::NO 79 | } 80 | } 81 | } 82 | 83 | extern "C" fn window(self_: &AnyObject, _sel: rt::Sel) -> *const AnyObject { 84 | unsafe { 85 | let self_: &SRBWindowController = mem::transmute(self_); 86 | let boxed = get_boxed_ref(self_); 87 | match boxed.window() { 88 | Some(window) => { 89 | let window: *const NSWindow = &*window; 90 | window as *const AnyObject 91 | } 92 | None => { 93 | ptr::null_mut() 94 | } 95 | } 96 | } 97 | } 98 | 99 | extern "C" fn set_window(self_: &AnyObject, _sel: rt::Sel, window: *mut AnyObject) { 100 | unsafe { 101 | let self_: &SRBWindowController = mem::transmute(self_); 102 | let boxed = get_boxed_ref(self_); 103 | 104 | let window = window as *mut NSWindow; 105 | let window = if window.is_null() { 106 | None 107 | } 108 | else { 109 | Some(ShareId::from_ptr(window)) 110 | }; 111 | boxed.set_window(window); 112 | } 113 | } 114 | 115 | extern "C" fn window_did_load(self_: &AnyObject, _sel: rt::Sel) { 116 | unsafe { 117 | let self_: &SRBWindowController = mem::transmute(self_); 118 | let boxed = get_boxed_ref(self_); 119 | boxed.window_did_load(); 120 | } 121 | } 122 | 123 | extern "C" fn window_will_load(self_: &AnyObject, _sel: rt::Sel) { 124 | unsafe { 125 | let self_: &SRBWindowController = mem::transmute(self_); 126 | let boxed = get_boxed_ref(self_); 127 | boxed.window_will_load(); 128 | } 129 | } 130 | 131 | let load_window = load_window as extern fn(&AnyObject, rt::Sel); 132 | let show_window = show_window as extern fn(&AnyObject, rt::Sel, *mut AnyObject); 133 | let is_window_loaded = is_window_loaded as extern fn(&AnyObject, rt::Sel) -> rt::BOOL; 134 | let window = window as extern fn(&AnyObject, rt::Sel) -> *const AnyObject; 135 | let set_window = set_window as extern fn(&AnyObject, rt::Sel, *mut AnyObject); 136 | let window_did_load = window_did_load as extern fn(&AnyObject, rt::Sel); 137 | let window_will_load = window_will_load as extern fn(&AnyObject, rt::Sel); 138 | 139 | unsafe { 140 | class_decl.add_method(sel!(loadWindow), load_window); 141 | class_decl.add_method(sel!(showWindow:), show_window); 142 | class_decl.add_method(sel!(isWindowLoaded), is_window_loaded); 143 | class_decl.add_method(sel!(window), window); 144 | class_decl.add_method(sel!(setWindow:), set_window); 145 | class_decl.add_method(sel!(windowDidLoad), window_did_load); 146 | class_decl.add_method(sel!(windowWillLoad), window_will_load); 147 | } 148 | } 149 | } 150 | 151 | impl IsNSWindowController for SRBWindowController { 152 | fn load_window(&self) { 153 | unsafe { 154 | msg_send![self, loadWindow]; 155 | } 156 | } 157 | 158 | fn show_window(&self, sender: Option>) { 159 | let sender_ptr: *const NSObject = match sender { 160 | Some(sender) => &*sender, 161 | None => ptr::null() 162 | }; 163 | let sender_ptr = sender_ptr as *const AnyObject; 164 | unsafe { 165 | msg_send![self, showWindow:sender_ptr]; 166 | } 167 | } 168 | 169 | fn is_window_loaded(&self) -> bool { 170 | unimplemented!(); 171 | } 172 | 173 | fn window(&self) -> Option> { 174 | unsafe { 175 | let window_ptr: *mut AnyObject = msg_send![self, window]; 176 | let window_ptr = window_ptr as *mut NSWindow; 177 | if window_ptr.is_null() { 178 | None 179 | } 180 | else { 181 | Some(ShareId::from_retained_ptr(window_ptr)) 182 | } 183 | } 184 | } 185 | 186 | fn set_window(&self, window: Option>) { 187 | let window_ptr = match window { 188 | Some(window) => &*window, 189 | None => ptr::null() 190 | }; 191 | let window_ptr = window_ptr as *const NSObject; 192 | unsafe { msg_send![self, setWindow:window_ptr]; } 193 | } 194 | 195 | fn window_did_load(&self) { 196 | unsafe { msg_send![self, windowDidLoad]; } 197 | } 198 | 199 | fn window_will_load(&self) { 200 | unsafe { msg_send![self, windowWillLoad]; } 201 | } 202 | } 203 | 204 | impl SRBWindowController { 205 | pub fn new(controller: Box) -> Id { 206 | unsafe { 207 | let self_: *mut SRBWindowController = new_wrapper_with_boxed(Box::new(controller)); 208 | let self_ = self_ as *mut NSWindowController; 209 | Id::from_retained_ptr(self_) 210 | } 211 | } 212 | } 213 | 214 | 215 | 216 | impl Duck> for T 217 | where T: IsNSWindowController + 'static 218 | { 219 | fn duck(self) -> Id { 220 | SRBWindowController::new(Box::new(self)) 221 | } 222 | } 223 | --------------------------------------------------------------------------------