Maison > développement back-end > C++ > le corps du texte

RGFW sous le capot : le cacao en Pure C

WBOY
Libérer: 2024-08-27 06:40:02
original
602 Les gens l'ont consulté

RGFW Under the Hood: Cocoa in Pure C

Introduction

Pour utiliser l'API Cocoa d'Apple, vous devez utiliser des appels de fonction Objective-C. Cependant, vous n'êtes pas obligé d'écrire du code Objective-C car Objective-C est accessible via des fonctions C telles que objc_msgSend.

La principale raison d'utiliser Pure-C plutôt qu'Objective-C est de pouvoir compiler votre projet en C. Ceci est utile si vous souhaitez créer un fichier à en-tête unique qui ne nécessite pas que l'utilisateur compile à l'aide d'Objective-C. C.

Deux exemples sont :

Silicon.h, un C-Wrapper pour l'API Cocoa qui enveloppe les appels de fonction Objective-C afin que vous puissiez utiliser l'API Cocoa dans du code C d'apparence normale, et RGFW, une bibliothèque légère de fenêtrage à en-tête unique.

Les deux projets peuvent être utilisés comme référence pour l'utilisation de Cocoa en C.

Aperçu

Un aperçu rapide des sujets abordés par l'article

1) Les bases de l'utilisation d'Objective-C en Pure C
2) Définir les types de cacao
3) Création d'une fenêtre Cocoa de base

1. Les bases de l'utilisation d'Objective-C en Pure C

Les fonctions Objective-C peuvent être appelées en utilisant objc_msgsend.

En raison des différences ABI, ARM utilise objc_msgsend pour tous les cas. Cependant, les processeurs x86_64 nécessitent l'utilisation de fonctions spécifiques pour les retours à virgule flottante et de structure.
objc_msgsend_fpret pour les fonctions avec retours en virgule flottante et
objc_msgsend_fstret pour les fonctions qui renvoient une structure.

RGFW gère cela comme ceci :

#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
Copier après la connexion

objc_msgSend est une fonction générique, son type doit donc être converti en fonction des types de retour et d'argument souhaités.

Par exemple : ((int (*)(id, SEL, int))objc_msgSend) pour une fonction qui prend un argument int et renvoie un int.

Pour éviter de répéter le transtypage couramment utilisé, RGFW définit des macros pour gérer les cas courants.

#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)
Copier après la connexion

Vous remarquerez peut-être deux arguments communs dans ces fonctions, id et SEL

L'argument id fait référence à l'identifiant d'un objet ou d'une classe Objective-C.

SEL fait référence au sélecteur de fonction.

Par exemple MyObject *bar = [MyObject objectWithString:@"RGFW"]; peut être traduit par id* bar = [id SEL:@"RGFW"];

Pour obtenir l'ID d'une classe Objective-C, vous devez exécuter objc_getClass

Par exemple : objc_getClass("NSWindow");

Pour obtenir le sélecteur d'une fonction Objective-C, vous devez utiliser sel_registerName

La syntaxe des fonctions Objective-C est la suivante , alors : est utilisé comme espace réservé pour un argument.

Une fonction avec un argument ressemblerait à ceci :

sel_registerName("makeKeyAndOrderFront:");

Cependant, une fonction sans argument ressemblerait à ceci.

sel_registerName("isKeyWindow");

Si la fonction a plusieurs arguments, vous devrez également ajouter les noms des arguments (sans compter le premier argument)

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

Pour définir une méthode de classe (par exemple une fonction de rappel pour l'objet), vous devez utiliser class_addMethod, cette fonction prend la classe déléguée (classe de l'objet qui appelle le délégué
), sélecteur de la fonction appelée, de la fonction que vous souhaitez appeler et des arguments attendus au format chaîne.

Mais d'abord, vous devez attribuer la classe déléguée pour y accéder.
Vous pouvez le faire en utilisant objc_allocateClassPair.

Par exemple pour allouer la classe déléguée pour un NSWindow :

Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
Copier après la connexion

Pour créer un rappel, vous devez utiliser class_addMethod pour le définir comme rappel pour la classe.

Créer un rappel pour windowWillResize de NSWindow ressemblerait à ceci :

class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) windowResize, "{NSSize=ff}@:{NSSize=ff}");
Copier après la connexion

Vous pouvez également ajouter des variables utilisateur personnalisées à la classe, celles-ci peuvent être utilisées pour attacher des données utilisateur à la classe.

Par exemple, RGFW attache un RGFW_window à la classe NSWindow pour modifier les données RGFW_window*. Cela peut être fait en utilisant class_addIvar

class_addIvar(
    delegateClass, "RGFW_window",
    sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))),
    "L" 
);
Copier après la connexion

Pour définir la variable à utiliser pour votre instance NSWindow, utilisez

object_setInstanceVariable

Pour définir l'objet RGFW_window comme instance de variable pour son objet NSWindow :

object_setInstanceVariable(délégué, "NSWindow", fenêtre);

Vous pouvez obtenir la variable d'instance d'un objet via object_getInstanceVariable

RGFW_window* win = NULL;

//The object variable would be called "self" in a callback
object_getInstanceVariable(self, "RGFW_window", (void*)&win);
Copier après la connexion

Définir les types de cacao

Les fichiers d'en-tête Cocoa sont écrits en Objective-C. Cela signifie que nous devrons définir nous-mêmes les types et les énumérations.

Les types de formes peuvent être définis comme des formes CG comme ceci :

typedef CGRect NSRect;
typedef CGPoint NSPoint;
typedef CGSize NSSize;
Copier après la connexion

Cocoa utilise également des noms de type entier personnalisés,
ceux-ci peuvent être définis comme étant leur type de données c correspondant.

typedef unsigned long NSUInteger;
typedef long NSInteger;
Copier après la connexion

Vous pouvez également définir des objets Cocoa. Je les définis comme vides pour pouvoir les utiliser comme pointeurs.

// 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;
Copier après la connexion

En ce qui concerne les énumérations, voici quelques-unes des énumérations que j'utiliserai dans ce tutoriel.

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.
};
Copier après la connexion

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"))
Copier après la connexion

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>
Copier après la connexion

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;
}
Copier après la connexion

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;
}
Copier après la connexion

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);
Copier après la connexion

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);
Copier après la connexion

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);
Copier après la connexion

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}");
Copier après la connexion

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);
Copier après la connexion

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"));
Copier après la connexion

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"));
Copier après la connexion

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"));
Copier après la connexion

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

Copier après la connexion

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"));
Copier après la connexion

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

NSRelease(pool);
Copier après la connexion

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;
}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!