#include #include "../../external/stb_ds.h" #include "Mw/LowLevel/Cocoa.h" #ifdef __clang__ #pragma clang push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif static NSRect rectFlip(NSRect originFrame) { NSScreen* zeroScreen = [[NSScreen screens] objectAtIndex:0]; double screenHeight = [zeroScreen frame].size.height; double originY = originFrame.origin.y; double frameHeight = originFrame.size.height; double destinationY = screenHeight - (originY + frameHeight); NSRect destinationFrame = originFrame; destinationFrame.origin.y = destinationY; return destinationFrame; } static NSPoint pointFlip(NSPoint point) { return rectFlip(NSMakeRect(point.x, point.y, 0, 0)).origin; } static NSRect localRectFlip(NSRect originFrame, NSView* view) { float viewHeight = [view bounds].size.height; NSRect destinationFrame = NSMakeRect(originFrame.origin.x, [view bounds].size.height - (originFrame.origin.y + originFrame.size.height), originFrame.size.width, originFrame.size.height); return destinationFrame; } static NSPoint localPointFlip(NSPoint point, NSView* view) { return localRectFlip(NSMakeRect(point.x, point.y, 0, 0), view).origin; } /* Recursively dispatch a key event to a widget and its children */ static void recursive_dispatch_key(MwLL handle, int* k) { MwWidget h = (MwWidget)handle->common.user; MwLLDispatch(handle, key, k); if(h) { int i; for(i = 0; i < arrlen(h->children); i++) { MwLLDispatch(h->children[i]->lowlevel, key, k); if(arrlen(h->children[i]->children) > 0) { recursive_dispatch_key(h->children[i]->lowlevel, k); } } } }; /* Recursively dispatch a key released event to a widget and its children */ static void recursive_dispatch_key_released(MwLL handle, int* k) { MwWidget h = (MwWidget)handle->common.user; MwLLDispatch(handle, key_released, k); if(h) { int i; for(i = 0; i < arrlen(h->children); i++) { MwLLDispatch(h->children[i]->lowlevel, key_released, k); if(arrlen(h->children[i]->children) > 0) { recursive_dispatch_key_released(h->children[i]->lowlevel, k); } } } }; @implementation MilskoCocoaPixmap + (MilskoCocoaPixmap*)newWithWidth:(int)width height:(int)height { MilskoCocoaPixmap* p = [MilskoCocoaPixmap alloc]; p->width = width; p->height = height; p->image = NULL; p->buf = malloc(width * height * 4); p->rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&p->buf pixelsWide:(int)width pixelsHigh:(int)height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:(int)width * 4 bitsPerPixel:32]; assert(p->rep); p->image = [[NSImage alloc] initWithSize:NSMakeSize(width, height)]; assert(p->image); [p->image addRepresentation:p->rep]; return p; } - (void)updateWithData:(unsigned char*)_data { memcpy(self->buf, _data, width * height * 4); if(self->rep) { [self->image removeRepresentation:self->rep]; [self->rep release]; } self->rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&self->buf pixelsWide:(int)width pixelsHigh:(int)height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:(int)width * 4 bitsPerPixel:32]; [self->image addRepresentation:self->rep]; } - (void)destroy { free(self->buf); [self->image removeRepresentation:self->rep]; [self->image release]; [self->rep release]; self->image = NULL; self->rep = NULL; } - (NSImage*)image { return self->image; } @end @implementation MilskoCocoa - (void)sendClipboardEvent { /*NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSArray* items = @[ @"public.utf8-plain-text", @"public.utf16-external-plain-text", @"com.apple.traditional-mac-plain-text", ]; MwLL this = self->handle.pointer; if([pasteboard canReadItemWithDataConformingToTypes:items]) { char* data = NULL; size_t size = 0; for(NSPasteboardItem* item in [pasteboard pasteboardItems]) { for(NSString* it in items) { NSString* itemData = [item stringForType:(NSString*)it]; if(itemData != NULL) { if(strHash != 0 && strHash != [itemData hash]) { char* text = malloc([itemData length]); strncpy(text, [itemData UTF8String], [itemData length]); MwLLDispatch(this, clipboard, text); printf("%s -> %p\n", text, this); free(text); } strHash = [itemData hash]; } } } } [pool release];*/ } - (void)destroy { [self->handle release]; [self->window release]; } - (MwLL)getParent { return self->parent; } - (NSView*)getView { return view; } - (NSWindow*)getWindow { return window; } - (void)nudge { MwLL p = self->parent; self->_eventsPending = MwTRUE; if(p) { [p->cocoa.real->view setNeedsDisplay:true]; p = p->cocoa.real->parent; } } - (void)pushCursor { [self->cursor push]; } - (void)popCursor { [self->cursor pop]; } - (MilskoFakePointer*)getHandle { return handle; } @end @implementation MilskoCocoaApplication - (void)sendEvent:(NSEvent*)event { self->doPending = MwTRUE; [super sendEvent:event]; } - (MwBool)pending { if(self->doPending) { self->doPending = MwFALSE; return MwTRUE; } return MwFALSE; } @end @implementation MilskoCocoaView - (id)initWithFrame:(NSRect)frame { givenRect = frame; x = frame.origin.x; y = frame.origin.y; width = frame.size.width; height = frame.size.height; self = [super initWithFrame:frame]; if(width == 0 || height == 0) { self->rep = NULL; self->context = NULL; } else { [self initRepAndContextWithWidth:width Height:height]; } self->_child = MwFALSE; return self; } - (void)setChild { self->_child = MwTRUE; } - (NSGraphicsContext*)context { return self->context; } - (NSBitmapImageRep*)getRep { return self->rep; } - (void)drawRect:(NSRect)dirtyRect { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSArray* subviews = [self subviews]; int i; if(!self->rep) { if(dirtyRect.size.width && dirtyRect.size.height) { [self initRepAndContextWithWidth:dirtyRect.size.width Height:dirtyRect.size.height]; self->width = dirtyRect.size.width; self->height = dirtyRect.size.height; } else { [pool release]; return; } } [self->rep drawInRect:NSMakeRect(dirtyRect.origin.x, dirtyRect.origin.y, [self->rep pixelsWide], [self->rep pixelsHigh])]; for(i = 0; i < [subviews count]; i++) { MilskoCocoaView* subview = [subviews objectAtIndex:i]; NSRect bounds = [subview bounds]; [subview drawRect:bounds]; } [pool release]; } - (void)initRepAndContextWithWidth:(float)w Height:(float)h { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; self->rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:w pixelsHigh:h bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:w * 4 bitsPerPixel:32]; assert(self->rep); [self->rep retain]; [self->rep setOpaque:MwFALSE]; [self->rep setAlpha:MwTRUE]; self->context = [NSGraphicsContext graphicsContextWithBitmapImageRep:self->rep]; assert(self->context); [self->context retain]; [pool release]; } - (void)destroy { [self->rep release]; [self->context release]; } - (void)setFrameSize:(NSSize)newSize { [super setFrameSize:newSize]; [self->rep setSize:newSize]; self->width = newSize.width; self->height = newSize.height; } - (BOOL)canBecomeKeyView { return YES; } - (BOOL)acceptsFirstResponder { return YES; } - (NSView*)hitTest:(NSPoint)aPoint { if(self->_child) { return NSPointInRect(aPoint, [self bounds]) ? self : nil; } else { return [super hitTest:aPoint]; } } - (void)handleKeyEvent:(NSEvent*)ev ll:(MwLL)ll down:(MwBool)isDown { int ch; enum { kVK_ANSI_A = 0x00, kVK_ANSI_S = 0x01, kVK_ANSI_D = 0x02, kVK_ANSI_F = 0x03, kVK_ANSI_H = 0x04, kVK_ANSI_G = 0x05, kVK_ANSI_Z = 0x06, kVK_ANSI_X = 0x07, kVK_ANSI_C = 0x08, kVK_ANSI_V = 0x09, kVK_ANSI_B = 0x0B, kVK_ANSI_Q = 0x0C, kVK_ANSI_W = 0x0D, kVK_ANSI_E = 0x0E, kVK_ANSI_R = 0x0F, kVK_ANSI_Y = 0x10, kVK_ANSI_T = 0x11, kVK_ANSI_1 = 0x12, kVK_ANSI_2 = 0x13, kVK_ANSI_3 = 0x14, kVK_ANSI_4 = 0x15, kVK_ANSI_6 = 0x16, kVK_ANSI_5 = 0x17, kVK_ANSI_Equal = 0x18, kVK_ANSI_9 = 0x19, kVK_ANSI_7 = 0x1A, kVK_ANSI_Minus = 0x1B, kVK_ANSI_8 = 0x1C, kVK_ANSI_0 = 0x1D, kVK_ANSI_RightBracket = 0x1E, kVK_ANSI_O = 0x1F, kVK_ANSI_U = 0x20, kVK_ANSI_LeftBracket = 0x21, kVK_ANSI_I = 0x22, kVK_ANSI_P = 0x23, kVK_ANSI_L = 0x25, kVK_ANSI_J = 0x26, kVK_ANSI_Quote = 0x27, kVK_ANSI_K = 0x28, kVK_ANSI_Semicolon = 0x29, kVK_ANSI_Backslash = 0x2A, kVK_ANSI_Comma = 0x2B, kVK_ANSI_Slash = 0x2C, kVK_ANSI_N = 0x2D, kVK_ANSI_M = 0x2E, kVK_ANSI_Period = 0x2F, kVK_ANSI_Grave = 0x32, kVK_Return = 0x24, kVK_Space = 0x31, kVK_Escape = 0x35, kVK_Shift = 0x38, kVK_Control = 0x3B, kVK_RightShift = 0x3C, kVK_RightControl = 0x3E, kVK_LeftArrow = 0x7B, kVK_RightArrow = 0x7C, kVK_DownArrow = 0x7D, kVK_UpArrow = 0x7E }; #define KEY_CASE(x, y) \ case x: \ ch = y; \ break; switch([ev keyCode]) { KEY_CASE(kVK_ANSI_A, 'a') KEY_CASE(kVK_ANSI_B, 'b') KEY_CASE(kVK_ANSI_C, 'c') KEY_CASE(kVK_ANSI_D, 'd') KEY_CASE(kVK_ANSI_E, 'e') KEY_CASE(kVK_ANSI_F, 'f') KEY_CASE(kVK_ANSI_G, 'g') KEY_CASE(kVK_ANSI_H, 'h') KEY_CASE(kVK_ANSI_I, 'i') KEY_CASE(kVK_ANSI_J, 'j') KEY_CASE(kVK_ANSI_K, 'k') KEY_CASE(kVK_ANSI_L, 'l') KEY_CASE(kVK_ANSI_M, 'm') KEY_CASE(kVK_ANSI_N, 'n') KEY_CASE(kVK_ANSI_O, 'o') KEY_CASE(kVK_ANSI_P, 'p') KEY_CASE(kVK_ANSI_Q, 'q') KEY_CASE(kVK_ANSI_R, 'r') KEY_CASE(kVK_ANSI_S, 's') KEY_CASE(kVK_ANSI_T, 't') KEY_CASE(kVK_ANSI_U, 'u') KEY_CASE(kVK_ANSI_V, 'v') KEY_CASE(kVK_ANSI_W, 'w') KEY_CASE(kVK_ANSI_X, 'x') KEY_CASE(kVK_ANSI_Y, 'y') KEY_CASE(kVK_ANSI_Z, 'z') KEY_CASE(kVK_ANSI_0, '0') KEY_CASE(kVK_ANSI_1, '1') KEY_CASE(kVK_ANSI_2, '2') KEY_CASE(kVK_ANSI_3, '3') KEY_CASE(kVK_ANSI_4, '4') KEY_CASE(kVK_ANSI_5, '5') KEY_CASE(kVK_ANSI_6, '6') KEY_CASE(kVK_ANSI_7, '7') KEY_CASE(kVK_ANSI_8, '8') KEY_CASE(kVK_ANSI_9, '9') KEY_CASE(kVK_ANSI_Quote, '\"') KEY_CASE(kVK_ANSI_Grave, '`') KEY_CASE(kVK_ANSI_Backslash, '/') KEY_CASE(kVK_ANSI_Comma, ',') KEY_CASE(kVK_ANSI_Equal, '=') KEY_CASE(kVK_Escape, MwLLKeyEscape) KEY_CASE(kVK_ANSI_LeftBracket, '[') KEY_CASE(kVK_ANSI_Minus, '-') KEY_CASE(kVK_ANSI_Period, '.') KEY_CASE(kVK_Return, MwLLKeyEnter) KEY_CASE(kVK_ANSI_RightBracket, ']') KEY_CASE(kVK_ANSI_Semicolon, ';') KEY_CASE(kVK_ANSI_Slash, '\\') KEY_CASE(kVK_Space, ' ') KEY_CASE(kVK_Control, MwLLKeyControl) KEY_CASE(kVK_RightControl, MwLLKeyControl) KEY_CASE(kVK_Shift, MwLLKeyLeftShift) KEY_CASE(kVK_RightShift, MwLLKeyRightShift) KEY_CASE(kVK_DownArrow, MwLLKeyDown) KEY_CASE(kVK_LeftArrow, MwLLKeyLeft) KEY_CASE(kVK_RightArrow, MwLLKeyRight) KEY_CASE(kVK_UpArrow, MwLLKeyUp) } if(isDown) { recursive_dispatch_key(ll, &ch); } else { recursive_dispatch_key_released(ll, &ch); } [ll->cocoa.real nudge]; } - (void)mouseEntered:(NSEvent*)ev { MwLL h = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; MwLLDispatch(h, focus_in, NULL); [h->cocoa.real pushCursor]; [super mouseEntered:ev]; } - (void)mouseExited:(NSEvent*)ev { MwLL h = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; MwLLDispatch(h, focus_out, NULL); [h->cocoa.real popCursor]; [super mouseExited:ev]; } - (void)keyDown:(NSEvent*)ev { MwLL h = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; [self handleKeyEvent:ev ll:h down:MwTRUE]; [super keyDown:ev]; } - (void)keyUp:(NSEvent*)ev { MwLL h = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; [self handleKeyEvent:ev ll:h down:MwFALSE]; [super keyUp:ev]; } - (void)mouseMoved:(NSEvent*)ev { MwLLMouse mouse; NSPoint mousePoint = pointFlip([ev locationInWindow]); MwLL this = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; mouse.button = MwLLMouseLeft; mouse.point.x = mousePoint.x; mouse.point.y = mousePoint.y; MwLLDispatch(this, move, &mouse); [this->cocoa.real nudge]; [super mouseMoved:ev]; } - (BOOL)mouseDownCanMoveWindow { return NO; } - (void)mouseDown:(NSEvent*)ev { MwLLMouse mouse; NSPoint mousePoint = [ev locationInWindow]; MwLL this = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; mousePoint.y = [[ev window] frame].size.height - mousePoint.y; mouse.button = MwLLMouseLeft; mouse.point.x = mousePoint.x; mouse.point.y = mousePoint.y; MwLLDispatch(this, down, &mouse); [this->cocoa.real nudge]; [super mouseDown:ev]; } - (void)mouseUp:(NSEvent*)ev { MwLLMouse mouse; NSPoint mousePoint = [ev locationInWindow]; MwLL this = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; mousePoint.y = [[ev window] frame].size.height - mousePoint.y; mouse.button = MwLLMouseLeft; mouse.point.x = mousePoint.x; mouse.point.y = mousePoint.y; MwLLDispatch(this, up, &mouse); [this->cocoa.real nudge]; [super mouseUp:ev]; } - (void)rightMouseDown:(NSEvent*)ev { MwLLMouse mouse; NSPoint mousePoint = [ev locationInWindow]; MwLL this = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; mousePoint.y = [[ev window] frame].size.height - mousePoint.y; mouse.button = MwLLMouseRight; mouse.point.x = mousePoint.x; mouse.point.y = mousePoint.y; MwLLDispatch(this, down, &mouse); [this->cocoa.real nudge]; [super rightMouseDown:ev]; } - (void)rightMouseUp:(NSEvent*)ev { MwLLMouse mouse; NSPoint mousePoint = [ev locationInWindow]; MwLL this = [((MilskoFakePointer*)[[self subviews] objectAtIndex:0]) pointer]; mousePoint.y = [[ev window] frame].size.height - mousePoint.y; mouse.button = MwLLMouseRight; mouse.point.x = mousePoint.x; mouse.point.y = mousePoint.y; MwLLDispatch(this, up, &mouse); [this->cocoa.real nudge]; [super rightMouseUp:ev]; } @end @implementation MilskoCocoaApplicationDelegate - (MilskoCocoaApplicationDelegate*)initWithAppl:(MilskoCocoaApplication*)_appl { self->appl = _appl; return self; } - (void)applicationDidBecomeActive:(NSNotification*)notification { [self->appl activateIgnoringOtherApps:true]; // printf("test\n"); } - (void)applicationWillFinishLaunching:(NSNotification*)notification { // printf("test\n"); // [self->appl activateIgnoringOtherApps:MwTRUE]; } - (void)applicationDidFinishLaunching:(NSNotification*)notification { // [self->appl activateIgnoringOtherApps:MwTRUE]; } @end @implementation MilskoCocoaWindowDelegate - (NSSize)windowWillResize:(NSWindow*)win toSize:(NSSize)frameSize; { if([[[win contentView] subviews] count] >= 1) { NSView* ptr = [[[win contentView] subviews] objectAtIndex:0]; if([ptr isKindOfClass:[MilskoFakePointer class]]) { MwLL h = [(MilskoFakePointer*)ptr pointer]; MwLLDispatch(h, resize, NULL); } } return frameSize; } - (void)windowDidBecomeMain:(NSNotification*)notification { if([[[w contentView] subviews] count] != 0) { MwLL h = [((MilskoFakePointer*)[[[w contentView] subviews] objectAtIndex:0]) pointer]; MwLLDispatch(h, focus_in, NULL); } [self->w makeKeyAndOrderFront:nil]; } - (void)windowDidResize:(NSNotification*)notification { (void)notification; } // This will close/terminate the application when the main window is closed. - (void)windowWillClose:(NSNotification*)notification { (void)notification; // MilskoCocoa* window = notification.object; // MwLL handle = [window getHandle].pointer; // MwLLDispatch(handle, close, NULL); } - (MilskoCocoaWindowDelegate*)initWithWin:(NSWindow*)win { self->w = win; return self; } @end @implementation MilskoFakePointer - (void)setPointer:(void*)pointer { [self setFrame:*(NSRect*)&pointer]; self->ptr = pointer; }; - (void*)pointer { return self->ptr; }; - (void)drawRect:(NSRect)dirtyRect { /* explicitly do nothing */ (void)dirtyRect; } - (void)destroy { } @end @implementation MilskoCocoaWindow - (BOOL)canBecomeKeyWindow { return true; } - (BOOL)canBecomeMainWindow { return true; } @end static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { MwLL r; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; MilskoCocoa* c = [MilskoCocoa alloc]; r = malloc(sizeof(*r)); MwLLCreateCommon(r); [c retain]; c->application = (MilskoCocoaApplication*)[MilskoCocoaApplication sharedApplication]; [c->application retain]; /* * MacOS doesn't really have a "default" window position, you're actually * meant to center the window yourself, so if the user passes MwDEFAULT * respond appropriately. Also for child windows just have it be 0. */ if(x == MwDEFAULT) { x = r ? ([[NSScreen mainScreen] frame].size.width / 2.) - (width / 2.) : 0; } if(y == MwDEFAULT) { y = r ? ([[NSScreen mainScreen] frame].size.height / 2.) - (height / 2.) : 0; } c->rect = NSMakeRect(x, y, width, height); if(parent == NULL) { c->window = [[NSWindow alloc] initWithContentRect:rectFlip(c->rect) styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:NO]; [c->window setDelegate:[[MilskoCocoaWindowDelegate alloc] initWithWin:c->window]]; [c->window retain]; [c->window makeKeyAndOrderFront:c->application]; [c->window makeFirstResponder:c->view]; c->view = [[MilskoCocoaView alloc] initWithFrame:c->rect]; [c->view retain]; [c->window setContentView:c->view]; if([c->application respondsToSelector:@selector(setActivationPolicy:)]) { [c->application setActivationPolicy:0 /* NSApplicationActivationPolicyRegular */]; } [c->application activateIgnoringOtherApps:MwTRUE]; [c->application setDelegate:[[MilskoCocoaApplicationDelegate alloc] initWithAppl:c->application]]; [c->application retain]; [c->view addTrackingRect:c->rect owner:c->view userData:NULL assumeInside:MwFALSE]; c->modalSession = [c->application beginModalSessionForWindow:c->window]; } else { MilskoCocoa* p = parent->cocoa.real; c->window = p->window; NSRect rect = localRectFlip(c->rect, p->view); c->view = [[MilskoCocoaView alloc] initWithFrame:rect]; [c->view setBounds:c->rect]; [c->view retain]; [c->view setChild]; [c->view addTrackingRect:c->rect owner:c->view userData:NULL assumeInside:MwFALSE]; [p->view addSubview:c->view]; c->modalSession = p->modalSession; } [c->window setAcceptsMouseMovedEvents:MwTRUE]; c->handle = [[MilskoFakePointer alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)]; [c->handle retain]; [c->handle setPointer:r]; [c->view addSubview:c->handle]; c->parent = parent; c->_forceRender = MwTRUE; c->strHash = 0; c->cursorPixmap = NULL; [c->application finishLaunching]; c->pointerLocked = MwFALSE; [pool release]; r->cocoa.real = c; return r; } static void MwLLDestroyImpl(MwLL handle) { MilskoCocoa* h = handle->cocoa.real; [h destroy]; MwLLDestroyCommon(handle); free(handle); } static void MwLLBeginDrawImpl(MwLL handle) { (void)handle; } static void MwLLEndDrawImpl(MwLL handle) { (void)handle; } static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count, MwLLColor color) { MilskoCocoa* self = handle->cocoa.real; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSGraphicsContext* ctx = [self->view context]; if(ctx) { int i; NSBezierPath* path = [NSBezierPath bezierPath]; /* whatever rect we use for coordinate flipping */ NSRect _rect = self->parent ? [self->view bounds] : [self->window frame]; NSColor* nscolor = [NSColor colorWithCalibratedRed:color->common.red / 255. green:color->common.green / 255. blue:color->common.blue / 255. alpha:1.0]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:ctx]; [nscolor setFill]; for(i = 0; i < points_count; i++) { NSPoint p = localPointFlip(NSMakePoint(points[i].x, points[i].y), self->view); if(i == 0) { [path moveToPoint:p]; } else { [path lineToPoint:p]; } } [path closePath]; [path fill]; [NSGraphicsContext restoreGraphicsState]; [self->view setNeedsDisplay:YES]; } [pool release]; } static void MwLLLineImpl(MwLL handle, MwPoint* points, MwLLColor color) { MilskoCocoa* self = handle->cocoa.real; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSGraphicsContext* ctx = [self->view context]; if(ctx) { int i; NSBezierPath* path = [NSBezierPath bezierPath]; /* whatever rect we use for coordinate flipping */ NSRect _rect = self->parent ? [self->view bounds] : [self->window frame]; NSColor* nscolor = [NSColor colorWithCalibratedRed:color->common.red / 255. green:color->common.green / 255. blue:color->common.blue / 255. alpha:1.0]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:ctx]; [nscolor setFill]; for(i = 0; i < 2; i++) { NSPoint p = localPointFlip(NSMakePoint(points[i].x, points[i].y), self->view); if(i == 0) { [path moveToPoint:p]; } else { [path lineToPoint:p]; } } [path closePath]; [path stroke]; [NSGraphicsContext restoreGraphicsState]; [self->view setNeedsDisplay:YES]; } [pool release]; } static MwLLColor MwLLAllocColorImpl(MwLL handle, int r, int g, int b) { MwLLColor c = malloc(sizeof(*c)); MwLLColorUpdate(handle, c, r, g, b); return c; } static void MwLLColorUpdateImpl(MwLL handle, MwLLColor c, int r, int g, int b) { (void)handle; c->common.red = r; c->common.green = g; c->common.blue = b; } static void MwLLGetXYWHImpl(MwLL handle, int* x, int* y, unsigned int* w, unsigned int* h) { MilskoCocoa* self = handle->cocoa.real; NSRect frame; if(self->parent) { frame = [self->view frame]; } else { frame = [self->window frame]; } frame = rectFlip(frame); *x = frame.origin.x; *y = frame.origin.y; *w = frame.size.width; *h = frame.size.height; } static void MwLLSetXYImpl(MwLL handle, int x, int y) { MilskoCocoa* self = handle->cocoa.real; NSRect frame; if(self->parent) { frame = [self->view frame]; } else { frame = [self->window frame]; } frame.origin.x = x; frame.origin.y = y; if(!self->parent) { frame = rectFlip(frame); [self->window setFrame:frame display:MwTRUE animate:MwTRUE]; } else { frame = localRectFlip(frame, self->parent->cocoa.real->view); [self->view setBounds:frame]; } [self nudge]; } static void MwLLSetWHImpl(MwLL handle, int w, int h) { MilskoCocoa* self = handle->cocoa.real; NSRect frame = [self->window frame]; frame.size.width = w; frame.size.height = h; self->rect = frame; [self->view setFrameSize:frame.size]; if(self->parent) { [self->view setFrame:frame]; } else { [self->window setFrame:frame display:YES animate:false]; } [self nudge]; } static void MwLLFreeColorImpl(MwLLColor color) { free(color); } static int MwLLPendingImpl(MwLL handle) { MilskoCocoa* self = handle->cocoa.real; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; MwBool isPending = [self->application pending]; [self->application runModalSession:self->modalSession]; [pool release]; return self->_forceRender || self->_eventsPending || isPending; } static void MwLLNextEventImpl(MwLL handle) { MilskoCocoa* self = handle->cocoa.real; MwLL h = [self->handle pointer]; if(self->_forceRender) { MwLLDispatch(h, draw, NULL); self->_forceRender = MwFALSE; } if(self->_eventsPending) { MwLLDispatch(h, draw, NULL); self->_eventsPending = MwFALSE; } [self sendClipboardEvent]; #ifdef __APPLE__ if(self->pointerLocked && [self->window isMainWindow] && self->lastEvent) { struct CGPoint pos; pos.x = [self->window frame].origin.x + [self->window frame].size.width / 2; pos.y = [self->window frame].origin.y + [self->window frame].size.height / 2; CGWarpMouseCursorPosition(pos); } #endif [self->application updateWindows]; } static void MwLLSetTitleImpl(MwLL handle, const char* title) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; [handle->cocoa.real->window setTitleWithRepresentedFilename:[NSString stringWithUTF8String:title]]; [pool release]; } static MwLLPixmap MwLLCreatePixmapImpl(MwLL handle, unsigned char* data, int width, int height) { (void)handle; MwLLPixmap r = malloc(sizeof(*r)); memset(r, 0, sizeof(*r)); r->common.raw = malloc(4 * width * height); memcpy(r->common.raw, data, 4 * width * height); r->common.width = width; r->common.height = height; r->cocoa.real = [MilskoCocoaPixmap newWithWidth:width height:height]; MwLLPixmapUpdate(r); return r; } static void MwLLPixmapUpdateImpl(MwLLPixmap pixmap) { MilskoCocoaPixmap* self = pixmap->cocoa.real; memcpy(self->buf, pixmap->common.raw, pixmap->common.width * pixmap->common.height * 4); if(self->rep) { [self->image removeRepresentation:self->rep]; [self->rep release]; } self->rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&self->buf pixelsWide:(int)pixmap->common.width pixelsHigh:(int)pixmap->common.height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:(int)pixmap->common.width * 4 bitsPerPixel:32]; [self->image addRepresentation:self->rep]; } static void MwLLDestroyPixmapImpl(MwLLPixmap pixmap) { [pixmap->cocoa.real destroy]; [pixmap->cocoa.real release]; free(pixmap); } static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { MilskoCocoa* self = handle->cocoa.real; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; MilskoCocoaPixmap* p = pixmap->cocoa.real; NSGraphicsContext* ctx = [self->view context]; if(ctx) { [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:ctx]; [[p image] drawInRect:localRectFlip(NSMakeRect(rect->x, rect->y, rect->width, rect->height), self->view) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; [NSGraphicsContext restoreGraphicsState]; [self->view setNeedsDisplay:YES]; } [pool release]; MwLLForceRender(handle); } static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) { [handle->cocoa.real->application setApplicationIconImage:[pixmap->cocoa.real image]]; } static void MwLLForceRenderImpl(MwLL handle) { handle->cocoa.real->_forceRender = MwTRUE; } static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) { MilskoCocoa* self = handle->cocoa.real; int y, x, ys, xs; unsigned char* di = malloc(image->width * image->height * 4); memset(di, 0, image->width * image->height * 4); if(self->cursorPixmap) { [self->cursorPixmap destroy]; } self->cursorPixmap = [MilskoCocoaPixmap newWithWidth:image->width height:image->height]; xs = -mask->x + image->x; ys = MwCursorDataHeight + mask->y; ys = MwCursorDataHeight + image->y - ys; for(y = 0; y < mask->height; y++) { unsigned int d = mask->data[y]; for(x = mask->width - 1; x >= 0; x--) { int px = 0; int idx = ((y * mask->width) + x) * 4; if(d & 1) { di[idx + 3] = 255; }; d = d >> 1; } } for(y = 0; y < image->height; y++) { unsigned int d = image->data[y]; for(x = image->width - 1; x >= 0; x--) { int px = 0; int idx = ((y * image->width) + x) * 4; if(d & 1) { px = 255; }; di[idx] = px; di[idx + 1] = px; di[idx + 2] = px; d = d >> 1; } } [self->cursorPixmap updateWithData:di]; self->cursor = [[NSCursor alloc] initWithImage:[self->cursorPixmap image] hotSpot:NSMakePoint(image->x, image->y + image->height)]; [self->cursor retain]; [self->cursor pop]; [self->cursor push]; free(di); } static void MwLLDetachImpl(MwLL handle, MwPoint* point) { [handle->cocoa.real->window setParentWindow:NULL]; } static void MwLLShowImpl(MwLL handle, int show) { (void)show; } static void MwLLMakePopupImpl(MwLL handle, MwLL parent) { (void)parent; } static void MwLLSetSizeHintsImpl(MwLL handle, int minx, int miny, int maxx, int maxy) { MilskoCocoa* self = handle->cocoa.real; [self->window setMinSize:NSMakeSize(minx, miny)]; [self->window setMaxSize:NSMakeSize(maxx, maxy)]; } static void MwLLMakeBorderlessImpl(MwLL handle, int toggle) { MilskoCocoa* self = handle->cocoa.real; MwU32 mask = [self->window styleMask]; if(toggle) { mask ^= NSBorderlessWindowMask; mask |= NSTitledWindowMask; } else { mask |= NSBorderlessWindowMask; mask ^= NSTitledWindowMask; } [self->window initWithContentRect:self->rect styleMask:mask backing:NSBackingStoreBuffered defer:NO]; } static void MwLLFocusImpl(MwLL handle) { [handle->cocoa.real->application activateIgnoringOtherApps:MwTRUE]; } static void MwLLGrabPointerImpl(MwLL handle, int toggle) { handle->cocoa.real->pointerLocked = toggle; } static void MwLLSetClipboardImpl(MwLL handle, const char* text) { (void)handle; (void)text; } static void MwLLGetClipboardImpl(MwLL handle) { (void)handle; } static void MwLLMakeToolWindowImpl(MwLL handle) { /* If my understand of what a "tool window" usually is is correct then I * highly doubt the Mac OS has this and if they did they probably outright * removed it along time ago is this kind of conflicts with modern UX. So * we'll just make it borderless idgaf */ MwLLMakeBorderlessImpl(handle, 1); } static void MwLLGetCursorCoordImpl(MwLL handle, MwPoint* point) { NSPoint p = [NSEvent mouseLocation]; point->x = p.x; point->y = p.y; } static void MwLLGetScreenSizeImpl(MwLL handle, MwRect* rect) { MilskoCocoa* self = handle->cocoa.real; NSScreen* screen = [self->window screen]; rect->x = [screen frame].origin.x; rect->y = [screen frame].origin.y; rect->width = [screen frame].size.width; rect->height = [screen frame].size.height; } static void MwLLBeginStateChangeImpl(MwLL handle) { MwLLShow(handle, 0); } static void MwLLEndStateChangeImpl(MwLL handle) { MwLLShow(handle, 1); } static int MwLLCocoaCallInitImpl(void) { #ifndef __APPLE__ printf("Using GNUStep Backend\n"); #endif [MilskoCocoaApplication sharedApplication]; return 0; } #include "call.c" CALL(Cocoa);