Fensteranwendung in pure c auf mac osx erstellen

Lesezeit: 11 Minuten

Benutzer-Avatar
Mago

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

Vorzugsweise 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 initieren und dann den Kontext an meinen C-Code zu senden, ist das in Ordnung.

Ich verwende kein xcode, nur einen einfachen Texteditor, in dem ich versucht habe, Kakao zu importieren, aber es hat nur viele Fehler erzeugt.

Zusammenfassend lautet meine Frage also: Wie in einfachem reinem C Code generieren, der das OSX-Fenster anzeigt?

  • Xcode ist keine Voraussetzung für das Schreiben einer Cocoa-App für OS X, aber es verringert die Schwierigkeit wesentlich. Wenn Sie nicht vorhaben, eine X-Window-Anwendung zu schreiben und sich für Ihr Rendering auf Quartz oder einen anderen Window-Manager 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 letztendlich finden Sie vieles davon möglicherweise einfacher, am Ende einfach nur Objective-C zu verwenden.

    – WhozCraig

    15. Mai 2015 um 22:04 Uhr

  • mögliches Duplikat von Wie man eine iOS-App rein in C schreibt

    – SevenBits

    15. Mai 2015 um 22:23 Uhr

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

    – JWWalker

    15. Mai 2015 um 23:31 Uhr

  • @SevenBits nicht genau, ich versuche auf osx, dass die Antwort für ios ist

    – Mago

    15. Mai 2015 um 23:35 Uhr

  • @JWWalker-Leinwand, auf der ich zeichnen werde

    – Mago

    15. Mai 2015 um 23:36 Uhr

Benutzer-Avatar
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"));
}

Benutzer-Avatar
Schreiendes 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 1 Fenster ausgeführt. Wie auf dem Screenshot unten

Geben Sie hier die Bildbeschreibung ein

Sie können Menüs 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 mithilfe der Objective-C-Laufzeit-API aufrufen und die UI-Elemente programmgesteuert erstellen. Es ist nicht praktikabel, aber es kann getan werden.

    – SevenBits

    15. Mai 2015 um 22:22 Uhr


  • Könnten Sie bitte ein Beispiel geben?

    – Weinendes Nilpferd

    15. Mai 2015 um 22:24 Uhr

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

    – SevenBits

    15. Mai 2015 um 22:25 Uhr

  • Dieser Beispielcode “mischt nicht C/C++ und Objective-C”, sondern rein Ziel c.

    – niemand

    15. Mai 2015 um 22:49 Uhr

  • 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 das OP gab an, dass er das Programm in reinem C schreiben wollte (dh ohne Objective-C).

    – SevenBits

    16. Mai 2015 um 12:30 Uhr

Kannst du das tun? Ja und nein (Sie können tun irgendetwas wenn du hartnäckig genug bist). ja du kannaber nein du sollte nicht. Unabhängig davon kann dies für die unglaublich Hartnäckigen unter Ihnen getan werden. Da das Programmieren eines Beispiels eine Weile dauern wird, habe ich im Netz eine großzügige Seele gefunden, die es bereits getan hat. Aussehen in diesem Repository auf GitHub für den vollständigen Code und Erklärungen. Hier sind 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 einen falschen 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 auf diese Weise tun, wird Ihre App wirklich eingeschränkt, da fortgeschrittenere Funktionen des Apple-Entwicklungsstapels wirklich auf Objective-C-Funktionen basieren. Obwohl es technisch möglich ist, alles auf diese Weise zu tun, machen Sie es sich viel schwerer, als es sein sollte.

Benutzer-Avatar
Tyler Curtis Jowers

Ich erinnere mich, dass ich diese Frage vor ungefähr einem Jahr gesehen habe, damals, als ich mir so verzweifelt wünschte, ich könnte ein Ad***-Fenster öffnen, tagelang googeln und nur die Art von Antworten finden, die Sie über diesem Beitrag sehen.

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

Ich googelte “BSD X Window Programming” und stolperte meinen Weg, endlich ein Fenster in reinem C zu öffnen.

Ich habe es gerade entdeckt, also bin ich noch kein Meister, aber schau dir diesen Link an https://en.wikibooks.org/wiki/X_Window_Programming/Xlib und gehen Sie zum Beispiel, achten Sie darauf, den Kommentaren oben zu folgen, um zu erfahren, wie Sie mit der X11-Bibliothek kompilieren (Sie können die Befehle -Wall und -O ignorieren, solange Sie das -lX11 haben).

Wenn Sie nicht kompilieren können, wenn es die Header-Dateien nicht finden kann, müssen Sie ihm helfen, die Header-Dateien zu finden.

Es könnte ein paar verschiedene Orte geben, die der X11 auf Ihrem System enthalten könnte. Höchstwahrscheinlich finden Sie es in /opt/X11/include die alle Definitionen der Header enthält, die Sie benötigen.

Sie könnten den vollständigen Pfad in Ihre C-Programme aufnehmen, wie zum Beispiel:

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

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

Oder geh zu dir .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 eine Box 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

Reines plattformübergreifendes C-Beispiel: (Windows/macOS/Linux)
https://nappgui.com/en/demo/products.html

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

Leider funktioniert die am besten bewertete Antwort aufgrund einer ABI-Nichtübereinstimmung nicht auf neuen Apple Silicon-Computern. Grundsätzlich können Sie auf ARM64 die objc_msgSend-Deklaration nicht mit variablen Argumenten verwenden, Sie müssen die richtigen Argumenttypen für jeden Aufruf 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"));
}

Benutzer-Avatar
CRD

Ich erstelle eine Anwendung in reinem C unter Mac OSX. Was ich möchte, ist ein Fenster zu 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 innerhalb von Terminal ausführen – einer TTY-Umgebung für “reines C”.

Wenn Sie eine doppelklickbare App möchten, können Sie ein AppleScript schreiben, das Terminal öffnet und Ihr C ausführt. So etwas wie:

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

Dies öffnet ein Terminalfenster, das “ex” anzeigt, und wenn es beendet wird, wird der Shell-Prozess beendet (so dass keine weiteren Befehle eingegeben werden können), aber es wird nicht schließen Sie das Terminal selbst – dafür müssen Sie härter arbeiten.

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

HTH

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

    – SevenBits

    16. Mai 2015 um 12:36 Uhr

  • @SevenBits – Da ich die Antwort geschrieben habe, hat das OP tatsächlich geschrieben, dass er nach einer “Leinwand sucht, auf der ich zeichnen werde”; Wenn sie also keine VT100-Grafik meinen, 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

1365140cookie-checkFensteranwendung in pure c auf mac osx erstellen

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

Privacy policy