在MacOS上用纯C创建窗口应用程序

rqdpfwrv  于 2023-05-16  发布在  Mac
关注(0)|答案(7)|浏览(183)

我正在Mac OSX上用纯C语言创建一个应用程序。我想要的是创建一个窗口,在其中显示我的应用程序。
我希望它是纯C的解决方案,但如果我必须使用Objective-C类来初始化窗口,然后向我的C代码发送上下文,那么就可以了。
我没有使用Xcode,只是一个简单的文本编辑器,我试图导入可可,但它产生了很多错误。
如何编写代码在简单的纯C代码,将显示一个窗口在OSX?

v1uwarro

v1uwarro1#

我翻译了Pure C的公认答案:

// 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"));
}
k4aesqcs

k4aesqcs2#

您可以使用Objective-C runtime API示例(iOS)Creating an iOS app in pure C
在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

这将运行带有1个窗口的可可应用程序。就像下面的截图

实际上,您可以使用NSMenu添加菜单

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

pvabu6sv3#

你能做到吗是和不是(如果你足够坚持,你可以做任何事情)。是的,你可以,但你不应该。无论如何,这可以为你们中间令人难以置信的持久性做。由于编写一个例子需要一段时间,我在网上找到了一个慷慨的人,他已经做了。查看at this repository on GitHub以获得完整的代码和解释。以下是一些片段:

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

正如您所注意到的,这段代码使用Objective-C运行时API来创建一个伪AppDelegate。创建窗口是一个复杂的过程:

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;

所以,是的。你可以用纯C写一个可可应用程序。但我不建议你这么做。90%的代码可以被xib文件替换,这样做确实限制了你的应用程序,因为苹果开发堆栈的更高级功能实际上是在Objective-C功能上。虽然从技术上讲,用这种方式做所有事情都是可能的,但你让它变得比应该的要困难得多。

yws3nbqq

yws3nbqq4#

我记得大约一年前看到这个问题,当时我非常希望我能打开一个d***窗口,谷歌搜索了几天,只找到你在这篇文章上面看到的答案类型。
我正在阅读Mac的操作系统--伯克利软件发行版。http://codex.cs.yale.edu/avi/os-book/OS9/appendices-dir/a.pdf在第17页上的短语“...麻省理工学院开发的X窗口系统”击中了我,我记得我是如何无法打开一个窗口,我是多么生气,我想也许这是最终的解决方案!
我在谷歌上搜索了“BSDXWindowProgramming”,终于用纯C打开了一个窗口。
我刚刚发现它,所以我还不是一个大师,但看看这个链接https://en.wikibooks.org/wiki/X_Window_Programming/Xlib并转到示例,确保遵循顶部的注解,了解如何使用X11库进行编译(只要你有-lX 11,你就可以忽略-Wall和-O命令)。
如果你不能编译,如果它找不到头文件,你就需要帮助它找到头文件。
X11可能在您的系统上包含几个不同的位置。您很可能会在/opt/X11/include中找到它,它将包含您需要的所有头文件的定义。
您可以在C程序中包含完整路径,例如:

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

但是我们希望它看起来像这样#include <X11/Xlib.h>,所以你可以在编译-I /opt/X11/include时将这个开关添加到GCC中。
或者转到主目录中的.profile.bashrc.bash_profile并添加:

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

简单的Xlib应用程序在窗口中绘制一个框。

  • /

来自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;
}

编译:

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

rbpvctlc5#

不幸的是,由于ABI不匹配,最高评级的答案在新的苹果硅动力机器上不起作用。基本上在ARM64上,你不能使用带有变量参数的objc_msgSend声明,你必须为每个调用指定正确的参数类型。以下是在Apple Silicon上运行的版本:

// 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"));
}
a2mppw5e

a2mppw5e6#

纯C跨平台示例:(Windows/macOS/Linux)https://nappgui.com/en/demo/products.html
关于macOS在纯C中的可移植性(已更新为BigSur和M1支持):https://nappgui.com/en/start/win_mac_linux.html#h2

jqjz2hbq

jqjz2hbq7#

我正在Mac OSX上用纯C语言创建一个应用程序。我想要的是创建一个窗口,我的应用程序将被存储。
您是否正在寻找TTY窗口?
如果是这样,你的应用程序需要创建窗口吗?
如果没有,那么你可以简单地编写你的纯C程序,并从终端中执行它-一个“纯C”的TTY环境。
如果你想要一个可双击的应用程序,你可以写一个AppleScript,它将打开终端并运行你的C。类似于:

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

这将打开一个显示“ex”的终端窗口,当它退出时,将终止shell进程(因此无法输入进一步的命令),但它不会关闭终端本身-为此,您将不得不更加努力。
如果你想让你的应用程序自己创建窗口,你要么需要自己写一个简单的TTY窗口,你可以找到一些你可以使用的类,或者你可以从一个开源的终端应用程序(如iterm)中借用代码。
高温加热

相关问题