- 
      
- 
        Save cmsj/43c14adbbe8381033de7 to your computer and use it in GitHub Desktop. 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | #import <Cocoa/Cocoa.h> | |
| #import <Carbon/Carbon.h> | |
| #import <lauxlib.h> | |
| /// === hs.drawing === | |
| /// | |
| /// Primitives for drawing on the screen in various ways | |
| // Useful definitions | |
| #define USERDATA_TAG "hs.drawing" | |
| #define get_item_arg(L, idx) ((drawing_t *)luaL_checkudata(L, idx, USERDATA_TAG)) | |
| // Declare our Lua userdata object and a storage container for them | |
| typedef struct _drawing_t { | |
| void *window; | |
| } drawing_t; | |
| NSMutableArray *drawingWindows; | |
| // Objective-C class interface definitions | |
| @interface HSDrawingWindow : NSWindow <NSWindowDelegate> | |
| @end | |
| @interface HSDrawingView : NSView | |
| @property BOOL HSFill; | |
| @property BOOL HSStroke; | |
| @property CGFloat HSLineWidth; | |
| @property (nonatomic, strong) NSColor *HSFillColor; | |
| @property (nonatomic, strong) NSColor *HSStrokeColor; | |
| @end | |
| @interface HSDrawingViewCircle : HSDrawingView | |
| @end | |
| // Objective-C class interface implementations | |
| @implementation HSDrawingWindow | |
| - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger __unused)windowStyle backing:(NSBackingStoreType __unused)bufferingType defer:(BOOL __unused)deferCreation { | |
| NSLog(@"HSDrawingWindow::initWithContentRect"); | |
| self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES ]; | |
| if (self) { | |
| [self setDelegate:self]; | |
| self.releasedWhenClosed = NO; | |
| self.backgroundColor = [NSColor clearColor]; | |
| self.opaque = NO; | |
| self.hasShadow = NO; | |
| self.ignoresMouseEvents = YES; | |
| self.restorable = NO; | |
| self.animationBehavior = NSWindowAnimationBehaviorNone; | |
| self.level = NSFloatingWindowLevel; | |
| } | |
| return self; | |
| } | |
| - (void)setLevelTop { | |
| self.level = NSFloatingWindowLevel; | |
| } | |
| - (void)setLevelBottom { | |
| self.level = CGWindowLevelForKey(kCGDesktopIconWindowLevelKey) - 1; | |
| } | |
| // NSWindowDelegate method, declining to close the window | |
| - (BOOL)windowShouldClose:(id __unused)sender { | |
| NSLog(@"HSDrawingWindow::windowShouldClose"); | |
| return NO; | |
| } | |
| // TODO: Do we need to implement this to GC ourselves? | |
| //- (void)windowWillClose:(NSNotification *)notification { | |
| //} | |
| @end | |
| @implementation HSDrawingView | |
| - (id)initWithFrame:(NSRect)frameRect { | |
| NSLog(@"HSDrawingView::initWithFrame"); | |
| self = [super initWithFrame:frameRect]; | |
| if (self) { | |
| // Set up our defaults | |
| self.HSFill = YES; | |
| self.HSStroke = YES; | |
| self.HSLineWidth = [NSBezierPath defaultLineWidth]; | |
| self.HSFillColor = [NSColor redColor]; | |
| self.HSStrokeColor = [NSColor blackColor]; | |
| } | |
| return self; | |
| } | |
| @end | |
| @implementation HSDrawingViewCircle | |
| - (void)drawRect:(NSRect)rect { | |
| NSLog(@"HSDrawingViewCircle::drawRect"); | |
| // Get the graphics context that we are currently executing under | |
| NSGraphicsContext* gc = [NSGraphicsContext currentContext]; | |
| // Save the current graphics context settings | |
| [gc saveGraphicsState]; | |
| // Set the color in the current graphics context for future draw operations | |
| [[self HSStrokeColor] setStroke]; | |
| [[self HSFillColor] setFill]; | |
| // Create our circle path | |
| //NSRect rect = NSMakeRect(10, 10, 10, 10); | |
| NSBezierPath* circlePath = [NSBezierPath bezierPath]; | |
| [circlePath appendBezierPathWithOvalInRect: rect]; | |
| // Outline and fill the path | |
| circlePath.lineWidth = self.HSLineWidth; | |
| if (self.HSStroke) { | |
| [circlePath stroke]; | |
| } | |
| if (self.HSFill) { | |
| [circlePath fill]; | |
| } | |
| // Restore the context to what it was before we messed with it | |
| [gc restoreGraphicsState]; | |
| } | |
| @end | |
| // Lua API implementation | |
| /// hs.drawing.circle(sizeRect) -> drawingObject or nil | |
| /// Constructor | |
| /// Creates a new circle object | |
| /// | |
| /// Parameters: | |
| /// * sizeRect - A rect-table containing the location/size of the circle | |
| /// | |
| /// Returns: | |
| /// * An `hs.drawing` object, or nil if an error occurs | |
| static int drawing_newCircle(lua_State *L) { | |
| NSRect windowRect; | |
| switch (lua_type(L, 1)) { | |
| case LUA_TTABLE: | |
| lua_getfield(L, 1, "x"); | |
| windowRect.origin.x = lua_tointeger(L, -1); | |
| lua_pop(L, 1); | |
| lua_getfield(L, 1, "y"); | |
| windowRect.origin.y = lua_tointeger(L, -1); | |
| lua_pop(L, 1); | |
| lua_getfield(L, 1, "w"); | |
| windowRect.size.width = lua_tointeger(L, -1); | |
| lua_pop(L, 1); | |
| lua_getfield(L, 1, "h"); | |
| windowRect.size.height = lua_tointeger(L, -1); | |
| lua_pop(L, 1); | |
| break; | |
| default: | |
| NSLog(@"ERROR: Unexpected type passed to hs.drawing.circle(): %d", lua_type(L, 1)); | |
| lua_pushnil(L); | |
| return 1; | |
| break; | |
| } | |
| HSDrawingWindow *circleWindow = [[HSDrawingWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES]; | |
| if (circleWindow) { | |
| drawing_t *drawingCircle = lua_newuserdata(L, sizeof(drawing_t)); | |
| memset(drawingCircle, 0, sizeof(drawing_t)); | |
| drawingCircle->window = (__bridge_retained void*)circleWindow; | |
| luaL_getmetatable(L, USERDATA_TAG); | |
| lua_setmetatable(L, -2); | |
| NSRect contentRect; | |
| contentRect.origin.x = 0; | |
| contentRect.origin.y = 0; | |
| contentRect.size = circleWindow.contentMaxSize; | |
| HSDrawingViewCircle *circleView = [[HSDrawingViewCircle alloc] initWithFrame:contentRect]; | |
| circleWindow.contentView = circleView; | |
| if (!drawingWindows) { | |
| drawingWindows = [[NSMutableArray alloc] init]; | |
| } | |
| [drawingWindows addObject:circleWindow]; | |
| } else { | |
| lua_pushnil(L); | |
| } | |
| return 1; | |
| } | |
| NSColor *getColorFromStack(lua_State *L, int idx) { | |
| CGFloat red, green, blue, alpha; | |
| switch (lua_type(L, idx)) { | |
| case LUA_TTABLE: | |
| lua_getfield(L, idx, "red"); | |
| red = lua_tonumber(L, -1); | |
| lua_pop(L, 1); | |
| lua_getfield(L, idx, "green"); | |
| green = lua_tonumber(L, -1); | |
| lua_pop(L, 1); | |
| lua_getfield(L, idx, "blue"); | |
| blue = lua_tonumber(L, -1); | |
| lua_pop(L, 1); | |
| lua_getfield(L, idx, "alpha"); | |
| alpha = lua_tonumber(L, -1); | |
| lua_pop(L, 1); | |
| break; | |
| default: | |
| NSLog(@"ERROR: Unexpected type passed to an hs.drawing color method: %d", lua_type(L, 1)); | |
| return 0; | |
| break; | |
| } | |
| return [NSColor colorWithSRGBRed:red green:green blue:blue alpha:alpha]; | |
| } | |
| /// hs.drawing:setFillColor(color) | |
| /// Method | |
| /// Sets the fill color of a drawing object | |
| /// | |
| /// Parameters: | |
| /// * color - A table containing color component values between 0.0 and 1.0 for each of the keys: | |
| /// * red | |
| /// * green | |
| /// * blue | |
| /// * alpha | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_setFillColor(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| NSColor *fillColor = getColorFromStack(L, 2); | |
| HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window; | |
| HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView; | |
| drawingView.HSFillColor = fillColor; | |
| drawingView.needsDisplay = YES; | |
| return 0; | |
| } | |
| /// hs.drawing:setStrokeColor(color) | |
| /// Method | |
| /// Sets the stroke color of a drawing object | |
| /// | |
| /// Parameters: | |
| /// * color - A table containing color component values between 0.0 and 1.0 for each of the keys: | |
| /// * red | |
| /// * green | |
| /// * blue | |
| /// * alpha | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_setStrokeColor(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| NSColor *strokeColor = getColorFromStack(L, 2); | |
| HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window; | |
| HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView; | |
| drawingView.HSStrokeColor = strokeColor; | |
| drawingView.needsDisplay = YES; | |
| return 0; | |
| } | |
| /// hs.drawing:setFill(doFill) | |
| /// Method | |
| /// Sets whether or not to fill a drawing object | |
| /// | |
| /// Parameters: | |
| /// * doFill - A boolean, true to fill the drawing object, false to not fill | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_setFill(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window; | |
| HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView; | |
| drawingView.HSFill = lua_toboolean(L, 2); | |
| drawingView.needsDisplay = YES; | |
| return 0; | |
| } | |
| /// hs.drawing:setStroke(doStroke) | |
| /// Method | |
| /// Sets whether or not to stroke a drawing object | |
| /// | |
| /// Parameters: | |
| /// * doStroke - A boolean, true to stroke the drawing object, false to not stroke | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_setStroke(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window; | |
| HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView; | |
| drawingView.HSStroke = lua_toboolean(L, 2); | |
| drawingView.needsDisplay = YES; | |
| return 0; | |
| } | |
| /// hs.drawing:setStrokeWidth(width) | |
| /// Method | |
| /// Sets the stroke width of a drawing object | |
| /// | |
| /// Parameters: | |
| /// * width - A number containing the width in points to stroke a drawing object | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_setStrokeWidth(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window; | |
| HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView; | |
| drawingView.HSLineWidth = lua_tonumber(L, 2); | |
| drawingView.needsDisplay = YES; | |
| return 0; | |
| } | |
| /// hs.drawing:show() | |
| /// Method | |
| /// Displays the drawing object | |
| /// | |
| /// Parameters: | |
| /// * None | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_show(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| [(__bridge HSDrawingWindow *)drawingObject->window makeKeyAndOrderFront:nil]; | |
| return 0; | |
| } | |
| /// hs.drawing:hide() | |
| /// Method | |
| /// Hides the drawing object | |
| /// | |
| /// Parameters: | |
| /// * None | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_hide(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| [(__bridge HSDrawingWindow *)drawingObject->window orderOut:nil]; | |
| return 0; | |
| } | |
| /// hs.drawing:delete() | |
| /// Method | |
| /// Destroys the drawing object | |
| /// | |
| /// Parameters: | |
| /// * None | |
| /// | |
| /// Returns: | |
| /// * None | |
| static int drawing_delete(lua_State *L) { | |
| drawing_t *drawingObject = get_item_arg(L, 1); | |
| HSDrawingWindow *drawingWindow = (__bridge_transfer HSDrawingWindow *)drawingObject->window; | |
| [drawingWindows removeObject:drawingWindow]; | |
| [drawingWindow close]; | |
| drawingWindow = nil; | |
| drawingObject->window = nil; | |
| drawingObject = nil; | |
| return 0; | |
| } | |
| // Lua metadata | |
| static const luaL_Reg drawinglib[] = { | |
| {"circle", drawing_newCircle}, | |
| {} | |
| }; | |
| static const luaL_Reg drawing_metalib[] = { | |
| {"setStroke", drawing_setStroke}, | |
| {"setStrokeWidth", drawing_setStrokeWidth}, | |
| {"setStrokeColor", drawing_setStrokeColor}, | |
| {"setFill", drawing_setFill}, | |
| {"setFillColor", drawing_setFillColor}, | |
| {"show", drawing_show}, | |
| {"hide", drawing_hide}, | |
| {"delete", drawing_delete}, | |
| {} | |
| }; | |
| static const luaL_Reg drawing_gclib[] = { | |
| {"__gc", drawing_delete}, | |
| {} | |
| }; | |
| int luaopen_hs_drawing_internal(lua_State *L) { | |
| // Metatable for creted objects | |
| luaL_newlib(L, drawing_metalib); | |
| lua_pushvalue(L, -1); | |
| lua_setfield(L, -2, "__index"); | |
| lua_setfield(L, LUA_REGISTRYINDEX, USERDATA_TAG); | |
| // Table for luaopen | |
| luaL_newlib(L, drawinglib); | |
| luaL_newlib(L, drawing_gclib); | |
| lua_setmetatable(L, -2); | |
| return 1; | |
| } | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment