Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
latenitefilms committed Feb 14, 2021
1 parent cededda commit c1d33c7
Show file tree
Hide file tree
Showing 10 changed files with 1,081 additions and 1 deletion.
302 changes: 302 additions & 0 deletions Hammerspoon.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

81 changes: 81 additions & 0 deletions extensions/speededitor/HSSpeedEditorDevice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// HSSpeedEditorDevice.h
// Hammerspoon
//
// Created by Chris Jones on 06/09/2017.
// Copyright © 2017 Hammerspoon. All rights reserved.
//

@import Foundation;
@import Cocoa;
@import IOKit;
@import IOKit.hid;
@import LuaSkin;


/*
#import "NSImage+BMP.h"
#import "NSImage+JPEG.h"
#import "NSImage+Rotated.h"
#import "NSImage+Flipped.h"
*/

#import "speededitor.h"


typedef enum : NSUInteger {
STREAMDECK_CODEC_UNKNOWN,
STREAMDECK_CODEC_BMP,
STREAMDECK_CODEC_JPEG,
} HSStreamDeckImageCodec;

@interface HSSpeedEditorDevice : NSObject {
NSString *serialNumberCache;
}
@property (nonatomic) IOHIDDeviceRef device;
@property (nonatomic) id manager;
@property (nonatomic) int selfRefCount;
@property (nonatomic) int buttonCallbackRef;
@property (nonatomic) BOOL isValid;

@property (nonatomic) NSString *deckType;
@property (nonatomic, readonly, getter=getKeyCount) int keyCount;
@property (nonatomic) int keyColumns;
@property (nonatomic) int keyRows;
@property (nonatomic) int imageWidth;
@property (nonatomic) int imageHeight;
@property (nonatomic) HSStreamDeckImageCodec imageCodec;
@property (nonatomic) BOOL imageFlipX;
@property (nonatomic) BOOL imageFlipY;
@property (nonatomic) int imageAngle;
@property (nonatomic) int simpleReportLength;
@property (nonatomic) int reportLength;
@property (nonatomic) int reportHeaderLength;
@property (nonatomic) int dataKeyOffset;

@property (nonatomic) NSMutableArray *buttonStateCache;
@property (nonatomic, readonly, getter=getSerialNumber) NSString *serialNumber;

- (id)initWithDevice:(IOHIDDeviceRef)device manager:(id)manager;
- (void)invalidate;
- (void)initialiseCaches;

- (IOReturn)deviceWriteSimpleReport:(uint8_t *)report reportLen:(int)reportLen;
- (IOReturn)deviceWrite:(NSData *)report;
- (void)deviceWriteImage:(NSData *)data button:(int)button;
- (NSData *)deviceRead:(int)resultLength reportID:(CFIndex)reportID;

- (int)transformKeyIndex:(int)sourceKey;
- (void)deviceDidSendInput:(NSArray*)newButtonStates;
- (BOOL)setBrightness:(int)brightness;
- (void)reset;

- (NSString *)cacheSerialNumber;
- (NSString *)firmwareVersion;
- (int)getKeyCount;

- (void)clearImage:(int)button;
- (void)setColor:(NSColor*)color forButton:(int)button;
//- (void)setImage:(NSImage*)image forButton:(int)button;

@end
193 changes: 193 additions & 0 deletions extensions/speededitor/HSSpeedEditorDevice.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
//
// HSSpeedEditorDevice.m
// Hammerspoon
//
// Created by Chris Jones on 06/09/2017.
// Copyright © 2017 Hammerspoon. All rights reserved.
//

#import "HSSpeedEditorDevice.h"

@interface HSSpeedEditorDevice ()
@property (nonatomic, copy) NSString *serialNumber;
@end

@implementation HSSpeedEditorDevice
- (id)initWithDevice:(IOHIDDeviceRef)device manager:(id)manager {
self = [super init];
if (self) {
self.device = device;
self.isValid = YES;
self.manager = manager;
self.buttonCallbackRef = LUA_NOREF;
self.selfRefCount = 0;

self.buttonStateCache = [[NSMutableArray alloc] init];

// These defaults are not necessary, all base classes will override them, but if we miss something, these are chosen to try and provoke a crash where possible, so we notice the lack of an override.
self.imageCodec = STREAMDECK_CODEC_UNKNOWN;
self.deckType = @"Unknown";
self.keyColumns = -1;
self.keyRows = -1;
self.imageFlipX = NO;
self.imageFlipY = NO;
self.imageAngle = 0;
self.simpleReportLength = 0;
self.reportLength = 0;
self.reportHeaderLength = 0;

self.dataKeyOffset = 0;

serialNumberCache = nil;
//NSLog(@"Added new Stream Deck device %p with IOKit device %p from manager %p", (__bridge void *)self, (void*)self.device, (__bridge void *)self.manager);
}
return self;
}

- (void)invalidate {
self.isValid = NO;
}

- (void)initialiseCaches {
for (int i = 0; i <= self.keyCount; i++) {
[self.buttonStateCache setObject:@0 atIndexedSubscript:i];
}
[self cacheSerialNumber];
}

- (IOReturn)deviceWriteSimpleReport:(uint8_t[])report reportLen:(int)reportLen {
if (self.simpleReportLength == 0) {
[LuaSkin logError:@"Initialising Stream Deck device with no simple report length defined"];
return kIOReturnInternalError;
}
NSMutableData *reportData = [NSMutableData dataWithLength:self.simpleReportLength];
[reportData replaceBytesInRange:NSMakeRange(0, reportLen) withBytes:report];
return [self deviceWrite:reportData];
}

- (IOReturn)deviceWrite:(NSData *)report {
const uint8_t *rawBytes = (const uint8_t*)report.bytes;
return IOHIDDeviceSetReport(self.device, kIOHIDReportTypeFeature, rawBytes[0], rawBytes, report.length);
}

- (NSData *)deviceRead:(int)resultLength reportID:(CFIndex)reportID {
CFIndex reportLength = resultLength + 5;
uint8_t *report = malloc(reportLength);

//NSLog(@"deviceRead: expecting resultLength %d, calculated report length %ld", resultLength, (long)reportLength);

IOHIDDeviceGetReport(self.device, kIOHIDReportTypeFeature, reportID, report, &reportLength);
char *c_data = (char *)(report + 5);
NSData *data = [NSData dataWithBytes:c_data length:resultLength];
free(report);

return data;
}

- (int)transformKeyIndex:(int)sourceKey {
//NSLog(@"transformKeyIndex: returning %d unmodified", sourceKey);
return sourceKey;
}

- (void)deviceDidSendInput:(NSArray*)newButtonStates {
//NSLog(@"Got an input event from device: %p: button:%@ isDown:%@", (__bridge void*)self, button, isDown);

if (!self.isValid) {
return;
}

LuaSkin *skin = [LuaSkin sharedWithState:NULL];
_lua_stackguard_entry(skin.L);
if (self.buttonCallbackRef == LUA_NOREF || self.buttonCallbackRef == LUA_REFNIL) {
[skin logError:@"hs.streamdeck received a button input, but no callback has been set. See hs.streamdeck:buttonCallback()"];
return;
}

//NSLog(@"buttonStateCache: %@", self.buttonStateCache);
//NSLog(@"newButtonStates: %@", newButtonStates);

for (int button=1; button <= self.keyCount; button++) {
if (![self.buttonStateCache[button] isEqual:newButtonStates[button]]) {
[skin pushLuaRef:speedEditorRefTable ref:self.buttonCallbackRef];
[skin pushNSObject:self];
lua_pushinteger(skin.L, button);
lua_pushboolean(skin.L, ((NSNumber*)(newButtonStates[button])).boolValue);
[skin protectedCallAndError:@"hs.streamdeck:buttonCallback" nargs:3 nresults:0];
self.buttonStateCache[button] = newButtonStates[button];
}
}

_lua_stackguard_exit(skin.L);
}

- (BOOL)setBrightness:(int)brightness {
NSException *exception = [NSException exceptionWithName:@"HSSpeedEditorDeviceUnimplemented"
reason:@"setBrightness method not implemented"
userInfo:nil];
[exception raise];
return NO;
}

- (void)reset {
NSException *exception = [NSException exceptionWithName:@"HSSpeedEditorDeviceUnimplemented"
reason:@"reset method not implemented"
userInfo:nil];
[exception raise];
}

- (NSString*)getSerialNumber {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdirect-ivar-access"

if (!serialNumberCache) {
// This shouldn't be necessary, since we cache the serial number when the device is initialised, but just in case
serialNumberCache = [self cacheSerialNumber];
}

return serialNumberCache;
#pragma clang diagnostic pop
}

- (NSString *)cacheSerialNumber {
NSException *exception = [NSException exceptionWithName:@"HSSpeedEditorDeviceUnimplemented"
reason:@"cacheSerialNumber method not implemented"
userInfo:nil];
[exception raise];
return nil;
}

- (NSString*)firmwareVersion {
NSException *exception = [NSException exceptionWithName:@"HSSpeedEditorDeviceUnimplemented"
reason:@"firmwareVersion method not implemented"
userInfo:nil];
[exception raise];
return nil;
}

- (int)getKeyCount {
return self.keyColumns * self.keyRows;
}

- (void)clearImage:(int)button {
[self setColor:[NSColor blackColor] forButton:button];
}

- (void)setColor:(NSColor *)color forButton:(int)button {
if (!self.isValid) {
return;
}

NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(self.imageWidth, self.imageHeight)];
[image lockFocus];
[color drawSwatchInRect:NSMakeRect(0, 0, self.imageWidth, self.imageHeight)];
[image unlockFocus];
//[self setImage:image forButton:button];
}

- (void)deviceWriteImage:(NSData *)data button:(int)button {
NSException *exception = [NSException exceptionWithName:@"HSSpeedEditorDeviceUnimplemented"
reason:@"deviceWriteImage method not implemented"
userInfo:nil];
[exception raise];
}
@end
30 changes: 30 additions & 0 deletions extensions/speededitor/HSSpeedEditorManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// HSSpeedEditorManager.h
// Hammerspoon
//
// Created by Chris Jones on 06/09/2017.
// Copyright © 2017 Hammerspoon. All rights reserved.
//

@import Foundation;
@import IOKit;
@import IOKit.hid;

@import LuaSkin;

//#import "HSSpeedEditorDevice.h"
#import "speededitor.h"

@interface HSSpeedEditorManager : NSObject
@property (nonatomic, strong) id ioHIDManager;
@property (nonatomic, strong) NSMutableArray *devices;
@property (nonatomic) int discoveryCallbackRef;

- (id)init;
- (void)doGC;
- (BOOL)startHIDManager;
- (BOOL)stopHIDManager;
//- (HSSpeedEditorDevice*)deviceDidConnect:(IOHIDDeviceRef)device;
- (void)deviceDidDisconnect:(IOHIDDeviceRef)device;

@end

0 comments on commit c1d33c7

Please sign in to comment.