summaryrefslogtreecommitdiff
path: root/app/mac/scene.mm
blob: 0ff5122a2e2af1c024e95799b293a1429cdf286d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#import "scene.hh"
#import "impl/display-spritekit.hh"
#import "impl/file-browser-posix.hh"
#import "impl/input-device-gc-keyboard.hh"
#import "impl/input-device-gc-gamepad.hh"
#import "impl/input-device-serial-controller.hh"
#import "nes/app/application.hh"
#import <memory>
#import <optional>

namespace
{
    constexpr auto const serial_controller_path = "/dev/cu.usbserial-FTB6SPL3";
} // namespace

@implementation Scene
{
	std::unique_ptr<nes::app::mac::display_spritekit> _display;
    std::unique_ptr<nes::app::mac::file_browser_posix> _fileBrowser;
    std::unique_ptr<nes::app::mac::input_device_serial_controller> _serialController;
	std::unique_ptr<nes::app::mac::input_device_gc_keyboard> _keyboard;
	std::vector<std::unique_ptr<nes::app::mac::input_device_gc_gamepad>> _controllers;
	std::unique_ptr<nes::app::application> _app;
    std::optional<NSTimeInterval> _lastTimestamp;
}

- (instancetype)init
{
    self = [super initWithSize:CGSizeMake(nes::display::width, nes::display::height)];
    if (self)
    {
        auto node = [[SKSpriteNode alloc] init];
        [node setAnchorPoint:CGPointZero];
        [node setSize:[self size]];

        [self setScaleMode:SKSceneScaleModeAspectFit];
        [self addChild:node];

        _display = std::make_unique<nes::app::mac::display_spritekit>(node);
        _fileBrowser = std::make_unique<nes::app::mac::file_browser_posix>();
        _serialController = std::make_unique<nes::app::mac::input_device_serial_controller>(serial_controller_path);
		_keyboard = std::make_unique<nes::app::mac::input_device_gc_keyboard>([[GCKeyboard coalescedKeyboard] keyboardInput]);
        _app = std::make_unique<nes::app::application>(*_display, *_keyboard, *_fileBrowser);

        _app->add_controller(*_serialController);

		for (GCController* controller in [GCController controllers])
		{
			auto gamepad = [controller extendedGamepad];
			if (gamepad)
			{
				auto adapter = std::make_unique<nes::app::mac::input_device_gc_gamepad>(gamepad);
				_app->add_controller(*adapter);
				_controllers.emplace_back(std::move(adapter));
			}
		}

		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(handleKeyboardConnected:)
													 name:GCKeyboardDidConnectNotification
												   object:nil];
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(handleControllerConnected:)
													 name:GCControllerDidConnectNotification
												   object:nil];
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(handleControllerDisconnected:)
													 name:GCControllerDidDisconnectNotification
												   object:nil];
    }
    return self;
}

- (void)dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)keyUp:(NSEvent*)event
{
	// Handled by Game Controller
}

- (void)keyDown:(NSEvent*)event
{
	// Handled by Game Controller
}

- (void)update:(NSTimeInterval)currentTime
{
    auto const lastTimestamp = _lastTimestamp.value_or(currentTime);
    _lastTimestamp = currentTime;
    auto deltaMicroseconds = static_cast<nes::u64>((currentTime - lastTimestamp) * 1000. * 1000.);
	deltaMicroseconds = std::min(deltaMicroseconds, nes::u64{ 50000 });
    _app->frame(std::chrono::microseconds{ deltaMicroseconds });
}

- (void)handleKeyboardConnected:(NSNotification*)notification
{
	_keyboard->set_profile([[GCKeyboard coalescedKeyboard] keyboardInput]);
}

- (void)handleControllerConnected:(NSNotification*)notification
{
	auto controller = (GCController*)[notification object];
	auto gamepad = [controller extendedGamepad];
	if (gamepad)
	{
		auto adapter = std::make_unique<nes::app::mac::input_device_gc_gamepad>(gamepad);
		_app->add_controller(*adapter);
		_controllers.emplace_back(std::move(adapter));
	}
}

- (void)handleControllerDisconnected:(NSNotification*)notification
{
	auto controller = (GCController*)[notification object];
	for (auto i = _controllers.size(); i > 0; --i)
	{
		auto& adapter = *_controllers[i - 1];
		if (adapter.get_controller() == controller)
		{
			_app->remove_controller(adapter);
			_controllers.erase(_controllers.begin() + static_cast<int>(i - 1));
		}
	}
}

@end