1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-10 16:30:07 +00:00

Merge branch 'master' into CRTC16bit

This commit is contained in:
Thomas Harte 2024-01-01 22:34:48 -05:00
commit 34b98be6d4
19 changed files with 265 additions and 73 deletions

View File

@ -56,10 +56,10 @@ MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::MachineTy
}
}
bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed) {
bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) {
bool was_consumed = false;
for(const auto &machine: machines_) {
was_consumed |= machine->get_keyboard().set_key_pressed(key, value, is_pressed);
was_consumed |= machine->get_keyboard().set_key_pressed(key, value, is_pressed, is_repeat);
}
return was_consumed;
}

View File

@ -31,7 +31,7 @@ class MultiKeyboardMachine: public MachineTypes::KeyboardMachine {
public:
MultiKeyboard(const std::vector<MachineTypes::KeyboardMachine *> &machines);
bool set_key_pressed(Key key, char value, bool is_pressed) final;
bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final;
void reset_all_keys() final;
const std::set<Key> &observed_keys() const final;
bool is_exclusive() const final;

View File

@ -21,7 +21,7 @@ Keyboard::Keyboard(const std::set<Key> &essential_modifiers) : essential_modifie
Keyboard::Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers) :
observed_keys_(observed_keys), essential_modifiers_(essential_modifiers), is_exclusive_(false) {}
bool Keyboard::set_key_pressed(Key key, char, bool is_pressed) {
bool Keyboard::set_key_pressed(Key key, char, bool is_pressed, bool) {
const size_t key_offset = size_t(key);
if(key_offset >= key_states_.size()) {
key_states_.resize(key_offset+1, false);

View File

@ -51,7 +51,7 @@ class Keyboard {
// Host interface.
/// @returns @c true if the key press affects the machine; @c false otherwise.
virtual bool set_key_pressed(Key key, char value, bool is_pressed);
virtual bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat);
virtual void reset_all_keys();
/// @returns a set of all Keys that this keyboard responds to.

View File

@ -276,10 +276,20 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
Keyboard(Processor *m6502) : m6502_(m6502) {}
void reset_all_keys() final {
open_apple_is_pressed = closed_apple_is_pressed = control_is_pressed = shift_is_pressed = key_is_down = false;
open_apple_is_pressed =
closed_apple_is_pressed =
control_is_pressed_ =
shift_is_pressed_ =
repeat_is_pressed_ =
key_is_down_ =
character_is_pressed_ = false;
}
bool set_key_pressed(Key key, char value, bool is_pressed) final {
bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final {
if constexpr (!is_iie()) {
if(is_repeat && !repeat_is_pressed_) return true;
}
// If no ASCII value is supplied, look for a few special cases.
switch(key) {
case Key::Left: value = 0x08; break;
@ -323,17 +333,27 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
}
case Key::LeftControl:
control_is_pressed = is_pressed;
control_is_pressed_ = is_pressed;
return true;
case Key::LeftShift:
case Key::RightShift:
shift_is_pressed = is_pressed;
shift_is_pressed_ = is_pressed;
return true;
case Key::F1: case Key::F2: case Key::F3: case Key::F4:
case Key::F5: case Key::F6: case Key::F7: case Key::F8:
case Key::F9: case Key::F10: case Key::F11: case Key::F12:
case Key::F9: case Key::F10: case Key::F11:
repeat_is_pressed_ = is_pressed;
if constexpr (!is_iie()) {
if(is_pressed && (!is_repeat || character_is_pressed_)) {
keyboard_input_ = uint8_t(last_pressed_character_ | 0x80);
}
}
return true;
case Key::F12:
case Key::PrintScreen:
case Key::ScrollLock:
case Key::Pause:
@ -356,10 +376,10 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
// Prior to the IIe, the keyboard could produce uppercase only.
if(!is_iie()) value = char(toupper(value));
if(control_is_pressed && isalpha(value)) value &= 0xbf;
if(control_is_pressed_ && isalpha(value)) value &= 0xbf;
// TODO: properly map IIe keys
if(!is_iie() && shift_is_pressed) {
if(!is_iie() && shift_is_pressed_) {
switch(value) {
case 0x27: value = 0x22; break; // ' -> "
case 0x2c: value = 0x3c; break; // , -> <
@ -383,11 +403,16 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
}
if(is_pressed) {
keyboard_input = uint8_t(value | 0x80);
key_is_down = true;
last_pressed_character_ = value;
character_is_pressed_ = true;
keyboard_input_ = uint8_t(value | 0x80);
key_is_down_ = true;
} else {
if((keyboard_input & 0x3f) == value) {
key_is_down = false;
if(value == last_pressed_character_) {
character_is_pressed_ = false;
}
if((keyboard_input_ & 0x3f) == value) {
key_is_down_ = false;
}
}
@ -395,24 +420,54 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
}
uint8_t get_keyboard_input() {
if(string_serialiser) {
return string_serialiser->head() | 0x80;
if(string_serialiser_) {
return string_serialiser_->head() | 0x80;
} else {
return keyboard_input;
return keyboard_input_;
}
}
bool shift_is_pressed = false;
bool control_is_pressed = false;
void clear_keyboard_input() {
keyboard_input_ &= 0x7f;
if(string_serialiser_ && !string_serialiser_->advance()) {
string_serialiser_.reset();
}
}
bool get_key_is_down() {
return key_is_down_;
}
void set_string_serialiser(std::unique_ptr<Utility::StringSerialiser> &&serialiser) {
string_serialiser_ = std::move(serialiser);
}
// The IIe has three keys that are wired directly to the same input as the joystick buttons.
bool open_apple_is_pressed = false;
bool closed_apple_is_pressed = false;
uint8_t keyboard_input = 0x00;
bool key_is_down = false;
std::unique_ptr<Utility::StringSerialiser> string_serialiser;
private:
Processor *const m6502_;
// Current keyboard input register, as exposed to the programmer; on the IIe the programmer
// can also poll for whether any key is currently down.
uint8_t keyboard_input_ = 0x00;
bool key_is_down_ = false;
// ASCII input state, referenced by the REPT key on models before the IIe.
char last_pressed_character_ = 0;
bool character_is_pressed_ = false;
// The repeat key itself.
bool repeat_is_pressed_ = false;
// Modifier states.
bool shift_is_pressed_ = false;
bool control_is_pressed_ = false;
// A string serialiser for receiving copy and paste.
std::unique_ptr<Utility::StringSerialiser> string_serialiser_;
// 6502 connection, for applying the reset button.
Processor *const m6502_;
};
Keyboard keyboard_;
@ -727,15 +782,11 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
break;
case 0xc010:
keyboard_.keyboard_input &= 0x7f;
if(keyboard_.string_serialiser) {
if(!keyboard_.string_serialiser->advance())
keyboard_.string_serialiser.reset();
}
keyboard_.clear_keyboard_input();
// On the IIe, reading C010 returns additional key info.
if(is_iie() && isReadOperation(operation)) {
*value = (keyboard_.key_is_down ? 0x80 : 0x00) | (keyboard_.keyboard_input & 0x7f);
*value = (keyboard_.get_key_is_down() ? 0x80 : 0x00) | (keyboard_.get_keyboard_input() & 0x7f);
}
break;
@ -879,7 +930,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
}
void type_string(const std::string &string) final {
keyboard_.string_serialiser = std::make_unique<Utility::StringSerialiser>(string, true);
keyboard_.set_string_serialiser(std::make_unique<Utility::StringSerialiser>(string, true));
}
bool can_type(char c) const final {

View File

@ -39,7 +39,7 @@ template <typename TimeUnit> class VideoSwitches {
set of potential flashing characters and alternate video modes.
*/
VideoSwitches(bool is_iie, TimeUnit delay, std::function<void(TimeUnit)> &&target) : delay_(delay), deferrer_(std::move(target)) {
character_zones_[0].xor_mask = 0;
character_zones_[0].xor_mask = 0xff;
character_zones_[0].address_mask = 0x3f;
character_zones_[1].xor_mask = 0;
character_zones_[1].address_mask = 0x3f;
@ -49,7 +49,7 @@ template <typename TimeUnit> class VideoSwitches {
character_zones_[3].address_mask = 0x3f;
if(is_iie) {
character_zones_[0].xor_mask =
character_zones_[1].xor_mask =
character_zones_[2].xor_mask =
character_zones_[3].xor_mask = 0xff;
character_zones_[2].address_mask =
@ -88,7 +88,7 @@ template <typename TimeUnit> class VideoSwitches {
if(alternative_character_set) {
character_zones_[1].address_mask = 0xff;
character_zones_[1].xor_mask = 0;
character_zones_[1].xor_mask = 0xff;
} else {
character_zones_[1].address_mask = 0x3f;
character_zones_[1].xor_mask = flash_mask();
@ -286,7 +286,9 @@ template <typename TimeUnit> class VideoSwitches {
// Update character set flashing; flashing is applied only when the alternative
// character set is not selected.
flash_ = (flash_ + 1) % (2 * flash_length);
character_zones_[1].xor_mask = flash_mask() * !internal_.alternative_character_set;
if(!internal_.alternative_character_set) {
character_zones_[1].xor_mask = flash_mask();
}
}
private:

View File

@ -75,12 +75,12 @@ class KeyboardMachine: public KeyActions {
(i) if @c symbol can be typed and this is a key down, @c type_string it;
(ii) if @c symbol cannot be typed, set @c key as @c is_pressed
*/
bool apply_key(Inputs::Keyboard::Key key, char symbol, bool is_pressed, bool map_logically) {
bool apply_key(Inputs::Keyboard::Key key, char symbol, bool is_pressed, bool is_repeat, bool map_logically) {
Inputs::Keyboard &keyboard = get_keyboard();
if(!map_logically) {
// Try a regular keypress first, and stop if that works.
if(keyboard.set_key_pressed(key, symbol, is_pressed)) {
if(keyboard.set_key_pressed(key, symbol, is_pressed, is_repeat)) {
return true;
}
@ -103,7 +103,7 @@ class KeyboardMachine: public KeyActions {
// That didn't work. Forward as a keypress. As, either:
// (i) this is a key down, but doesn't have a symbol, or is an untypeable symbol; or
// (ii) this is a key up, which it won't be an issue to miscommunicate.
return keyboard.set_key_pressed(key, symbol, is_pressed);
return keyboard.set_key_pressed(key, symbol, is_pressed, is_repeat);
}
}
};

View File

@ -6592,6 +6592,7 @@
"$(inherited)",
IGNORE_APPLE,
);
HEADER_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/Frameworks/SDL2.framework/Headers";
LD_RUNPATH_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -6613,6 +6614,7 @@
NDEBUG,
IGNORE_APPLE,
);
HEADER_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/Frameworks/SDL2.framework/Headers";
LD_RUNPATH_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.13;
ONLY_ACTIVE_ARCH = YES;

View File

@ -345,14 +345,14 @@ class MachineDocument:
/// Forwards key down events directly to the machine.
func keyDown(_ event: NSEvent) {
if let machine = self.machine {
machine.setKey(event.keyCode, characters: event.characters, isPressed: true)
machine.setKey(event.keyCode, characters: event.characters, isPressed: true, isRepeat: event.isARepeat)
}
}
/// Forwards key up events directly to the machine.
func keyUp(_ event: NSEvent) {
if let machine = self.machine {
machine.setKey(event.keyCode, characters: event.characters, isPressed: false)
machine.setKey(event.keyCode, characters: event.characters, isPressed: false, isRepeat: false)
}
}
@ -361,19 +361,19 @@ class MachineDocument:
if let machine = self.machine {
if newModifiers.modifierFlags.contains(.shift) != shiftIsDown {
shiftIsDown = newModifiers.modifierFlags.contains(.shift)
machine.setKey(VK_Shift, characters: nil, isPressed: shiftIsDown)
machine.setKey(VK_Shift, characters: nil, isPressed: shiftIsDown, isRepeat: false)
}
if newModifiers.modifierFlags.contains(.control) != controlIsDown {
controlIsDown = newModifiers.modifierFlags.contains(.control)
machine.setKey(VK_Control, characters: nil, isPressed: controlIsDown)
machine.setKey(VK_Control, characters: nil, isPressed: controlIsDown, isRepeat: false)
}
if newModifiers.modifierFlags.contains(.command) != commandIsDown {
commandIsDown = newModifiers.modifierFlags.contains(.command)
machine.setKey(VK_Command, characters: nil, isPressed: commandIsDown)
machine.setKey(VK_Command, characters: nil, isPressed: commandIsDown, isRepeat: false)
}
if newModifiers.modifierFlags.contains(.option) != optionIsDown {
optionIsDown = newModifiers.modifierFlags.contains(.option)
machine.setKey(VK_Option, characters: nil, isPressed: optionIsDown)
machine.setKey(VK_Option, characters: nil, isPressed: optionIsDown, isRepeat: false)
}
}
}

View File

@ -769,11 +769,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>23.12.26</string>
<string>23.12.28</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>23.12.26</string>
<string>23.12.28</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>LSMinimumSystemVersion</key>

View File

@ -67,7 +67,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
- (void)start;
- (void)stop;
- (void)setKey:(uint16_t)key characters:(nullable NSString *)characters isPressed:(BOOL)isPressed;
- (void)setKey:(uint16_t)key characters:(nullable NSString *)characters isPressed:(BOOL)isPressed isRepeat:(BOOL)isRepeat;
- (void)clearAllKeys;
- (void)setMouseButton:(int)button isPressed:(BOOL)isPressed;

View File

@ -366,7 +366,7 @@ struct ActivityObserver: public Activity::Observer {
[self updateJoystickTimer];
}
- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed {
- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed isRepeat:(BOOL)isRepeat {
[self applyInputEvent:^{
auto keyboard_machine = self->_machine->keyboard_machine();
if(keyboard_machine && (self.inputMode != CSMachineKeyboardInputModeJoystick || !keyboard_machine->get_keyboard().is_exclusive())) {
@ -437,7 +437,13 @@ struct ActivityObserver: public Activity::Observer {
}
@synchronized(self) {
if(keyboard_machine->apply_key(mapped_key, pressedKey, isPressed, self.inputMode == CSMachineKeyboardInputModeKeyboardLogical)) {
if(keyboard_machine->apply_key(
mapped_key,
pressedKey,
isPressed,
isRepeat,
self.inputMode == CSMachineKeyboardInputModeKeyboardLogical)
) {
return;
}
}

View File

@ -1,4 +1,5 @@
QT += core gui multimedia widgets
greaterThan(5, QT_MAJOR_VERSION) QT += openglwidgets
# Be specific about C++17 but also try the vaguer C++1z for older
# versions of Qt.
@ -24,10 +25,10 @@ DEFINES += TARGET_QT
DEFINES += IGNORE_APPLE
QMAKE_CXXFLAGS_RELEASE += -DNDEBUG
# Generate warnings for any use of APIs deprecated prior to Qt 6.0.0.
# Development was performed against Qt 5.14.
# Generate warnings for any use of APIs deprecated prior to Qt 7.0.0.
# Development was performed against Qt 6.6.1 and Qt 5.15.2
DEFINES += QT_DEPRECATED_WARNINGS
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x070000
SRC = $$PWD/../..

View File

@ -2,11 +2,17 @@
#include "settings.h"
#include "timer.h"
#include <QtGlobal>
#include <QObject>
#include <QStandardPaths>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QAudioDevice>
#include <QMediaDevices>
#endif
#include <QtWidgets>
#include <QtGlobal>
#include <cstdio>
@ -314,24 +320,44 @@ void MainWindow::launchMachine() {
static constexpr size_t samplesPerBuffer = 256; // TODO: select this dynamically.
const auto speaker = audio_producer->get_speaker();
if(speaker) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QAudioDevice device(QMediaDevices::defaultAudioOutput());
if(true) { // TODO: how to check that audio output is available in Qt6?
QAudioFormat idealFormat = device.preferredFormat();
#else
const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultOutputDevice();
if(!defaultDeviceInfo.isNull()) {
QAudioFormat idealFormat = defaultDeviceInfo.preferredFormat();
#endif
// Use the ideal format's sample rate, provide stereo as long as at least two channels
// are available, and — at least for now — assume a good buffer size.
audioIsStereo = (idealFormat.channelCount() > 1) && speaker->get_is_stereo();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
audioIs8bit = idealFormat.sampleFormat() == QAudioFormat::UInt8;
#else
audioIs8bit = idealFormat.sampleSize() < 16;
#endif
idealFormat.setChannelCount(1 + int(audioIsStereo));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
idealFormat.setSampleFormat(audioIs8bit ? QAudioFormat::UInt8 : QAudioFormat::Int16);
#else
idealFormat.setSampleSize(audioIs8bit ? 8 : 16);
#endif
speaker->set_output_rate(idealFormat.sampleRate(), samplesPerBuffer, audioIsStereo);
speaker->set_delegate(this);
audioThread.start();
audioThread.performAsync([this, idealFormat] {
audioThread.performAsync([&] {
// Create an audio output.
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
audioOutput = std::make_unique<QAudioSink>(device, idealFormat);
#else
audioOutput = std::make_unique<QAudioOutput>(idealFormat);
#endif
// Start the output. The additional `audioBuffer` is meant to minimise latency,
// believe it or not, given Qt's semantics.
@ -373,13 +399,17 @@ void MainWindow::launchMachine() {
QAction *const asKeyboardAction = new QAction(tr("Use Keyboard as Keyboard"), this);
asKeyboardAction->setCheckable(true);
asKeyboardAction->setChecked(true);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
asKeyboardAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K));
#endif
inputMenu->addAction(asKeyboardAction);
QAction *const asJoystickAction = new QAction(tr("Use Keyboard as Joystick"), this);
asJoystickAction->setCheckable(true);
asJoystickAction->setChecked(false);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
asJoystickAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J));
#endif
inputMenu->addAction(asJoystickAction);
connect(asKeyboardAction, &QAction::triggered, this, [=] {
@ -755,12 +785,13 @@ void MainWindow::dropEvent(QDropEvent* event) {
QString unusedRoms;
for(const auto &url: event->mimeData()->urls()) {
const char *const name = url.toLocalFile().toUtf8();
FILE *const file = fopen(name, "rb");
const std::string name = url.toLocalFile().toStdString();
FILE *const file = fopen(name.c_str(), "rb");
if(!file) continue;
const auto contents = fileContentsAndClose(file);
if(!contents) continue;
CRC::CRC32 generator;
const uint32_t crc = generator.compute_crc(*contents);
@ -889,7 +920,7 @@ bool MainWindow::processEvent(QKeyEvent *event) {
if(!keyboardMachine) return true;
auto &keyboard = keyboardMachine->get_keyboard();
keyboard.set_key_pressed(*key, event->text().size() ? event->text()[0].toLatin1() : '\0', isPressed);
keyboard.set_key_pressed(*key, event->text().size() ? event->text()[0].toLatin1() : '\0', isPressed, event->isAutoRepeat());
if(keyboard.is_exclusive() || keyboard.observed_keys().find(*key) != keyboard.observed_keys().end()) {
return false;
}
@ -962,6 +993,7 @@ void MainWindow::setButtonPressed(int index, bool isPressed) {
#include "../../Analyser/Static/Macintosh/Target.hpp"
#include "../../Analyser/Static/MSX/Target.hpp"
#include "../../Analyser/Static/Oric/Target.hpp"
#include "../../Analyser/Static/PCCompatible/Target.hpp"
#include "../../Analyser/Static/ZX8081/Target.hpp"
#include "../../Analyser/Static/ZXSpectrum/Target.hpp"
@ -984,6 +1016,7 @@ void MainWindow::startMachine() {
TEST(macintosh);
TEST(msx);
TEST(oric);
TEST(pc);
TEST(spectrum);
TEST(vic20);
TEST(zx80);
@ -1182,6 +1215,23 @@ void MainWindow::start_oric() {
launchTarget(std::move(target));
}
void MainWindow::start_pc() {
using Target = Analyser::Static::PCCompatible::Target;
auto target = std::make_unique<Target>();
switch(ui->pcSpeedComboBox->currentIndex()) {
default: target->speed = Target::Speed::ApproximatelyOriginal; break;
case 1: target->speed = Target::Speed::Fast; break;
}
switch(ui->pcVideoAdaptorComboBox->currentIndex()) {
default: target->adaptor = Target::VideoAdaptor::MDA; break;
case 1: target->adaptor = Target::VideoAdaptor::CGA; break;
}
launchTarget(std::move(target));
}
void MainWindow::start_spectrum() {
using Target = Analyser::Static::ZXSpectrum::Target;
auto target = std::make_unique<Target>();

View File

@ -1,12 +1,18 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QAudioOutput>
#include <QtGlobal>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QAudioSink>
#else
#include <QAudioOutput>
#endif
#include <QMainWindow>
#include <memory>
#include <mutex>
#include <optional>
#include "audiobuffer.h"
#include "timer.h"
@ -71,7 +77,11 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
std::unique_ptr<Machine::DynamicMachine> machine;
std::mutex machineMutex;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
std::unique_ptr<QAudioSink> audioOutput;
#else
std::unique_ptr<QAudioOutput> audioOutput;
#endif
bool audioIs8bit = false, audioIsStereo = false;
void speaker_did_complete_samples(Outputs::Speaker::Speaker *speaker, const std::vector<int16_t> &buffer) override;
AudioBuffer audioBuffer;
@ -96,6 +106,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void start_macintosh();
void start_msx();
void start_oric();
void start_pc();
void start_spectrum();
void start_vic20();
void start_zx80();

View File

@ -852,6 +852,76 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="pcTab">
<attribute name="title">
<string>PC Compatible</string>
</attribute>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<item>
<layout class="QFormLayout">
<item row="0" column="0">
<widget class="QLabel">
<property name="text">
<string>Video Adaptor:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="pcVideoAdaptorComboBox">
<item>
<property name="text">
<string>MDA</string>
</property>
</item>
<item>
<property name="text">
<string>CGA</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel">
<property name="text">
<string>Speed:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="pcSpeedComboBox">
<item>
<property name="text">
<string>Similar to Original</string>
</property>
</item>
<item>
<property name="text">
<string>Turbo</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="vic20Tab">
<attribute name="title">
<string>Vic-20</string>
@ -1170,9 +1240,6 @@
</item>
<item>
<widget class="QPlainTextEdit" name="missingROMsBox">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>

View File

@ -3,7 +3,6 @@
#include <QApplication>
#include <QCursor>
#include <QDebug>
#include <QDesktopWidget>
#include <QGuiApplication>
#include <QKeyEvent>
#include <QMouseEvent>
@ -30,10 +29,7 @@ void ScanTargetWidget::paintGL() {
requestedRedrawTime = 0;
}
// TODO: if Qt 5.14 can be guaranteed, just use window()->screen().
const auto screenNumber = QApplication::desktop()->screenNumber(this);
QScreen *const screen = QGuiApplication::screens()[screenNumber];
QScreen *const screen = window()->screen();
const float newOutputScale = float(screen->devicePixelRatio());
if(outputScale != newOutputScale) {
outputScale = newOutputScale;

View File

@ -976,9 +976,11 @@ int main(int argc, char *argv[]) {
SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
SDL_Keycode keycode = SDLK_UNKNOWN;
bool is_down = true;
bool repeat = false;
KeyPress(uint32_t timestamp, const char *text) : timestamp(timestamp), input(text) {}
KeyPress(uint32_t timestamp, SDL_Scancode scancode, SDL_Keycode keycode, bool is_down) : timestamp(timestamp), scancode(scancode), keycode(keycode), is_down(is_down) {}
KeyPress(uint32_t timestamp, SDL_Scancode scancode, SDL_Keycode keycode, bool is_down, bool repeat) :
timestamp(timestamp), scancode(scancode), keycode(keycode), is_down(is_down), repeat(repeat) {}
KeyPress() {}
};
std::vector<KeyPress> keypresses;
@ -1129,7 +1131,12 @@ int main(int argc, char *argv[]) {
break;
}
keypresses.emplace_back(event.text.timestamp, event.key.keysym.scancode, event.key.keysym.sym, event.type == SDL_KEYDOWN);
keypresses.emplace_back(
event.text.timestamp,
event.key.keysym.scancode,
event.key.keysym.sym,
event.type == SDL_KEYDOWN,
event.key.repeat);
} break;
case SDL_MOUSEBUTTONDOWN:
@ -1214,14 +1221,14 @@ int main(int argc, char *argv[]) {
// is sufficiently untested on SDL, and somewhat too reliant on empirical timestamp behaviour,
// for it to be trustworthy enough otherwise to expose.
if(logical_keyboard) {
if(keyboard_machine->apply_key(key, keypress.input.size() ? keypress.input[0] : 0, keypress.is_down, logical_keyboard)) {
if(keyboard_machine->apply_key(key, keypress.input.size() ? keypress.input[0] : 0, keypress.is_down, keypress.repeat, logical_keyboard)) {
continue;
}
} else {
// This is a slightly terrible way of obtaining a symbol for the key, e.g. for letters it will always return
// the capital letter version, at least empirically. But it'll have to do for now.
const char *key_name = SDL_GetKeyName(keypress.keycode);
if(keyboard_machine->get_keyboard().set_key_pressed(key, (strlen(key_name) == 1) ? key_name[0] : 0, keypress.is_down)) {
if(keyboard_machine->get_keyboard().set_key_pressed(key, (strlen(key_name) == 1) ? key_name[0] : 0, keypress.is_down, keypress.repeat)) {
continue;
}
}

View File

@ -33,7 +33,6 @@ void Parser::install_track(const Storage::Disk::Track::Address &address) {
std::map<int, Storage::Encodings::MFM::Sector> sectors_by_id;
if(density_) {
append(parse_track(*track, *density_), sectors_by_id);
return;
} else {
// Just try all three in succession.
append(parse_track(*track, Density::Single), sectors_by_id);