diff options
| author | Hannes Schulze <haschu0103@outlook.de> | 2025-01-29 00:58:07 +0100 |
|---|---|---|
| committer | Hannes Schulze <haschu0103@outlook.de> | 2025-01-29 00:58:07 +0100 |
| commit | c1062208bb54cce8dffa04e9697ae124f271ecaf (patch) | |
| tree | 5d387dcb76ec26a87d7ecafb983939b359c8f11e /app | |
| parent | 117f584b3f3d7945732a29848eb6d4ead1f57bee (diff) | |
use game controller api for input
Diffstat (limited to 'app')
| -rw-r--r-- | app/mac/CMakeLists.txt | 17 | ||||
| -rw-r--r-- | app/mac/controller-adapter-gamepad.hh | 16 | ||||
| -rw-r--r-- | app/mac/controller-adapter-gamepad.mm | 5 | ||||
| -rw-r--r-- | app/mac/controller-adapter-keyboard.hh | 14 | ||||
| -rw-r--r-- | app/mac/controller-adapter-keyboard.mm | 44 | ||||
| -rw-r--r-- | app/mac/controller-adapter.hh | 11 | ||||
| -rw-r--r-- | app/mac/main.mm | 24 | ||||
| -rw-r--r-- | app/mac/scene.hh | 12 | ||||
| -rw-r--r-- | app/mac/scene.mm | 109 | ||||
| -rw-r--r-- | app/mac/view-controller.hh | 17 | ||||
| -rw-r--r-- | app/mac/view-controller.mm | 46 |
11 files changed, 200 insertions, 115 deletions
diff --git a/app/mac/CMakeLists.txt b/app/mac/CMakeLists.txt index a4f25c9..d6ea464 100644 --- a/app/mac/CMakeLists.txt +++ b/app/mac/CMakeLists.txt @@ -1,7 +1,8 @@ add_executable(nes_mac) -find_library(FRAMEWORK_COCOA Cocoa REQUIRED) -find_library(FRAMEWORK_SPRITEKIT SpriteKit REQUIRED) +find_library(FRAMEWORK_Cocoa Cocoa REQUIRED) +find_library(FRAMEWORK_SpriteKit SpriteKit REQUIRED) +find_library(FRAMEWORK_GameController GameController REQUIRED) target_include_directories(nes_mac PRIVATE .) target_link_libraries( @@ -9,12 +10,18 @@ target_link_libraries( PRIVATE nes::options nes::nes - ${FRAMEWORK_COCOA} - ${FRAMEWORK_SPRITEKIT}) + ${FRAMEWORK_Cocoa} + ${FRAMEWORK_SpriteKit} + ${FRAMEWORK_GameController}) target_sources( nes_mac PRIVATE main.mm scene.hh - scene.mm)
\ No newline at end of file + scene.mm + view-controller.hh + view-controller.mm + controller-adapter.hh + controller-adapter-keyboard.hh + controller-adapter-keyboard.mm)
\ No newline at end of file diff --git a/app/mac/controller-adapter-gamepad.hh b/app/mac/controller-adapter-gamepad.hh new file mode 100644 index 0000000..14bfeae --- /dev/null +++ b/app/mac/controller-adapter-gamepad.hh @@ -0,0 +1,16 @@ +// +// Created by Hannes Schulze on 29.01.25. +// + +#ifndef NES_CONTROLLER_ADAPTER_GAMEPAD_HH +#define NES_CONTROLLER_ADAPTER_GAMEPAD_HH + + + +class ControllerAdapterGamepad { + +}; + + + +#endif //NES_CONTROLLER_ADAPTER_GAMEPAD_HH diff --git a/app/mac/controller-adapter-gamepad.mm b/app/mac/controller-adapter-gamepad.mm new file mode 100644 index 0000000..132fa85 --- /dev/null +++ b/app/mac/controller-adapter-gamepad.mm @@ -0,0 +1,5 @@ +// +// Created by Hannes Schulze on 29.01.25. +// + +#include "controller-adapter-gamepad.hh" diff --git a/app/mac/controller-adapter-keyboard.hh b/app/mac/controller-adapter-keyboard.hh new file mode 100644 index 0000000..d91f069 --- /dev/null +++ b/app/mac/controller-adapter-keyboard.hh @@ -0,0 +1,14 @@ +#pragma once + +#import "controller-adapter.hh" +#import <GameController/GameController.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface ControllerAdapterKeyboard : NSObject <ControllerAdapter> + +@property (readonly, nullable) GCKeyboard* keyboard; + +@end + +NS_ASSUME_NONNULL_END
\ No newline at end of file diff --git a/app/mac/controller-adapter-keyboard.mm b/app/mac/controller-adapter-keyboard.mm new file mode 100644 index 0000000..ad3dda8 --- /dev/null +++ b/app/mac/controller-adapter-keyboard.mm @@ -0,0 +1,44 @@ +#import "controller-adapter-keyboard.hh" + +@implementation ControllerAdapterKeyboard + +- (instancetype)init +{ + self = [super init]; + if (self) + { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleKeyboardConnected:) + name:GCKeyboardDidConnectNotification + object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)handleKeyboardConnected:(NSNotification*)notification +{ + _keyboard = [GCKeyboard coalescedKeyboard]; +} + +- (nes::button_mask)readPressedButtons +{ + auto profile = [[self keyboard] keyboardInput]; + + auto res = nes::button_mask{}; + if ([[profile buttonForKeyCode:GCKeyCodeUpArrow] isPressed]) { res.add(nes::buttons::up); } + if ([[profile buttonForKeyCode:GCKeyCodeDownArrow] isPressed]) { res.add(nes::buttons::down); } + if ([[profile buttonForKeyCode:GCKeyCodeLeftArrow] isPressed]) { res.add(nes::buttons::left); } + if ([[profile buttonForKeyCode:GCKeyCodeRightArrow] isPressed]) { res.add(nes::buttons::right); } + if ([[profile buttonForKeyCode:GCKeyCodeKeyZ] isPressed]) { res.add(nes::buttons::a); } + if ([[profile buttonForKeyCode:GCKeyCodeKeyX] isPressed]) { res.add(nes::buttons::b); } + if ([[profile buttonForKeyCode:GCKeyCodeReturnOrEnter] isPressed]) { res.add(nes::buttons::start); } + if ([[profile buttonForKeyCode:GCKeyCodeSpacebar] isPressed]) { res.add(nes::buttons::select); } + return res; +} + +@end
\ No newline at end of file diff --git a/app/mac/controller-adapter.hh b/app/mac/controller-adapter.hh new file mode 100644 index 0000000..d0fc33a --- /dev/null +++ b/app/mac/controller-adapter.hh @@ -0,0 +1,11 @@ +#pragma once + +#import "nes/util/button-mask.hh" +#import <Foundation/Foundation.h> + +/// Adapts inputs from a GCPhysicalInputProfile to a NES button mask. +@protocol ControllerAdapter <NSObject> + +- (nes::button_mask)readPressedButtons; + +@end
\ No newline at end of file diff --git a/app/mac/main.mm b/app/mac/main.mm index 5ca7c5c..4e96cea 100644 --- a/app/mac/main.mm +++ b/app/mac/main.mm @@ -1,8 +1,7 @@ -#import "scene.hh" +#import "view-controller.hh" #import <iostream> #import <Cocoa/Cocoa.h> -#import <SpriteKit/SpriteKit.h> int main(int argc, char** argv) { @@ -34,23 +33,12 @@ int main(int argc, char** argv) [mainMenu addItem:item]; [app setMainMenu:mainMenu]; - auto scene = [[Scene alloc] initWithFilePath:filePath]; - auto minSize = [scene size]; - - auto view = [[SKView alloc] init]; - [view setIgnoresSiblingOrder:YES]; - [view setShowsFPS:YES]; - [view presentScene:scene]; - - auto window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0., 0., minSize.width * 2, minSize.height * 2) - styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | - NSWindowMiniaturizeButton | NSWindowStyleMaskResizable) - backing:NSBackingStoreBuffered - defer:false]; + auto viewController = [[ViewController alloc] initWithFilePath:filePath]; + auto window = [NSWindow windowWithContentViewController:viewController]; [window setTitle:@"NES"]; - [window setContentAspectRatio:minSize]; - [window setContentMinSize:minSize]; - [window setContentView:view]; + [window setContentAspectRatio:[viewController minimumDisplaySize]]; + [window setContentMinSize:[viewController minimumDisplaySize]]; + [window setContentSize:[viewController preferredDisplaySize]]; [window makeKeyAndOrderFront:nil]; [app activateIgnoringOtherApps:YES]; diff --git a/app/mac/scene.hh b/app/mac/scene.hh index 1660e66..ed56f2f 100644 --- a/app/mac/scene.hh +++ b/app/mac/scene.hh @@ -1,12 +1,22 @@ #pragma once +#import "controller-adapter.hh" #import <SpriteKit/SpriteKit.h> +NS_ASSUME_NONNULL_BEGIN + @interface Scene : SKScene - (instancetype)initWithFilePath:(NSString*)filePath NS_DESIGNATED_INITIALIZER; + (instancetype)sceneWithSize:(CGSize)size NS_UNAVAILABLE; - (instancetype)initWithSize:(CGSize)size NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + +@property (nonatomic, nullable) id<ControllerAdapter> primaryController; +@property (nonatomic, nullable) id<ControllerAdapter> secondaryController; + +@end -@end
\ No newline at end of file +NS_ASSUME_NONNULL_END
\ No newline at end of file diff --git a/app/mac/scene.mm b/app/mac/scene.mm index b71f9f6..0d50a7e 100644 --- a/app/mac/scene.mm +++ b/app/mac/scene.mm @@ -1,7 +1,6 @@ #import "scene.hh" #import "nes/nes.hh" #import "nes/util/display.hh" -#import "nes/util/rgb.hh" #import <memory> #import <optional> #import <iostream> @@ -32,13 +31,10 @@ namespace buffer_back_ = buffer_front_; buffer_front_ = tmp; bytes_back_ = static_cast<std::uint8_t*>([buffer_back_ mutableBytes]); - } - auto actually_switch_buffers() -> void - { - auto texture = [SKTexture textureWithData:buffer_front_ size:CGSizeMake(width, height) flipped:YES]; - [texture setFilteringMode:SKTextureFilteringNearest]; - [node_ setTexture:texture]; + auto texture = [SKTexture textureWithData:buffer_front_ size:CGSizeMake(width, height) flipped:YES]; + [texture setFilteringMode:SKTextureFilteringNearest]; + [node_ setTexture:texture]; } virtual auto set(unsigned x, unsigned y, nes::rgb value) -> void override @@ -71,9 +67,9 @@ namespace @implementation Scene { - std::unique_ptr<display_impl> display_; - std::unique_ptr<nes::nes> nes_; - std::optional<NSTimeInterval> last_time_; + std::unique_ptr<display_impl> _display; + std::unique_ptr<nes::nes> _nes; + std::optional<NSTimeInterval> _lastTimestamp; } - (instancetype)initWithFilePath:(NSString*)filePath @@ -95,97 +91,28 @@ namespace std::abort(); } - self->display_ = std::make_unique<display_impl>(node); - self->nes_ = std::make_unique<nes::nes>(std::move(cartridge), *self->display_); + _display = std::make_unique<display_impl>(node); + _nes = std::make_unique<nes::nes>(std::move(cartridge), *self->_display); } return self; } -- (void)keyDown:(NSEvent*)event -{ - auto& controller_1 = self->nes_->ref_controller_1(); - - switch ([event keyCode]) - { - case 0x7e: // up arrow - controller_1.ref_pressed().add(nes::buttons::up); - break; - case 0x7d: // down arrow - controller_1.ref_pressed().add(nes::buttons::down); - break; - case 0x7b: // left arrow - controller_1.ref_pressed().add(nes::buttons::left); - break; - case 0x7c: // right arrow - controller_1.ref_pressed().add(nes::buttons::right); - break; - case 0x6: // "Z" - controller_1.ref_pressed().add(nes::buttons::a); - break; - case 0x7: // "X" - controller_1.ref_pressed().add(nes::buttons::b); - break; - case 0x24: // return - controller_1.ref_pressed().add(nes::buttons::start); - break; - case 0x31: // space - controller_1.ref_pressed().add(nes::buttons::select); - break; - default: - [super keyDown:event]; - break; - } -} - -- (void)keyUp:(NSEvent*)event +- (void)update:(NSTimeInterval)currentTime { - auto& controller_1 = self->nes_->ref_controller_1(); + _nes->ref_controller_1().set_pressed(_primaryController ? [_primaryController readPressedButtons] : nes::button_mask{}); + _nes->ref_controller_2().set_pressed(_secondaryController ? [_secondaryController readPressedButtons] : nes::button_mask{}); - switch ([event keyCode]) - { - case 0x7e: // up arrow - controller_1.ref_pressed().remove(nes::buttons::up); - break; - case 0x7d: // down arrow - controller_1.ref_pressed().remove(nes::buttons::down); - break; - case 0x7b: // left arrow - controller_1.ref_pressed().remove(nes::buttons::left); - break; - case 0x7c: // right arrow - controller_1.ref_pressed().remove(nes::buttons::right); - break; - case 0x6: // "Z" - controller_1.ref_pressed().remove(nes::buttons::a); - break; - case 0x7: // "X" - controller_1.ref_pressed().remove(nes::buttons::b); - break; - case 0x24: // return - controller_1.ref_pressed().remove(nes::buttons::start); - break; - case 0x31: // space - controller_1.ref_pressed().remove(nes::buttons::select); - break; - default: - [super keyUp:event]; - break; - } -} + auto const lastTimestamp = _lastTimestamp.value_or(currentTime); + _lastTimestamp = currentTime; + auto deltaMicroseconds = static_cast<std::uint64_t>((currentTime - lastTimestamp) * 1000. * 1000.); + deltaMicroseconds = std::min(deltaMicroseconds, std::uint64_t{ 50000 }); + _nes->step(std::chrono::microseconds{ deltaMicroseconds }); -- (void)update:(NSTimeInterval)currentTime -{ - auto const last_time = self->last_time_.value_or(currentTime); - last_time_ = currentTime; - auto delta_us = static_cast<std::uint64_t>((currentTime - last_time) * 1000. * 1000.); - delta_us = std::min(delta_us, std::uint64_t{ 50000 }); - self->nes_->step(std::chrono::microseconds{ delta_us }); - if (self->nes_->get_status() != nes::status::success) + if (_nes->get_status() != nes::status::success) { - std::cerr << "Invalid state: " << to_string(self->nes_->get_status()) << std::endl; + std::cerr << "Invalid state: " << to_string(_nes->get_status()) << std::endl; std::abort(); } - self->display_->actually_switch_buffers(); } @end
\ No newline at end of file diff --git a/app/mac/view-controller.hh b/app/mac/view-controller.hh new file mode 100644 index 0000000..4947b99 --- /dev/null +++ b/app/mac/view-controller.hh @@ -0,0 +1,17 @@ +#pragma once + +#import <Cocoa/Cocoa.h> +#import <GameController/GameController.h> + +@interface ViewController : GCEventViewController + +- (instancetype)initWithFilePath:(NSString*)filePath NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; + +@property (readonly) NSSize minimumDisplaySize; +@property (readonly) NSSize preferredDisplaySize; + +@end
\ No newline at end of file diff --git a/app/mac/view-controller.mm b/app/mac/view-controller.mm new file mode 100644 index 0000000..8a1fcbd --- /dev/null +++ b/app/mac/view-controller.mm @@ -0,0 +1,46 @@ +#import "view-controller.hh" +#import "controller-adapter-keyboard.hh" +#import "scene.hh" + +@implementation ViewController +{ + Scene* _scene; + NSSize _sceneSize; + ControllerAdapterKeyboard* _keyboardController; +} + +- (instancetype)initWithFilePath:(NSString*)filePath +{ + self = [super initWithNibName:nil bundle:nil]; + if (self) + { + _scene = [[Scene alloc] initWithFilePath:filePath]; + _sceneSize = [_scene size]; + + _keyboardController = [[ControllerAdapterKeyboard alloc] init]; + [_scene setPrimaryController:_keyboardController]; + } + return self; +} + +- (void)loadView +{ + auto view = [[SKView alloc] init]; + [view setIgnoresSiblingOrder:YES]; + [view setShowsFPS:YES]; + [view presentScene:_scene]; + + [self setView:view]; +} + +- (NSSize)minimumDisplaySize +{ + return _sceneSize; +} + +- (NSSize)preferredDisplaySize +{ + return NSMakeSize(_sceneSize.width * 3., _sceneSize.height * 3.); +} + +@end
\ No newline at end of file |
