首頁 >後端開發 >C++ >RGFW 幕後花絮:純 C 中的可可

RGFW 幕後花絮:純 C 中的可可

WBOY
WBOY原創
2024-08-27 06:40:02872瀏覽

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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn