Apple의 Cocoa API를 사용하려면 Objective-C 함수 호출을 사용해야 합니다. 하지만 Objective-C는 objc_msgSend와 같은 C 함수를 통해 접근할 수 있기 때문에 굳이 Objective-C 코드를 작성할 필요는 없습니다.
Objective-C 대신 Pure-C를 사용하는 주된 이유는 프로젝트를 C로 컴파일할 수 있기 때문입니다. 이는 사용자가 Objective-C를 사용하여 컴파일할 필요가 없는 단일 헤더 파일을 생성하려는 경우에 유용합니다. 다.
이에 대한 두 가지 예는 다음과 같습니다.
일반적인 C 코드에서 Cocoa API를 사용할 수 있도록 Objective-C 함수 호출을 래핑하는 Cocoa API용 C-Wrapper인 Silicon.h와 경량 단일 헤더 윈도우 라이브러리인 RGFW입니다.
두 프로젝트 모두 C에서 Cocoa를 사용하는 데 참고 자료로 사용할 수 있습니다.
기사에서 다룰 주제에 대한 간략한 개요
1) 순수 C에서 Objective-C 사용의 기본
2) 코코아 종류 정의
3) 기본 코코아 창 만들기
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 인수를 취하고 int를 반환하는 함수의 경우 ((int (*)(id, SEL, int))objc_msgSend)입니다.
일반적으로 사용되는 유형 변환을 반복하지 않기 위해 RGFW는 일반적인 경우를 처리하는 매크로를 정의합니다.
#define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) #define objc_msgSend_id_id ((id (*)(id, SEL, id))objc_msgSend) #define objc_msgSend_id_rect ((id (*)(id, SEL, NSRect))objc_msgSend) #define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) #define objc_msgSend_int ((NSInteger (*)(id, SEL))objc_msgSend) #define objc_msgSend_SEL ((SEL (*)(id, SEL))objc_msgSend) #define objc_msgSend_float ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) #define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) #define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) #define objc_msgSend_double ((double (*)(id, SEL))objc_msgSend) #define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) #define objc_msgSend_void_uint ((void (*)(id, SEL, NSUInteger))objc_msgSend) #define objc_msgSend_void_int ((void (*)(id, SEL, NSInteger))objc_msgSend) #define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) #define objc_msgSend_void_float ((void (*)(id, SEL, CGFloat))objc_msgSend) #define objc_msgSend_void_double ((void (*)(id, SEL, double))objc_msgSend) #define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) #define objc_msgSend_id_char_const ((id (*)(id, SEL, char const *))objc_msgSend)
이러한 함수에는 id와 SEL이라는 두 가지 공통 인수가 있습니다
id 인수는 Objective-C 개체 또는 클래스의 ID를 나타냅니다.
SEL은 해당 기능의 선택자를 의미합니다.
예를 들어 MyObject *bar = [MyObject objectWithString:@"RGFW"]; id* bar = [id SEL:@"RGFW"];
로 번역될 수 있습니다.Objective-C 클래스의 ID를 얻으려면 objc_getClass를 실행해야 합니다
예: objc_getClass("NSWindow");
Objective-C 함수에 대한 선택기를 얻으려면 sel_registerName을 사용해야 합니다
Objective-C 함수의 구문은 <함수 이름>과 같으며, 인수에 대한 자리 표시자로 :가 사용됩니다.
하나의 인수를 갖는 함수는 다음과 같습니다.
sel_registerName("makeKeyAndOrderFront:");
그러나 인수가 없는 함수는 다음과 같습니다.
sel_registerName("isKeyWindow");
함수에 인수가 여러 개 있는 경우 인수 이름도 추가해야 합니다(첫 번째 인수는 제외)
sel_registerName("initWithContentRect:styleMask:backing:defer:");
클래스 메서드(예: 개체에 대한 콜백 함수)를 정의하려면 class_addMethod를 사용해야 합니다. 이 함수는 대리자 클래스(대리자를 호출하는 개체의 클래스)를 사용합니다.
), 호출되는 함수의 선택자, 호출하려는 함수, 문자열 형식으로 예상되는 인수.
하지만 먼저 액세스하려면 위임 클래스를 할당해야 합니다.
objc_allocateClassPair를 사용하여 이 작업을 수행할 수 있습니다.
예를 들어 NSWindow에 대리자 클래스를 할당하려면 다음을 수행하세요.
Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
콜백을 생성하려면 class_addMethod를 사용하여 이를 클래스의 콜백으로 설정해야 합니다.
NSWindow의 windowWillResize에 대한 콜백을 생성하는 방법은 다음과 같습니다.
class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) windowResize, "{NSSize=ff}@:{NSSize=ff}");
클래스에 사용자 정의 사용자 변수를 추가할 수도 있으며, 이를 사용하여 클래스에 사용자 데이터를 연결할 수 있습니다.
예를 들어 RGFW는 RGFW_window* 데이터를 수정하기 위해 RGFW_window를 NSWindow 클래스에 연결합니다. 이는 class_addIvar를 사용하여 수행할 수 있습니다
class_addIvar( delegateClass, "RGFW_window", sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))), "L" );
NSWindow 인스턴스에 사용할 변수를 설정하려면
을 사용하세요.object_setInstanceVariable
RGFW_window 개체를 NSWindow 개체의 변수 인스턴스로 설정하려면:
object_setInstanceVariable(delegate, "NSWindow", window);
object_getInstanceVariable을 통해 객체의 인스턴스 변수를 가져올 수 있습니다
RGFW_window* win = NULL; //The object variable would be called "self" in a callback object_getInstanceVariable(self, "RGFW_window", (void*)&win);
Cocoa 헤더 파일은 Objective-C로 작성되었습니다. 즉, 유형과 열거형을 직접 정의해야 합니다.
모양 유형은 다음과 같이 CG 모양으로 정의할 수 있습니다.
typedef CGRect NSRect; typedef CGPoint NSPoint; typedef CGSize NSSize;
Cocoa는 사용자 정의 정수 유형 이름도 사용합니다.
이는 일치하는 c 데이터 유형으로 정의될 수 있습니다.
typedef unsigned long NSUInteger; typedef long NSInteger;
Cocoa 개체를 정의할 수도 있습니다. 포인터로 사용할 수 있도록 void로 정의합니다.
// Note: void is being used here // type* can be used ( type* == void* == id ) typedef void NSEvent; typedef void NSString; typedef void NSWindow; typedef void NSApplication;
열거형에 관해서는 이 튜토리얼에서 사용할 열거형 중 일부는 다음과 같습니다.
Many of the other enums can be found in Cocoa's headers, documentation, RGFW.h, or Silicon. h.
/* this macro is used to give an enum a type */ #define NS_ENUM(type, name) type name; enum typedef NS_ENUM(NSUInteger, NSWindowStyleMask) { NSWindowStyleMaskBorderless = 0, NSWindowStyleMaskTitled = 1 << 0, NSWindowStyleMaskClosable = 1 << 1, NSWindowStyleMaskMiniaturizable = 1 << 2, NSWindowStyleMaskResizable = 1 << 3, NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, NSWindowStyleMaskFullScreen = 1 << 14, NSWindowStyleMaskFullSizeContentView = 1 << 15, NSWindowStyleMaskUtilityWindow = 1 << 4, NSWindowStyleMaskDocModalWindow = 1 << 6, NSWindowStyleMaskNonactivatingPanel = 1 << 7, NSWindowStyleMaskHUDWindow = 1 << 13 }; typedef NS_ENUM(NSUInteger, NSBackingStoreType) { NSBackingStoreRetained = 0, NSBackingStoreNonretained = 1, NSBackingStoreBuffered = 2 }; typedef NS_ENUM(NSUInteger, NSEventType) { /* various types of events */ NSEventTypeLeftMouseDown = 1, NSEventTypeLeftMouseUp = 2, NSEventTypeRightMouseDown = 3, NSEventTypeRightMouseUp = 4, NSEventTypeMouseMoved = 5, NSEventTypeLeftMouseDragged = 6, NSEventTypeRightMouseDragged = 7, NSEventTypeMouseEntered = 8, NSEventTypeMouseExited = 9, NSEventTypeKeyDown = 10, NSEventTypeKeyUp = 11, NSEventTypeFlagsChanged = 12, NSEventTypeAppKitDefined = 13, NSEventTypeSystemDefined = 14, NSEventTypeApplicationDefined = 15, NSEventTypePeriodic = 16, NSEventTypeCursorUpdate = 17, NSEventTypeScrollWheel = 22, NSEventTypeTabletPoint = 23, NSEventTypeTabletProximity = 24, NSEventTypeOtherMouseDown = 25, NSEventTypeOtherMouseUp = 26, NSEventTypeOtherMouseDragged = 27, /* The following event types are available on some hardware on 10.5.2 and later */ NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, }; typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */ NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown, NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp, NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown, NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp, NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved, NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged, NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged, NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered, NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited, NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown, NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp, NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged, NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined, NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined, NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined, NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic, NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate, NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel, NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint, NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity, NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown, NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp, NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged, }; /* The following event masks are available on some hardware on 10.5.2 and later */ #define NSEventMaskGesture API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeGesture) #define NSEventMaskMagnify API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeMagnify) #define NSEventMaskSwipe API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeSwipe) #define NSEventMaskRotate API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeRotate) #define NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeBeginGesture) #define NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeEndGesture) /* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. */ #define NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) (1ULL << NSEventTypeSmartMagnify) #define NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) (1ULL << NSEventTypePressure) #define NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) (1ULL << NSEventTypeDirectTouch) #define NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) (1ULL << NSEventTypeChangeMode) #define NSEventMaskAny NSUIntegerMax typedef NS_ENUM(NSUInteger, NSEventModifierFlags) { NSEventModifierFlagCapsLock = 1 << 16, // Set if Caps Lock key is pressed. NSEventModifierFlagShift = 1 << 17, // Set if Shift key is pressed. NSEventModifierFlagControl = 1 << 18, // Set if Control key is pressed. NSEventModifierFlagOption = 1 << 19, // Set if Option or Alternate key is pressed. NSEventModifierFlagCommand = 1 << 20, // Set if Command key is pressed. NSEventModifierFlagNumericPad = 1 << 21, // Set if any key in the numeric keypad is pressed. NSEventModifierFlagHelp = 1 << 22, // Set if the Help key is pressed. NSEventModifierFlagFunction = 1 << 23, // Set if any function key is pressed. };
RGFW also defines NSAlloc and NSRelease because they're basic functions that are used a lot.
#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) #define NSRelease(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("release"))
Now that you understand the basics of calling Objective-C functions from C, and the setup required to use Cocoa, it's time to apply that for creating a basic window using Cocoa.
First, some library headers are required.
#include <CoreVideo/CVDisplayLink.h> // ! #include <ApplicationServices/ApplicationServices.h> #include <string.h>
These functions will be used for printing information about the current event.
const char* NSEventTypeToChar(NSEventType eventType); const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags); // These will be placed after the main function and will be defined like this: const char* NSEventTypeToChar(NSEventType eventType) { switch (eventType) { case NSEventTypeLeftMouseDown: return "LeftMouseDown"; case NSEventTypeLeftMouseUp: return "LeftMouseUp"; case NSEventTypeRightMouseDown: return "RightMouseDown"; case NSEventTypeRightMouseUp: return "RightMouseUp"; case NSEventTypeMouseMoved: return "MouseMoved"; case NSEventTypeLeftMouseDragged: return "LeftMouseDragged"; case NSEventTypeRightMouseDragged: return "RightMouseDragged"; case NSEventTypeMouseEntered: return "MouseEntered"; case NSEventTypeMouseExited: return "MouseExited"; case NSEventTypeKeyDown: return "KeyDown"; case NSEventTypeKeyUp: return "KeyUp"; case NSEventTypeFlagsChanged: return "FlagsChanged"; case NSEventTypeAppKitDefined: return "AppKitDefined"; case NSEventTypeSystemDefined: return "SystemDefined"; case NSEventTypeApplicationDefined: return "ApplicationDefined"; case NSEventTypePeriodic: return "Periodic"; case NSEventTypeCursorUpdate: return "CursorUpdate"; case NSEventTypeScrollWheel: return "ScrollWheel"; case NSEventTypeTabletPoint: return "TabletPoint"; case NSEventTypeTabletProximity: return "TabletProximity"; case NSEventTypeOtherMouseDown: return "OtherMouseDown"; case NSEventTypeOtherMouseUp: return "OtherMouseUp"; case NSEventTypeOtherMouseDragged: return "OtherMouseDragged"; default: return "N/A"; } } char* ns_strcat(register char *s, register const char *append) { char *save = s; for (; *s; ++s); while ((*s++ = *append++)); return save; } const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags) { static char result[100]; result[0] = '\0'; if ((modifierFlags & NSEventModifierFlagCapsLock) == NSEventModifierFlagCapsLock) ns_strcat(result, "CapsLock, "); if ((modifierFlags & NSEventModifierFlagShift) == NSEventModifierFlagShift) ns_strcat(result, "NShift, "); if ((modifierFlags & NSEventModifierFlagControl) == NSEventModifierFlagControl) ns_strcat(result, "Control, "); if ((modifierFlags & NSEventModifierFlagOption) == NSEventModifierFlagOption) ns_strcat(result, "Option, "); if ((modifierFlags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand) ns_strcat(result, "Command, "); if ((modifierFlags & NSEventModifierFlagNumericPad) == NSEventModifierFlagNumericPad) ns_strcat(result, "NumericPad, "); if ((modifierFlags & NSEventModifierFlagHelp) == NSEventModifierFlagHelp) ns_strcat(result, "Help, "); if ((modifierFlags & NSEventModifierFlagFunction) == NSEventModifierFlagFunction) ns_strcat(result, "Function, "); return result; }
Cocoa does not handle certain events using the event loop.
Instead, they're fed to the NSWindow via callbacks.
Here's how these functions will be defined for this example:
bool running = true; unsigned int onClose(void* self) { NSWindow* win = NULL; object_getInstanceVariable(self, "NSWindow", (void*)&win); if (win == NULL) return true; running = false; return true; } NSSize windowResize(void* self, SEL sel, NSSize frameSize) { NSWindow* win = NULL; object_getInstanceVariable(self, "NSWindow", (void*)&win); if (win == NULL) return frameSize; printf("window resized to %f %f\n", frameSize.width, frameSize.height); return frameSize; }
The first thing that I will do in the main function is define the windowShouldClose callback.
This is so that way the program doesn't keep running after the window is closed.
class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP) onClose, 0);
Next, the NSApplication is set up.
This requires the use of sharedApplication and setActivationPolicy
NSApplication* NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); objc_msgSend_void_id(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
Now you can create a NSWindow can be created, I broke the window creation process into three steps so it's more readable.
The window is created using initWithContentRect
NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable; SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); NSWindow* window = ((id (*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) (NSAlloc(objc_getClass("NSWindow")), func, (NSRect){{200, 200}, {200, 200}}, macArgs, macArgs, false);
You can then set up the delegate class and window resize callback.
Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); class_addIvar( delegateClass, "NSWindow", sizeof(NSWindow*), rint(log2(sizeof(NSWindow*))), "L" ); class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) windowResize, "{NSSize=ff}@:{NSSize=ff}");
After that, the delegate must be initialized using init
Then I will set the delegate's variable data as our NSWindow and set the NSWindow's delegate to be the delegate we initialized using setDelegate
id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init")); object_setInstanceVariable(delegate, "NSWindow", window); objc_msgSend_void_id(window, sel_registerName("setDelegate:"), delegate);
Then the window can be shown using setIsVisible and made key and front via makeKeyAndOrderFront, this puts it in focus.
activatIgnoredOtherApps is required to open the window
objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); ((id(*)(id, SEL, SEL))objc_msgSend)(window, sel_registerName("makeKeyAndOrderFront:"), NULL); objc_msgSend_void_bool(window, sel_registerName("setIsVisible:"), true); objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
Now, in the draw loop, I'd start by creating a memory pool.
This is so that way all memory allocated by event checking can be freed at once, avoiding a memory leak.
This can be done using NSAutoreleasePool
id pool = objc_msgSend_id(NSAlloc(objc_getClass("NSAutoreleasePool")), sel_registerName("init"));
Now the current event can be checked using an NSEvent object and nextEventMatchingMask
The event type can be found using type
The event mouse point can be found using locationInWindow
The event modifier flags can be found using modifierFlags
NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) (NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), ULONG_MAX, NULL, ((id(*)(id, SEL, const char*))objc_msgSend) ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "kCFRunLoopDefaultMode"), true); unsigned int type = objc_msgSend_uint(e, sel_registerName("type")); NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
Before I check the event, I make sure there is an event.
if (type == 0) printf("Event [type=%s location={%f, %f} modifierFlags={%s}]\n", NSEventTypeToChar(type), p.x, p.y, NSEventModifierFlagsToChar(objc_msgSend_uint(e, sel_registerName("modifierFlags"))));
The event can be pushed out using sendEvent and the window can be updated using updateWindows
objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
At the end of the draw loop, the event pool should be freed.
NSRelease(pool);
// compile with: // gcc example.c -lm -framework Foundation -framework AppKit -framework CoreVideo #include#include #include #include #ifdef __arm64__ /* ARM just uses objc_msgSend */ #define abi_objc_msgSend_stret objc_msgSend #define abi_objc_msgSend_fpret objc_msgSend #else /* __i386__ */ /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ #define abi_objc_msgSend_stret objc_msgSend_stret #define abi_objc_msgSend_fpret objc_msgSend_fpret #endif typedef CGRect NSRect; typedef CGPoint NSPoint; typedef CGSize NSSize; typedef void NSEvent; typedef void NSString; typedef void NSWindow; typedef void NSApplication; typedef unsigned long NSUInteger; typedef long NSInteger; #define NS_ENUM(type, name) type name; enum typedef NS_ENUM(NSUInteger, NSWindowStyleMask) { NSWindowStyleMaskBorderless = 0, NSWindowStyleMaskTitled = 1 << 0, NSWindowStyleMaskClosable = 1 << 1, NSWindowStyleMaskMiniaturizable = 1 << 2, NSWindowStyleMaskResizable = 1 << 3, NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, NSWindowStyleMaskFullScreen = 1 << 14, NSWindowStyleMaskFullSizeContentView = 1 << 15, NSWindowStyleMaskUtilityWindow = 1 << 4, NSWindowStyleMaskDocModalWindow = 1 << 6, NSWindowStyleMaskNonactivatingPanel = 1 << 7, NSWindowStyleMaskHUDWindow = 1 << 13 }; typedef NS_ENUM(NSUInteger, NSBackingStoreType) { NSBackingStoreRetained = 0, NSBackingStoreNonretained = 1, NSBackingStoreBuffered = 2 }; typedef NS_ENUM(NSUInteger, NSEventType) { /* various types of events */ NSEventTypeLeftMouseDown = 1, NSEventTypeLeftMouseUp = 2, NSEventTypeRightMouseDown = 3, NSEventTypeRightMouseUp = 4, NSEventTypeMouseMoved = 5, NSEventTypeLeftMouseDragged = 6, NSEventTypeRightMouseDragged = 7, NSEventTypeMouseEntered = 8, NSEventTypeMouseExited = 9, NSEventTypeKeyDown = 10, NSEventTypeKeyUp = 11, NSEventTypeFlagsChanged = 12, NSEventTypeAppKitDefined = 13, NSEventTypeSystemDefined = 14, NSEventTypeApplicationDefined = 15, NSEventTypePeriodic = 16, NSEventTypeCursorUpdate = 17, NSEventTypeScrollWheel = 22, NSEventTypeTabletPoint = 23, NSEventTypeTabletProximity = 24, NSEventTypeOtherMouseDown = 25, NSEventTypeOtherMouseUp = 26, NSEventTypeOtherMouseDragged = 27, /* The following event types are available on some hardware on 10.5.2 and later */ NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, }; typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */ NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown, NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp, NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown, NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp, NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved, NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged, NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged, NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered, NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited, NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown, NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp, NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged, NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined, NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined, NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined, NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic, NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate, NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel, NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint, NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity, NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown, NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp, NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged, }; /* The following event masks are available on some hardware on 10.5.2 and later */ #define NSEventMaskGesture API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeGesture) #define NSEventMaskMagnify API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeMagnify) #define NSEventMaskSwipe API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeSwipe) #define NSEventMaskRotate API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeRotate) #define NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeBeginGesture) #define NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) (1ULL << NSEventTypeEndGesture) /* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. */ #define NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) (1ULL << NSEventTypeSmartMagnify) #define NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) (1ULL << NSEventTypePressure) #define NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) (1ULL << NSEventTypeDirectTouch) #define NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) (1ULL << NSEventTypeChangeMode) #define NSEventMaskAny NSUIntegerMax typedef NS_ENUM(NSUInteger, NSEventModifierFlags) { NSEventModifierFlagCapsLock = 1 << 16, // Set if Caps Lock key is pressed. NSEventModifierFlagShift = 1 << 17, // Set if Shift key is pressed. NSEventModifierFlagControl = 1 << 18, // Set if Control key is pressed. NSEventModifierFlagOption = 1 << 19, // Set if Option or Alternate key is pressed. NSEventModifierFlagCommand = 1 << 20, // Set if Command key is pressed. NSEventModifierFlagNumericPad = 1 << 21, // Set if any key in the numeric keypad is pressed. NSEventModifierFlagHelp = 1 << 22, // Set if the Help key is pressed. NSEventModifierFlagFunction = 1 << 23, // Set if any function key is pressed. }; #define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) #define objc_msgSend_id_id ((id (*)(id, SEL, id))objc_msgSend) #define objc_msgSend_id_rect ((id (*)(id, SEL, NSRect))objc_msgSend) #define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) #define objc_msgSend_int ((NSInteger (*)(id, SEL))objc_msgSend) #define objc_msgSend_SEL ((SEL (*)(id, SEL))objc_msgSend) #define objc_msgSend_float ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) #define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) #define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) #define objc_msgSend_double ((double (*)(id, SEL))objc_msgSend) #define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) #define objc_msgSend_void_uint ((void (*)(id, SEL, NSUInteger))objc_msgSend) #define objc_msgSend_void_int ((void (*)(id, SEL, NSInteger))objc_msgSend) #define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) #define objc_msgSend_void_float ((void (*)(id, SEL, CGFloat))objc_msgSend) #define objc_msgSend_void_double ((void (*)(id, SEL, double))objc_msgSend) #define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) #define objc_msgSend_id_char_const ((id (*)(id, SEL, char const *))objc_msgSend) typedef enum NSApplicationActivationPolicy { NSApplicationActivationPolicyRegular, NSApplicationActivationPolicyAccessory, NSApplicationActivationPolicyProhibited } NSApplicationActivationPolicy; #define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) #define NSRelease(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("release")) bool running = true; unsigned int onClose(void* self) { NSWindow* win = NULL; object_getInstanceVariable(self, "NSWindow", (void*)&win); if (win == NULL) return true; running = false; return true; } NSSize windowResize(void* self, SEL sel, NSSize frameSize) { NSWindow* win = NULL; object_getInstanceVariable(self, "NSWindow", (void*)&win); if (win == NULL) return frameSize; printf("window resized to %f %f\n", frameSize.width, frameSize.height); return frameSize; } #include const char* NSEventTypeToChar(NSEventType eventType); const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags); int main(int argc, char* argv[]) { class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP) onClose, 0); NSApplication* NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); objc_msgSend_void_int(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular); NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable; SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); NSWindow* window = ((id (*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) (NSAlloc(objc_getClass("NSWindow")), func, (NSRect){{200, 200}, {200, 200}}, macArgs, macArgs, false); Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); class_addIvar( delegateClass, "NSWindow", sizeof(NSWindow*), rint(log2(sizeof(NSWindow*))), "L" ); class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) windowResize, "{NSSize=ff}@:{NSSize=ff}"); id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init")); object_setInstanceVariable(delegate, "NSWindow", window); objc_msgSend_void_id(window, sel_registerName("setDelegate:"), delegate); objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); ((id(*)(id, SEL, SEL))objc_msgSend)(window, sel_registerName("makeKeyAndOrderFront:"), NULL); objc_msgSend_void_bool(window, sel_registerName("setIsVisible:"), true); objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); while (running) { id pool = objc_msgSend_id(NSAlloc(objc_getClass("NSAutoreleasePool")), sel_registerName("init")); NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) (NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), ULONG_MAX, NULL, ((id(*)(id, SEL, const char*))objc_msgSend) ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "kCFRunLoopDefaultMode"), true); unsigned int type = objc_msgSend_uint(e, sel_registerName("type")); NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); if (type != 0) printf("Event [type=%s location={%f, %f} modifierFlags={%s}]\n", NSEventTypeToChar(type), p.x, p.y, NSEventModifierFlagsToChar(objc_msgSend_uint(e, sel_registerName("modifierFlags")))); objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows")); NSRelease(pool); } } const char* NSEventTypeToChar(NSEventType eventType) { switch (eventType) { case NSEventTypeLeftMouseDown: return "LeftMouseDown"; case NSEventTypeLeftMouseUp: return "LeftMouseUp"; case NSEventTypeRightMouseDown: return "RightMouseDown"; case NSEventTypeRightMouseUp: return "RightMouseUp"; case NSEventTypeMouseMoved: return "MouseMoved"; case NSEventTypeLeftMouseDragged: return "LeftMouseDragged"; case NSEventTypeRightMouseDragged: return "RightMouseDragged"; case NSEventTypeMouseEntered: return "MouseEntered"; case NSEventTypeMouseExited: return "MouseExited"; case NSEventTypeKeyDown: return "KeyDown"; case NSEventTypeKeyUp: return "KeyUp"; case NSEventTypeFlagsChanged: return "FlagsChanged"; case NSEventTypeAppKitDefined: return "AppKitDefined"; case NSEventTypeSystemDefined: return "SystemDefined"; case NSEventTypeApplicationDefined: return "ApplicationDefined"; case NSEventTypePeriodic: return "Periodic"; case NSEventTypeCursorUpdate: return "CursorUpdate"; case NSEventTypeScrollWheel: return "ScrollWheel"; case NSEventTypeTabletPoint: return "TabletPoint"; case NSEventTypeTabletProximity: return "TabletProximity"; case NSEventTypeOtherMouseDown: return "OtherMouseDown"; case NSEventTypeOtherMouseUp: return "OtherMouseUp"; case NSEventTypeOtherMouseDragged: return "OtherMouseDragged"; default: return "N/A"; } } char* ns_strcat(register char *s, register const char *append) { char *save = s; for (; *s; ++s); while ((*s++ = *append++)); return save; } const char* NSEventModifierFlagsToChar(NSEventModifierFlags modifierFlags) { static char result[100]; result[0] = '\0'; if ((modifierFlags & NSEventModifierFlagCapsLock) == NSEventModifierFlagCapsLock) ns_strcat(result, "CapsLock, "); if ((modifierFlags & NSEventModifierFlagShift) == NSEventModifierFlagShift) ns_strcat(result, "NShift, "); if ((modifierFlags & NSEventModifierFlagControl) == NSEventModifierFlagControl) ns_strcat(result, "Control, "); if ((modifierFlags & NSEventModifierFlagOption) == NSEventModifierFlagOption) ns_strcat(result, "Option, "); if ((modifierFlags & NSEventModifierFlagCommand) == NSEventModifierFlagCommand) ns_strcat(result, "Command, "); if ((modifierFlags & NSEventModifierFlagNumericPad) == NSEventModifierFlagNumericPad) ns_strcat(result, "NumericPad, "); if ((modifierFlags & NSEventModifierFlagHelp) == NSEventModifierFlagHelp) ns_strcat(result, "Help, "); if ((modifierFlags & NSEventModifierFlagFunction) == NSEventModifierFlagFunction) ns_strcat(result, "Function, "); return result; }
위 내용은 RGFW 세부 정보: 순수 C의 코코아의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!