Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fuzzing function for shedding some light on the keys of MK3 controllers #33

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
10 changes: 10 additions & 0 deletions KompleteSynthesia.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
BB1A48DC2B5B152A00BCB863 /* FuzzingWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB1A48DA2B5B152A00BCB863 /* FuzzingWindowController.m */; };
BB1A48DD2B5B152A00BCB863 /* FuzzingWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB1A48DB2B5B152A00BCB863 /* FuzzingWindowController.xib */; };
BB229913299040C500CFFB7C /* ApplicationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = BB229912299040C500CFFB7C /* ApplicationObserver.m */; };
BB31821629679983005EB410 /* MIDIController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB31821529679983005EB410 /* MIDIController.m */; };
BB3182192967A037005EB410 /* HIDController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB3182182967A037005EB410 /* HIDController.m */; };
Expand All @@ -33,6 +35,9 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
BB1A48D92B5B152A00BCB863 /* FuzzingWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FuzzingWindowController.h; sourceTree = "<group>"; };
BB1A48DA2B5B152A00BCB863 /* FuzzingWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FuzzingWindowController.m; sourceTree = "<group>"; };
BB1A48DB2B5B152A00BCB863 /* FuzzingWindowController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FuzzingWindowController.xib; sourceTree = "<group>"; };
BB229911299040C500CFFB7C /* ApplicationObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ApplicationObserver.h; sourceTree = "<group>"; };
BB229912299040C500CFFB7C /* ApplicationObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ApplicationObserver.m; sourceTree = "<group>"; };
BB31821429679983005EB410 /* MIDIController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MIDIController.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -189,6 +194,9 @@
BB7AB64129807A700081B05E /* PaletteViewController.xib */,
BB7AB63C298042520081B05E /* ColorField.h */,
BB7AB63D298042530081B05E /* ColorField.m */,
BB1A48D92B5B152A00BCB863 /* FuzzingWindowController.h */,
BB1A48DA2B5B152A00BCB863 /* FuzzingWindowController.m */,
BB1A48DB2B5B152A00BCB863 /* FuzzingWindowController.xib */,
);
name = UI;
sourceTree = "<group>";
Expand Down Expand Up @@ -265,6 +273,7 @@
BB49B3B4295CC8AF00D18605 /* Assets.xcassets in Resources */,
BB7AB64329807A700081B05E /* PaletteViewController.xib in Resources */,
BB49B3B7295CC8AF00D18605 /* MainMenu.xib in Resources */,
BB1A48DD2B5B152A00BCB863 /* FuzzingWindowController.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -314,6 +323,7 @@
BB76A1A4297B333D00A869D8 /* PreferencesWindowController.m in Sources */,
BB5C2E9B2AF6E96A0043F444 /* VirtualEvent.m in Sources */,
BBA0166929611D1D0018B2DB /* MIDI2HIDController.m in Sources */,
BB1A48DC2B5B152A00BCB863 /* FuzzingWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
38 changes: 37 additions & 1 deletion KompleteSynthesia/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#import "ApplicationObserver.h"
#import "CoreAudioTools.h"
#import "FuzzingWindowController.h"
#import "HIDController.h"
#import "LogViewController.h"
#import "MIDI2HIDController.h"
Expand All @@ -27,6 +28,7 @@ @interface AppDelegate ()
@property (nonatomic, strong) LogViewController* log;
@property (nonatomic, strong) SynthesiaController* synthesia;
@property (nonatomic, strong) PreferencesWindowController* preferences;
@property (nonatomic, strong) FuzzingWindowController* fuzzing;
@property (nonatomic, strong) ApplicationObserver* observer;

@property (nonatomic, strong) NSPopover* popover;
Expand Down Expand Up @@ -146,6 +148,8 @@ - (void)applicationDidFinishLaunching:(NSNotification*)aNotification
- (void)applicationDidFinishInitializingWithUSBHighwayOpen:(BOOL)usbHighwayOpen
{
usbAvailable = usbHighwayOpen;

// Bootstrap Synthesia.
_synthesia = [[SynthesiaController alloc] initWithLogViewController:_log delegate:self];
[_synthesia cachedAssertSynthesiaConfiguration];

Expand Down Expand Up @@ -190,6 +194,7 @@ - (void)applicationDidFinishInitializingWithSynthesiaRunning
_midi2hidController.forwardButtonsToSynthesiaOnly = [userDefaults boolForKey:kAppDefaultActivateSynthesia];

if (usbAvailable == YES) {
// FIXME: So far we only support MK2 controllers for Synthesia window mirroring.
if (_hidController.mk == 2) {
_videoController = [[VideoController alloc] initWithUSBController:_usbController
logViewController:_log
Expand Down Expand Up @@ -231,6 +236,8 @@ - (void)applicationDidFinishInitializingWithSynthesiaRunning
[menu addItemWithTitle:@"Reset" action:@selector(reset:) keyEquivalent:@""];
[menu addItemWithTitle:@"Show Log" action:@selector(showLog:) keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:@"Fuzzing MK3" action:@selector(showFuzzing:) keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];

menu.delegate = self;
Expand All @@ -242,7 +249,7 @@ - (void)applicationDidFinishInitializingWithSynthesiaRunning
[userDefaults registerDefaults:@{kAppDefaultCheckForUpdate : @(YES)}];
BOOL checkForUpdate = [userDefaults boolForKey:kAppDefaultCheckForUpdate];
if (checkForUpdate) {
[UpdateManager UpdateCheckWithCompletion:^(NSString* state) {
[UpdateManager updateCheckWithCompletion:^(NSString* state) {
NSString* message = [NSString stringWithFormat:@"update check: %@", state];
[self.log logLine:message];
}];
Expand All @@ -268,6 +275,23 @@ - (void)preferences:(id)sender
[[NSApplication sharedApplication] activateIgnoringOtherApps:NO];
}

- (void)showFuzzing:(id)sender
{
if (_fuzzing == nil) {
_fuzzing = [[FuzzingWindowController alloc] initWithWindowNibName:@"FuzzingWindowController"];
_fuzzing.delegate = self;
}
_fuzzing.hidController = _hidController;

NSWindow* window = [_fuzzing window];

// We need to do some trickery here as the Application itself has no window. Not sure
// if this really works in all cases but it does for me, so far.
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[window makeKeyAndOrderFront:sender];
[[NSApplication sharedApplication] activateIgnoringOtherApps:NO];
}

- (void)showStatusMenu:(id)sender
{
self.statusItem.menu = self.statusMenu;
Expand Down Expand Up @@ -418,6 +442,12 @@ - (void)synthesiaStateUpdate:(NSString*)status
{
if (self.statusMenu.itemArray.count > 1) {
NSMenuItem* item = self.statusMenu.itemArray[1];
if ([item.title compare:status] == NSOrderedSame) {
// Lets avoid useless further UI and controller updates as we are receiving
// a lot of state updates for Synthesia.
// FIXME: Seems weird that we get flooded by state updates.
return;
}
item.title = status;
}
[self updateButtonStates];
Expand Down Expand Up @@ -453,6 +483,12 @@ - (void)preferencesUpdatedKeyState:(int)keyState forKeyIndex:(int)index

NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:keyState forKey:userDefaultKeys[index]];

assert(index < kColorMapSize);
_midi2hidController.colors[index] = keyState;
if (index == 0) {
[_midi2hidController lightsDefault];
}
}

@end
4 changes: 2 additions & 2 deletions KompleteSynthesia/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22154"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
Expand Down
35 changes: 35 additions & 0 deletions KompleteSynthesia/FuzzingWindowController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// FuzzingWindowController.h
// KompleteSynthesia
//
// Created by Till Toenshoff on 19.01.24.
//

#import <Cocoa/Cocoa.h>

NS_ASSUME_NONNULL_BEGIN

@class HIDController;
@protocol PreferencesDelegate;

@interface FuzzingWindowController : NSWindowController <NSTextFieldDelegate>

@property (nonatomic, weak) IBOutlet NSTextField* initialCommand;
@property (nonatomic, weak) IBOutlet NSTextField* currentControlCommand;
@property (nonatomic, weak) IBOutlet NSSliderCell* delaySlider;

@property (nonatomic, weak) IBOutlet NSButton* pauseButton;
@property (nonatomic, weak) IBOutlet NSButton* stopButton;
@property (nonatomic, weak) IBOutlet NSButton* startButton;

@property (nonatomic, weak) HIDController* hidController;

@property (nonatomic, weak) id<PreferencesDelegate> delegate;

- (IBAction)start:(id)sender;
- (IBAction)stop:(id)sender;
- (IBAction)pause:(id)sender;

@end

NS_ASSUME_NONNULL_END
167 changes: 167 additions & 0 deletions KompleteSynthesia/FuzzingWindowController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//
// FuzzingWindowController.m
// KompleteSynthesia
//
// Created by Till Toenshoff on 19.01.24.
//

#import "FuzzingWindowController.h"
#import "HIDController.h"
#import "PreferencesWindowController.h"

/// Hacked together window for getting some ideas on how to control the lightguide on MK3 devices - totally ugly!

static const NSTimeInterval kCommandUpdateTimerDelay = 0.01;
static const NSTimeInterval kFuzzTimerDelay = 0.05;

@interface FuzzingWindowController ()
@end

@implementation FuzzingWindowController {
NSTimer* commandUpdateTimer;
NSTimer* fuzzTimer;
BOOL paused;
}

- (NSString*)hexStringFromBinaryData:(unsigned char*)data withLength:(size_t)length
{
NSString* output = @"";
for (int i = 0; i < length; i++) {
if (i > 0) {
output = [NSString stringWithFormat:@"%@ ", output];
}
output = [NSString stringWithFormat:@"%@%02X", output, data[i]];
}
return output;
}

- (void)binaryDataFromHexString:(NSString*)input withData:(unsigned char*)data withLength:(size_t)length
{
NSArray* hexStringBytes = [input componentsSeparatedByString:@" "];
size_t byteCount = MIN(length, hexStringBytes.count);
for (size_t i = 0; i < byteCount; i++) {
NSString* hex = hexStringBytes[i];
const char* chars = [hex UTF8String];
data[i] = strtoul(chars, NULL, 16);
}
}

- (void)windowDidLoad
{
[super windowDidLoad];

_initialCommand.stringValue = [self hexStringFromBinaryData:_hidController.initialCommand
withLength:_hidController.initialCommandLength];
_initialCommand.delegate = self;

[_delegate preferencesUpdatedKeyState:0x00 forKeyIndex:0];

_delaySlider.target = self;
_delaySlider.action = @selector(delayChanged:);

commandUpdateTimer = [NSTimer
scheduledTimerWithTimeInterval:kCommandUpdateTimerDelay
repeats:YES
block:^(NSTimer* timer) {
self->_currentControlCommand.stringValue =
[self hexStringFromBinaryData:self->_hidController.lightGuideUpdateMessage
withLength:self->_hidController.lightGuideUpdateMessageSize];
}];
paused = NO;
[self updateButtonStates];
}

- (void)controlTextDidEndEditing:(NSNotification*)notification
{
NSTextField* textField = [notification object];
if (textField != _initialCommand) {
return;
}
[self binaryDataFromHexString:textField.stringValue
withData:_hidController.initialCommand
withLength:_hidController.initialCommandLength];
}

- (void)delayChanged:(NSSlider*)sender
{
[self pause:sender];
[self pause:sender];
}

- (void)updateButtonStates
{
if (paused) {
_startButton.enabled = NO;
_pauseButton.enabled = YES;
_pauseButton.state = NSControlStateValueMixed;
[_pauseButton setButtonType:NSButtonTypePushOnPushOff];
_stopButton.enabled = YES;
} else {
_startButton.enabled = fuzzTimer == nil;
_stopButton.enabled = fuzzTimer != nil;
_pauseButton.enabled = fuzzTimer != nil;
_pauseButton.state = fuzzTimer != nil ? NSControlStateValueOn : NSControlStateValueOff;
[_pauseButton setButtonType:NSButtonTypeMomentaryPushIn];
}
// [_pauseButton highlight:_pauseButton.state == NSControlStateValueMixed];
}

- (void)stopTimer
{
if (fuzzTimer != nil) {
[fuzzTimer invalidate];
}
fuzzTimer = nil;
}

- (IBAction)stop:(id)sender
{
[self stopTimer];
paused = NO;
[self updateButtonStates];
}

- (IBAction)pause:(id)sender
{
if (fuzzTimer != nil) {
paused = YES;
[self stopTimer];
} else if (paused == YES) {
paused = NO;
[self startTimer];
}
[self updateButtonStates];
}

- (void)startTimer
{
if (fuzzTimer != nil) {
[fuzzTimer invalidate];
}

fuzzTimer = [NSTimer
scheduledTimerWithTimeInterval:kFuzzTimerDelay * _delaySlider.intValue
repeats:YES
block:^(NSTimer* timer) {
[self->_hidController initKeyboardController:nil];
if (self->_hidController.lightGuideUpdateMessage[0] == 0xFF) {
self->_hidController.lightGuideUpdateMessage[1] += 0x0C;
}
[self->_delegate
preferencesUpdatedKeyState:self->_hidController.lightGuideUpdateMessage[1]
forKeyIndex:0];
self->_hidController.lightGuideUpdateMessage[0]++;
}];
}

- (IBAction)start:(id)sender
{
_hidController.lightGuideUpdateMessage[0] = 0x01;
[_delegate preferencesUpdatedKeyState:0x06 forKeyIndex:0];

[self startTimer];
paused = NO;
[self updateButtonStates];
}

@end
Loading