首页 > 后端开发 > C++ > 正文

RGFW 幕后花絮:纯 C 中的可可

WBOY
发布: 2024-08-27 06:40:02
原创
853 人浏览过

RGFW Under the Hood: Cocoa in Pure C

介绍

要使用Apple的Cocoa API,您必须使用Objective-C函数调用。但是,您不必编写 Objective-C 代码,因为可以通过 objc_msgSend 等 C 函数访问 Objective-C。

使用 Pure-C 而不是 Objective-C 的主要原因是能够用 C 语言编译你的项目。如果你想创建一个不需要用户使用 Objective-进行编译的单头文件,这会很有帮助 - C.

这方面的两个例子是:

Silicon.h,一个用于 Cocoa API 的 C 包装器,它包装了 Objective-C 函数调用,以便您可以在普通的 C 代码中使用 Cocoa API,以及 RGFW,一个轻量级单头窗口库。

这两个项目都可以作为在 C 语言中使用 Cocoa 的参考。

概述

本文将涵盖的主题的快速概述

1) 在纯 C 中使用 Objective-C 的基础知识
2) 定义可可类型
3) 创建一个基本的 Cocoa 窗口

1. 在 Pure C 中使用 Objective-C 的基础知识

可以使用 objc_msgsend 调用 Objective-C 函数。

由于 ABI 差异,ARM 在所有情况下都使用 objc_msgsend。然而,x86_64 CPU 需要使用特定的浮点和结构返回函数。
objc_msgsend_fpret 用于具有浮点返回值和
的函数 objc_msgsend_fstret 用于返回结构的函数。

RGFW 是这样处理的:

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

#ifdef __arm64__
/* ARM just uses objc_msgSend */
#define abi_objc_msgSend_stret objc_msgSend
#define abi_objc_msgSend_fpret objc_msgSend
#else /* __i386__ */
/* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
#define abi_objc_msgSend_stret objc_msgSend_stret
#define abi_objc_msgSend_fpret objc_msgSend_fpret
#endif
登录后复制

objc_msgSend 是一个泛型函数,因此它的类型必须根据您想要的返回和参数类型进行转换。

例如: ((int (*)(id, SEL, int))objc_msgSend) 用于接受 int 参数并返回 int 的函数。

为了避免重复常用的类型转换,RGFW 定义了宏来处理常见情况。

#define objc_msgSend_id             ((id (*)(id, SEL))objc_msgSend)
#define objc_msgSend_id_id          ((id (*)(id, SEL, id))objc_msgSend)
#define objc_msgSend_id_rect        ((id (*)(id, SEL, NSRect))objc_msgSend)
#define objc_msgSend_uint           ((NSUInteger (*)(id, SEL))objc_msgSend)
#define objc_msgSend_int            ((NSInteger (*)(id, SEL))objc_msgSend)
#define objc_msgSend_SEL            ((SEL (*)(id, SEL))objc_msgSend)
#define objc_msgSend_float          ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)
#define objc_msgSend_bool           ((BOOL (*)(id, SEL))objc_msgSend)
#define objc_msgSend_void           ((void (*)(id, SEL))objc_msgSend)
#define objc_msgSend_double         ((double (*)(id, SEL))objc_msgSend)
#define objc_msgSend_void_id        ((void (*)(id, SEL, id))objc_msgSend)
#define objc_msgSend_void_uint      ((void (*)(id, SEL, NSUInteger))objc_msgSend)
#define objc_msgSend_void_int       ((void (*)(id, SEL, NSInteger))objc_msgSend)
#define objc_msgSend_void_bool      ((void (*)(id, SEL, BOOL))objc_msgSend)
#define objc_msgSend_void_float     ((void (*)(id, SEL, CGFloat))objc_msgSend)
#define objc_msgSend_void_double    ((void (*)(id, SEL, double))objc_msgSend)
#define objc_msgSend_void_SEL       ((void (*)(id, SEL, SEL))objc_msgSend)
#define objc_msgSend_id_char_const  ((id (*)(id, SEL, char const *))objc_msgSend)
登录后复制

您可能会注意到这些函数中的两个常见参数,id 和 SEL

id 参数指的是 Objective-C 对象或类的 ID。

SEL 指的是函数的选择器。

例如 MyObject *bar = [MyObject objectWithString:@"RGFW"];可以翻译为 id* bar = [id SEL:@"RGFW"];

要获取 Objective-C 类的 ID,您必须运行 objc_getClass

例如:objc_getClass("NSWindow");

要获取 Objective-C 函数的选择器,您必须使用 sel_registerName

Objective-C 函数的语法是这样的 ,然后 : 用作参数的占位符。

具有一个参数的函数如下所示:

sel_registerName("makeKeyAndOrderFront:");

但是,没有参数的函数看起来像这样。

sel_registerName("isKeyWindow");

如果函数有多个参数,您还必须添加参数名称(不包括第一个参数)

sel_registerName("initWithContentRect:styleMask:backing:defer:");

要定义类方法(例如对象的回调函数),您必须使用 class_addMethod 该函数采用委托类(正在调用委托的对象的类
)、被调用函数的选择器、要调用的函数以及字符串格式的参数。

但首先,您必须分配委托类才能访问它。
您可以使用 objc_allocateClassPair 来完成此操作。

例如为 NSWindow 分配委托类:

Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
登录后复制

要创建回调,您必须使用 class_addMethod 将其设置为类的回调。

为 NSWindow 的 windowWillResize 创建回调将如下所示:

class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) windowResize, "{NSSize=ff}@:{NSSize=ff}");
登录后复制

您还可以向类添加自定义用户变量,这些变量可用于将用户数据附加到类。

例如,RGFW 将 RGFW_window 附加到 NSWindow 类来修改 RGFW_window* 数据。这可以使用 class_addIvar
来完成

class_addIvar(
    delegateClass, "RGFW_window",
    sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))),
    "L" 
);
登录后复制

要设置用于 NSWindow 实例的变量,请使用

object_setInstanceVariable

将 RGFW_window 对象设置为其 NSWindow 对象的变量实例:

object_setInstanceVariable(delegate, "NSWindow", window);

可以通过object_getInstanceVariable获取对象的实例变量

RGFW_window* win = NULL;

//The object variable would be called "self" in a callback
object_getInstanceVariable(self, "RGFW_window", (void*)&win);
登录后复制

定义可可类型

Cocoa 头文件是用 Objective-C 编写的。这意味着我们必须自己定义类型和枚举。

形状类型可以定义为 CG 形状,如下所示:

typedef CGRect NSRect;
typedef CGPoint NSPoint;
typedef CGSize NSSize;
登录后复制

Cocoa 还使用自定义整数类型名称,
这些可以定义为它们匹配的 c 数据类型。

typedef unsigned long NSUInteger;
typedef long NSInteger;
登录后复制

您还可以定义 Cocoa 对象。我将它们定义为 void,这样我就可以将它们用作指针。

// Note: void is being used here 
// type* can be used ( type* == void* == id ) 
typedef void NSEvent;
typedef void NSString;
typedef void NSWindow;  
typedef void NSApplication;
登录后复制

至于枚举,这里是我将在本教程中使用的一些枚举。

Many of the other enums can be found in Cocoa's headers, documentation, RGFW.h, or Silicon. h.

/* this macro is used to give an enum a type */ 
#define NS_ENUM(type, name) type name; enum

typedef NS_ENUM(NSUInteger, NSWindowStyleMask) {
    NSWindowStyleMaskBorderless = 0,
    NSWindowStyleMaskTitled = 1 << 0,
    NSWindowStyleMaskClosable = 1 << 1,
    NSWindowStyleMaskMiniaturizable = 1 << 2,
    NSWindowStyleMaskResizable = 1 << 3,
    NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
    NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
    NSWindowStyleMaskFullScreen = 1 << 14,
    NSWindowStyleMaskFullSizeContentView = 1 << 15,
    NSWindowStyleMaskUtilityWindow = 1 << 4,
    NSWindowStyleMaskDocModalWindow = 1 << 6,
    NSWindowStyleMaskNonactivatingPanel = 1 << 7,
    NSWindowStyleMaskHUDWindow = 1 << 13
};

typedef NS_ENUM(NSUInteger, NSBackingStoreType) {
    NSBackingStoreRetained = 0,
    NSBackingStoreNonretained = 1,
    NSBackingStoreBuffered = 2
};

typedef NS_ENUM(NSUInteger, NSEventType) {        /* various types of events */
    NSEventTypeLeftMouseDown             = 1,
    NSEventTypeLeftMouseUp               = 2,
    NSEventTypeRightMouseDown            = 3,
    NSEventTypeRightMouseUp              = 4,
    NSEventTypeMouseMoved                = 5,
    NSEventTypeLeftMouseDragged          = 6,
    NSEventTypeRightMouseDragged         = 7,
    NSEventTypeMouseEntered              = 8,
    NSEventTypeMouseExited               = 9,
    NSEventTypeKeyDown                   = 10,
    NSEventTypeKeyUp                     = 11,
    NSEventTypeFlagsChanged              = 12,
    NSEventTypeAppKitDefined             = 13,
    NSEventTypeSystemDefined             = 14,
    NSEventTypeApplicationDefined        = 15,
    NSEventTypePeriodic                  = 16,
    NSEventTypeCursorUpdate              = 17,
    NSEventTypeScrollWheel               = 22,
    NSEventTypeTabletPoint               = 23,
    NSEventTypeTabletProximity           = 24,
    NSEventTypeOtherMouseDown            = 25,
    NSEventTypeOtherMouseUp              = 26,
    NSEventTypeOtherMouseDragged         = 27,
    /* The following event types are available on some hardware on 10.5.2 and later */
    NSEventTypeGesture API_AVAILABLE(macos(10.5))       = 29,
    NSEventTypeMagnify API_AVAILABLE(macos(10.5))       = 30,
    NSEventTypeSwipe   API_AVAILABLE(macos(10.5))       = 31,
    NSEventTypeRotate  API_AVAILABLE(macos(10.5))       = 18,
    NSEventTypeBeginGesture API_AVAILABLE(macos(10.5))  = 19,
    NSEventTypeEndGesture API_AVAILABLE(macos(10.5))    = 20,

    NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32,
    NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33,

    NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34,
    NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37,

    NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38,
};

typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */
    NSEventMaskLeftMouseDown         = 1ULL << NSEventTypeLeftMouseDown,
    NSEventMaskLeftMouseUp           = 1ULL << NSEventTypeLeftMouseUp,
    NSEventMaskRightMouseDown        = 1ULL << NSEventTypeRightMouseDown,
    NSEventMaskRightMouseUp          = 1ULL << NSEventTypeRightMouseUp,
    NSEventMaskMouseMoved            = 1ULL << NSEventTypeMouseMoved,
    NSEventMaskLeftMouseDragged      = 1ULL << NSEventTypeLeftMouseDragged,
    NSEventMaskRightMouseDragged     = 1ULL << NSEventTypeRightMouseDragged,
    NSEventMaskMouseEntered          = 1ULL << NSEventTypeMouseEntered,
    NSEventMaskMouseExited           = 1ULL << NSEventTypeMouseExited,
    NSEventMaskKeyDown               = 1ULL << NSEventTypeKeyDown,
    NSEventMaskKeyUp                 = 1ULL << NSEventTypeKeyUp,
    NSEventMaskFlagsChanged          = 1ULL << NSEventTypeFlagsChanged,
    NSEventMaskAppKitDefined         = 1ULL << NSEventTypeAppKitDefined,
    NSEventMaskSystemDefined         = 1ULL << NSEventTypeSystemDefined,
    NSEventMaskApplicationDefined    = 1ULL << NSEventTypeApplicationDefined,
    NSEventMaskPeriodic              = 1ULL << NSEventTypePeriodic,
    NSEventMaskCursorUpdate          = 1ULL << NSEventTypeCursorUpdate,
    NSEventMaskScrollWheel           = 1ULL << NSEventTypeScrollWheel,
    NSEventMaskTabletPoint           = 1ULL << NSEventTypeTabletPoint,
    NSEventMaskTabletProximity       = 1ULL << NSEventTypeTabletProximity,
    NSEventMaskOtherMouseDown        = 1ULL << NSEventTypeOtherMouseDown,
    NSEventMaskOtherMouseUp          = 1ULL << NSEventTypeOtherMouseUp,
    NSEventMaskOtherMouseDragged     = 1ULL << NSEventTypeOtherMouseDragged,
};
/* The following event masks are available on some hardware on 10.5.2 and later */
#define NSEventMaskGesture API_AVAILABLE(macos(10.5))          (1ULL << NSEventTypeGesture)
#define NSEventMaskMagnify API_AVAILABLE(macos(10.5))          (1ULL << NSEventTypeMagnify)
#define NSEventMaskSwipe API_AVAILABLE(macos(10.5))            (1ULL << NSEventTypeSwipe)
#define NSEventMaskRotate API_AVAILABLE(macos(10.5))           (1ULL << NSEventTypeRotate)
#define NSEventMaskBeginGesture API_AVAILABLE(macos(10.5))     (1ULL << NSEventTypeBeginGesture)
#define NSEventMaskEndGesture API_AVAILABLE(macos(10.5))       (1ULL << NSEventTypeEndGesture)

/* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. */
#define NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) (1ULL << NSEventTypeSmartMagnify)
#define NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) (1ULL << NSEventTypePressure)
#define NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) (1ULL << NSEventTypeDirectTouch)
#define NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) (1ULL << NSEventTypeChangeMode)
#define NSEventMaskAny              NSUIntegerMax

typedef NS_ENUM(NSUInteger, NSEventModifierFlags) {
    NSEventModifierFlagCapsLock           = 1 << 16, // Set if Caps Lock key is pressed.
    NSEventModifierFlagShift              = 1 << 17, // Set if Shift key is pressed.
    NSEventModifierFlagControl            = 1 << 18, // Set if Control key is pressed.
    NSEventModifierFlagOption             = 1 << 19, // Set if Option or Alternate key is pressed.
    NSEventModifierFlagCommand            = 1 << 20, // Set if Command key is pressed.
    NSEventModifierFlagNumericPad         = 1 << 21, // Set if any key in the numeric keypad is pressed.
    NSEventModifierFlagHelp               = 1 << 22, // Set if the Help key is pressed.
    NSEventModifierFlagFunction           = 1 << 23, // Set if any function key is pressed.
};
登录后复制

RGFW also defines NSAlloc and NSRelease because they're basic functions that are used a lot.

#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
#define NSRelease(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("release"))
登录后复制

3. Creating a Basic Cocoa Window

Now that you understand the basics of calling Objective-C functions from C, and the setup required to use Cocoa, it's time to apply that for creating a basic window using Cocoa.

First, some library headers are required.

#include <CoreVideo/CVDisplayLink.h> // !
#include <ApplicationServices/ApplicationServices.h>

#include <string.h>
登录后复制

These functions will be used for printing information about the current event.

const char* NSEventTypeToChar(NSEventType eventType);
const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags);

// These will be placed after the main function and will be defined like this:
const char* NSEventTypeToChar(NSEventType eventType) {
    switch (eventType) {
        case NSEventTypeLeftMouseDown: return "LeftMouseDown";
        case NSEventTypeLeftMouseUp: return "LeftMouseUp";
        case NSEventTypeRightMouseDown: return "RightMouseDown";
        case NSEventTypeRightMouseUp: return "RightMouseUp";
        case NSEventTypeMouseMoved: return "MouseMoved";
        case NSEventTypeLeftMouseDragged: return "LeftMouseDragged";
        case NSEventTypeRightMouseDragged: return "RightMouseDragged";
        case NSEventTypeMouseEntered: return "MouseEntered";
        case NSEventTypeMouseExited: return "MouseExited";
        case NSEventTypeKeyDown: return "KeyDown";
        case NSEventTypeKeyUp: return "KeyUp";
        case NSEventTypeFlagsChanged: return "FlagsChanged";
        case NSEventTypeAppKitDefined: return "AppKitDefined";
        case NSEventTypeSystemDefined: return "SystemDefined";
        case NSEventTypeApplicationDefined: return "ApplicationDefined";
        case NSEventTypePeriodic: return "Periodic";
        case NSEventTypeCursorUpdate: return "CursorUpdate";
        case NSEventTypeScrollWheel: return "ScrollWheel";
        case NSEventTypeTabletPoint: return "TabletPoint";
        case NSEventTypeTabletProximity: return "TabletProximity";
        case NSEventTypeOtherMouseDown: return "OtherMouseDown";
        case NSEventTypeOtherMouseUp: return "OtherMouseUp";
        case NSEventTypeOtherMouseDragged: return "OtherMouseDragged";
        default: return "N/A";
    }
}

char* ns_strcat(register char *s, register const char *append) {
    char *save = s;

    for (; *s; ++s);
    while ((*s++ = *append++));
    return save;
}

const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags) {
    static char result[100];
    result[0] = '\0';

    if ((modifierFlags & NSEventModifierFlagCapsLock) == NSEventModifierFlagCapsLock) ns_strcat(result, "CapsLock, ");
    if ((modifierFlags & NSEventModifierFlagShift) == NSEventModifierFlagShift) ns_strcat(result, "NShift, ");
    if ((modifierFlags & NSEventModifierFlagControl) == NSEventModifierFlagControl) ns_strcat(result, "Control, ");
    if ((modifierFlags & NSEventModifierFlagOption) == NSEventModifierFlagOption) ns_strcat(result, "Option, ");
    if ((modifierFlags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand) ns_strcat(result, "Command, ");
    if ((modifierFlags & NSEventModifierFlagNumericPad) == NSEventModifierFlagNumericPad) ns_strcat(result, "NumericPad, ");
    if ((modifierFlags & NSEventModifierFlagHelp) == NSEventModifierFlagHelp) ns_strcat(result, "Help, ");
    if ((modifierFlags & NSEventModifierFlagFunction) == NSEventModifierFlagFunction) ns_strcat(result, "Function, ");

    return result;
}
登录后复制

Cocoa does not handle certain events using the event loop.
Instead, they're fed to the NSWindow via callbacks.

Here's how these functions will be defined for this example:

bool running = true;

unsigned int onClose(void* self) {
    NSWindow* win = NULL;
    object_getInstanceVariable(self, "NSWindow", (void*)&win);
    if (win == NULL)
        return true;

    running = false;

    return true;
}

NSSize windowResize(void* self, SEL sel, NSSize frameSize) {
    NSWindow* win = NULL;
    object_getInstanceVariable(self, "NSWindow", (void*)&win);
    if (win == NULL)
        return frameSize;

    printf("window resized to %f %f\n", frameSize.width, frameSize.height);
    return frameSize;
}
登录后复制

The first thing that I will do in the main function is define the windowShouldClose callback.

This is so that way the program doesn't keep running after the window is closed.

class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP) onClose, 0);
登录后复制

Next, the NSApplication is set up.

This requires the use of sharedApplication and setActivationPolicy

NSApplication* NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
objc_msgSend_void_id(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
登录后复制

Now you can create a NSWindow can be created, I broke the window creation process into three steps so it's more readable.

The window is created using initWithContentRect

NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable;

SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:");

NSWindow* window = ((id (*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
            (NSAlloc(objc_getClass("NSWindow")), func, 
                        (NSRect){{200, 200}, {200, 200}}, 
                        macArgs, macArgs, false);
登录后复制

You can then set up the delegate class and window resize callback.

Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);

class_addIvar(
        delegateClass, "NSWindow",
        sizeof(NSWindow*), rint(log2(sizeof(NSWindow*))),
        "L"
);

class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) windowResize, "{NSSize=ff}@:{NSSize=ff}");
登录后复制

After that, the delegate must be initialized using init

Then I will set the delegate's variable data as our NSWindow and set the NSWindow's delegate to be the delegate we initialized using setDelegate

id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init"));

object_setInstanceVariable(delegate, "NSWindow", window);

objc_msgSend_void_id(window, sel_registerName("setDelegate:"), delegate);
登录后复制

Then the window can be shown using setIsVisible and made key and front via makeKeyAndOrderFront, this puts it in focus.

activatIgnoredOtherApps is required to open the window

objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
((id(*)(id, SEL, SEL))objc_msgSend)(window, sel_registerName("makeKeyAndOrderFront:"), NULL);
objc_msgSend_void_bool(window, sel_registerName("setIsVisible:"), true);

objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
登录后复制

Now, in the draw loop, I'd start by creating a memory pool.

This is so that way all memory allocated by event checking can be freed at once, avoiding a memory leak.

This can be done using NSAutoreleasePool

id pool = objc_msgSend_id(NSAlloc(objc_getClass("NSAutoreleasePool")), sel_registerName("init"));
登录后复制

Now the current event can be checked using an NSEvent object and nextEventMatchingMask

The event type can be found using type
The event mouse point can be found using locationInWindow
The event modifier flags can be found using modifierFlags

NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) (NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), ULONG_MAX, NULL,                           ((id(*)(id, SEL, const char*))objc_msgSend) ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "kCFRunLoopDefaultMode"), true);

unsigned int type = objc_msgSend_uint(e, sel_registerName("type"));  

NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
登录后复制

Before I check the event, I make sure there is an event.

if (type == 0)
    printf("Event [type=%s location={%f, %f} modifierFlags={%s}]\n", 
                        NSEventTypeToChar(type), 
                        p.x, p.y, 
                        NSEventModifierFlagsToChar(objc_msgSend_uint(e, sel_registerName("modifierFlags"))));

登录后复制

The event can be pushed out using sendEvent and the window can be updated using updateWindows

objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
登录后复制

At the end of the draw loop, the event pool should be freed.

NSRelease(pool);
登录后复制

Full code example

// compile with:
// gcc example.c -lm -framework Foundation -framework AppKit -framework CoreVideo

#include 
#include 
#include 
#include 

#ifdef __arm64__
/* ARM just uses objc_msgSend */
#define abi_objc_msgSend_stret objc_msgSend
#define abi_objc_msgSend_fpret objc_msgSend
#else /* __i386__ */
/* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
#define abi_objc_msgSend_stret objc_msgSend_stret
#define abi_objc_msgSend_fpret objc_msgSend_fpret
#endif

typedef CGRect NSRect;
typedef CGPoint NSPoint;
typedef CGSize NSSize;

typedef void NSEvent;
typedef void NSString;
typedef void NSWindow;  
typedef void NSApplication;

typedef unsigned long NSUInteger;
typedef long NSInteger;

#define NS_ENUM(type, name) type name; enum 

typedef NS_ENUM(NSUInteger, NSWindowStyleMask) {
    NSWindowStyleMaskBorderless = 0,
    NSWindowStyleMaskTitled = 1 << 0,
    NSWindowStyleMaskClosable = 1 << 1,
    NSWindowStyleMaskMiniaturizable = 1 << 2,
    NSWindowStyleMaskResizable = 1 << 3,
    NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
    NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
    NSWindowStyleMaskFullScreen = 1 << 14,
    NSWindowStyleMaskFullSizeContentView = 1 << 15,
    NSWindowStyleMaskUtilityWindow = 1 << 4,
    NSWindowStyleMaskDocModalWindow = 1 << 6,
    NSWindowStyleMaskNonactivatingPanel = 1 << 7,
    NSWindowStyleMaskHUDWindow = 1 << 13
};

typedef NS_ENUM(NSUInteger, NSBackingStoreType) {
    NSBackingStoreRetained = 0,
    NSBackingStoreNonretained = 1,
    NSBackingStoreBuffered = 2
};

typedef NS_ENUM(NSUInteger, NSEventType) {        /* various types of events */
    NSEventTypeLeftMouseDown             = 1,
    NSEventTypeLeftMouseUp               = 2,
    NSEventTypeRightMouseDown            = 3,
    NSEventTypeRightMouseUp              = 4,
    NSEventTypeMouseMoved                = 5,
    NSEventTypeLeftMouseDragged          = 6,
    NSEventTypeRightMouseDragged         = 7,
    NSEventTypeMouseEntered              = 8,
    NSEventTypeMouseExited               = 9,
    NSEventTypeKeyDown                   = 10,
    NSEventTypeKeyUp                     = 11,
    NSEventTypeFlagsChanged              = 12,
    NSEventTypeAppKitDefined             = 13,
    NSEventTypeSystemDefined             = 14,
    NSEventTypeApplicationDefined        = 15,
    NSEventTypePeriodic                  = 16,
    NSEventTypeCursorUpdate              = 17,
    NSEventTypeScrollWheel               = 22,
    NSEventTypeTabletPoint               = 23,
    NSEventTypeTabletProximity           = 24,
    NSEventTypeOtherMouseDown            = 25,
    NSEventTypeOtherMouseUp              = 26,
    NSEventTypeOtherMouseDragged         = 27,
    /* The following event types are available on some hardware on 10.5.2 and later */
    NSEventTypeGesture API_AVAILABLE(macos(10.5))       = 29,
    NSEventTypeMagnify API_AVAILABLE(macos(10.5))       = 30,
    NSEventTypeSwipe   API_AVAILABLE(macos(10.5))       = 31,
    NSEventTypeRotate  API_AVAILABLE(macos(10.5))       = 18,
    NSEventTypeBeginGesture API_AVAILABLE(macos(10.5))  = 19,
    NSEventTypeEndGesture API_AVAILABLE(macos(10.5))    = 20,

    NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32,
    NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33,

    NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34,
    NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37,

    NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38,
};

typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */
    NSEventMaskLeftMouseDown         = 1ULL << NSEventTypeLeftMouseDown,
    NSEventMaskLeftMouseUp           = 1ULL << NSEventTypeLeftMouseUp,
    NSEventMaskRightMouseDown        = 1ULL << NSEventTypeRightMouseDown,
    NSEventMaskRightMouseUp          = 1ULL << NSEventTypeRightMouseUp,
    NSEventMaskMouseMoved            = 1ULL << NSEventTypeMouseMoved,
    NSEventMaskLeftMouseDragged      = 1ULL << NSEventTypeLeftMouseDragged,
    NSEventMaskRightMouseDragged     = 1ULL << NSEventTypeRightMouseDragged,
    NSEventMaskMouseEntered          = 1ULL << NSEventTypeMouseEntered,
    NSEventMaskMouseExited           = 1ULL << NSEventTypeMouseExited,
    NSEventMaskKeyDown               = 1ULL << NSEventTypeKeyDown,
    NSEventMaskKeyUp                 = 1ULL << NSEventTypeKeyUp,
    NSEventMaskFlagsChanged          = 1ULL << NSEventTypeFlagsChanged,
    NSEventMaskAppKitDefined         = 1ULL << NSEventTypeAppKitDefined,
    NSEventMaskSystemDefined         = 1ULL << NSEventTypeSystemDefined,
    NSEventMaskApplicationDefined    = 1ULL << NSEventTypeApplicationDefined,
    NSEventMaskPeriodic              = 1ULL << NSEventTypePeriodic,
    NSEventMaskCursorUpdate          = 1ULL << NSEventTypeCursorUpdate,
    NSEventMaskScrollWheel           = 1ULL << NSEventTypeScrollWheel,
    NSEventMaskTabletPoint           = 1ULL << NSEventTypeTabletPoint,
    NSEventMaskTabletProximity       = 1ULL << NSEventTypeTabletProximity,
    NSEventMaskOtherMouseDown        = 1ULL << NSEventTypeOtherMouseDown,
    NSEventMaskOtherMouseUp          = 1ULL << NSEventTypeOtherMouseUp,
    NSEventMaskOtherMouseDragged     = 1ULL << NSEventTypeOtherMouseDragged,
};
/* The following event masks are available on some hardware on 10.5.2 and later */
#define NSEventMaskGesture API_AVAILABLE(macos(10.5))          (1ULL << NSEventTypeGesture)
#define NSEventMaskMagnify API_AVAILABLE(macos(10.5))          (1ULL << NSEventTypeMagnify)
#define NSEventMaskSwipe API_AVAILABLE(macos(10.5))            (1ULL << NSEventTypeSwipe)
#define NSEventMaskRotate API_AVAILABLE(macos(10.5))           (1ULL << NSEventTypeRotate)
#define NSEventMaskBeginGesture API_AVAILABLE(macos(10.5))     (1ULL << NSEventTypeBeginGesture)
#define NSEventMaskEndGesture API_AVAILABLE(macos(10.5))       (1ULL << NSEventTypeEndGesture)

/* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. */
#define NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) (1ULL << NSEventTypeSmartMagnify)
#define NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) (1ULL << NSEventTypePressure)
#define NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) (1ULL << NSEventTypeDirectTouch)
#define NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) (1ULL << NSEventTypeChangeMode)
#define NSEventMaskAny              NSUIntegerMax

typedef NS_ENUM(NSUInteger, NSEventModifierFlags) {
    NSEventModifierFlagCapsLock           = 1 << 16, // Set if Caps Lock key is pressed.
    NSEventModifierFlagShift              = 1 << 17, // Set if Shift key is pressed.
    NSEventModifierFlagControl            = 1 << 18, // Set if Control key is pressed.
    NSEventModifierFlagOption             = 1 << 19, // Set if Option or Alternate key is pressed.
    NSEventModifierFlagCommand            = 1 << 20, // Set if Command key is pressed.
    NSEventModifierFlagNumericPad         = 1 << 21, // Set if any key in the numeric keypad is pressed.
    NSEventModifierFlagHelp               = 1 << 22, // Set if the Help key is pressed.
    NSEventModifierFlagFunction           = 1 << 23, // Set if any function key is pressed.
};

#define objc_msgSend_id             ((id (*)(id, SEL))objc_msgSend)
#define objc_msgSend_id_id          ((id (*)(id, SEL, id))objc_msgSend)
#define objc_msgSend_id_rect        ((id (*)(id, SEL, NSRect))objc_msgSend)
#define objc_msgSend_uint           ((NSUInteger (*)(id, SEL))objc_msgSend)
#define objc_msgSend_int            ((NSInteger (*)(id, SEL))objc_msgSend)
#define objc_msgSend_SEL            ((SEL (*)(id, SEL))objc_msgSend)
#define objc_msgSend_float          ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)
#define objc_msgSend_bool           ((BOOL (*)(id, SEL))objc_msgSend)
#define objc_msgSend_void           ((void (*)(id, SEL))objc_msgSend)
#define objc_msgSend_double         ((double (*)(id, SEL))objc_msgSend)
#define objc_msgSend_void_id        ((void (*)(id, SEL, id))objc_msgSend)
#define objc_msgSend_void_uint      ((void (*)(id, SEL, NSUInteger))objc_msgSend)
#define objc_msgSend_void_int       ((void (*)(id, SEL, NSInteger))objc_msgSend)
#define objc_msgSend_void_bool      ((void (*)(id, SEL, BOOL))objc_msgSend)
#define objc_msgSend_void_float     ((void (*)(id, SEL, CGFloat))objc_msgSend)
#define objc_msgSend_void_double    ((void (*)(id, SEL, double))objc_msgSend)
#define objc_msgSend_void_SEL       ((void (*)(id, SEL, SEL))objc_msgSend)
#define objc_msgSend_id_char_const  ((id (*)(id, SEL, char const *))objc_msgSend)

typedef enum NSApplicationActivationPolicy {
    NSApplicationActivationPolicyRegular,
    NSApplicationActivationPolicyAccessory,
    NSApplicationActivationPolicyProhibited
} NSApplicationActivationPolicy;

#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
#define NSRelease(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("release"))

bool running = true;

unsigned int onClose(void* self) {
    NSWindow* win = NULL;
    object_getInstanceVariable(self, "NSWindow", (void*)&win);
    if (win == NULL)
        return true;

    running = false;

    return true;
}

NSSize windowResize(void* self, SEL sel, NSSize frameSize) {
    NSWindow* win = NULL;
    object_getInstanceVariable(self, "NSWindow", (void*)&win);
    if (win == NULL)
        return frameSize;

    printf("window resized to %f %f\n", frameSize.width, frameSize.height);
    return frameSize;
}



#include 

const char* NSEventTypeToChar(NSEventType eventType);
const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags);

int main(int argc, char* argv[]) {
    class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP) onClose, 0);

    NSApplication* NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
    objc_msgSend_void_int(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);

    NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable;

    SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:");

    NSWindow* window = ((id (*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
            (NSAlloc(objc_getClass("NSWindow")), func, 
                        (NSRect){{200, 200}, {200, 200}}, 
                        macArgs, macArgs, false);

    Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);

    class_addIvar(
        delegateClass, "NSWindow",
        sizeof(NSWindow*), rint(log2(sizeof(NSWindow*))),
        "L"
    );

    class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) windowResize, "{NSSize=ff}@:{NSSize=ff}");

    id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init"));

    object_setInstanceVariable(delegate, "NSWindow", window);

    objc_msgSend_void_id(window, sel_registerName("setDelegate:"), delegate);

    objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
    ((id(*)(id, SEL, SEL))objc_msgSend)(window, sel_registerName("makeKeyAndOrderFront:"), NULL);
    objc_msgSend_void_bool(window, sel_registerName("setIsVisible:"), true);

    objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));

    while (running) {
        id pool = objc_msgSend_id(NSAlloc(objc_getClass("NSAutoreleasePool")), sel_registerName("init"));

        NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) (NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), ULONG_MAX, NULL, ((id(*)(id, SEL, const char*))objc_msgSend) ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "kCFRunLoopDefaultMode"), true);



        unsigned int type = objc_msgSend_uint(e, sel_registerName("type"));  

        NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));

        if (type != 0)  
            printf("Event [type=%s location={%f, %f} modifierFlags={%s}]\n", 
                                NSEventTypeToChar(type), 
                                p.x, p.y, 
                                NSEventModifierFlagsToChar(objc_msgSend_uint(e, sel_registerName("modifierFlags"))));

        objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
        ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));

        NSRelease(pool);
    }
}

const char* NSEventTypeToChar(NSEventType eventType) {
    switch (eventType) {
        case NSEventTypeLeftMouseDown: return "LeftMouseDown";
        case NSEventTypeLeftMouseUp: return "LeftMouseUp";
        case NSEventTypeRightMouseDown: return "RightMouseDown";
        case NSEventTypeRightMouseUp: return "RightMouseUp";
        case NSEventTypeMouseMoved: return "MouseMoved";
        case NSEventTypeLeftMouseDragged: return "LeftMouseDragged";
        case NSEventTypeRightMouseDragged: return "RightMouseDragged";
        case NSEventTypeMouseEntered: return "MouseEntered";
        case NSEventTypeMouseExited: return "MouseExited";
        case NSEventTypeKeyDown: return "KeyDown";
        case NSEventTypeKeyUp: return "KeyUp";
        case NSEventTypeFlagsChanged: return "FlagsChanged";
        case NSEventTypeAppKitDefined: return "AppKitDefined";
        case NSEventTypeSystemDefined: return "SystemDefined";
        case NSEventTypeApplicationDefined: return "ApplicationDefined";
        case NSEventTypePeriodic: return "Periodic";
        case NSEventTypeCursorUpdate: return "CursorUpdate";
        case NSEventTypeScrollWheel: return "ScrollWheel";
        case NSEventTypeTabletPoint: return "TabletPoint";
        case NSEventTypeTabletProximity: return "TabletProximity";
        case NSEventTypeOtherMouseDown: return "OtherMouseDown";
        case NSEventTypeOtherMouseUp: return "OtherMouseUp";
        case NSEventTypeOtherMouseDragged: return "OtherMouseDragged";
        default: return "N/A";
    }
}

char* ns_strcat(register char *s, register const char *append) {
    char *save = s;

    for (; *s; ++s);
    while ((*s++ = *append++));
    return save;
}

const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags) {
    static char result[100];
    result[0] = '\0';

    if ((modifierFlags & NSEventModifierFlagCapsLock) == NSEventModifierFlagCapsLock) ns_strcat(result, "CapsLock, ");
    if ((modifierFlags & NSEventModifierFlagShift) == NSEventModifierFlagShift) ns_strcat(result, "NShift, ");
    if ((modifierFlags & NSEventModifierFlagControl) == NSEventModifierFlagControl) ns_strcat(result, "Control, ");
    if ((modifierFlags & NSEventModifierFlagOption) == NSEventModifierFlagOption) ns_strcat(result, "Option, ");
    if ((modifierFlags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand) ns_strcat(result, "Command, ");
    if ((modifierFlags & NSEventModifierFlagNumericPad) == NSEventModifierFlagNumericPad) ns_strcat(result, "NumericPad, ");
    if ((modifierFlags & NSEventModifierFlagHelp) == NSEventModifierFlagHelp) ns_strcat(result, "Help, ");
    if ((modifierFlags & NSEventModifierFlagFunction) == NSEventModifierFlagFunction) ns_strcat(result, "Function, ");

    return result;
}
登录后复制

以上是RGFW 幕后花絮:纯 C 中的可可的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板