C-Macs – a pure C macOS application
github.com
github.com
From the wonderful CNLohr's rawdraw justification[0]. I always enjoy these kinds of efforts because they embody the true hacker spirit. This is Hacker News after all!
0: https://github.com/cnlohr/rawdrawandroid?tab=readme-ov-file#...
Think of it as exercise: it sucks up front but you won't end up an obese diabetic on the WALL-E ship later.
1) A program that copies all data from standard input to the standard output. Try to write this without Googling. Simple enough, eh?
2) A program that serves as a middleware between servers and clients, both of which are behind NATs so they connect to your middleware, using gRPC methods. Servers use gRPC streams to maintain a connection, send their ID as the first message, receive request messages, and send reply messages. Clients send ID and request in a message, and receive reply message.
I hope you're willing to show me how wrong I am by writing the code :)
public class Test {
public static void main(String[] args) {
System.in.transferTo(System.out);
}
}
2 is anything but trivial in any language, unless it is some kind of a language specifically designed to build gRPC programs.Never versions of Java are quite pleasant to work with. There are a lots of quality-of-life improvements (switch expression, multi-line string literals, string templates (currently in preview), lots of useful stdlib APIs), as well as quite fundamental additions like Loom.
edit: formatting, explanations
Ok, that's really convenient :) I suppose it would be much harder to do it without the transferTo function, but that would be beyond the point. I'll instead focus on the example 2.
> 2 is anything but trivial in any language, unless it is some kind of a language specifically designed to build gRPC programs.
The gRPC protocol here is only an example. Feel free to use any protocol you like. As a counter-example, here's a TCP version in <200 lines of Go: https://gist.github.com/paskozdilar/6871fef7b0b245a0846bd27e...
> Never versions of Java are quite pleasant to work with
I know this is a typo, but you accidentally wrote something completely true :')
I can try doing it later today, but I’m 100% sure that TCP example will look pretty much the same in Java, in terms of complexity.
> The gRPC protocol here is only an example. Feel free to use any protocol you like.
FWIW, this is a pretty huge change. gRPC is quite complex, as it includes protobuf, http/2 and its own RPC mechanism. A simple TCP protocol is peanuts compared to that.
No, it would not:
public class Test {
public static void main(String[] args) throws Exception {
byte[] buf = new byte[4096];
int read;
while ((read = System.in.read(buf)) > 0) {
System.out.write(buf, 0, read);
}
}
}
I imagine it will look pretty much the same in the majority of languages which have similar I/O abstractions.It is under 120 lines, uses Java SE API only, and the complexity is approximately the same. Go does have advantage of built-in selects and channels, which make parts of the logic nicer, but not by much IMO.
On the other hand, in Java almost no one uses standard library to write network apps like this. If I had to use external libraries, which would be the case in the majority of real-world projects, the implementation would look even better and clearer.
In Go, this is avoided by `defer conn.Close()` which guarantees that the connection will be closed at the end of the function execution, no matter what. It's a sort-of nicer syntax of "try {} finally {}" that let's you write the cleanup code immediately after the setup code, instead of at the end.
> On the other hand, in Java almost no one uses standard library to write network apps like this
I'm curious about the way the average Java programmer would write such an application. Would you please write an example of such a program, using an external library that would make implementation both correct and easy to read?
https://github.com/lapcat/NiblessMenu
https://lapcatsoftware.com/articles/working-without-a-nib-pa...
There's also Carbon (which I always assumed directly hit the same underlying quartz apis), but that's long deprecated and iirc never supported the UI portions in 64-bit
That's something I really like about Windows APIs — I can pick a new programming language I want to play with, as long as there is a way to interface with C I can try to port the Win32 hello world then play around.
0: https://learn.microsoft.com/en-us/windows/win32/learnwin32/y...
But it's quite straightforward and easy to use. Any language that has FFI can call into it and build GUI apps, I've made a windowed Hello World in PHP with it.
And it has been that way for 30+ years at this point. It's quite remarkable, really!
I didn't really understand any of what I was reading, but I typed in the whole basic sample complete with HWNDs and all that nonsense. I can't remember if I could even get it to compile, but the idea of an event-loop was beyond my comprehension as a 12yo. I'm sure if I went back it would make a lot more sense to me, but back in 1994 it seemed like dark magic, especially compared to the much simpler and more understandable OWL components.
.NET isn't as convenient as VB 6 was, fully embracing COM as the VBX replacement model, technically introduced in VB5, but still some stuff was lacking.
Then there is Delphi and C++ Builder.
It beat me that having doubled down on COM since how Longhorn went down, and Windows team getting their way doing avoiding .NET to take over, they hardly managed to create nice tooling as the competition.
Editing IDL files with a Notepad like experience, manually merging generated code, and a couple of frameworks that barely go beyond yet another way to do AddRef/Release/QueryInterface and aggregation.
Meanwhile D-BUS, XPC and AIDL, provide much better dev experience.
Pity that Borland products are kind of tainted due to mismanagement decisions, otherwise maybe fixing COM dev experience would already been seriously taken by VS team.
Ah, nowadays WIL is probably the best approach when having only to consume COM.
Source: worked professionally for years on an MFC-based app that communicated with an out-of-process COM server which we also wrote. The COM bits sucked.
Just like nowadays WinRT is just having IInspectable alongside IUnknown, shipping .NET metadata file instead of a TLB and having an application identity.
MFC can pretty well do regular COM, and those macros are much developer friendly than the ATL template junk with multiple inheritance and IDL files without any kind of Visual Studio editing tooling support.
COM sucks no matter what when using Microsoft tools, for whatever reason Microsoft isn't able to deliver as good development experience for COM, as third parties, even though they push it everywhere.
For decades I expected them to finally catch up to Borland or Qt, C++/CX seemed to finally be it, but no, the "ATL rulez!" folks in Redmond had their way.
Wuhoo loosely stands for W indows U sing H eaders O nly. It is an attempt to create a single-header library (in the spirit of STB) for graphics related window management, compatible with both C and C++.
It works on Windows, Linux and Mac.
(I'm a big fan of SDL now.)
[1] https://github.com/libsdl-org/SDL/tree/main/src/video/cocoa
Considering CodaFi works for Apple now, he knows. Spoiler: he knew 11 years ago too.
It is mostly for engine writers to plug into Metal, instead of dealing directly with Objective-C++.
(Where they would have found many many dusty but detailed man pages and docs waiting for them)
Or install from the project homepage [1]. Then just launch the X11 app. Note that it does require the application to be built for Mac - it’s not an emulator, just an implementation of the X11 APIs.
The code in this template is interfacing with the Objective-C runtime but with pure C.
It was fun building an app in this a few years ago, but was difficult to keep up with updates to MacOS breaking my code.
In a highly simplified way you can think of Objective-C as preprocessor which replaces the ObjC syntax sugar with C function calls into the ObjC runtime (it's not how it works in reality, but how it could work).
It’s somewhat doable by hand because Objc is a thin lawyer.
Over 15 years ago I did stuff similar to this project to call some Objc code from a C++ app. Most of it was exposed to normal C APIs but one feature only available in AppKit. It was much simpler to do it this way than figure out how to make GCC or Objc like our C++ or any mess with bridging headers.
I think the move to Swift has made that harder in some ways.
But then again I don’t want to write C or C++ these days if I can avoid it.
There's a number of apps that run on modern Macs, that were written in C, but it is unusual to see ones that leverage the GUI.
That said, it's possible to walk from Boston to Portland (OR), but I'd rather take a plane.
When I first started programming Cocoa, I was always kind of miffed that the happy-path was: "Use Objective-C and main() should just hand-off the reigns to NSApplicationMain()". Kind of like the happy-path on Windows is "WinMain plus a bunch of boilerplate crud".
It's always felt somehow vaguely "correct" to have main() be your actual application entry point and have it manage whatever main loop you need yourself. I see that this project doesn't actually quite get there either. It's the same kind of voodoo: boilerplate code, and then cmacs_void_msgSend(NSApp, sel_getUid("run")); hands over the reigns.
I’m not sure you want to spend your time rewriting what NSApp is doing!
How often do you need to get support assistance from Apple? Just do what you want. If it means using Obj-C or Swift for ease, do that. If it means integrating another language, do that.
Ultimately, because new people keep being born and missed the years where this was pretty common and haven't yet bumped into the corners where it still is.
The repo and the SO discussion it was inspired by are themselves 11 years old and seem to be rooted in a new generation of iOS app developers starting to get more deeply curious about the system they're running on and how else it might be approached, which this person then ran with on MacOS.
Apple invites people to get started in making software for their platforms using (what's meant to be) more accessible tooling like Objective-C, Interface Builder, Swift, SwiftUI, etc but there's of course a whole BSD-rooted operating system sitting there once those those developers start digging. It's no secret, but it's a discovery that some people need to make on their own.
I get Apple's target market is quite literally the opposite kind of user that I am. That being said, I always find it curious that people still tout Mac as this kind of "Super Polished Desktop/Workstation Unix" and often cite the Unix certification. It feels like the more you try to use a Mac like you would any other Unix machine, the more you have to fight it. Often it doesn't feel any different to trying to wrangle Windows with WSL. I had less hiccups and trip-ups learning Plan 9 than I did coming to terms with macOS.
Typically things like this have binary compatibility reasons. (It wouldn't be because they forgot, actually I happen to know the same person has worked on dyld since the 90s.)
> but it's one of many very sloppy things I noticed coming from a strong Unix background to Mac
Funny thing to complain about. Sloppiness /is/ Unix design, that's intentional. It's called "worse is better".
On the other hand, nobody uses Plan9 because it's too well-designed to actually work.
That's missing the point. It's about simplicity through well designed abstraction. Unix was a runaway game engine that became Bell Lab's standard for talking to computers because it was vastly simpler than the poorly engineered MULTICS and friends. That's why it won.
> On the other hand, nobody uses Plan9
I use Plan 9 every day. I'm replying to you from inside abaco. I run a tablet with it, have a whole network of machines (most of them virtual). I'm in the middle of a project to create a light fixture I can talk to in 9p. It's not the first DIY IoT thing I've made to control over a Plan 9 terminal. My grid also runs my email, my file servers, an LLM chatbot (though this is actually served from a Linux machine via Inferno tsk tsk.) The only reason I use anything else is due to my job requiring me to ship a product in C++ and OpenGL. For something that doesn't "actually work", I find that it actually works excellently.
I've always understood people to mean that it's a polished desktop operating system (though that's becoming increasingly questionable these days) that also happens to run the same CLI stuff they're used to using on Linux servers.
Pre-WSL just that was a pretty nice selling point by itself.
The whole UNIX infrastructure was to get into the DoJ stuff that required POSIX compliance.
Just like with Microsoft, the approach was to allow UNIX software to come into NeXTSTEP, not to be created in NeXTSTEP and then leave it for other UNIX vendors.
All the relevant programs in NeXTSTEP used Objective-C, and even the drivers were written in Objective-C.
Apple was clever marketing OS X to the Desktop Linux crowd, that actually only cared about a POSIX experience and not so much about Linux itself.
Many of them helped Apple get out of red, into the company it is today.
This is how Microsoft finally understood how bad they did with POSIX on Windows NT linage, and out of Project Astoria ashes, WSL was born.
Had Microsoft actually kept POSIX subsystem up to date, and in parity with Win32 subsystem, and most likely Linux would never taken off, as everyone would have had their proper POSIX right there, without needing to look elsewhere.
It's similar how you might attempt to build a C++ class from only C components by creating a vtable.
Unfortunately there is no screenshot and I have no access to a Mac at the moment, but if View.c is where things actually happen, that is a huge amount of memory just for a (resizeable?) window with a little filled rectangle in it. The memory usage of a trivial app like this should be measured in kilobytes.
Comparing applications written in the same language across platforms is IMHO a good gauge of their relative efficiency. From what I've seen, Win32 and Linux (Xlib) are pretty close but Mac is clearly very "think different". I'm sure the liberal use of string constants here doesn't help either.
Here's a related interesting comparison: https://zserge.com/posts/fenster/
Here is a picture: https://cs.joshstrange.com/VSgrY06f
https://github.com/garettbass/oc
...I also experimented a bit with parsing macOS system headers via clang-ast-dump and then code-generating C and Zig bindings but that didn't get far:
https://github.com/floooh/objc-ast-experiments
...with a bit of effort and maybe using libclang instead of clang-ast-dump that's definitely feasible though.
I guess a similar approach is used by the official C++ bindings for Metal:
https://developer.apple.com/metal/cpp/
...also shameless plug: the sokol headers allow to write simple macOS applications (just a Metal canvas in a window) in various non-Apple languages (currently C, C++, Zig, Rust, Odin, Nim):
https://github.com/floooh/sokol
...I'm cheating though and use ObjC under the hood to talk to Cocoa and Metal ;)
We wanted a cross-platform C++ layer and native Cocoa front end. Objective C++ wasn’t a thing then, and having built a plain C shim previously I didn’t want to repeat the experience.
We built our own bridge by registering our C++ classes with the Obj-C runtime, generating selectors for all the methods so you could send messages to (carefully constructed) C++ objects using Obj-C syntax, or even subclass from C++ to Obj-C.
It was a pretty neat trick, but would’ve been difficult to port to the Obj-C 2 runtime.
https://clang.llvm.org/docs/ClangCommandLineReference.html#c...
Its open source:
For example, there was a time where Steve Jobs threatened to forbid iPhone apps not written in Obj-C, which would have destroyed the cross-platform ecosystem, at Apple's expense. Luckily, he was talked down from being that extreme.
You can’t definitively prove something was written in Objective-C or Swift, but you can often definitively prove something was not written in either pretty easily just by looking at the assembly.
Perhaps you want to release this into the public domain (see SQLite)?
“ Use it at your own risk, and don't blame me if anything bad happens. Oh, and if you extend it, make sure there isn't any Objective-C in it!”
I’d classify it as Open Source.
Informal license
An “informal license” means a statement such as “do whatever you like with this” or “you can redistribute this code and change it.”
In the United States, these licenses are supposed to be interpreted based on what the author seems to intend. So they probably mean what they appear to mean. That would make them non-copyleft free software licenses and compatible with the GNU GPL. However, an unlucky choice of wording could give it a different meaning.
However, many other countries have a more rigid approach to copyright licenses. There is no telling what courts in those countries might decide an informal statement means. Courts might even decide that it is not a license at all.
If you want your code to be free, don't invite gratuitous trouble for your users. Please choose and apply an established free software license. We offer recommendations that we suggest you follow.
https://github.com/CodaFi/C-Macs/blob/master/CMacs/View.c#L5...
I would definitely ask permission and encourage the author to change the license before using this code.
Since API calls are purely functional and not covered by copyright you can mimic the behavior here but you'll need to rewrite everything from scratch, notably the most expressive parts, which are:
https://github.com/CodaFi/C-Macs/blob/master/CMacs/CMacsType...
https://github.com/CodaFi/C-Macs/blob/master/CMacs/CMacsType...
The organization and structure of this code is arbitrary and within the author's creative expression.
FWIW, I do court ordered code inspections to assess alleged copyright infringement for a living.