See more at the announce forum.
Last week, I decided to get an Apple Macintosh computer for my main office. This isn't my first time trying to use these, I previously had one of their all-in-one desktop units as well as a portable laptop model, both manufactured in 2011, but neither actually got me to do what I intended to do with them - work on the simpledisplay -version=OSXCocoa port. (The desktop unit was too big to keep set up here and I moved it to make space for more important things and it currently acts as a glorified dvd player upstairs, and the laptop model started beeping out failed RAM messages and it seems impossible to repair. Besides, both are old enough that it is hard to even run dmd on them anymore.)
This new unit, one of the Mac Mini models manufactured in 2014, I think is going to work for me. It is small enough and connections by ordinary cables to ordinary peripheral devices that I can stack it with my Windows box and put it on a KVM switch, meaning it doesn't take up any more space than I'm already using, practically speaking, so it remains convenient to access when I want it. Additionally, it seems to do a good job on power management, doing a kind of light sleep mode that keeps it accessible pretty quickly without eating a lot of electricity; since setting it up, it has consumed an average of about 2.3 W, ranging from about 2 W when sleeping to about 15 W when actively used, and around 5 W when I'm just sshed into it without activating its display. I can live with that.
Anyway, I got it set up Thursday night and did some extern(Objective-C) experimentation over the weekend, recreating parts of simpledisplay's support code. Last night, I got it drawing again, this time with more understanding of the code.
I did a public release of simpledisplay in April 2011 and shortly thereafter, a user on the D forum contributed an implementation of most the interface for Mac. This implementation used a lot of extern(C) objc_msgSend calls to define the necessary classes and call the associated methods to make things work. This implemented most the public interface at the time: window creation, basic drawing, character input, and the pulse timer, but I didn't understand the code and had no computer suitable for testing it, so I didn't keep up with it.
As Windows and Linux simpledisplay got enhanced keyboard and mouse support, the Mac version barely kept compiling. About four years later, I got that other Mac desktop computer, but I still didn't know the code. Instead, I switched the default build of simpledisplay on mac to use X11; XQuartz worked well on that computer and this solved some problems. At least something worked and it had most the features working thanks to the wonderful X Windowing System.
However, another couple years passed, and Apple decided to deprecate the XQuartz system. You could still download and install it separately (and indeed, I'm pretty sure you still can, though I haven't tried it on this new system yet), but you probably can't expect most users to do this. But, I also was gifted the Macbook Air, and used it to revive the old Cocoa code and at least got it compiling again - mostly by stubbing out unimplemented functions to throw a NotYetImplementedException instead of static asserting. At last you could do something with it. (It was this experience that I had in mind while writing this blog entry: http://dpldocs.info/this-week-in-d/Blog.Posted_2023_02_20.html#static-assert-patterns-arguably-harmful-for-porting about static assert considered harmful)
I also fixed some bugs in dmd's extern(Objective-C) code; by this point, I at least had some understanding of how the objc runtime worked and could compare output with Apple's compilers, but it was still kinda painful to use this... and, before long, that freebie computer died on me too anyway. That desktop still worked, but refused to update to run some of the new dmd compilers in addition to still being inconvenient to use, so eh, this got put on the wait list again.
I even tried doing a Mac VM, and had some success with it, but the VM was so hard to use - mouse capture and cursor sync was broken and the cpu consumption would randomly explode (I suspect that is some anti-piracy measure), so another general failure. I just deleted this VM last night, freeing up 50 GB on my main computer (which is getting full, so that's significant)!
The lesson is: to make this work, it needs to be easy for me. And I decided I'd spend a little bit of money this time (about $180 total), and thanks to Steven Schveighoffer for his advice in picking out appropriate hardware to achieve my varied goals.
simpledisplay's OSXCocoa implementation does work for basic animations, but the key and mouse events are bare minimum and most other things don't even exist. But, I can now read the existing code, and have started porting it to use dmd's extern(Objective-C) interface. This code is much nicer than it was even just a couple years ago - the bindings are convenient to write and easy to use.
There is one major downside though: it only works on dmd. On ldc, even the basics won't even compile; the feature is almost entirely unimplemented. On the other hand, pragma(linkerDirective) works on ldc to link in the objective-c framework libraries from the operating system, but this is unimplemented in dmd. Similarly, Apple has a new CPU in their recent models, as well as their iphone and ipad units (new and old), which has support on ldc but not on dmd. So, there is no D compiler that has full support for the breadth of Apple systems. dmd is as close as you get on the Intel cpu models, ldc is as close as you get on the others, but you sacrifice something either way.
I don't know the current status of any of these features on gdc; I've never tried gdc on mac.
Extending pragma(linkerDirective) to work for frameworks on dmd should be reasonably easy - the code has been written twice for a different named pragma, in a couple hundred lines, but never merged. Extending extern(Objective-C) to work on ldc is more work; the code in dmd is about 1,600 lines longer than what is present in ldc right now (see the file objc_glue.d in dmd's source). Much of that ought to be reusable in ldc - it does some ast glue to turn nice D function calls into uglier extern(C) objc runtime calls and I suspect this can, but some of it won't be, since it needs to handle linker sections and such. I don't think this is massively difficult, but to be honest, I don't know ldc's code well enough to be confident in that thought. To my knowledge, nobody has actually attempted to do this. (Indeed, it seems like only a few people have even worked on it on dmd, including me.)
I think both these projects would be worthwhile, and they're both on my own todo list.... and have been for years, still near the bottom of it. I might do it myself eventually, but it would be much better if someone else did.
BTW: the main reason why I don't want to do it is that setting up a ldc dev environment is a pain to me. But if that was already set up I might be able to do it.
In any case, it left me with a conundrum: do I use the much nicer extern(Objective-C) feature and have better code but less compiler compatibility, or do I keep up some of the extern(C) runtime calls for more compat but uglier code? Or a third option: like I did with arsd.jni, it would be possible to write some fairly nice code but then metaprogram the necessary glue in a library module that can work across compilers.
The extern(C) runtime calls are ugly and make writing the code much harder than it has to be. I'm not a Mac programmer, so I'd be both learning the necessary APIs and converting them to ugliness at the same time, and I frankly just don't want to.
Writing a metaprogram thing would work but... it would also be work, and I again just don't want to. It'd increase compile times, take a bunch of annoying effort, and it just seems silly to redo the very same things dmd already knows how to do. Even if I had that kind of time (and I really don't; simpledisplay on Mac would be nice to have but isn't terribly important so it must be done separately from my day job obligations and I only have so much spare time to spend on code beyond that at all anymore), it'd feel like a waste of that time.
If I'm going to spend a couple days implementing extern(Objective-C) in a library... I'd probably prefer to instead just implement it in ldc.
So my mind was made up: I'm going to abandon simpledisplay's existing extern(C) based code and rewrite it with extern(Objective-C), even if this means losing ldc support. Hopefully, ldc will pick up the feature too and then all will work again and joy can be found for D's mac/iphone/ipad users everywhere. But right now, I'm punting on it and just making my code work for me.
I already have my port drawing some things again and the framework is looking decent. One weird thing is the menu doesn't seem to work; the internet suggests this is a change in recent versions of the operating system, but I suspect there's something else going on. I don't know though, the items are in the menu, but they're all disabled and I don't know why.
Drawing, however, is going very well, and event handling should not take long now. Binding the apis has been quick and easy and I think I'll catch up on much of the functionality pretty soon this time.
I might even be able to get things like OperatingSystemFont working. Probably not things like drag and drop yet, but if OperatingSystemFont, the main event delegates, and ScreenPainter all work pretty completely, I should be able to get minigui's custom_widgets working too.
Obviously minigui would ideally use the native controls, but even just the custom widgets on the native drawing would be nice to have and works as a nice test case for some of the more obscure bits (indeed, many of the more obscure bits of simpledisplay were added to support minigui).
If all this works, I may even see about adding the simpleaudio and joystick modules to mac as well, which would let the arsd game things work (in theory at least).
Another interesting thing I'll have to consider is the arsd.core event loops. I still haven't integrated simpledisplay itself into that, and doing it on the Mac might require some changes. arsd.core already has a different concept for UI thread event loops and other worker thread loops, since this helps on Windows, and I suspect that will be necessary on Mac too. Previously, I assumed the FreeBSD kqueue implementation would transition straight over, but I'm not so sure now.
We'll see. This is why arsd.core's api is explicitly unstable - I'm reasonably happy with it, but the act of finishing these implementations and integrations are still likely to change things here.
Finally, simpledisplay will soon be getting some touch and drawing tablet events. I know how they work on Windows and Linux (and it is fairly different there), but adding the Mac to the list may change the plan somewhat. We'll see though, I don't know if i'll actually be able to test touch on this thing. I do have some old ipads but not sure if i'll be able to run any D code on them (well aside from the web, of course, but that doesn't help test the cocoa touch api implementation)! We'll see.
Let me show you the current cocoa test code. It has some objc bindings, a couple helpers, some class definitions, and a main.
I did encounter one compiler bug along the way: I can't have a reference to a ObjC object in a struct due to a missing init symbol. Should be an easy fix (the init should just be null!), but I just worked around it by wrapping the member in a union to work around it.
Otherwise, I thought it was fairly pleasant.
1 module main; 2 3 extern (Objective-C) { 4 import core.attribute : selector; 5 6 alias NSUInteger = size_t; 7 alias NSInteger = ptrdiff_t; 8 alias const(char)* SEL; 9 alias void* id; 10 11 extern class NSObject { 12 static NSObject alloc() @selector("alloc"); 13 void retain() @selector("retain"); 14 void release() @selector("release"); 15 } 16 17 extern class NSString : NSObject { 18 override static NSString alloc() @selector("alloc"); 19 20 NSString initWithUTF8String(const scope char* str) @selector("initWithUTF8String:"); 21 22 NSString initWithBytes( 23 const(ubyte)* bytes, 24 NSUInteger length, 25 NSStringEncoding encoding 26 ) @selector("initWithBytes:length:encoding:"); 27 } 28 29 extern class NSResponder : NSObject { 30 NSMenu menu() @selector("menu"); 31 void menu(NSMenu menu) @selector("setMenu:"); 32 } 33 34 extern class NSApplication : NSResponder { 35 static NSApplication shared_() @selector("sharedApplication"); 36 37 NSApplicationDelegate delegate_() @selector("delegate"); 38 void delegate_(NSApplicationDelegate) @selector("setDelegate:"); 39 40 bool setActivationPolicy(NSApplicationActivationPolicy activationPolicy) @selector("setActivationPolicy:"); 41 42 void activateIgnoringOtherApps(bool flag) @selector("activateIgnoringOtherApps:"); 43 44 @property NSMenu mainMenu() @selector("mainMenu"); 45 @property NSMenu mainMenu(NSMenu) @selector("setMainMenu:"); 46 47 void run() @selector("run"); 48 49 void terminate(void*) @selector("terminate:"); 50 } 51 52 extern interface NSApplicationDelegate { 53 void applicationWillFinishLaunching(NSNotification notification) @selector("applicationWillFinishLaunching:"); 54 void applicationDidFinishLaunching(NSNotification notification) @selector("applicationDidFinishLaunching:"); 55 bool applicationShouldTerminateAfterLastWindowClosed(NSNotification notification) @selector("applicationShouldTerminateAfterLastWindowClosed:"); 56 } 57 58 extern class NSNotification : NSObject { 59 60 } 61 62 enum NSApplicationActivationPolicy : ptrdiff_t { 63 /* The application is an ordinary app that appears in the Dock and may have a user interface. This is the default for bundled apps, unless overridden in the Info.plist. */ 64 regular, 65 66 /* The application does not appear in the Dock and does not have a menu bar, but it may be activated programmatically or by clicking on one of its windows. This corresponds to LSUIElement=1 in the Info.plist. */ 67 accessory, 68 69 /* The application does not appear in the Dock and may not create windows or be activated. This corresponds to LSBackgroundOnly=1 in the Info.plist. This is also the default for unbundled executables that do not have Info.plists. */ 70 prohibited 71 } 72 73 extern class NSGraphicsContext : NSObject { 74 static NSGraphicsContext currentContext() @selector("currentContext"); 75 NSGraphicsContext graphicsPort() @selector("graphicsPort"); 76 } 77 78 extern class NSMenu : NSObject { 79 override static NSMenu alloc() @selector("alloc"); 80 81 NSMenu init() @selector("init"); 82 NSMenu init(NSString title) @selector("initWithTitle:"); 83 84 void setSubmenu(NSMenu menu, NSMenuItem item) @selector("setSubmenu:forItem:"); 85 void addItem(NSMenuItem newItem) @selector("addItem:"); 86 87 NSMenuItem addItem( 88 NSString title, 89 SEL selector, 90 NSString charCode 91 ) @selector("addItemWithTitle:action:keyEquivalent:"); 92 } 93 94 extern class NSMenuItem : NSObject { 95 override static NSMenuItem alloc() @selector("alloc"); 96 NSMenuItem init() @selector("init"); 97 98 NSMenuItem init( 99 NSString title, 100 SEL selector, 101 NSString charCode 102 ) @selector("initWithTitle:action:keyEquivalent:"); 103 104 void enabled(bool) @selector("setEnabled:"); 105 106 NSResponder target(NSResponder) @selector("setTarget:"); 107 } 108 109 enum NSWindowStyleMask : size_t { 110 borderless = 0, 111 titled = 1 << 0, 112 closable = 1 << 1, 113 miniaturizable = 1 << 2, 114 resizable = 1 << 3, 115 116 /* Specifies a window with textured background. Textured windows generally don't draw a top border line under the titlebar/toolbar. To get that line, use the NSUnifiedTitleAndToolbarWindowMask mask. 117 */ 118 texturedBackground = 1 << 8, 119 120 /* Specifies a window whose titlebar and toolbar have a unified look - that is, a continuous background. Under the titlebar and toolbar a horizontal separator line will appear. 121 */ 122 unifiedTitleAndToolbar = 1 << 12, 123 124 /* When set, the window will appear full screen. This mask is automatically toggled when toggleFullScreen: is called. 125 */ 126 fullScreen = 1 << 14, 127 128 /* If set, the contentView will consume the full size of the window; it can be combined with other window style masks, but is only respected for windows with a titlebar. 129 Utilizing this mask opts-in to layer-backing. Utilize the contentLayoutRect or auto-layout contentLayoutGuide to layout views underneath the titlebar/toolbar area. 130 */ 131 fullSizeContentView = 1 << 15, 132 133 /* The following are only applicable for NSPanel (or a subclass thereof) 134 */ 135 utilityWindow = 1 << 4, 136 docModalWindow = 1 << 6, 137 nonactivatingPanel = 1 << 7, // Specifies that a panel that does not activate the owning application 138 hUDWindow = 1 << 13 // Specifies a heads up display panel 139 } 140 141 extern class NSWindow : NSObject { 142 override static NSWindow alloc() @selector("alloc"); 143 144 NSWindow init() @selector("init"); 145 146 NSWindow initWithContentRect( 147 NSRect contentRect, 148 NSWindowStyleMask style, 149 NSBackingStoreType bufferingType, 150 bool flag 151 ) @selector("initWithContentRect:styleMask:backing:defer:"); 152 153 void makeKeyAndOrderFront(id sender) @selector("makeKeyAndOrderFront:"); 154 NSView contentView() @selector("contentView"); 155 void orderFrontRegardless() @selector("orderFrontRegardless"); 156 void center() @selector("center"); 157 void contentView(NSView view) @selector("setContentView:"); 158 159 NSString title() @selector("title"); 160 void title(NSString value) @selector("setTitle:"); 161 } 162 163 extern class NSView : NSObject { 164 NSView init() @selector("init"); 165 NSView initWithFrame(NSRect frameRect) @selector("initWithFrame:"); 166 167 void addSubview(NSView view) @selector("addSubview:"); 168 169 bool wantsLayer() @selector("wantsLayer"); 170 void wantsLayer(bool value) @selector("setWantsLayer:"); 171 172 CALayer layer() @selector("layer"); 173 void uiDelegate(NSObject) @selector("setUIDelegate:"); 174 175 void drawRect(NSRect rect) @selector("drawRect:"); 176 bool isFlipped() @selector("isFlipped"); 177 bool acceptsFirstResponder() @selector("acceptsFirstResponder"); 178 bool setNeedsDisplay(bool) @selector("setNeedsDisplay:"); 179 } 180 181 182 extern class NSColor : NSObject { 183 override static NSColor alloc() @selector("alloc"); 184 static NSColor redColor() @selector("redColor"); 185 186 CGColorRef CGColor() @selector("CGColor"); 187 } 188 189 extern class CALayer : NSObject { 190 CGFloat borderWidth() @selector("borderWidth"); 191 void borderWidth(CGFloat value) @selector("setBorderWidth:"); 192 193 CGColorRef borderColor() @selector("borderColor"); 194 void borderColor(CGColorRef) @selector("setBorderColor:"); 195 } 196 197 198 extern class NSViewController : NSObject { 199 NSView view() @selector("view"); 200 void view(NSView view) @selector("setView:"); 201 } 202 203 enum NSBackingStoreType : size_t { 204 retained = 0, 205 nonretained = 1, 206 buffered = 2 207 } 208 209 enum NSStringEncoding : NSUInteger { 210 NSASCIIStringEncoding = 1, /* 0..127 only */ 211 NSUTF8StringEncoding = 4, 212 NSUnicodeStringEncoding = 10, 213 214 NSUTF16StringEncoding = NSUnicodeStringEncoding, 215 NSUTF16BigEndianStringEncoding = 0x90000100, 216 NSUTF16LittleEndianStringEncoding = 0x94000100, 217 NSUTF32StringEncoding = 0x8c000100, 218 NSUTF32BigEndianStringEncoding = 0x98000100, 219 NSUTF32LittleEndianStringEncoding = 0x9c000100 220 } 221 222 223 struct CGColor; 224 alias CGColorRef = CGColor*; 225 226 alias CGFloat = double; 227 228 struct NSPoint { 229 CGFloat x; 230 CGFloat y; 231 } 232 233 struct NSSize { 234 CGFloat width; 235 CGFloat height; 236 } 237 238 struct NSRect { 239 NSPoint origin; 240 NSSize size; 241 } 242 243 alias NSPoint CGPoint; 244 alias NSSize CGSize; 245 alias NSRect CGRect; 246 247 pragma(inline, true) NSPoint NSMakePoint(CGFloat x, CGFloat y) { 248 NSPoint p; 249 p.x = x; 250 p.y = y; 251 return p; 252 } 253 254 pragma(inline, true) NSSize NSMakeSize(CGFloat w, CGFloat h) { 255 NSSize s; 256 s.width = w; 257 s.height = h; 258 return s; 259 } 260 261 pragma(inline, true) NSRect NSMakeRect(CGFloat x, CGFloat y, CGFloat w, CGFloat h) { 262 NSRect r; 263 r.origin.x = x; 264 r.origin.y = y; 265 r.size.width = w; 266 r.size.height = h; 267 return r; 268 } 269 270 271 } 272 273 // helper raii refcount object 274 struct MacString { 275 union { 276 // must be wrapped cuz of bug in dmd 277 // referencing an init symbol when it should 278 // just be null. but the union makes it work 279 NSString s; 280 } 281 282 // FIXME: if a string literal it would be kinda nice to use 283 // the other function. but meh 284 285 this(scope const char[] str) { 286 this.s = NSString.alloc.initWithBytes( 287 cast(const(ubyte)*) str.ptr, 288 str.length, 289 NSStringEncoding.NSUTF8StringEncoding 290 ); 291 } 292 293 NSString borrow() { 294 return s; 295 } 296 297 this(this) { 298 if(s !is null) 299 s.retain(); 300 } 301 302 ~this() { 303 if(s !is null) { 304 s.release(); 305 s = null; 306 } 307 } 308 } 309 310 extern(C) { 311 enum CGPathDrawingMode { 312 kCGPathFill, 313 kCGPathEOFill, 314 kCGPathStroke, 315 kCGPathFillStroke, 316 kCGPathEOFillStroke 317 } 318 319 alias NSGraphicsContext CGContextRef; 320 321 void CGContextSetRGBFillColor(CGContextRef c, double red, double green, double blue, double alpha); 322 323 void CGContextBeginPath(CGContextRef c); 324 void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode); 325 void CGContextAddEllipseInRect(CGContextRef c, CGRect rect); 326 void CGContextAddArc(CGContextRef c, double x, double y, double radius, double startAngle, double endAngle, long clockwise); 327 void CGContextAddRect(CGContextRef c, CGRect rect); 328 } 329 330 extern (C) void NSLog(NSString, ...); 331 332 extern (Objective-C) 333 __gshared NSApplication NSApp_; 334 335 NSApplication NSApp() { 336 if(NSApp_ is null) 337 NSApp_ = NSApplication.shared_; 338 return NSApp_; 339 } 340 341 342 /+ +++++++++++++++++++++++++ +/ 343 344 extern(Objective-C) 345 class AppDelegate : NSObject, NSApplicationDelegate { 346 override static AppDelegate alloc() @selector("alloc"); 347 348 override void applicationWillFinishLaunching(NSNotification notification) @selector("applicationWillFinishLaunching:") { 349 immutable style = NSWindowStyleMask.resizable | 350 NSWindowStyleMask.closable | 351 NSWindowStyleMask.miniaturizable | 352 NSWindowStyleMask.titled; 353 354 NSMenu mainMenu = NSMenu.alloc.init(MacString("Main").borrow); 355 356 { 357 auto item = mainMenu.addItem(MacString("Test").borrow, null, MacString("").borrow); 358 auto menu = NSMenu.alloc.init(MacString("Test2").borrow); 359 mainMenu.setSubmenu(menu, item); 360 361 auto newItem = menu.addItem(MacString("Quit").borrow, "terminate:", MacString("q").borrow); 362 newItem.target = NSApp; 363 auto newItem2 = menu.addItem(MacString("Idk").borrow, "quit:", MacString("x").borrow); 364 newItem2.target = NSApp; 365 } 366 367 { 368 auto item = mainMenu.addItem(MacString("Test3").borrow, null, MacString("").borrow); 369 auto menu = NSMenu.alloc.init(MacString("Test4").borrow); // this is the title actually used 370 mainMenu.setSubmenu(menu, item); 371 372 auto newItem = menu.addItem(MacString("Quit2").borrow, "stop:", MacString("s").borrow); 373 auto newItem2 = menu.addItem(MacString("Idk2").borrow, "quit:", MacString("b").borrow); 374 } 375 376 377 NSApp.menu = mainMenu; 378 379 380 auto window = NSWindow.alloc.initWithContentRect( 381 NSMakeRect(10, 10, 300, 300), 382 style, 383 NSBackingStoreType.buffered, 384 false 385 ); 386 387 window.title = MacString("D Rox").borrow; 388 389 // auto controller = ViewController.alloc.init; 390 auto view = SDGraphicsView.alloc.init; 391 window.contentView = view; 392 393 window.center(); 394 395 /+ 396 this.window = window; 397 this.controller = controller; 398 +/ 399 400 window.makeKeyAndOrderFront(null); 401 } 402 403 override void applicationDidFinishLaunching(NSNotification notification) @selector("applicationDidFinishLaunching:") { 404 NSApplication.shared_.activateIgnoringOtherApps(true); 405 } 406 override bool applicationShouldTerminateAfterLastWindowClosed(NSNotification notification) @selector("applicationShouldTerminateAfterLastWindowClosed:") { 407 return true; 408 } 409 } 410 411 extern(Objective-C) 412 class SDGraphicsView : NSView { 413 override static SDGraphicsView alloc() @selector("alloc"); 414 415 override void drawRect(NSRect rect) @selector("drawRect:") { 416 417 auto context = NSGraphicsContext.currentContext.graphicsPort; 418 419 import arsd.color; 420 auto color = Color.red; 421 CGContextSetRGBFillColor(context, color.r/255.0f, color.g/255.0f, color.b/255.0f, color.a/255.0f); 422 423 NSLog(MacString("here").borrow); 424 425 CGContextBeginPath(context); 426 //auto rect2 = CGRect(CGPoint(30, 30), CGSize(90, 90)); 427 CGContextAddRect(context, rect); 428 CGContextDrawPath(context, CGPathDrawingMode.kCGPathFillStroke); 429 430 } 431 432 override bool isFlipped() @selector("isFlipped") { 433 return true; 434 } 435 override bool acceptsFirstResponder() @selector("acceptsFirstResponder") { 436 return true; 437 } 438 439 // SimpleWindow simpleWindow; 440 void simpledisplay_pulse() @selector("simpledisplay_pulse") { 441 setNeedsDisplay = true; 442 } 443 } 444 445 void main() { 446 auto dg = AppDelegate.alloc; 447 NSApp.delegate_ = dg; 448 449 NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular); 450 451 // auto view = SDGraphicsView.alloc; 452 453 NSLog(MacString("Hello there").borrow); 454 NSApp.run(); 455 NSLog(MacString("Bye").borrow); 456 }
On the mac, I wanted it convenient to use, so I did a minimal vim thing that puts dmdi % on F3 and runcurrent % on F1. Meaning it will do a dmd -i currentfile.d when I hit that key and then run the program if i hit the other, so I rarely need to actually go to the other keyboard. That's actually kinda nice. I should try running gui apps on Windows through ssh too and see if it works similarly. It is weird, X is better, but this is useful in its own way.
It also rsync's the arsd folder to the mac on every build. This adds some lag to each built but also means there's a single source of those files without bothering with git nonsense or making a shared drive work or anything else. I just edit them on my main computer and hit the magic key and see the difference. It makes working on this side computer far more convenient.
And the way the mac wakes up when i ssh it and goes to sleep when i leave it automatically means it *almost* feels like just an extension of my regular computer. Let's hope its RAM doesn't die on me anytime soon lol.
I got a Mac and made some solid progress on actually using it in the arsd libs. Some history and future.