ホームページ >バックエンド開発 >C++ >RGFW の内部: Pure C のココア

RGFW の内部: Pure C のココア

WBOY
WBOYオリジナル
2024-08-27 06:40:02876ブラウズ

RGFW Under the Hood: Cocoa in Pure C

導入

Apple の Cocoa API を使用するには、Objective-C 関数呼び出しを使用する必要があります。ただし、Objective-C には objc_msgSend.

などの C 関数経由でアクセスできるため、Objective-C コードを記述する必要はありません。

Objective-C ではなく Pure-C を使用する主な理由は、プロジェクトを C でコンパイルできることです。これは、ユーザーが Objective-C を使用してコンパイルする必要のない単一ヘッダー ファイルを作成する場合に役立ちます。 C.

この 2 つの例は次のとおりです:

Silicon.h (通常の C コードで Cocoa API を使用できるように Objective-C 関数呼び出しをラップする Cocoa API の C ラッパー)、および軽量の単一ヘッダー ウィンドウ処理ライブラリである RGFW。

どちらのプロジェクトも、C で Cocoa を使用するためのリファレンスとして使用できます。

概要

この記事で取り上げるトピックの簡単な概要

1) Pure C での Objective-C の使用の基本
2) カカオの種類の定義
3) 基本的な Cocoa ウィンドウの作成

1. Pure C での Objective-C の使用の基本

Objective-C 関数は、objc_msgsend を使用して呼び出すことができます。

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)

これらの関数の 2 つの共通引数、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 関数の構文は、この <関数名> のようなもので、その後、: が引数のプレースホルダーとして使用されます。

引数が 1 つある関数は次のようになります:

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(デリゲート, "NSWindow", ウィンドウ);

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>




</p>
<p>These functions will be used for printing information about the current event.<br>
</p>

<pre class="brush:php;toolbar:false">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 の内部: Pure C のココアの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。