C-Macs – a pure C macOS application

(github.com)

192 points | by dgellow 13 days ago

27 comments

  • cnity 13 days ago
    > A little bit of this also has to do to stick it to all those Luddites on the internet who post "that's impossible" or "you're doing it wrong" to Stack Overflow questions... Requesting permissions in the JNI "oh you have to do that in Java" or other dumb stuff like that. I am completely uninterested in your opinions of what is or is not possible. This is computer science. There aren't restrictions. I can do anything I want. It's just bits. You don't own me.

    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#...

    • wil421 13 days ago
      I like this guys spirit. Once I had to pick up old code from a guy with this spirit and it was f-ing horrible.
      • mtillman 13 days ago
        I love this project (from 2014?) and perhaps my favorite part other than the f-u of the whole thing is the commit message "complete refactoring".
      • shrimp_emoji 13 days ago
        Beats using Java

        Think of it as exercise: it sucks up front but you won't end up an obese diabetic on the WALL-E ship later.

        • SpaghettiCthulu 12 days ago
          Java is only a nightmare when you have to follow corporate decade-old "best" practices
          • bheadmaster 12 days ago
            In my experience, Java is a nightmare the moment you try to do anything beyond what C++ can (easily) do.
            • augusto-moura 12 days ago
              Which would be?
              • bheadmaster 12 days ago
                Ok, here are two examples on the top of my head, of fairly trivial programs that are a pain to do right in Java.

                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 :)

                • netvl 12 days ago
                  1 is really trivial and took me like 5 seconds to write:

                      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

                  • bheadmaster 12 days ago
                    > System.in.transferTo(System.out);

                    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 :')

                    • netvl 12 days ago
                      > I suppose it would be much harder to do it without the transferTo function,

                      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.
                    • netvl 11 days ago
                      So this is a direct reimplementation of your example in Java - https://gist.github.com/netvl/32698cc97b88267fecee9b30f00d16...

                      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.

                      • bheadmaster 11 days ago
                        That's impressive. However, even the direct reimplementation has introduced a subtle bug - a connection that sends something other than "server" or "client" is left hanging and never closed (use nc with -N flag, and press Ctrl+D to send EOF to see what I mean - it should disconnect and yet it doesn't). While not a functional bug, it could still cause a DoS attack if someone wanted to hog all the available sockets.

                        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?

                        • the-smug-one 11 days ago
                          Use try-with-resources instead of try/finally.
                    • netvl 12 days ago
                      Typo, yeah :( I don’t agree that it is 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.

  • lapcat 13 days ago
    This app uses objc_msgSend, which feels a lot like cheating, but if you simply want to avoid using nibs (while still using Objective-C or Swift), check out my NiblessMenu project and my "Working without a nib" blog series.

    https://github.com/lapcat/NiblessMenu

    https://lapcatsoftware.com/articles/working-without-a-nib-pa...

    • cellularmitosis 12 days ago
      I just Ed to say thank you for keeping your nibless posts from 2007 online. Handy for those of us doing a bit of retro computing on Tiger and Leopard. https://forums.macrumors.com/threads/developing-for-tiger-an...
    • krackers 13 days ago
      You can probably do a "true" pure C application (as in no Cocoa at all and no linking to objc-runtime) by dropping down to the CoreGraphics layer, but a lot of it is undocumented.

      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

  • dgellow 13 days ago
    I found this repo while looking for an equivalent to Win32 hello world[0] as a learning exercise during a long flight (with my work MacBook instead of my personal Windows machines).

    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...

    • tredre3 13 days ago
      The WIN32 API is somewhat ugly. I hate how all types are capitalized and their short abbreviations in arguments are sometimes weird. I hate how they have their own version of lots of standard C types that you have to be careful about (they weren't standard at the time, to be fair).

      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!

      • ndiddy 12 days ago
        Win32 is straightforward and easy to use until you get to COM stuff: https://learn.microsoft.com/en-us/windows/win32/shell/common...
        • Koshkin 12 days ago
          Yes, for COM programming you better switch to C++.
          • pjmlp 12 days ago
            Even then, MFC and C++/CX were the only productive ways to use it from Microsoft SDKs.

            .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.

            https://github.com/microsoft/wil

            • electroly 11 days ago
              MFC's only COM support was support for ActiveX controls; it otherwise didn't do anything to help you with COM interaction. You may be thinking of ATL, which was specifically intended to help with 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.

              • pjmlp 11 days ago
                ActiveX is just a brand name for a set of COM interfaces.

                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.

      • QuercusMax 13 days ago
        I remember ages ago when I was about 12, I got a copy of Turbo C++ for Windows 3.1 for my birthday. (I had only written in Basic before, and wanted to learn a "real" programming language. My dad was a Fortran programming physicist, and had heard that C++ was what everyone was using then-a-days.) I mostly wrote simple console programs, and it came with the Borland ObjectWindows library with a fun GUI builder, but there was a section in the manual talking about how to write directly using the Windows API.

        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.

      • dgellow 13 days ago
        Also, UTF-16 (or WTF-16 as defined by https://simonsapin.github.io/wtf-8/#wtf-16)
  • vineek 13 days ago
    Very nice. In similar fashion, a few years back I set out to create Wuhoo.

    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.

    https://github.com/ViNeek/wuhoo

  • JKCalhoun 13 days ago
    I did a straight C++ app for MacOS but 1) I used SDL2 and 2) it was a full-screen game so no Cocoa UI needed. It was kind of fun though in a retro-computing way.

    (I'm a big fan of SDL now.)

    • speps 13 days ago
      The linked project doesn't use any ObjC files at all. SDL2 has a bunch of Cocoa files[1] so you did use Cocoa even if unknowingly.

      [1] https://github.com/libsdl-org/SDL/tree/main/src/video/cocoa

      • LeoNatan25 13 days ago
        The linked project also uses Cocoa, and guess what, considering Cocoa itself is written in ObjC, the linked project also uses OBjC “unknowingly”.

        Considering CodaFi works for Apple now, he knows. Spoiler: he knew 11 years ago too.

        • anamexis 13 days ago
          The OP says it is Cocoa-less in the readme, is that not true?
          • electroly 13 days ago
            It's not. Open up any of the .c files and you'll see lots of references to Cocoa. One of the comments refers to Cocoa by name! This is using objc_msgSend to call into Cocoa. You'd have to squint your eyes pretty hard for the claim in the readme to be true. What it doesn't have is Objective-C code, which is different than not having Cocoa.
      • JKCalhoun 13 days ago
        Yeah, that's okay. It wasn't like a "purity thing" for me, just nice to write portable code.
    • musicale 12 days ago
      Looks like there's a C++ interface to Metal as well.

      https://developer.apple.com/metal/cpp/

      • pjmlp 12 days ago
        Kind of, yes there is, however it is suboptimal versus Objective-C or the Swift bindings.

        It is mostly for engine writers to plug into Metal, instead of dealing directly with Objective-C++.

    • jonhohle 13 days ago
      Don’t people do that all the time with Qt?
  • cornstalks 13 days ago
    If you want to optimize this (and you enjoy pain), you can eliminate the dependency on objc/* headers and use compiler attributes and link sections to compile your code to the same(ish) assembly that Objective-C compiles to. I don't have a C example on hand, but here's a Rust example that's pretty easy to translate to C: https://github.com/objrs/objrs/blob/master/HOW_IT_WORKS.md
  • magicmicah85 13 days ago
    Good example of digging with a spoon as they stated was the original goal. And it still compiles on an M1 macbook despite being a decade old code base.
    • trenchgun 12 days ago
      Really? Thats awesome, and unexpected! Got to try it out today then.
  • 9dev 13 days ago
    As someone with no experience in native application development, could someone explain to me why this is significant? I have a rough idea, but I would like to understand it properly.
    • lsllc 13 days ago
      It's an exercise in recreating how an Objective-C app works in C from first-principles. For example, the creation of the AppDelegate (see CreateAppDelegate() in AppDelegate.c) is interesting because it shows how to create a class from NSObject and attach the applicationDidFinishLaunching: method along with it's implementation in C. I've used objc_msgSend() before from C (to access the pasteboard from a CLI app), but never implemented any ObjC classes using C!

      It's similar how you might attempt to build a C++ class from only C components by creating a vtable.

    • williamcotton 13 days ago
      MacOS apps are typically written in either Objective-C or Swift as these are the officially supported languages for the MacOS APIs.

      The code in this template is interfacing with the Objective-C runtime but with pure C.

      • rankam 13 days ago
        Does this mean that, theoretically, this could lead to the ability to build MacOS apps in higher languages that interoperate well with C such as Python? I know you can build MacOS apps with Python now, but does this potentially improve the experience?
        • flohofwoe 13 days ago
          You can already do this in the traditional way by building an ObjC shim which exposes a C API. The solution shown here just skips ObjC and talks directly to the ObjC runtime (which has a C API but is not as convenient to use as doing the same thing in ObjC or Swift).

          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).

          • jackjeff 13 days ago
            That’s essentially what this project does. It creates the C code that the ObjC compiler would generate to “implement methods” or “send messages”.

            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.

          • gwking 13 days ago
            In fact, early objective-c was a preprocessor according to Wikipedia!
        • detourdog 13 days ago
          That is what objC scripting brige is for.

          https://developer.apple.com/documentation/scriptingbridge

        • jimbokun 13 days ago
          I believe RubyMotion does basically this:

          http://www.rubymotion.com

          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.

        • CodeWriter23 13 days ago
          There’s already PyCocoa and pretty sure *Cocoa exists for a variety of languages.
      • ChrisMarshallNY 13 days ago
        That has always been possible. Also, under the ObjC layer, is good ol' ANSI C (FreeBSD Unix).

        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.

        • ryandrake 13 days ago
          I knew this was possible, but I had never actually seen it implemented before. And to think, the project is over 10 years old!

          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.

          • billforsternz 12 days ago
            I agree. I recall the Windows API and the official Hello World. I'm sure they could have got to the same place in a slightly reorganised way that kept main() as the starting point and interfaced with the event loop and the entire windowing system via some API calls that hid the cruddiest parts of the boilerplate crud (apt phrasing, thank you) away in headers and libraries. Then progressively reveal the details when they're really necessary for more elaborate applications.
          • williamcotton 13 days ago
            It has to hand over the reigns to the Objective-C runtime to allow for all the messages being passed between objects.

            I’m not sure you want to spend your time rewriting what NSApp is doing!

            • saagarjha 13 days ago
              The runtime knows how to do that already. -[NSApplication run] sets up the runloop to drain events among other things.
          • astrange 13 days ago
            Why would you want to manage your own runloop? It's both not interesting and possible to get wrong.
            • adastra22 13 days ago
              Sometimes there are conflicting things you need to do in the same main thread.
        • thenipper 13 days ago
          You know i took the train to Boston to PDX, it was really relaxing. Not quite walking but definitely a lot different then a plane...
        • jonhohle 13 days ago
          It has, but there was also Carbon until around the Intel transition.
          • LeoNatan25 13 days ago
            Full Carbon was there until the 64bit transition officially, but much of it remained and still remains until now. For example, Apple only started using AppKit to draw menus in Sonoma; it was Carbon until now.
      • mycall 13 days ago
        > officially supported languages for the MacOS APIs

        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.

    • swatcoder 13 days ago
      > why this is significant?

      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.

      • LeoNatan25 13 days ago
        This isn’t an example of that. Have you looked at the code? This uses ObjC and Cocoa implementations in an unintuitive method. Nice exercise to learn about the ObjC runtime, but has zero practicality or use.
      • johnnyjeans 13 days ago
        The funny thing is, Apple themselves seems to have forgotten about the whole BSD-rooted operating system. Anybody who ventures off the beaten path of developing software for a modern Mac will inevitably encounter a lot of cobwebs. One of my favorites: When Apple implemented app bundles, they never updated dyld's search paths to be aware of the app bundle directory structure, meaning you have to manually patch your rpaths. Not a huge deal, and one that's hidden from you if you only ever know xcode, but it's one of many very sloppy things I noticed coming from a strong Unix background to Mac. There's a lot of really weird incongruence where the deeper you go into the system, the more everything feels covered in dust and neglected. I was always sold on Macs having really great top-to-bottom integration and have all the polish and attention to detail you can expect out of a corporate Unix-like, but that's not what I found. It's more like a really shiny superstructure bolted on top of an ancient, rust-eaten BSD. Don't get me started on how a lot of the "newer" stuff at that low level tends to be some of the absolute worst takes on a concept. The "App Sandbox" might be the most disgusting, slap-dash design for process isolation I've ever seen bolted onto a kernel.

        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.

        • astrange 13 days ago
          > When Apple implemented app bundles, they never updated dyld's search paths to be aware of the app bundle directory structure, meaning you have to manually patch your rpaths.

          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.

          • nxobject 13 days ago
            Oh wow, employed since NeXT? (There needs to be an "Apple/NeXT long-hauler oral history" or something. Chris Espinosa, hired 1976, is still around...)
            • mattl 12 days ago
              Avie Tevanian and Jon Rubenstein both have oral histories on the Computer History Museum website/YouTube.
          • johnnyjeans 12 days ago
            > Sloppiness /is/ Unix design, that's intentional. It's called "worse is better".

            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.

            • worewood 12 days ago
              Hyperbole; "nobody" means an insignificant amount of people.
              • astrange 11 days ago
                Go programmers use something Plan9-y, but I don't think much of Go's design either; it's a combination of Unix's underdesign with Plan9's inflexible and idiosyncratic taste.
        • pjmlp 12 days ago
          Nah, this was already like this at NeXT.

          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.

        • deergomoo 13 days ago
          > 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

          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.

        • saagarjha 13 days ago
          App bundles have no fixed layout. How would rpaths work if you didn’t specify them yourself?
          • johnnyjeans 12 days ago
            They do if you want them to be signed.
    • jojobas 13 days ago
      I guess it's normally impossible to write a GUI application for mac os without objective-c libraries doing the talking to the OS, but what do I know.
      • swatcoder 13 days ago
        It is not. This was just a new wave of people finally looking under the hood.

        (Where they would have found many many dusty but detailed man pages and docs waiting for them)

      • steve1977 13 days ago
        Beneath Cocoa are Core Foundation and things like Core Graphics for example, which both are C.
      • astrange 13 days ago
        You can write one in X11 if you like, if you make people install Xquartz.
        • ngcc_hk 13 days ago
          Is there example how to do that on mac. Just happy to have an old emu ibm 5100 sun x program. Wonder I need to boot up a vm or can try squartz …
          • jmb99 12 days ago
            brew install --cask xquartz

            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.

            [1] https://www.xquartz.org/

  • userbinator 12 days ago
    Uses under 1.5 MB of memory at any one time (most of it is used for drawing the window).

    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/

    • eschaton 11 days ago
      It also depends on how the operating system and frameworks actually work. For instance, the backing store for the window may be “charged to” the application on macOS but may be provided by the OS on another platform, or there may not even be one.
    • joshstrange 12 days ago
      > Unfortunately there is no screenshot and I have no access to a Mac at the moment,

      Here is a picture: https://cs.joshstrange.com/VSgrY06f

  • flohofwoe 13 days ago
    Here's something similar by Garrett Bass:

    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 ;)

  • w10-1 13 days ago
    Draw a line through this 12+-year-old effort from Robert Widmann and his 8-year-old exercise in type-lifting Swift [1] and ask yourself: what is he doing now at Apple?

    [1] https://github.com/typelift/Swiftz

    • saagarjha 13 days ago
      You can peek at his GitHub
  • rdrsss 13 days ago
    Love this, if I never have to write a wrapper around objc again I'd be in heaven.
    • LeoNatan25 13 days ago
      This uses the ObjC runtime to obtain the underlying ObjC method implementation functions, to call directly, instead of relying on the runtime to call them. If you find this more elegant than a wrapper with some ObjC, good for you.
    • icedchai 13 days ago
      I find Objective C more pleasant to use than C++. Maybe I'm crazy.
  • MaxLeiter 13 days ago
    For an iOS attempt, I've often gone back to this wonderful StackOverflow post: https://stackoverflow.com/a/10289913
  • eyesee 12 days ago
    Reminds me of an app I built ~20 years ago now.

    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.

  • chunsj 13 days ago
    It seems that the code is the result of the ObjC preprocessor. :-)
  • quelsolaar 12 days ago
    We used the same technique to port our C applications to Apple platforms, but to do so we wrote an automated tool that created a C wrapper for any cocoa API we need. It works on all current Apple platforms we have tried it on iOS,iPadOS, and Apple Silicon Macs.

    Its open source:

    https://felixk15.github.io/posts/c_ocoa/

  • KerrAvon 13 days ago
    Seeing some confusion. This is using what are effectively FFI interfaces to the Objective-C runtime. There’s really no good reason to do this in production code that isn’t a language bridge. It’s not as efficient at runtime as writing the functionally equivalent Objective-C, because the ObjC compiler statically allocates class/method data structures, and it’s not as safe, because you’re bypassing ARC.
  • eschaton 11 days ago
    This is basically what Go PenPoint programming was like. When you read the Go documentation it’s very clear they intended to use Objective-C or an equivalent preprocessor; I suspect they were stymied by NeXT’s acquisition or Stepstone and their own lack of support for GNU C.
  • jbverschoor 13 days ago
    This would've been a lot easier to wrap my head around when I had to work with Objective C for the first time
  • hgs3 13 days ago
    It would be nice to have something like this except for Metal. I'm unsure why Apple made Metal an Objective-C API when C + Core Foundation would suffice. One big advantage of a C API is it's easy to interop with other programming languages.
    • AceJohnny2 13 days ago
      Apple is opinionated about which languages should be used on their platforms, and they enforce it with these kinds of decisions.

      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.

      • extraduder_ire 12 days ago
        Is there any way to enforce that in a way that can't be worked around with a shim? Demanding source code? Looking for signs of other languages in the binary and not allowing them in the appstore if detected?
        • jsjohnst 12 days ago
          > Is there any way to enforce that in a way that can't be worked around with a shim?

          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.

        • AceJohnny2 12 days ago
          I suppose the difficulty of proving that, short of demanding source code, helped prevent it from happening.
  • ks2048 13 days ago
    This would seem more complete if it had a simple Makefile rather than an xcodeproj.
  • slmjkdbtl 12 days ago
    You don't need a xcodeproj at all for a pure C macOS application (or do you need it 10 years ago? I see the last commit is 10 years)
  • cossinle 13 days ago
    Crazy that this has to be a thing .. Why does Objective C even exist anyways??
    • astrange 13 days ago
      Because Brad Cox liked Smalltalk.
  • codazoda 13 days ago
    No license typically means copyright with all rights reserved in the U.S.

    Perhaps you want to release this into the public domain (see SQLite)?

    • actionfromafar 13 days ago
      I’d say the license is:

      “ 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.

      • williamcotton 13 days ago
        It's open source but it is still under copyright. There are notices in the source file:

        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.

        • actionfromafar 13 days ago
          In most jurisdictions almost everything is under copyright, until it expires. (Which is approximately right before the heat death of the universe, after Disney get their way. Or 70 years after the authors death, or something like that.)
        • numpad0 13 days ago
          Copyleft licenses depend on copyright. I think it was in an FSF FAQ somewhere. GPL is as copyright maximalist as it gets.
        • mattl 12 days ago
          I’d call it source available not open source. It doesn’t meet the open source definition.
        • williamcotton 13 days ago
          I always see the strangest fluctuations in upvotes and downvotes whenever I state the laws and doctrines of copyright.

          FWIW, I do court ordered code inspections to assess alleged copyright infringement for a living.

      • dec0dedab0de 13 days ago
        It’s definitely not Open Source with that Objective-C restriction. Though it does fit the attitude of the project.
      • nicky0 13 days ago
        Oh no that won't fly at all. People around here really like "proper" licenses. And they'll tell you about it.
        • gleenn 13 days ago
          I'm not a lawyer but one would definitely let you know that at least in the US, software without a license is more dangerous than software with even a very restrictive one. Someone can come along and determine the license at any time. So there is good reason to point it out unless you crave legal jeopardy.
          • nicky0 10 days ago
            GNU/FSF's opinion on informal licenses (https://www.gnu.org/licenses/license-list.html#informal):

            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.

        • actionfromafar 13 days ago
          I don't like the (perhaps actually a) license, because it set's limits on what the user can do with the source code. (Not allowing Objective C in it.)
          • fao_ 13 days ago
            yeah i accidentally added objective c code and now im kissing my ass bracing for the lawsuit!!!!!!!111
      • embirico 13 days ago
        I do love the vibe of that license
  • beardedwizard 13 days ago
    [flagged]