Erstellen einer Fensteranwendung in reinem C unter MacOS

Lesezeit: 11 Minuten

Magos Benutzeravatar
Mago

Ich erstelle eine Anwendung in reinem C unter Mac OSX. Ich möchte ein Fenster erstellen, in dem meine App angezeigt wird.

Am liebsten möchte ich, dass es sich um eine reine C-Lösung handelt, aber wenn ich die Objective-C-Klasse verwenden muss, um das Fenster zu initialisieren und dann einen Kontext an meinen C-Code zu senden, ist das in Ordnung.

Ich verwende kein Xcode, sondern nur einen einfachen Texteditor. Ich habe versucht, Cocoa zu importieren, aber es wurden nur viele Fehler generiert.

Wie schreibe ich Code in einfachem reinem C-Code, der ein Fenster in OSX anzeigt?

  • Xcode ist keine Voraussetzung zum Schreiben einer Cocoa-App für OS X, aber es verringert die Schwierigkeit wesentlich. Sofern Sie nicht vorhaben, eine X-Window-Anwendung zu schreiben und sich beim Rendern auf Quartz oder einen anderen Fenstermanager zu verlassen, wird Pure-C so gut wie nicht passieren. Minimalistischer Kakao mit Objective-C dann ist es machbar, Ihre Kernlogik in C zu vergraben, aber am Ende fällt es Ihnen vielleicht leichter, am Ende einfach nur Objective-C zu verwenden.

    – WhozCraig

    15. Mai 2015 um 22:04

  • Was möchten Sie im Fenster anzeigen? Statischer Text, bearbeitbarer Text, UI-Steuerelemente, 3D-Grafiken …?

    – JWWalker

    15. Mai 2015 um 23:31 Uhr

  • @JWWalker-Leinwand, auf der ich zeichnen werde

    – Mago

    15. Mai 2015 um 23:36

  • Wenn Sie bereit sind, Ihre Zeichnungen mit OpenGL zu erstellen, können Sie das GLUT-Framework verwenden. Sehen glutInit, glutCreateWindow usw.

    – JWWalker

    16. Mai 2015 um 0:21

  • @Mago Diese Frage ist ein Duplikat der anderen Frage, da sie dasselbe fragt – wie man eine Cocoa-App in C schreibt. Die zugrunde liegende Technik für iOS und OS X ist dieselbe – verwenden Sie die Laufzeit-API. In dieser Frage gibt es sogar ein OS X-Beispiel. Diese Frage ist also ein Duplikat.

    – SevenBits

    16. Mai 2015 um 12:33


Hasens Benutzeravatar
hasen

Ich habe eine Übersetzung der akzeptierten Antwort auf Pure C gemacht:

// based on https://stackoverflow.com/a/30269562
// Minimal Pure C code to create a window in Cocoa

// $ clang minimal.c -framework Cocoa -o minimal.app

#include <objc/runtime.h>
#include <objc/message.h>

#include <Carbon/Carbon.h>

#define cls objc_getClass
#define sel sel_getUid
#define msg ((id (*)(id, SEL, ...))objc_msgSend)
#define cls_msg ((id (*)(Class, SEL, ...))objc_msgSend)

// poor man's bindings!
typedef enum NSApplicationActivationPolicy {
    NSApplicationActivationPolicyRegular   = 0,
    NSApplicationActivationPolicyAccessory = 1,
    NSApplicationActivationPolicyERROR     = 2,
} NSApplicationActivationPolicy;

typedef enum NSWindowStyleMask {
    NSWindowStyleMaskBorderless     = 0,
    NSWindowStyleMaskTitled         = 1 << 0,
    NSWindowStyleMaskClosable       = 1 << 1,
    NSWindowStyleMaskMiniaturizable = 1 << 2,
    NSWindowStyleMaskResizable      = 1 << 3,
} NSWindowStyleMask;

typedef enum NSBackingStoreType {
    NSBackingStoreBuffered = 2,
} NSBackingStoreType;

int main(int argc, char *argv[])
{
    // id app = [NSApplication sharedApplication];
    id app = cls_msg(cls("NSApplication"), sel("sharedApplication"));

    // [app setActivationPolicy:NSApplicationActivationPolicyRegular];
    msg(app, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);

    struct CGRect frameRect = {0, 0, 600, 500};

    // id window = [[NSWindow alloc] initWithContentRect:frameRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
    id window = msg(cls_msg(cls("NSWindow"), sel("alloc")),
                    sel("initWithContentRect:styleMask:backing:defer:"),
                    frameRect,
                    NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable,
                    NSBackingStoreBuffered,
                    false);
    msg(window, sel("setTitle:"), cls_msg(cls("NSString"), sel("stringWithUTF8String:"), "Pure C App"));

    // [window makeKeyAndOrderFront:nil];
    msg(window, sel("makeKeyAndOrderFront:"), nil);

    // [app activateIgnoringOtherApps:YES];
    msg(app, sel("activateIgnoringOtherApps:"), true);

    msg(app, sel("run"));
}

CryingHippos Benutzeravatar
Weinendes Nilpferd

Sie können verwenden Objective-C-Laufzeit-API Beispiel (iOS) Erstellen einer iOS-App in reinem C

Alternativ den gleichen Code in obj-c:

echo '#import <Cocoa/Cocoa.h>
int main ()
    {
        @autoreleasepool{
            [NSApplication sharedApplication];
            [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
            id applicationName = [[NSProcessInfo processInfo] processName];
            id window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 120, 120)
                styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
            [window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
            [window setTitle: applicationName];
            [window makeKeyAndOrderFront:nil];
            [NSApp activateIgnoringOtherApps:YES];
            [NSApp run];
        }
        return 0;
}' | gcc -fobjc-arc -framework Cocoa -x objective-c -o MicroApp - ; ./MicroApp

Dadurch wird die Cocoa-App mit einem Fenster ausgeführt. Wie auf dem Screenshot unten

Geben Sie hier eine Bildbeschreibung ein

Sie können ein Menü tatsächlich mit NSMenu hinzufügen

    id applicationMenuBar = [NSMenu new];
    id appMenuItem        = [NSMenuItem new];
    [applicationMenuBar addItem:appMenuItem];
    [NSApp setMainMenu: applicationMenuBar];

  • Diese Antwort ist falsch. Sie müssen Objective-C nicht für die Benutzeroberfläche verwenden. Sie können die meisten Objective-C-Klassen von C aus mithilfe der Objective-C-Laufzeit-API aufrufen und die UI-Elemente programmgesteuert erstellen. Es ist nicht praktikabel, aber es ist machbar.

    – SevenBits

    15. Mai 2015 um 22:22


  • Könnten Sie bitte ein Beispiel geben?

    – Weinendes Nilpferd

    15. Mai 2015 um 22:24

  • Das gilt für iOS, gilt aber gleichermaßen auch für OS X.

    – SevenBits

    15. Mai 2015 um 22:25

  • Bei diesem Beispielcode handelt es sich nicht um eine „Mischung von C/C++ und Objective-C“, sondern vielmehr rein Ziel c.

    – niemand

    15. Mai 2015 um 22:49

  • Diese Antwort hätte nicht akzeptiert werden dürfen. Es ist nicht die richtige Antwort auf die Frage – dieses Programm ist reines Objective-C, und der OP gab an, dass er das Programm in reinem C (dh ohne Objective-C) schreiben wollte.

    – SevenBits

    16. Mai 2015 um 12:30 Uhr

Kannst du das tun? Ja und nein (das können Sie tun). irgendetwas wenn Sie hartnäckig genug sind). ja du dürfenaber nein du sollte nicht. Unabhängig davon kann dies für die unglaublich Hartnäckigen unter Ihnen getan werden. Da das Codieren eines Beispiels eine Weile dauern wird, habe ich im Internet eine großzügige Seele gefunden, die es bereits getan hat. Suchen in diesem Repository auf GitHub für den vollständigen Code und Erklärungen. Hier einige Ausschnitte:

cmacs_simple_msgSend((id)objc_getClass("NSApplication"), sel_getUid("sharedApplication"));

if (NSApp == NULL) {
    fprintf(stderr,"Failed to initialized NSApplication...  terminating...\n");
    return;
}

id appDelObj = cmacs_simple_msgSend((id)objc_getClass("AppDelegate"), sel_getUid("alloc"));
appDelObj = cmacs_simple_msgSend(appDelObj, sel_getUid("init"));

cmacs_void_msgSend1(NSApp, sel_getUid("setDelegate:"), appDelObj);
cmacs_void_msgSend(NSApp, sel_getUid("run"));

Wie Sie feststellen werden, verwendet dieser Code die Objective-C-Laufzeit-API, um ein falsches AppDelegate zu erstellen. Und das Erstellen des Fensters ist ein komplizierter Prozess:

self->window = cmacs_simple_msgSend((id)objc_getClass("NSWindow"), sel_getUid("alloc"));

/// Create an instance of the window.
self->window = cmacs_window_init_msgSend(self->window, sel_getUid("initWithContentRect:styleMask:backing:defer:"), (CMRect){0,0,1024,460}, (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask), 0, false);

/// Create an instance of our view class.
///
/// Relies on the view having declared a constructor that allocates a class pair for it.
id view = cmacs_rect_msgSend1(cmacs_simple_msgSend((id)objc_getClass("View"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), (CMRect){ 0, 0, 320, 480 });

// here we simply add the view to the window.
cmacs_void_msgSend1(self->window, sel_getUid("setContentView:"), view);
cmacs_simple_msgSend(self->window, sel_getUid("becomeFirstResponder"));

// Shows our window in the bottom-left hand corner of the screen.
cmacs_void_msgSend1(self->window, sel_getUid("makeKeyAndOrderFront:"), self);
return YES;

Also ja. Sie können eine Cocoa-App in reinem C schreiben. Aber ich würde es nicht empfehlen. 90 % dieses Codes können durch eine xib-Datei ersetzt werden, und wenn Sie dies tun, wird Ihre App wirklich eingeschränkt, da erweiterte Funktionen des Apple-Entwicklungsstapels wirklich auf Objective-C-Funktionen basieren. Obwohl es technisch möglich ist, alles auf diese Weise zu machen, machen Sie es viel schwieriger, als es sein sollte.

Benutzeravatar von Tyler Curtis Jowers
Tyler Curtis Jowers

Ich erinnere mich, dass ich diese Frage vor etwa einem Jahr gesehen habe, damals, als ich mir so verzweifelt wünschte, ich könnte das Werbefenster öffnen, tagelang gegoogelt und nur die Art von Antworten gefunden habe, die Sie über diesem Beitrag sehen.

Ich habe mich über das Betriebssystem informiert, auf dem der Mac basiert – Berkley Software Distribution. http://codex.cs.yale.edu/avi/os-book/OS9/appendices-dir/a.pdf Wo mir auf Seite 17 der Satz „…X Windowing System entwickelt am MIT“ ins Auge fiel und ich mich daran erinnerte, wie ich ein Fenster nicht öffnen konnte und wie sauer ich darüber war, und ich dachte, das wäre vielleicht endlich die Lösung!

Ich habe nach „BSD X Window Programming“ gegoogelt und bin zufällig dazu gelangt, endlich ein Fenster in reinem C zu öffnen.

Ich habe es gerade erst entdeckt, also bin ich noch kein Meister, aber schauen Sie sich diesen Link an https://en.wikibooks.org/wiki/X_Window_Programming/Xlib und gehen Sie zum Beispiel. Befolgen Sie unbedingt die Kommentare oben zum Kompilieren mit der X11-Bibliothek (Sie können die Befehle -Wall und -O ignorieren, solange Sie über -lX11 verfügen).

Wenn Sie nicht kompilieren können oder die Header-Dateien nicht finden können, müssen Sie ihm beim Auffinden der Header-Dateien helfen.

Es kann sein, dass sich die X11-Inhalte an verschiedenen Stellen auf Ihrem System befinden. Höchstwahrscheinlich werden Sie es finden /opt/X11/include Hier finden Sie alle Definitionen der benötigten Header.

Sie könnten den vollständigen Pfad in Ihre C-Programme einbinden, z. B.:

#include "/opt/X11/include/X11/Xlib.h"

Aber wir wollen, dass es so aussieht #include <X11/Xlib.h>
Sie können diesen Schalter also beim Kompilieren zu GCC hinzufügen -I /opt/X11/include

Oder gehen Sie zu Ihrem .profile oder .bashrc oder .bash_profile in Ihrem Home-Verzeichnis und fügen Sie hinzu:

export C_INCLUDE_PATH="$C_INCLUDE_PATH:/opt/X11/include"
/*

Einfache Xlib-Anwendung, die ein Feld in einem Fenster zeichnet.

*/

Aus dem Wiki:

#include<X11/Xlib.h>
#include<stdio.h>
#include<stdlib.h> // prevents error for exit on line   18 when compiling with gcc
int main() {
  Display *d;
  int s;
  Window w;
  XEvent e;

                    /* open connection with the server */
  d=XOpenDisplay(NULL);
  if(d==NULL) {
    printf("Cannot open display\n");
    exit(1);
  }
  s=DefaultScreen(d);

                    /* create window */
  w=XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
                     BlackPixel(d, s), WhitePixel(d, s));

  // Process Window Close Event through event handler so      XNextEvent does Not fail
  Atom delWindow = XInternAtom( d, "WM_DELETE_WINDOW", 0 );
  XSetWMProtocols(d , w, &delWindow, 1);

                    /* select kind of events we are interested in */
  XSelectInput(d, w, ExposureMask | KeyPressMask);

                    /* map (show) the window */
  XMapWindow(d, w);

                    /* event loop */
  while(1) {
    XNextEvent(d, &e);
                    /* draw or redraw the window */
    if(e.type==Expose) {
      XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
    }
                    /* exit on key press */
    if(e.type==KeyPress)
      break;

    // Handle Windows Close Event
    if(e.type==ClientMessage)
       break;
  }

                    /* destroy our window */
  XDestroyWindow(d, w);

                    /* close connection to server */
  XCloseDisplay(d);

  return 0;
}

Kompilieren:

 gcc -O2 -Wall -o test test.c -L /usr/X11R6/lib -lX11 -lm

Benutzeravatar von Meownoid
miauen

Leider funktioniert die am besten bewertete Antwort aufgrund einer ABI-Nichtübereinstimmung nicht auf neuen Apple Silicon-Geräten. Grundsätzlich können Sie auf ARM64 die objc_msgSend-Deklaration nicht mit variablen Argumenten verwenden, Sie müssen für jeden Aufruf die richtigen Argumenttypen angeben. Hier ist die Version, die auf Apple Silicon läuft:

// based on https://stackoverflow.com/a/59596600/834108
// Minimal Pure C code to create a window in Cocoa
// Adapted to work on ARM64

// $ clang minimal.c -framework Cocoa -o minimal.app

#include <objc/runtime.h>
#include <objc/message.h>

#include <Carbon/Carbon.h>

#define cls objc_getClass
#define sel sel_getUid
#define msg ((id (*)(id, SEL))objc_msgSend)
#define msg_int ((id (*)(id, SEL, int))objc_msgSend)
#define msg_id  ((id (*)(id, SEL, id))objc_msgSend)
#define msg_ptr ((id (*)(id, SEL, void*))objc_msgSend)
#define msg_cls ((id (*)(Class, SEL))objc_msgSend)
#define msg_cls_chr ((id (*)(Class, SEL, char*))objc_msgSend)

// poor man's bindings!
typedef enum NSApplicationActivationPolicy {
    NSApplicationActivationPolicyRegular   = 0,
    NSApplicationActivationPolicyAccessory = 1,
    NSApplicationActivationPolicyERROR     = 2,
} NSApplicationActivationPolicy;

typedef enum NSWindowStyleMask {
    NSWindowStyleMaskBorderless     = 0,
    NSWindowStyleMaskTitled         = 1 << 0,
    NSWindowStyleMaskClosable       = 1 << 1,
    NSWindowStyleMaskMiniaturizable = 1 << 2,
    NSWindowStyleMaskResizable      = 1 << 3,
} NSWindowStyleMask;

typedef enum NSBackingStoreType {
    NSBackingStoreBuffered = 2,
} NSBackingStoreType;

int main(int argc, char *argv[])
{
    // id app = [NSApplication sharedApplication];
    id app = msg_cls(cls("NSApplication"), sel("sharedApplication"));

    // [app setActivationPolicy:NSApplicationActivationPolicyRegular];
    msg_int(app, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);

    struct CGRect frameRect = {0, 0, 600, 500};

    // id window = [[NSWindow alloc] initWithContentRect:frameRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
    id window = ((id (*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)(
        msg_cls(cls("NSWindow"), sel("alloc")),
        sel("initWithContentRect:styleMask:backing:defer:"),
        frameRect,
        NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable,
        NSBackingStoreBuffered,
        false
    );
    msg_id(window, sel("setTitle:"), msg_cls_chr(cls("NSString"), sel("stringWithUTF8String:"), "Pure C App"));

    // [window makeKeyAndOrderFront:nil];
    msg_ptr(window, sel("makeKeyAndOrderFront:"), nil);

    // [app activateIgnoringOtherApps:YES];
    msg_int(app, sel("activateIgnoringOtherApps:"), true);

    msg(app, sel("run"));
}

Frangs Benutzeravatar
frang

Beispiel für eine reine C-Plattform: (Windows/macOS/Linux)
https://nappgui.com/en/demo/products.html

Informationen zur macOS-Portabilität in reinem C (aktualisiert auf BigSur- und M1-Unterstützung):
https://nappgui.com/en/start/win_mac_linux.html#h2

Benutzeravatar von CRD
CRD

Ich erstelle eine Anwendung in reinem C unter Mac OSX. Ich möchte ein Fenster erstellen, in dem meine App gespeichert wird.

Suchen Sie ein TTY-Fenster?

Wenn ja, muss Ihre Anwendung das Fenster erstellen?

Wenn nicht, können Sie einfach Ihr reines C-Programm schreiben und es im Terminal ausführen – einer TTY-Umgebung für „reines C“.

Wenn Sie eine doppelklickbare App wünschen, können Sie ein AppleScript schreiben, das Terminal öffnet und Ihr C ausführt. Etwas wie:

tell application "Terminal"
   do script "ex /tmp/test; exit"
end tell

Dadurch wird ein Terminalfenster geöffnet, in dem „ex“ angezeigt wird. Wenn dieses Fenster geschlossen wird, wird der Shell-Prozess beendet (es können also keine weiteren Befehle eingegeben werden). Dies geschieht jedoch nicht Schließen Sie das Terminal selbst – dafür müssen Sie sich mehr anstrengen.

Wenn Sie möchten, dass Ihre Anwendung das Fenster selbst erstellt, müssen Sie entweder Ihr eigenes einfaches TTY-Fenster schreiben, möglicherweise finden Sie einige Klassen, die Sie verwenden können, oder Sie können Code von einer Open-Source-Terminal-App ausleihen, z iterm.

HTH

  • Er machte deutlich, dass er möchte, dass C-Code ein OS X-Fenster (dh Cocoa) generiert. Daher glaube ich nicht, dass die Verwendung einer terminalbasierten App für seine Bedürfnisse angemessen wäre.

    – SevenBits

    16. Mai 2015 um 12:36 Uhr

  • @SevenBits – Da ich die Antwort geschrieben habe, sucht der OP tatsächlich nach einer „Leinwand, auf der ich zeichnen werde“; Wenn es sich also nicht um VT100-Grafiken handelt, was ich bezweifle, ist diese Antwort nicht geeignet, wie Sie richtig beobachten. Zukünftige Leser möchten jedoch möglicherweise „TTY-Code“ in einem Fenster unter OS X ausführen, daher habe ich die Antwort hinterlassen.

    – CRD

    16. Mai 2015 um 17:05 Uhr

1454100cookie-checkErstellen einer Fensteranwendung in reinem C unter MacOS

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy