在mac osx上用pure c创建窗口应用程序

时间:2015-05-15 21:54:27

标签: c macos cocoa

我在Mac OSX上用纯C创建一个应用程序。 我想要的是创建一个窗口,我的应用程序将存储。

最好我希望它是纯C解决方案,但是如果我必须使用objective-c类来初始化窗口然后将上下文发送到我的C代码那么它就没问题了。

我没有使用xcode,只有简单的文本编辑器,我试图导入cocoa但它只是产生了很多错误。

总而言之,我的问题是: 如何在简单的纯C中生成将显示osx窗口的代码?

6 个答案:

答案 0 :(得分:9)

您可以使用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个窗口的Cocoa应用程序。比如下面的截图

enter image description here

您实际上可以使用NSMenu

添加菜单
    id applicationMenuBar = [NSMenu new];
    id appMenuItem        = [NSMenuItem new];
    [applicationMenuBar addItem:appMenuItem];
    [NSApp setMainMenu: applicationMenuBar];

答案 1 :(得分:9)

你能做到吗?是和否(如果你足够坚持,你可以做任何事情)。是的,可以,但不是不应该。无论如何,这可以为你们中的难以置信的持久性做到。由于编写一个例子需要一段时间,我在网上找到了一个慷慨的灵魂已经做到了。查看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写一个Cocoa应用程序。但是我不推荐它。 90%的代码可以被xib文件替换,这样做实际上限制了你的应用程序,因为Apple开发堆栈的更多高级功能确实在Objective-C功能上。虽然技术上可以通过这种方式完成所有事情,但是你做得比它应该更难。

答案 2 :(得分:5)

我翻译了接受的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"));
}

答案 3 :(得分:1)

  

我在Mac OSX上用纯C创建应用程序。我想要的是创建一个存储我的应用程序的窗口。

您在寻找TTY窗口吗?

如果是,那么您的应用程序是否需要创建窗口?

如果没有,那么您可以简单地编写纯C程序并在终端内执行 - 一个TTY环境,用于&#34;纯C&#34;。

如果您想要一个可双击的应用程序,您可以编写AppleScript,它将打开终端并运行您的C.类似于:

$string = '<p style="position:absolute;top:98px;left:472px;white-space:nowrap">';

 $test = str_replace('left:', 'display:none;[', $string );
 $test = str_replace('white-space', ']white-space', $test );
 $out = delete_all_between('[', ']', $test);
 print($out); // output

function delete_all_between($beginning, $end, $string) {
  $beginningPos = strpos($string, $beginning);
  $endPos = strpos($string, $end);
  if ($beginningPos === false || $endPos === false) {
    return $string;
  }

  $textToDelete = substr($string, $beginningPos, ($endPos + strlen($end)) - $beginningPos);

  return str_replace($textToDelete, '', $string);
}

这将打开一个终端窗口,显示&#34; ex&#34;当退出时将终止shell进程(因此不能输入其他命令),但它关闭终端本身 - 为此你必须更加努力。

如果您确实希望应用程序创建窗口本身,您需要编写自己的简单TTY窗口,您可能会找到一些可以使用的类,或者您可以从开源终端应用程序借用代码,例如iterm

HTH

答案 4 :(得分:1)

我记得大约一年前看到这个问题,当时我非常希望我可以打开d ***窗口,谷歌搜索几天,只找到您在这篇文章上方看到的答案类型。

我正在阅读Mac所基于的操作系统-Berkley Software Distribution。 http://codex.cs.yale.edu/avi/os-book/OS9/appendices-dir/a.pdf在第17页上的那句话“ ... X由MIT开发的Windowing System”打在我身上,我想起了我无法打开窗户,以及对此的不满,我以为这可能是终于解决了!

我用Google搜索“ BSD X窗口编程”,偶然发现了最终用纯C语言打开窗口的方法。

我刚刚发现它,所以我还不是一个大师,但请查看此链接https://en.wikibooks.org/wiki/X_Window_Programming/Xlib并转到示例,请确保遵循顶部的注释以了解如何使用X11库进行编译(您可以只要拥有-lX11,就可以忽略-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

答案 5 :(得分:0)

纯C跨平台示例:(Windows/macOS/Linux) https://nappgui.com/en/demo/products.html

关于纯 C 语言的 macOS 可移植性(更新到 BigSur 和 M1 支持): https://nappgui.com/en/start/win_mac_linux.html#h2