blob: de9c58713b74a69f4c00806115af537b8b1c2db1 (
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
|
#import "scene.hh"
#import "nes/app/application.hh"
#import "nes/app/input/input-device-keyboard.hh"
#import "nes/app/input/input-device-controller.hh"
#import "nes/common/display.hh"
#import <memory>
#import <optional>
namespace
{
class display_impl final : public nes::display
{
SKSpriteNode* node_;
NSMutableData* buffer_front_;
NSMutableData* buffer_back_;
std::uint8_t* bytes_back_;
public:
explicit display_impl(SKSpriteNode* node)
: node_{ node }
, buffer_front_{ make_buffer() }
, buffer_back_{ make_buffer() }
, bytes_back_{ static_cast<std::uint8_t*>([buffer_back_ mutableBytes]) }
{
}
~display_impl() = default;
virtual auto switch_buffers() -> void override
{
auto tmp = buffer_back_;
buffer_back_ = buffer_front_;
buffer_front_ = tmp;
bytes_back_ = static_cast<std::uint8_t*>([buffer_back_ mutableBytes]);
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
{
auto const offset = (y * width + x) * 4;
bytes_back_[offset + 0] = value.r;
bytes_back_[offset + 1] = value.g;
bytes_back_[offset + 2] = value.b;
bytes_back_[offset + 3] = 255;
}
virtual auto get(unsigned x, unsigned y) const -> nes::rgb override
{
auto const offset = (y * width + x) * 4;
auto res = nes::rgb{};
res.r = bytes_back_[offset + 0];
res.g = bytes_back_[offset + 1];
res.b = bytes_back_[offset + 2];
return res;
}
private:
static auto make_buffer() -> NSMutableData*
{
auto const length = NSUInteger{ width * height * 4 };
return [NSMutableData dataWithLength:length];
}
};
class input_device_keyboard_impl final : public nes::input_device_keyboard
{
GCKeyboardInput* profile_;
public:
explicit input_device_keyboard_impl(GCKeyboardInput* profile)
: profile_{ profile }
{
}
auto set_profile(GCKeyboardInput* profile) -> void { profile_ = profile; }
auto read_key_up() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeUpArrow] isPressed]; }
auto read_key_down() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeDownArrow] isPressed]; }
auto read_key_left() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeLeftArrow] isPressed]; }
auto read_key_right() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeRightArrow] isPressed]; }
auto read_key_a() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeKeyZ] isPressed]; }
auto read_key_b() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeKeyX] isPressed]; }
auto read_key_select() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeSpacebar] isPressed]; }
auto read_key_start() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeReturnOrEnter] isPressed]; }
auto read_key_escape() -> bool override { return [[profile_ buttonForKeyCode:GCKeyCodeEscape] isPressed]; }
};
class input_device_controller_impl final : public nes::input_device_controller
{
GCExtendedGamepad* profile_;
public:
explicit input_device_controller_impl(GCExtendedGamepad* profile)
: profile_{ profile }
{
}
auto is_reliable() const -> bool override { return true; }
auto get_controller() const -> GCController* { return [profile_ controller]; }
auto get_name() const -> std::string_view override { return [[get_controller() vendorName] UTF8String]; }
auto get_index() const -> index override
{
switch ([get_controller() playerIndex])
{
case GCControllerPlayerIndex1:
return index::player_1;
case GCControllerPlayerIndex2:
return index::player_2;
case GCControllerPlayerIndex3:
case GCControllerPlayerIndex4:
case GCControllerPlayerIndexUnset:
break;
}
return index::unused;
}
auto set_index(index idx) -> void override
{
switch (idx)
{
case index::unused:
[get_controller() setPlayerIndex:GCControllerPlayerIndexUnset];
break;
case index::player_1:
[get_controller() setPlayerIndex:GCControllerPlayerIndex1];
break;
case index::player_2:
[get_controller() setPlayerIndex:GCControllerPlayerIndex2];
break;
}
}
auto read_key_up() -> bool override { return [[[profile_ dpad] up] isPressed]; }
auto read_key_down() -> bool override { return [[[profile_ dpad] down] isPressed]; }
auto read_key_left() -> bool override { return [[[profile_ dpad] left] isPressed]; }
auto read_key_right() -> bool override { return [[[profile_ dpad] right] isPressed]; }
auto read_key_a() -> bool override { return [[profile_ buttonA] isPressed]; }
auto read_key_b() -> bool override { return [[profile_ buttonB] isPressed]; }
auto read_key_select() -> bool override { return [[profile_ leftTrigger] isPressed]; }
auto read_key_start() -> bool override { return [[profile_ rightTrigger] isPressed]; }
};
} // namespace
@implementation Scene
{
std::unique_ptr<display_impl> _display;
std::unique_ptr<input_device_keyboard_impl> _keyboard;
std::vector<std::unique_ptr<input_device_controller_impl>> _controllers;
std::unique_ptr<nes::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<display_impl>(node);
_keyboard = std::make_unique<input_device_keyboard_impl>([[GCKeyboard coalescedKeyboard] keyboardInput]);
_app = std::make_unique<nes::application>(*_display, *_keyboard);
for (GCController* controller in [GCController controllers])
{
auto gamepad = [controller extendedGamepad];
if (gamepad)
{
auto adapter = std::make_unique<input_device_controller_impl>(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<std::uint64_t>((currentTime - lastTimestamp) * 1000. * 1000.);
deltaMicroseconds = std::min(deltaMicroseconds, std::uint64_t{ 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<input_device_controller_impl>(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
|