mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-26 00:30:29 +00:00
Started sketching out an Acorn Electron emulation, as it's a platform I'm familiar with and will force me to figure out PAL decoding. Factored out NTSC-specific parts of the display decoding logic and hence added RGB output mode.
This commit is contained in:
parent
96503a3ac5
commit
aa0714fe27
@ -15,6 +15,8 @@
|
||||
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414591B58879D00E04248 /* CPU6502AllRAM.cpp */; };
|
||||
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */; };
|
||||
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; };
|
||||
4B2E2D921C399B9900138695 /* ElectronDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D911C399B9900138695 /* ElectronDocument.swift */; };
|
||||
4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; };
|
||||
4B366DFC1B5C165A0026627B /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B366DFA1B5C165A0026627B /* CRT.cpp */; };
|
||||
4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* TimingTests.swift */; };
|
||||
4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
|
||||
@ -324,6 +326,8 @@
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = "<group>"; };
|
||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = "<group>"; };
|
||||
4B2632551B631A510082A461 /* CRTFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CRTFrame.h; path = ../../Outputs/CRTFrame.h; sourceTree = "<group>"; };
|
||||
4B2E2D911C399B9900138695 /* ElectronDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElectronDocument.swift; sourceTree = "<group>"; };
|
||||
4B2E2D941C399D1200138695 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ElectronDocument.xib; sourceTree = "<group>"; };
|
||||
4B366DFA1B5C165A0026627B /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CRT.cpp; path = ../../Outputs/CRT.cpp; sourceTree = "<group>"; };
|
||||
4B366DFB1B5C165A0026627B /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRT.hpp; path = ../../Outputs/CRT.hpp; sourceTree = "<group>"; };
|
||||
4B6D7F921B58822000787C9A /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = "<group>"; };
|
||||
@ -970,6 +974,7 @@
|
||||
4BB73EA01B587A5100552FC2 /* Clock Signal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B2E2D931C399D1200138695 /* ElectronDocument.xib */,
|
||||
4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */,
|
||||
4B14144A1B5883E500E04248 /* CSAtari2600.h */,
|
||||
4B14144B1B5883E500E04248 /* CSAtari2600.mm */,
|
||||
@ -982,6 +987,7 @@
|
||||
4BB73EA81B587A5100552FC2 /* Assets.xcassets */,
|
||||
4BB73EAA1B587A5100552FC2 /* MainMenu.xib */,
|
||||
4BB73EAD1B587A5100552FC2 /* Info.plist */,
|
||||
4B2E2D911C399B9900138695 /* ElectronDocument.swift */,
|
||||
);
|
||||
path = "Clock Signal";
|
||||
sourceTree = "<group>";
|
||||
@ -1140,6 +1146,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */,
|
||||
4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */,
|
||||
4BB73EA71B587A5100552FC2 /* Atari2600Document.xib in Resources */,
|
||||
4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */,
|
||||
@ -1438,6 +1445,7 @@
|
||||
4B366DFC1B5C165A0026627B /* CRT.cpp in Sources */,
|
||||
4BB73EA41B587A5100552FC2 /* Atari2600Document.swift in Sources */,
|
||||
4B14144E1B5883E500E04248 /* CSAtari2600.mm in Sources */,
|
||||
4B2E2D921C399B9900138695 /* ElectronDocument.swift in Sources */,
|
||||
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */,
|
||||
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */,
|
||||
4B14144F1B5883E500E04248 /* CSCathodeRayView.m in Sources */,
|
||||
@ -1482,6 +1490,14 @@
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
4B2E2D931C399D1200138695 /* ElectronDocument.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
4B2E2D941C399D1200138695 /* Base */,
|
||||
);
|
||||
name = ElectronDocument.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BB73EA51B587A5100552FC2 /* Atari2600Document.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
|
@ -95,7 +95,7 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
|
||||
|
||||
- (void)setView:(CSCathodeRayView *)view {
|
||||
_view = view;
|
||||
_view.signalDecoder = [NSString stringWithUTF8String:_atari2600.get_signal_decoder()];
|
||||
[_view setSignalDecoder:[NSString stringWithUTF8String:_atari2600.get_signal_decoder()] type:CSCathodeRayViewSignalTypeNTSC];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
|
@ -22,6 +22,11 @@
|
||||
- (void)flagsChanged:(nonnull NSEvent *)newModifiers;
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSCathodeRayViewSignalType) {
|
||||
CSCathodeRayViewSignalTypeNTSC,
|
||||
CSCathodeRayViewSignalTypeRGB
|
||||
};
|
||||
|
||||
@interface CSCathodeRayView : NSOpenGLView
|
||||
|
||||
@property (nonatomic, weak) id <CSCathodeRayViewDelegate> delegate;
|
||||
@ -30,6 +35,6 @@
|
||||
- (void)invalidate;
|
||||
|
||||
- (BOOL)pushFrame:(nonnull CRTFrame *)crtFrame;
|
||||
- (void)setSignalDecoder:(nonnull NSString *)signalDecoder;
|
||||
- (void)setSignalDecoder:(nonnull NSString *)decoder type:(CSCathodeRayViewSignalType)type;
|
||||
|
||||
@end
|
||||
|
@ -33,6 +33,7 @@
|
||||
CRTFrame *_crtFrame;
|
||||
|
||||
NSString *_signalDecoder;
|
||||
CSCathodeRayViewSignalType _signalType;
|
||||
int32_t _signalDecoderGeneration;
|
||||
int32_t _compiledSignalDecoderGeneration;
|
||||
}
|
||||
@ -201,80 +202,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
|
||||
#pragma mark - Frame output
|
||||
|
||||
// the main job of the vertex shader is just to map from an input area of [0,1]x[0,1], with the origin in the
|
||||
// top left to OpenGL's [-1,1]x[-1,1] with the origin in the lower left, and to convert input data coordinates
|
||||
// from integral to floating point.
|
||||
static NSString *const vertexShader =
|
||||
@"#version 150\n"
|
||||
"\n"
|
||||
"in vec2 position;\n"
|
||||
"in vec2 srcCoordinates;\n"
|
||||
"in float lateral;\n"
|
||||
"\n"
|
||||
"out vec2 srcCoordinatesVarying[4];\n"
|
||||
"out float lateralVarying;\n"
|
||||
"out float phase;\n"
|
||||
"out vec2 shadowMaskCoordinates;\n"
|
||||
"\n"
|
||||
"uniform vec2 textureSize;\n"
|
||||
"\n"
|
||||
"const float shadowMaskMultiple = 300;\n"
|
||||
"\n"
|
||||
"void main (void)\n"
|
||||
"{\n"
|
||||
"srcCoordinatesVarying[0] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n"
|
||||
"srcCoordinatesVarying[3] = srcCoordinatesVarying[0] + vec2(0.375 / textureSize.x, 0.0);\n"
|
||||
"srcCoordinatesVarying[2] = srcCoordinatesVarying[0] + vec2(0.125 / textureSize.x, 0.0);\n"
|
||||
"srcCoordinatesVarying[1] = srcCoordinatesVarying[0] - vec2(0.125 / textureSize.x, 0.0);\n"
|
||||
"srcCoordinatesVarying[0] = srcCoordinatesVarying[0] - vec2(0.325 / textureSize.x, 0.0);\n"
|
||||
"\n"
|
||||
"lateralVarying = lateral + 1.0707963267949;\n"
|
||||
"phase = srcCoordinates.x * 6.283185308;\n"
|
||||
"\n"
|
||||
"shadowMaskCoordinates = position * vec2(shadowMaskMultiple, shadowMaskMultiple * 0.85057471264368);\n"
|
||||
"\n"
|
||||
"gl_Position = vec4(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0 + position.x / 131.0, 0.0, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
// TODO: this should be factored out and be per project
|
||||
|
||||
static NSString *const fragmentShader =
|
||||
@"#version 150\n"
|
||||
"\n"
|
||||
"in vec2 srcCoordinatesVarying[4];\n"
|
||||
"in float lateralVarying;\n"
|
||||
"in float phase;\n"
|
||||
"in vec2 shadowMaskCoordinates;\n"
|
||||
"out vec4 fragColour;\n"
|
||||
"\n"
|
||||
"uniform sampler2D texID;\n"
|
||||
"uniform sampler2D shadowMaskTexID;\n"
|
||||
"uniform float alpha;\n"
|
||||
"\n"
|
||||
"%@"
|
||||
"\n"
|
||||
"// for conversion from i and q are in the range [-0.5, 0.5] (so i needs to be multiplied by 1.1914 and q by 1.0452)\n"
|
||||
"const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);\n"
|
||||
"\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
"vec4 angles = vec4(phase) + vec4(-2.35619449019234, -0.78539816339745, 0.78539816339745, 2.35619449019234);\n"
|
||||
"vec4 samples = vec4("
|
||||
" sample(srcCoordinatesVarying[0], angles.x),"
|
||||
" sample(srcCoordinatesVarying[1], angles.y),"
|
||||
" sample(srcCoordinatesVarying[2], angles.z),"
|
||||
" sample(srcCoordinatesVarying[3], angles.w)"
|
||||
");\n"
|
||||
"\n"
|
||||
"float y = dot(vec4(0.25), samples);\n"
|
||||
"samples -= vec4(y);\n"
|
||||
"\n"
|
||||
"float i = dot(cos(angles), samples);\n"
|
||||
"float q = dot(sin(angles), samples);\n"
|
||||
"\n"
|
||||
"fragColour = 5.0 * texture(shadowMaskTexID, shadowMaskCoordinates) * vec4(yiqToRGB * vec3(y, i, q), 1.0);//sin(lateralVarying));\n"
|
||||
"}\n";
|
||||
|
||||
#if defined(DEBUG)
|
||||
- (void)logErrorForObject:(GLuint)object
|
||||
{
|
||||
@ -302,12 +229,130 @@ static NSString *const fragmentShader =
|
||||
return shader;
|
||||
}
|
||||
|
||||
- (void)setSignalDecoder:(nonnull NSString *)signalDecoder
|
||||
- (void)setSignalDecoder:(nonnull NSString *)signalDecoder type:(CSCathodeRayViewSignalType)type
|
||||
{
|
||||
_signalDecoder = [signalDecoder copy];
|
||||
OSAtomicIncrement32(&_signalDecoderGeneration);
|
||||
}
|
||||
|
||||
- (nonnull NSString *)vertexShaderForType:(CSCathodeRayViewSignalType)type
|
||||
{
|
||||
// the main job of the vertex shader is just to map from an input area of [0,1]x[0,1], with the origin in the
|
||||
// top left to OpenGL's [-1,1]x[-1,1] with the origin in the lower left, and to convert input data coordinates
|
||||
// from integral to floating point; there's also some setup for NTSC, PAL or whatever.
|
||||
|
||||
NSString *const ntscVertexShaderGlobals =
|
||||
@"out vec2 srcCoordinatesVarying[4];\n"
|
||||
"out float phase;\n";
|
||||
|
||||
NSString *const ntscVertexShaderBody =
|
||||
@"phase = srcCoordinates.x * 6.283185308;\n"
|
||||
"\n"
|
||||
"srcCoordinatesVarying[0] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n"
|
||||
"srcCoordinatesVarying[3] = srcCoordinatesVarying[0] + vec2(0.375 / textureSize.x, 0.0);\n"
|
||||
"srcCoordinatesVarying[2] = srcCoordinatesVarying[0] + vec2(0.125 / textureSize.x, 0.0);\n"
|
||||
"srcCoordinatesVarying[1] = srcCoordinatesVarying[0] - vec2(0.125 / textureSize.x, 0.0);\n"
|
||||
"srcCoordinatesVarying[0] = srcCoordinatesVarying[0] - vec2(0.325 / textureSize.x, 0.0);\n";
|
||||
|
||||
NSString *const rgbVertexShaderGlobals =
|
||||
@"out vec2 srcCoordinatesVarying;\n";
|
||||
|
||||
NSString *const rgbVertexShaderBody =
|
||||
@"srcCoordinatesVarying[0] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n";
|
||||
|
||||
NSString *const vertexShader =
|
||||
@"#version 150\n"
|
||||
"\n"
|
||||
"in vec2 position;\n"
|
||||
"in vec2 srcCoordinates;\n"
|
||||
"in float lateral;\n"
|
||||
"\n"
|
||||
"out float lateralVarying;\n"
|
||||
"out vec2 shadowMaskCoordinates;\n"
|
||||
"\n"
|
||||
"uniform vec2 textureSize;\n"
|
||||
"\n"
|
||||
"const float shadowMaskMultiple = 600;\n"
|
||||
"\n"
|
||||
"%@\n"
|
||||
"void main (void)\n"
|
||||
"{\n"
|
||||
"lateralVarying = lateral + 1.0707963267949;\n"
|
||||
"\n"
|
||||
"shadowMaskCoordinates = position * vec2(shadowMaskMultiple, shadowMaskMultiple * 0.85057471264368);\n"
|
||||
"\n"
|
||||
"%@\n"
|
||||
"\n"
|
||||
"gl_Position = vec4(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0 + position.x / 131.0, 0.0, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
switch(_signalType)
|
||||
{
|
||||
case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:vertexShader, ntscVertexShaderGlobals, ntscVertexShaderBody];
|
||||
case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:vertexShader, rgbVertexShaderGlobals, rgbVertexShaderBody];
|
||||
}
|
||||
}
|
||||
|
||||
- (nonnull NSString *)fragmentShaderForType:(CSCathodeRayViewSignalType)type
|
||||
{
|
||||
|
||||
NSString *const fragmentShader =
|
||||
@"#version 150\n"
|
||||
"\n"
|
||||
"in float lateralVarying;\n"
|
||||
"in vec2 shadowMaskCoordinates;\n"
|
||||
"out vec4 fragColour;\n"
|
||||
"\n"
|
||||
"uniform sampler2D texID;\n"
|
||||
"uniform sampler2D shadowMaskTexID;\n"
|
||||
"uniform float alpha;\n"
|
||||
"\n"
|
||||
"%@\n"
|
||||
"\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
"%@\n"
|
||||
"}\n";
|
||||
|
||||
NSString *const ntscFragmentShaderGlobals =
|
||||
@"in vec2 srcCoordinatesVarying[4];\n"
|
||||
"in float phase;\n"
|
||||
"\n"
|
||||
"// for conversion from i and q are in the range [-0.5, 0.5] (so i needs to be multiplied by 1.1914 and q by 1.0452)\n"
|
||||
"const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);\n"
|
||||
"\n"
|
||||
"\%@\n";
|
||||
|
||||
NSString *const ntscFragmentShaderBody =
|
||||
@"vec4 angles = vec4(phase) + vec4(-2.35619449019234, -0.78539816339745, 0.78539816339745, 2.35619449019234);\n"
|
||||
"vec4 samples = vec4("
|
||||
" sample(srcCoordinatesVarying[0], angles.x),"
|
||||
" sample(srcCoordinatesVarying[1], angles.y),"
|
||||
" sample(srcCoordinatesVarying[2], angles.z),"
|
||||
" sample(srcCoordinatesVarying[3], angles.w)"
|
||||
");\n"
|
||||
"\n"
|
||||
"float y = dot(vec4(0.25), samples);\n"
|
||||
"samples -= vec4(y);\n"
|
||||
"\n"
|
||||
"float i = dot(cos(angles), samples);\n"
|
||||
"float q = dot(sin(angles), samples);\n"
|
||||
"\n"
|
||||
"fragColour = 5.0 * texture(shadowMaskTexID, shadowMaskCoordinates) * vec4(yiqToRGB * vec3(y, i, q), 1.0);//sin(lateralVarying));\n";
|
||||
|
||||
NSString *const rgbFragmentShaderGlobals =
|
||||
@"";
|
||||
|
||||
NSString *const rgbFragmentShaderBody =
|
||||
@"fragColour = texture(srcCoordinatesVarying, srcCoordinatesVarying);//sin(lateralVarying));\n";
|
||||
|
||||
switch(_signalType)
|
||||
{
|
||||
case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:fragmentShader, ntscFragmentShaderGlobals, ntscFragmentShaderBody];
|
||||
case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:fragmentShader, rgbFragmentShaderGlobals, rgbFragmentShaderBody];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareShader
|
||||
{
|
||||
if(_shaderProgram)
|
||||
@ -320,9 +365,15 @@ static NSString *const fragmentShader =
|
||||
if(!_signalDecoder)
|
||||
return;
|
||||
|
||||
NSString *const vertexShader = [self vertexShaderForType:_signalType];
|
||||
NSString *const fragmentShader = [self fragmentShaderForType:_signalType];
|
||||
|
||||
_shaderProgram = glCreateProgram();
|
||||
_vertexShader = [self compileShader:[vertexShader UTF8String] type:GL_VERTEX_SHADER];
|
||||
_fragmentShader = [self compileShader:[[NSString stringWithFormat:fragmentShader, _signalDecoder] UTF8String] type:GL_FRAGMENT_SHADER];
|
||||
_fragmentShader = [self compileShader:_signalDecoder ?
|
||||
[[NSString stringWithFormat:fragmentShader, _signalDecoder] UTF8String] :
|
||||
[fragmentShader UTF8String]
|
||||
type:GL_FRAGMENT_SHADER];
|
||||
|
||||
glAttachShader(_shaderProgram, _vertexShader);
|
||||
glAttachShader(_shaderProgram, _fragmentShader);
|
||||
@ -379,7 +430,7 @@ static NSString *const fragmentShader =
|
||||
[self.openGLContext makeCurrentContext];
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
|
||||
while(!_shaderProgram || (_signalDecoderGeneration != _compiledSignalDecoderGeneration)) {
|
||||
while((!_shaderProgram || (_signalDecoderGeneration != _compiledSignalDecoderGeneration)) && _signalDecoder) {
|
||||
_compiledSignalDecoderGeneration = _signalDecoderGeneration;
|
||||
[self prepareShader];
|
||||
}
|
||||
|
46
OSBindings/Mac/Clock Signal/ElectronDocument.swift
Normal file
46
OSBindings/Mac/Clock Signal/ElectronDocument.swift
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// ElectronDocument.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/01/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ElectronDocument: NSDocument, CSCathodeRayViewResponderDelegate, CSCathodeRayViewDelegate {
|
||||
|
||||
@IBOutlet weak var openGLView: CSCathodeRayView!
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
|
||||
openGLView.delegate = self
|
||||
openGLView.responderDelegate = self
|
||||
// atari2600!.view = openGLView!
|
||||
|
||||
// bind the content aspect ratio to remain 4:3 from now on
|
||||
aController.window!.contentAspectRatio = NSSize(width: 4.0, height: 3.0)
|
||||
}
|
||||
|
||||
override var windowNibName: String? {
|
||||
return "ElectronDocument"
|
||||
}
|
||||
|
||||
override func readFromData(data: NSData, ofType typeName: String) throws {
|
||||
}
|
||||
|
||||
override func close() {
|
||||
super.close()
|
||||
openGLView.invalidate()
|
||||
}
|
||||
|
||||
// MARK: CSOpenGLViewDelegate
|
||||
func openGLView(view: CSCathodeRayView, didUpdateToTime time: CVTimeStamp) {
|
||||
}
|
||||
|
||||
// MARK: CSOpenGLViewResponderDelegate
|
||||
func keyDown(event: NSEvent) {}
|
||||
func keyUp(event: NSEvent) {}
|
||||
func flagsChanged(newModifiers: NSEvent) {}
|
||||
|
||||
}
|
@ -27,6 +27,20 @@
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).Atari2600Document</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>uef</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Electron/BBC Tape Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).ElectronDocument</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user