Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for RGFW Under the Hood: Cocoa in Pure C
Colleague Riley
Colleague Riley

Posted on

     

RGFW Under the Hood: Cocoa in Pure C

Introduction

To use Apple's Cocoa API, you must use Objective-C function calls. However, you do not have to write Objective-C code because Objective-C can be accessed via C functions such asobjc_msgSend.

The main reason to use Pure-C over Objective-C is to be able to compile your project in C. This is helpful if you want to create a single-header file that does not require the user to compile using Objective-C.

Two examples of this are:

Silicon.h, a C-Wrapper for the Cocoa API that wraps around the Objective-C function calls so you can use the Cocoa API in normal-looking C code, andRGFW, a lightweight single-header windowing library.

Both projects can be used as a reference for using Cocoa in C.

Overview

A quick overview of the topics the article will cover

1) The Basics of Using Objective-C in Pure C
2) Defining Cocoa Types
3) Creating a Basic Cocoa Window

1. The Basics of using Objective-C in Pure C

Objective-C functions can be called usingobjc_msgsend.

Due to ABI differences, ARM usesobjc_msgsend for all cases. Howeverx86_64 CPUs require the use of specific functions for floating point and structure returns.
objc_msgsend_fpret for functions with floating point returns and
objc_msgsend_fstret for functions that return a structure.

RGFW handles this like this:

#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
Enter fullscreen modeExit fullscreen mode

objc_msgSend is a generic function, so its type must be cast based on the return and argument types you want.

For example:((int (*)(id, SEL, int))objc_msgSend) for a function that takes an int argument and returns an int.

To avoid repeating commonly used type-casting, RGFW defines macros to handle common cases.

#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)
Enter fullscreen modeExit fullscreen mode

You might notice two common arguments in these functions,id andSEL

Theid argument refers to an ID of an Objective-C object or class.

SEL refers to the function's selector.

For exampleMyObject *bar = [MyObject objectWithString:@"RGFW"]; can be translated toid* bar = [id SEL:@"RGFW"];

To get the ID to an Objective-C class you must runobjc_getClass

For example:objc_getClass("NSWindow");

To get the selector for an Objective-C function you must usesel_registerName

The syntax of Objective-C functions is like this<function-name>, then: is used as a placeholder for an argument.

A function with one argument would look like this:

sel_registerName("makeKeyAndOrderFront:");

However, a function with no arguments would look like this.

sel_registerName("isKeyWindow");

If the function has multiple arguments you will have to also add the argument names (not including the first argument)

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

To define a class method (eg. a callback function for the object) you must useclass_addMethod this function takes thedelegate class (class of the object that is calling'sdelegate
), selector of the function being called, the function you want to be called, and the arguments expected in string format.

But first, you must allocate the delegate class to access it.
You can do this usingobjc_allocateClassPair.

For example to allocate the delegate class for an NSWindow:

ClassdelegateClass=objc_allocateClassPair(objc_getClass("NSObject"),"WindowDelegate",0);
Enter fullscreen modeExit fullscreen mode

To create a call back you have to useclass_addMethod to set it as the callback for the class.

Creating a call back for NSWindow'swindowWillResize would look like this:

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

You can also add custom user variables to the class, these can be used to attach user data to the class.

For example, RGFW attaches anRGFW_window to the NSWindow class to modify theRGFW_window* data. This can be done usingclass_addIvar

class_addIvar(delegateClass,"RGFW_window",sizeof(RGFW_window*),rint(log2(sizeof(RGFW_window*))),"L");
Enter fullscreen modeExit fullscreen mode

To set the variable to use for your NSWindow instance, use

object_setInstanceVariable

To set theRGFW_window object to be the variable instance for its NSWindow object:

object_setInstanceVariable(delegate, "NSWindow", window);

You can get the instance variable of an object viaobject_getInstanceVariable

RGFW_window*win=NULL;//The object variable would be called "self" in a callbackobject_getInstanceVariable(self,"RGFW_window",(void*)&win);
Enter fullscreen modeExit fullscreen mode

Defining Cocoa Types

The Cocoa header files are written in Objective-C. This means we'll have to define the types and enums ourselves.

The shape types can be defined as the CG Shapes like this:

typedefCGRectNSRect;typedefCGPointNSPoint;typedefCGSizeNSSize;
Enter fullscreen modeExit fullscreen mode

Cocoa also uses custom integer-type names,
these can be defined to be their matching c datatype.

typedefunsignedlongNSUInteger;typedeflongNSInteger;
Enter fullscreen modeExit fullscreen mode

You can also define Cocoa objects. I define them as void so I can use them as pointers.

// Note: void is being used here// type* can be used ( type* == void* == id )typedefvoidNSEvent;typedefvoidNSString;typedefvoidNSWindow;typedefvoidNSApplication;
Enter fullscreen modeExit fullscreen mode

As for the enums, here are some of the enums I will be using in this tutorial.

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; enumtypedefNS_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};typedefNS_ENUM(NSUInteger,NSBackingStoreType){NSBackingStoreRetained=0,NSBackingStoreNonretained=1,NSBackingStoreBuffered=2};typedefNS_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 */NSEventTypeGestureAPI_AVAILABLE(macos(10.5))=29,NSEventTypeMagnifyAPI_AVAILABLE(macos(10.5))=30,NSEventTypeSwipeAPI_AVAILABLE(macos(10.5))=31,NSEventTypeRotateAPI_AVAILABLE(macos(10.5))=18,NSEventTypeBeginGestureAPI_AVAILABLE(macos(10.5))=19,NSEventTypeEndGestureAPI_AVAILABLE(macos(10.5))=20,NSEventTypeSmartMagnifyAPI_AVAILABLE(macos(10.8))=32,NSEventTypeQuickLookAPI_AVAILABLE(macos(10.8))=33,NSEventTypePressureAPI_AVAILABLE(macos(10.10.3))=34,NSEventTypeDirectTouchAPI_AVAILABLE(macos(10.10))=37,NSEventTypeChangeModeAPI_AVAILABLE(macos(10.15))=38,};typedefNS_ENUM(unsignedlonglong,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              NSUIntegerMaxtypedefNS_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.};
Enter fullscreen modeExit fullscreen mode

RGFW also definesNSAlloc andNSRelease 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"))
Enter fullscreen modeExit fullscreen mode

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>
Enter fullscreen modeExit fullscreen mode

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

constchar*NSEventTypeToChar(NSEventTypeeventType);constchar*NSEventModifierFlagsToChar(NSEventModifierFlagsmodifierFlags);// These will be placed after the main function and will be defined like this:constchar*NSEventTypeToChar(NSEventTypeeventType){switch(eventType){caseNSEventTypeLeftMouseDown:return"LeftMouseDown";caseNSEventTypeLeftMouseUp:return"LeftMouseUp";caseNSEventTypeRightMouseDown:return"RightMouseDown";caseNSEventTypeRightMouseUp:return"RightMouseUp";caseNSEventTypeMouseMoved:return"MouseMoved";caseNSEventTypeLeftMouseDragged:return"LeftMouseDragged";caseNSEventTypeRightMouseDragged:return"RightMouseDragged";caseNSEventTypeMouseEntered:return"MouseEntered";caseNSEventTypeMouseExited:return"MouseExited";caseNSEventTypeKeyDown:return"KeyDown";caseNSEventTypeKeyUp:return"KeyUp";caseNSEventTypeFlagsChanged:return"FlagsChanged";caseNSEventTypeAppKitDefined:return"AppKitDefined";caseNSEventTypeSystemDefined:return"SystemDefined";caseNSEventTypeApplicationDefined:return"ApplicationDefined";caseNSEventTypePeriodic:return"Periodic";caseNSEventTypeCursorUpdate:return"CursorUpdate";caseNSEventTypeScrollWheel:return"ScrollWheel";caseNSEventTypeTabletPoint:return"TabletPoint";caseNSEventTypeTabletProximity:return"TabletProximity";caseNSEventTypeOtherMouseDown:return"OtherMouseDown";caseNSEventTypeOtherMouseUp:return"OtherMouseUp";caseNSEventTypeOtherMouseDragged:return"OtherMouseDragged";default:return"N/A";}}char*ns_strcat(registerchar*s,registerconstchar*append){char*save=s;for(;*s;++s);while((*s++=*append++));returnsave;}constchar*NSEventModifierFlagsToChar(NSEventModifierFlagsmodifierFlags){staticcharresult[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, ");returnresult;}
Enter fullscreen modeExit fullscreen mode

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:

boolrunning=true;unsignedintonClose(void*self){NSWindow*win=NULL;object_getInstanceVariable(self,"NSWindow",(void*)&win);if(win==NULL)returntrue;running=false;returntrue;}NSSizewindowResize(void*self,SELsel,NSSizeframeSize){NSWindow*win=NULL;object_getInstanceVariable(self,"NSWindow",(void*)&win);if(win==NULL)returnframeSize;printf("window resized to %f %f\n",frameSize.width,frameSize.height);returnframeSize;}
Enter fullscreen modeExit fullscreen mode

The first thing that I will do in the main function is define thewindowShouldClose 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);
Enter fullscreen modeExit fullscreen mode

Next, theNSApplication is set up.

This requires the use ofsharedApplication andsetActivationPolicy

NSApplication*NSApp=objc_msgSend_id((id)objc_getClass("NSApplication"),sel_registerName("sharedApplication"));objc_msgSend_void_id(NSApp,sel_registerName("setActivationPolicy:"),NSApplicationActivationPolicyRegular);
Enter fullscreen modeExit fullscreen mode

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

The window is created usinginitWithContentRect

NSBackingStoreTypemacArgs=NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSBackingStoreBuffered|NSWindowStyleMaskTitled|NSWindowStyleMaskResizable;SELfunc=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);
Enter fullscreen modeExit fullscreen mode

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

ClassdelegateClass=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}");
Enter fullscreen modeExit fullscreen mode

After that, the delegate must be initialized usinginit

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

iddelegate=objc_msgSend_id(NSAlloc(delegateClass),sel_registerName("init"));object_setInstanceVariable(delegate,"NSWindow",window);objc_msgSend_void_id(window,sel_registerName("setDelegate:"),delegate);
Enter fullscreen modeExit fullscreen mode

Then the window can be shown usingsetIsVisible and made key and front viamakeKeyAndOrderFront, 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"));
Enter fullscreen modeExit fullscreen mode

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 usingNSAutoreleasePool

idpool=objc_msgSend_id(NSAlloc(objc_getClass("NSAutoreleasePool")),sel_registerName("init"));
Enter fullscreen modeExit fullscreen mode

Now the current event can be checked using anNSEvent object andnextEventMatchingMask

The event type can be found usingtype
The event mouse point can be found usinglocationInWindow
The event modifier flags can be found usingmodifierFlags

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,constchar*))objc_msgSend)((id)objc_getClass("NSString"),sel_registerName("stringWithUTF8String:"),"kCFRunLoopDefaultMode"),true);unsignedinttype=objc_msgSend_uint(e,sel_registerName("type"));NSPointp=((NSPoint(*)(id,SEL))objc_msgSend)(e,sel_registerName("locationInWindow"));
Enter fullscreen modeExit fullscreen mode

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"))));
Enter fullscreen modeExit fullscreen mode

The event can be pushed out usingsendEvent and the window can be updated usingupdateWindows

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

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

NSRelease(pool);
Enter fullscreen modeExit fullscreen mode

Full code example

// compile with:// gcc example.c -lm -framework Foundation -framework AppKit -framework CoreVideo#include<objc/runtime.h>#include<objc/message.h>#include<CoreVideo/CVDisplayLink.h>#include<ApplicationServices/ApplicationServices.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#endiftypedefCGRectNSRect;typedefCGPointNSPoint;typedefCGSizeNSSize;typedefvoidNSEvent;typedefvoidNSString;typedefvoidNSWindow;typedefvoidNSApplication;typedefunsignedlongNSUInteger;typedeflongNSInteger;#define NS_ENUM(type, name) type name; enumtypedefNS_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};typedefNS_ENUM(NSUInteger,NSBackingStoreType){NSBackingStoreRetained=0,NSBackingStoreNonretained=1,NSBackingStoreBuffered=2};typedefNS_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 */NSEventTypeGestureAPI_AVAILABLE(macos(10.5))=29,NSEventTypeMagnifyAPI_AVAILABLE(macos(10.5))=30,NSEventTypeSwipeAPI_AVAILABLE(macos(10.5))=31,NSEventTypeRotateAPI_AVAILABLE(macos(10.5))=18,NSEventTypeBeginGestureAPI_AVAILABLE(macos(10.5))=19,NSEventTypeEndGestureAPI_AVAILABLE(macos(10.5))=20,NSEventTypeSmartMagnifyAPI_AVAILABLE(macos(10.8))=32,NSEventTypeQuickLookAPI_AVAILABLE(macos(10.8))=33,NSEventTypePressureAPI_AVAILABLE(macos(10.10.3))=34,NSEventTypeDirectTouchAPI_AVAILABLE(macos(10.10))=37,NSEventTypeChangeModeAPI_AVAILABLE(macos(10.15))=38,};typedefNS_ENUM(unsignedlonglong,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              NSUIntegerMaxtypedefNS_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)typedefenumNSApplicationActivationPolicy{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"))boolrunning=true;unsignedintonClose(void*self){NSWindow*win=NULL;object_getInstanceVariable(self,"NSWindow",(void*)&win);if(win==NULL)returntrue;running=false;returntrue;}NSSizewindowResize(void*self,SELsel,NSSizeframeSize){NSWindow*win=NULL;object_getInstanceVariable(self,"NSWindow",(void*)&win);if(win==NULL)returnframeSize;printf("window resized to %f %f\n",frameSize.width,frameSize.height);returnframeSize;}#include<string.h>constchar*NSEventTypeToChar(NSEventTypeeventType);constchar*NSEventModifierFlagsToChar(NSEventModifierFlagsmodifierFlags);intmain(intargc,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);NSBackingStoreTypemacArgs=NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSBackingStoreBuffered|NSWindowStyleMaskTitled|NSWindowStyleMaskResizable;SELfunc=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);ClassdelegateClass=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}");iddelegate=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){idpool=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,constchar*))objc_msgSend)((id)objc_getClass("NSString"),sel_registerName("stringWithUTF8String:"),"kCFRunLoopDefaultMode"),true);unsignedinttype=objc_msgSend_uint(e,sel_registerName("type"));NSPointp=((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);}}constchar*NSEventTypeToChar(NSEventTypeeventType){switch(eventType){caseNSEventTypeLeftMouseDown:return"LeftMouseDown";caseNSEventTypeLeftMouseUp:return"LeftMouseUp";caseNSEventTypeRightMouseDown:return"RightMouseDown";caseNSEventTypeRightMouseUp:return"RightMouseUp";caseNSEventTypeMouseMoved:return"MouseMoved";caseNSEventTypeLeftMouseDragged:return"LeftMouseDragged";caseNSEventTypeRightMouseDragged:return"RightMouseDragged";caseNSEventTypeMouseEntered:return"MouseEntered";caseNSEventTypeMouseExited:return"MouseExited";caseNSEventTypeKeyDown:return"KeyDown";caseNSEventTypeKeyUp:return"KeyUp";caseNSEventTypeFlagsChanged:return"FlagsChanged";caseNSEventTypeAppKitDefined:return"AppKitDefined";caseNSEventTypeSystemDefined:return"SystemDefined";caseNSEventTypeApplicationDefined:return"ApplicationDefined";caseNSEventTypePeriodic:return"Periodic";caseNSEventTypeCursorUpdate:return"CursorUpdate";caseNSEventTypeScrollWheel:return"ScrollWheel";caseNSEventTypeTabletPoint:return"TabletPoint";caseNSEventTypeTabletProximity:return"TabletProximity";caseNSEventTypeOtherMouseDown:return"OtherMouseDown";caseNSEventTypeOtherMouseUp:return"OtherMouseUp";caseNSEventTypeOtherMouseDragged:return"OtherMouseDragged";default:return"N/A";}}char*ns_strcat(registerchar*s,registerconstchar*append){char*save=s;for(;*s;++s);while((*s++=*append++));returnsave;}constchar*NSEventModifierFlagsToChar(NSEventModifierFlagsmodifierFlags){staticcharresult[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, ");returnresult;}
Enter fullscreen modeExit fullscreen mode

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

An objectivist, C programmer who's been programming for 4-5 years. I specialize in system programming, mainly with low level tech currently.
  • Joined

More fromColleague Riley

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp