mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #637 from TomHarte/UserInfo
Improves Macintosh user communications
This commit is contained in:
commit
9c517d07d4
@ -399,3 +399,8 @@ void IWM::set_component_prefers_clocking(ClockingHint::Source *component, Clocki
|
||||
drive_is_rotating_[1] = is_rotating;
|
||||
}
|
||||
}
|
||||
|
||||
void IWM::set_activity_observer(Activity::Observer *observer) {
|
||||
if(drives_[0]) drives_[0]->set_activity_observer(observer, "Internal Drive", true);
|
||||
if(drives_[1]) drives_[1]->set_activity_observer(observer, "External Drive", true);
|
||||
}
|
||||
|
@ -9,8 +9,11 @@
|
||||
#ifndef IWM_hpp
|
||||
#define IWM_hpp
|
||||
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||
|
||||
#include "../../Storage/Disk/Drive.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@ -67,6 +70,10 @@ class IWM:
|
||||
/// Connects a drive to the IWM.
|
||||
void set_drive(int slot, IWMDrive *drive);
|
||||
|
||||
/// Registers the currently-connected drives as @c Activity::Sources ;
|
||||
/// the first will be declared 'Internal', the second 'External'.
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
// Storage::Disk::Drive::EventDelegate.
|
||||
void process_event(const Storage::Disk::Drive::Event &event) override;
|
||||
|
@ -71,7 +71,9 @@ void DoubleDensityDrive::set_rotation_speed(float revolutions_per_minute) {
|
||||
|
||||
// MARK: - Control input/output.
|
||||
|
||||
void DoubleDensityDrive::set_enabled(bool) {
|
||||
void DoubleDensityDrive::set_enabled(bool enabled) {
|
||||
// Disabling a drive also stops its motor.
|
||||
if(!enabled) set_motor_on(false);
|
||||
}
|
||||
|
||||
void DoubleDensityDrive::set_control_lines(int lines) {
|
||||
|
@ -23,8 +23,8 @@ class Keyboard {
|
||||
public:
|
||||
enum class Key {
|
||||
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PrintScreen, ScrollLock, Pause,
|
||||
BackTick, k1, k2, k3, k4, k5, k6, k7, k8, k9, k0, Hyphen, Equals, BackSpace,
|
||||
Tab, Q, W, E, R, T, Y, U, I, O, P, OpenSquareBracket, CloseSquareBracket, BackSlash,
|
||||
BackTick, k1, k2, k3, k4, k5, k6, k7, k8, k9, k0, Hyphen, Equals, Backspace,
|
||||
Tab, Q, W, E, R, T, Y, U, I, O, P, OpenSquareBracket, CloseSquareBracket, Backslash,
|
||||
CapsLock, A, S, D, F, G, H, J, K, L, Semicolon, Quote, Hash, Enter,
|
||||
LeftShift, Z, X, C, V, B, N, M, Comma, FullStop, ForwardSlash, RightShift,
|
||||
LeftControl, LeftOption, LeftMeta, Space, RightMeta, RightOption, RightControl,
|
||||
|
@ -31,12 +31,12 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||
BIND(F11, KeyRightSquareBracket);
|
||||
BIND(F12, KeyClear);
|
||||
|
||||
BIND(Hyphen, KeyMinus); BIND(Equals, KeyCaret); BIND(BackSpace, KeyDelete);
|
||||
BIND(Hyphen, KeyMinus); BIND(Equals, KeyCaret); BIND(Backspace, KeyDelete);
|
||||
BIND(Tab, KeyTab);
|
||||
|
||||
BIND(OpenSquareBracket, KeyAt);
|
||||
BIND(CloseSquareBracket, KeyLeftSquareBracket);
|
||||
BIND(BackSlash, KeyBackSlash);
|
||||
BIND(Backslash, KeyBackSlash);
|
||||
|
||||
BIND(CapsLock, KeyCapsLock);
|
||||
BIND(Semicolon, KeyColon);
|
||||
|
@ -795,7 +795,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
case Key::Right: value = 0x15; break;
|
||||
case Key::Down: value = 0x0a; break;
|
||||
case Key::Up: value = 0x0b; break;
|
||||
case Key::BackSpace: value = 0x7f; break;
|
||||
case Key::Backspace: value = 0x7f; break;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
88
Machines/Apple/Macintosh/Keyboard.cpp
Normal file
88
Machines/Apple/Macintosh/Keyboard.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// Keyboard.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/08/2019.
|
||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Keyboard.hpp"
|
||||
|
||||
using namespace Apple::Macintosh;
|
||||
|
||||
uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||
using Key = Inputs::Keyboard::Key;
|
||||
using MacKey = Apple::Macintosh::Key;
|
||||
switch(key) {
|
||||
default: return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||
|
||||
#define Bind(x, y) case Key::x: return uint16_t(y)
|
||||
|
||||
Bind(BackTick, MacKey::BackTick);
|
||||
Bind(k1, MacKey::k1); Bind(k2, MacKey::k2); Bind(k3, MacKey::k3);
|
||||
Bind(k4, MacKey::k4); Bind(k5, MacKey::k5); Bind(k6, MacKey::k6);
|
||||
Bind(k7, MacKey::k7); Bind(k8, MacKey::k8); Bind(k9, MacKey::k9);
|
||||
Bind(k0, MacKey::k0);
|
||||
Bind(Hyphen, MacKey::Hyphen);
|
||||
Bind(Equals, MacKey::Equals);
|
||||
Bind(Backspace, MacKey::Backspace);
|
||||
|
||||
Bind(Tab, MacKey::Tab);
|
||||
Bind(Q, MacKey::Q); Bind(W, MacKey::W); Bind(E, MacKey::E); Bind(R, MacKey::R);
|
||||
Bind(T, MacKey::T); Bind(Y, MacKey::Y); Bind(U, MacKey::U); Bind(I, MacKey::I);
|
||||
Bind(O, MacKey::O); Bind(P, MacKey::P);
|
||||
Bind(OpenSquareBracket, MacKey::OpenSquareBracket);
|
||||
Bind(CloseSquareBracket, MacKey::CloseSquareBracket);
|
||||
|
||||
Bind(CapsLock, MacKey::CapsLock);
|
||||
Bind(A, MacKey::A); Bind(S, MacKey::S); Bind(D, MacKey::D); Bind(F, MacKey::F);
|
||||
Bind(G, MacKey::G); Bind(H, MacKey::H); Bind(J, MacKey::J); Bind(K, MacKey::K);
|
||||
Bind(L, MacKey::L);
|
||||
Bind(Semicolon, MacKey::Semicolon);
|
||||
Bind(Quote, MacKey::Quote);
|
||||
Bind(Enter, MacKey::Return);
|
||||
|
||||
Bind(LeftShift, MacKey::Shift);
|
||||
Bind(Z, MacKey::Z); Bind(X, MacKey::X); Bind(C, MacKey::C); Bind(V, MacKey::V);
|
||||
Bind(B, MacKey::B); Bind(N, MacKey::N); Bind(M, MacKey::M);
|
||||
Bind(Comma, MacKey::Comma);
|
||||
Bind(FullStop, MacKey::FullStop);
|
||||
Bind(ForwardSlash, MacKey::ForwardSlash);
|
||||
Bind(RightShift, MacKey::Shift);
|
||||
|
||||
Bind(Left, MacKey::Left);
|
||||
Bind(Right, MacKey::Right);
|
||||
Bind(Up, MacKey::Up);
|
||||
Bind(Down, MacKey::Down);
|
||||
|
||||
Bind(LeftOption, MacKey::Option);
|
||||
Bind(RightOption, MacKey::Option);
|
||||
Bind(LeftMeta, MacKey::Command);
|
||||
Bind(RightMeta, MacKey::Command);
|
||||
|
||||
Bind(Space, MacKey::Space);
|
||||
Bind(Backslash, MacKey::Backslash);
|
||||
|
||||
Bind(KeyPadDelete, MacKey::KeyPadDelete);
|
||||
Bind(KeyPadEquals, MacKey::KeyPadEquals);
|
||||
Bind(KeyPadSlash, MacKey::KeyPadSlash);
|
||||
Bind(KeyPadAsterisk, MacKey::KeyPadAsterisk);
|
||||
Bind(KeyPadMinus, MacKey::KeyPadMinus);
|
||||
Bind(KeyPadPlus, MacKey::KeyPadPlus);
|
||||
Bind(KeyPadEnter, MacKey::KeyPadEnter);
|
||||
Bind(KeyPadDecimalPoint, MacKey::KeyPadDecimalPoint);
|
||||
|
||||
Bind(KeyPad9, MacKey::KeyPad9);
|
||||
Bind(KeyPad8, MacKey::KeyPad8);
|
||||
Bind(KeyPad7, MacKey::KeyPad7);
|
||||
Bind(KeyPad6, MacKey::KeyPad6);
|
||||
Bind(KeyPad5, MacKey::KeyPad5);
|
||||
Bind(KeyPad4, MacKey::KeyPad4);
|
||||
Bind(KeyPad3, MacKey::KeyPad3);
|
||||
Bind(KeyPad2, MacKey::KeyPad2);
|
||||
Bind(KeyPad1, MacKey::KeyPad1);
|
||||
Bind(KeyPad0, MacKey::KeyPad0);
|
||||
|
||||
#undef Bind
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
#define Apple_Macintosh_Keyboard_hpp
|
||||
|
||||
#include "../../KeyboardMachine.hpp"
|
||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
@ -17,6 +18,72 @@
|
||||
namespace Apple {
|
||||
namespace Macintosh {
|
||||
|
||||
static const uint16_t KeypadMask = 0x100;
|
||||
|
||||
/*!
|
||||
Defines the keycodes that could be passed directly to a Macintosh via set_key_pressed.
|
||||
*/
|
||||
enum class Key: uint16_t {
|
||||
/*
|
||||
See p284 of the Apple Guide to the Macintosh Family Hardware
|
||||
for documentation of the mapping below.
|
||||
*/
|
||||
BackTick = 0x65,
|
||||
k1 = 0x25, k2 = 0x27, k3 = 0x29, k4 = 0x2b, k5 = 0x2f,
|
||||
k6 = 0x2d, k7 = 0x35, k8 = 0x39, k9 = 0x33, k0 = 0x3b,
|
||||
|
||||
Hyphen = 0x37,
|
||||
Equals = 0x31,
|
||||
Backspace = 0x67,
|
||||
Tab = 0x61,
|
||||
|
||||
Q = 0x19, W = 0x1b, E = 0x1d, R = 0x1f, T = 0x23, Y = 0x21, U = 0x41, I = 0x45, O = 0x3f, P = 0x47,
|
||||
A = 0x01, S = 0x03, D = 0x05, F = 0x07, G = 0x0b, H = 0x09, J = 0x4d, K = 0x51, L = 0x4b,
|
||||
Z = 0x0d, X = 0x0f, C = 0x11, V = 0x13, B = 0x17, N = 0x5b, M = 0x5d,
|
||||
|
||||
OpenSquareBracket = 0x43,
|
||||
CloseSquareBracket = 0x3d,
|
||||
Semicolon = 0x53,
|
||||
Quote = 0x4f,
|
||||
Comma = 0x57,
|
||||
FullStop = 0x5f,
|
||||
ForwardSlash = 0x59,
|
||||
|
||||
CapsLock = 0x73,
|
||||
Shift = 0x71,
|
||||
Option = 0x75,
|
||||
Command = 0x6f,
|
||||
|
||||
Space = 0x63,
|
||||
Backslash = 0x55,
|
||||
Return = 0x49,
|
||||
|
||||
Left = KeypadMask | 0x0d,
|
||||
Right = KeypadMask | 0x05,
|
||||
Up = KeypadMask | 0x1b,
|
||||
Down = KeypadMask | 0x11,
|
||||
|
||||
KeyPadDelete = KeypadMask | 0x0f,
|
||||
KeyPadEquals = KeypadMask | 0x11,
|
||||
KeyPadSlash = KeypadMask | 0x1b,
|
||||
KeyPadAsterisk = KeypadMask | 0x05,
|
||||
KeyPadMinus = KeypadMask | 0x1d,
|
||||
KeyPadPlus = KeypadMask | 0x0d,
|
||||
KeyPadEnter = KeypadMask | 0x19,
|
||||
KeyPadDecimalPoint = KeypadMask | 0x03,
|
||||
|
||||
KeyPad9 = KeypadMask | 0x39,
|
||||
KeyPad8 = KeypadMask | 0x37,
|
||||
KeyPad7 = KeypadMask | 0x33,
|
||||
KeyPad6 = KeypadMask | 0x31,
|
||||
KeyPad5 = KeypadMask | 0x2f,
|
||||
KeyPad4 = KeypadMask | 0x2d,
|
||||
KeyPad3 = KeypadMask | 0x2b,
|
||||
KeyPad2 = KeypadMask | 0x29,
|
||||
KeyPad1 = KeypadMask | 0x27,
|
||||
KeyPad0 = KeypadMask | 0x25
|
||||
};
|
||||
|
||||
class Keyboard {
|
||||
public:
|
||||
void set_input(bool data) {
|
||||
@ -147,14 +214,16 @@ class Keyboard {
|
||||
|
||||
// Keys on the keypad are preceded by a $79 keycode; in the internal naming scheme
|
||||
// they are indicated by having bit 8 set. So add the $79 prefix if required.
|
||||
if(key & 0x100) {
|
||||
if(key & KeypadMask) {
|
||||
key_queue_.insert(key_queue_.begin(), 0x79);
|
||||
}
|
||||
key_queue_.insert(key_queue_.begin(), (is_pressed ? 0x00 : 0x80) | uint8_t(key));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Performs the pre-ADB Apple keyboard protocol command @c command, returning
|
||||
/// the proper result if the command were to terminate now. So, it treats inquiry
|
||||
/// and instant as the same command.
|
||||
int perform_command(int command) {
|
||||
switch(command) {
|
||||
case 0x10: // Inquiry.
|
||||
@ -180,22 +249,41 @@ class Keyboard {
|
||||
return 0x7b; // No key transition.
|
||||
}
|
||||
|
||||
/// Maintains the current operating mode — a record of what the
|
||||
/// keyboard is doing now.
|
||||
enum class Mode {
|
||||
/// The keyboard is waiting to begin a transaction.
|
||||
Waiting,
|
||||
/// The keyboard is currently clocking in a new command.
|
||||
AcceptingCommand,
|
||||
/// The keyboard is waiting for the computer to indicate that it is ready for a response.
|
||||
AwaitingEndOfCommand,
|
||||
/// The keyboard is in the process of performing the command it most-recently received.
|
||||
/// If the command was an 'inquiry', this state may persist for a non-neglibible period of time.
|
||||
PerformingCommand,
|
||||
/// The keyboard is currently shifting a response back to the computer.
|
||||
SendingResponse,
|
||||
PerformingCommand
|
||||
} mode_ = Mode::Waiting;
|
||||
|
||||
/// Holds a count of progress through the current @c Mode. Exact meaning depends on mode.
|
||||
int phase_ = 0;
|
||||
/// Holds the most-recently-received command; the command is shifted into here as it is received
|
||||
/// so this may not be valid prior to Mode::PerformingCommand.
|
||||
int command_ = 0;
|
||||
/// Populated during PerformingCommand as the response to the most-recently-received command, this
|
||||
/// is then shifted out to teh host computer. So it is guaranteed valid at the beginning of Mode::SendingResponse,
|
||||
/// but not afterwards.
|
||||
int response_ = 0;
|
||||
|
||||
/// The current state of the serial connection's data input.
|
||||
bool data_input_ = false;
|
||||
/// The current clock output from this keyboard.
|
||||
bool clock_output_ = false;
|
||||
|
||||
// TODO: improve this very, very simple implementation.
|
||||
/// Guards multithread access to key_queue_.
|
||||
std::mutex key_queue_mutex_;
|
||||
/// A FIFO queue for key events, in the form they'd be communicated to the Macintosh,
|
||||
/// with the newest events towards the front.
|
||||
std::vector<uint8_t> key_queue_;
|
||||
};
|
||||
|
||||
@ -203,89 +291,7 @@ class Keyboard {
|
||||
Provides a mapping from idiomatic PC keys to Macintosh keys.
|
||||
*/
|
||||
class KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) override {
|
||||
using Key = Inputs::Keyboard::Key;
|
||||
switch(key) {
|
||||
default: return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||
|
||||
/*
|
||||
See p284 of the Apple Guide to the Macintosh Family Hardware
|
||||
for documentation of the mapping below.
|
||||
*/
|
||||
|
||||
case Key::BackTick: return 0x65;
|
||||
case Key::k1: return 0x25;
|
||||
case Key::k2: return 0x27;
|
||||
case Key::k3: return 0x29;
|
||||
case Key::k4: return 0x2b;
|
||||
case Key::k5: return 0x2f;
|
||||
case Key::k6: return 0x2d;
|
||||
case Key::k7: return 0x35;
|
||||
case Key::k8: return 0x39;
|
||||
case Key::k9: return 0x33;
|
||||
case Key::k0: return 0x3b;
|
||||
case Key::Hyphen: return 0x37;
|
||||
case Key::Equals: return 0x31;
|
||||
case Key::BackSpace: return 0x67;
|
||||
|
||||
case Key::Tab: return 0x61;
|
||||
case Key::Q: return 0x19;
|
||||
case Key::W: return 0x1b;
|
||||
case Key::E: return 0x1d;
|
||||
case Key::R: return 0x1f;
|
||||
case Key::T: return 0x23;
|
||||
case Key::Y: return 0x21;
|
||||
case Key::U: return 0x41;
|
||||
case Key::I: return 0x45;
|
||||
case Key::O: return 0x3f;
|
||||
case Key::P: return 0x47;
|
||||
case Key::OpenSquareBracket: return 0x43;
|
||||
case Key::CloseSquareBracket: return 0x3d;
|
||||
|
||||
case Key::CapsLock: return 0x73;
|
||||
case Key::A: return 0x01;
|
||||
case Key::S: return 0x03;
|
||||
case Key::D: return 0x05;
|
||||
case Key::F: return 0x07;
|
||||
case Key::G: return 0x0b;
|
||||
case Key::H: return 0x09;
|
||||
case Key::J: return 0x4d;
|
||||
case Key::K: return 0x51;
|
||||
case Key::L: return 0x4b;
|
||||
case Key::Semicolon: return 0x53;
|
||||
case Key::Quote: return 0x4f;
|
||||
case Key::Enter: return 0x49;
|
||||
|
||||
case Key::LeftShift: return 0x71;
|
||||
case Key::Z: return 0x0d;
|
||||
case Key::X: return 0x0f;
|
||||
case Key::C: return 0x11;
|
||||
case Key::V: return 0x13;
|
||||
case Key::B: return 0x17;
|
||||
case Key::N: return 0x5b;
|
||||
case Key::M: return 0x5d;
|
||||
case Key::Comma: return 0x57;
|
||||
case Key::FullStop: return 0x5f;
|
||||
case Key::ForwardSlash: return 0x59;
|
||||
case Key::RightShift: return 0x71;
|
||||
|
||||
case Key::Left: return 0x100 | 0x0d;
|
||||
case Key::Right: return 0x100 | 0x05;
|
||||
case Key::Up: return 0x100 | 0x1b;
|
||||
case Key::Down: return 0x100 | 0x11;
|
||||
|
||||
case Key::LeftOption:
|
||||
case Key::RightOption: return 0x75;
|
||||
case Key::LeftMeta:
|
||||
case Key::RightMeta: return 0x6f;
|
||||
|
||||
case Key::Space: return 0x63;
|
||||
case Key::BackSlash: return 0x55;
|
||||
|
||||
/* TODO: the numeric keypad. */
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "RealTimeClock.hpp"
|
||||
#include "Video.hpp"
|
||||
|
||||
#include "../../../Activity/Source.hpp"
|
||||
#include "../../CRTMachine.hpp"
|
||||
#include "../../KeyboardMachine.hpp"
|
||||
#include "../../MediaTarget.hpp"
|
||||
@ -55,7 +56,8 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
public MouseMachine::Machine,
|
||||
public CPU::MC68000::BusHandler,
|
||||
public KeyboardMachine::MappedMachine,
|
||||
public Zilog::SCC::z8530::Delegate {
|
||||
public Zilog::SCC::z8530::Delegate,
|
||||
public Activity::Source {
|
||||
public:
|
||||
using Target = Analyser::Static::Macintosh::Target;
|
||||
|
||||
@ -460,6 +462,11 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Activity Source
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
iwm_.iwm.set_activity_observer(observer);
|
||||
}
|
||||
|
||||
private:
|
||||
void update_video() {
|
||||
video_.run_for(time_since_video_update_.flush<HalfCycles>());
|
||||
|
@ -34,7 +34,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||
BIND(OpenSquareBracket, KeyAt);
|
||||
BIND(CloseSquareBracket, KeyAsterisk);
|
||||
|
||||
BIND(BackSlash, KeyRestore);
|
||||
BIND(Backslash, KeyRestore);
|
||||
BIND(Hash, KeyUp);
|
||||
BIND(F10, KeyUp);
|
||||
|
||||
@ -59,7 +59,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||
|
||||
BIND(Enter, KeyReturn);
|
||||
BIND(Space, KeySpace);
|
||||
BIND(BackSpace, KeyDelete);
|
||||
BIND(Backspace, KeyDelete);
|
||||
|
||||
BIND(Escape, KeyRunStop);
|
||||
BIND(F1, KeyF1);
|
||||
|
@ -41,7 +41,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||
BIND(LeftShift, KeyShift); BIND(RightShift, KeyShift);
|
||||
|
||||
BIND(Hyphen, KeyMinus);
|
||||
BIND(Delete, KeyDelete); BIND(BackSpace, KeyDelete);
|
||||
BIND(Delete, KeyDelete); BIND(Backspace, KeyDelete);
|
||||
BIND(Enter, KeyReturn); BIND(KeyPadEnter, KeyReturn);
|
||||
|
||||
BIND(KeyPad0, Key0); BIND(KeyPad1, Key1); BIND(KeyPad2, Key2); BIND(KeyPad3, Key3); BIND(KeyPad4, Key4);
|
||||
|
@ -47,12 +47,12 @@ uint16_t MSX::KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||
BIND(FullStop, KeyFullStop);
|
||||
BIND(Comma, KeyComma);
|
||||
BIND(ForwardSlash, KeyForwardSlash);
|
||||
BIND(BackSlash, KeyBackSlash);
|
||||
BIND(Backslash, KeyBackSlash);
|
||||
BIND(BackTick, KeyGrave);
|
||||
|
||||
BIND(Enter, KeyEnter);
|
||||
BIND(Space, KeySpace);
|
||||
BIND(BackSpace, KeyBackspace);
|
||||
BIND(Backspace, KeyBackspace);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
@ -26,10 +26,10 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||
|
||||
BIND(Left, KeyLeft); BIND(Right, KeyRight); BIND(Up, KeyUp); BIND(Down, KeyDown);
|
||||
|
||||
BIND(Hyphen, KeyMinus); BIND(Equals, KeyEquals); BIND(BackSlash, KeyBackSlash);
|
||||
BIND(Hyphen, KeyMinus); BIND(Equals, KeyEquals); BIND(Backslash, KeyBackSlash);
|
||||
BIND(OpenSquareBracket, KeyOpenSquare); BIND(CloseSquareBracket, KeyCloseSquare);
|
||||
|
||||
BIND(BackSpace, KeyDelete); BIND(Delete, KeyDelete);
|
||||
BIND(Backspace, KeyDelete); BIND(Delete, KeyDelete);
|
||||
|
||||
BIND(Semicolon, KeySemiColon); BIND(Quote, KeyQuote);
|
||||
BIND(Comma, KeyComma); BIND(FullStop, KeyFullStop); BIND(ForwardSlash, KeyForwardSlash);
|
||||
|
@ -210,6 +210,8 @@
|
||||
4B622AE5222E0AD5008B59F2 /* DisplayMetrics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */; };
|
||||
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; };
|
||||
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
|
||||
4B65086022F4CF8D009C1100 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */; };
|
||||
4B65086122F4CFE0009C1100 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */; };
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
|
||||
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; };
|
||||
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
|
||||
@ -940,6 +942,7 @@
|
||||
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
|
||||
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
|
||||
4B643F3E1D77B88000D431D6 /* DocumentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentController.swift; sourceTree = "<group>"; };
|
||||
4B65085F22F4CF8D009C1100 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
|
||||
4B698D1A1FE768A100696C91 /* SampleSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SampleSource.hpp; sourceTree = "<group>"; };
|
||||
4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tape.cpp; sourceTree = "<group>"; };
|
||||
4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Tape.hpp; sourceTree = "<group>"; };
|
||||
@ -3182,6 +3185,7 @@
|
||||
children = (
|
||||
4B9378E222A199C600973513 /* Audio.cpp */,
|
||||
4BB4BFAC22A33DE50069048D /* DriveSpeedAccumulator.cpp */,
|
||||
4B65085F22F4CF8D009C1100 /* Keyboard.cpp */,
|
||||
4BCE0058227CFFCA000CA200 /* Macintosh.cpp */,
|
||||
4BCE005E227D39AB000CA200 /* Video.cpp */,
|
||||
4B9378E322A199C600973513 /* Audio.hpp */,
|
||||
@ -3900,6 +3904,7 @@
|
||||
4B055AA71FAE85EF0060FFFF /* SegmentParser.cpp in Sources */,
|
||||
4BB0A65E204500A900FB3688 /* StaticAnalyser.cpp in Sources */,
|
||||
4B055AC11FAE98DC0060FFFF /* MachineForTarget.cpp in Sources */,
|
||||
4B65086122F4CFE0009C1100 /* Keyboard.cpp in Sources */,
|
||||
4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */,
|
||||
4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */,
|
||||
4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */,
|
||||
@ -4170,6 +4175,7 @@
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
|
||||
4B4518841F75E91A00926311 /* UnformattedTrack.cpp in Sources */,
|
||||
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */,
|
||||
4B65086022F4CF8D009C1100 /* Keyboard.cpp in Sources */,
|
||||
4B894528201967B4007DE474 /* Disk.cpp in Sources */,
|
||||
4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */,
|
||||
4B89453A201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
|
@ -154,6 +154,11 @@ class MachineDocument:
|
||||
// a sheet mysteriously floating on its own. For now, use windowDidUpdate as a proxy to know that the window
|
||||
// is visible, though it's a little premature.
|
||||
func windowDidUpdate(_ notification: Notification) {
|
||||
// Grab the regular window title, if it's not already stored.
|
||||
if self.unadornedWindowTitle.count == 0 {
|
||||
self.unadornedWindowTitle = self.windowControllers[0].window!.title
|
||||
}
|
||||
|
||||
// If an interaction mode is not yet in effect, pick the proper one and display the relevant thing.
|
||||
if self.interactionMode == .notStarted {
|
||||
// If a full machine exists, just continue showing it.
|
||||
@ -613,7 +618,17 @@ class MachineDocument:
|
||||
try! pngData?.write(to: url)
|
||||
}
|
||||
|
||||
// MARK: Activity display.
|
||||
// MARK: - Window Title Updates.
|
||||
private var unadornedWindowTitle = ""
|
||||
func openGLViewDidCaptureMouse(_ view: CSOpenGLView) {
|
||||
self.windowControllers[0].window?.title = self.unadornedWindowTitle + " (press ⌘+command to release mouse)"
|
||||
}
|
||||
|
||||
func openGLViewDidReleaseMouse(_ view: CSOpenGLView) {
|
||||
self.windowControllers[0].window?.title = self.unadornedWindowTitle
|
||||
}
|
||||
|
||||
// MARK: - Activity Display.
|
||||
|
||||
private class LED {
|
||||
let levelIndicator: NSLevelIndicator
|
||||
|
@ -415,7 +415,7 @@ struct ActivityObserver: public Activity::Observer {
|
||||
BIND(VK_ANSI_Quote, Quote); BIND(VK_ANSI_Grave, BackTick);
|
||||
|
||||
BIND(VK_ANSI_Semicolon, Semicolon);
|
||||
BIND(VK_ANSI_Backslash, BackSlash); BIND(VK_ANSI_Slash, ForwardSlash);
|
||||
BIND(VK_ANSI_Backslash, Backslash); BIND(VK_ANSI_Slash, ForwardSlash);
|
||||
BIND(VK_ANSI_Comma, Comma); BIND(VK_ANSI_Period, FullStop);
|
||||
|
||||
BIND(VK_ANSI_KeypadDecimal, KeyPadDecimalPoint); BIND(VK_ANSI_KeypadEquals, KeyPadEquals);
|
||||
@ -424,7 +424,7 @@ struct ActivityObserver: public Activity::Observer {
|
||||
BIND(VK_ANSI_KeypadClear, KeyPadDelete); BIND(VK_ANSI_KeypadEnter, KeyPadEnter);
|
||||
|
||||
BIND(VK_Return, Enter); BIND(VK_Tab, Tab);
|
||||
BIND(VK_Space, Space); BIND(VK_Delete, BackSpace);
|
||||
BIND(VK_Space, Space); BIND(VK_Delete, Backspace);
|
||||
BIND(VK_Control, LeftControl); BIND(VK_Option, LeftOption);
|
||||
BIND(VK_Command, LeftMeta); BIND(VK_Shift, LeftShift);
|
||||
BIND(VK_RightControl, RightControl); BIND(VK_RightOption, RightOption);
|
||||
|
@ -37,6 +37,19 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
|
||||
*/
|
||||
- (void)openGLView:(nonnull CSOpenGLView *)view didReceiveFileAtURL:(nonnull NSURL *)URL;
|
||||
|
||||
/*!
|
||||
Announces 'capture' of the mouse — i.e. that the view is now preventing the mouse from exiting
|
||||
the window, in order to forward continuous mouse motion.
|
||||
@param view The view making the announcement.
|
||||
*/
|
||||
- (void)openGLViewDidCaptureMouse:(nonnull CSOpenGLView *)view;
|
||||
|
||||
/*!
|
||||
Announces that the mouse is no longer captured.
|
||||
@param view The view making the announcement.
|
||||
*/
|
||||
- (void)openGLViewDidReleaseMouse:(nonnull CSOpenGLView *)view;
|
||||
|
||||
@end
|
||||
|
||||
@protocol CSOpenGLViewResponderDelegate <NSObject>
|
||||
|
@ -223,6 +223,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
_mouseIsCaptured = NO;
|
||||
CGAssociateMouseAndMouseCursorPosition(true);
|
||||
[NSCursor unhide];
|
||||
[self.delegate openGLViewDidReleaseMouse:self];
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,6 +282,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
_mouseIsCaptured = YES;
|
||||
[NSCursor hide];
|
||||
CGAssociateMouseAndMouseCursorPosition(false);
|
||||
[self.delegate openGLViewDidCaptureMouse:self];
|
||||
|
||||
// Don't report the first click to the delegate; treat that as merely
|
||||
// an invitation to capture the cursor.
|
||||
|
@ -189,11 +189,11 @@ bool KeyboardKeyForSDLScancode(SDL_Keycode scancode, Inputs::Keyboard::Key &key)
|
||||
|
||||
BIND(PRINTSCREEN, PrintScreen) BIND(SCROLLLOCK, ScrollLock) BIND(PAUSE, Pause)
|
||||
|
||||
BIND(GRAVE, BackTick) BIND(MINUS, Hyphen) BIND(EQUALS, Equals) BIND(BACKSPACE, BackSpace)
|
||||
BIND(GRAVE, BackTick) BIND(MINUS, Hyphen) BIND(EQUALS, Equals) BIND(BACKSPACE, Backspace)
|
||||
|
||||
BIND(TAB, Tab)
|
||||
BIND(LEFTBRACKET, OpenSquareBracket) BIND(RIGHTBRACKET, CloseSquareBracket)
|
||||
BIND(BACKSLASH, BackSlash)
|
||||
BIND(BACKSLASH, Backslash)
|
||||
|
||||
BIND(CAPSLOCK, CapsLock) BIND(SEMICOLON, Semicolon)
|
||||
BIND(APOSTROPHE, Quote) BIND(RETURN, Enter)
|
||||
@ -302,6 +302,32 @@ std::string system_get(const char *command) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Maintains a communicative window title.
|
||||
*/
|
||||
class DynamicWindowTitler {
|
||||
public:
|
||||
DynamicWindowTitler(SDL_Window *window) : window_(window), file_name_(SDL_GetWindowTitle(window)) {}
|
||||
|
||||
std::string window_title() {
|
||||
if(!mouse_is_captured_) return file_name_;
|
||||
return file_name_ + " (press control+escape to release mouse)";
|
||||
}
|
||||
|
||||
void set_mouse_is_captured(bool is_captured) {
|
||||
mouse_is_captured_ = is_captured;
|
||||
update_window_title();
|
||||
}
|
||||
|
||||
private:
|
||||
void update_window_title() {
|
||||
SDL_SetWindowTitle(window_, window_title().c_str());
|
||||
}
|
||||
bool mouse_is_captured_ = false;
|
||||
SDL_Window *window_ = nullptr;
|
||||
const std::string file_name_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@ -351,7 +377,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// Determine the machine for the supplied file.
|
||||
Analyser::Static::TargetList targets = Analyser::Static::GetTargets(arguments.file_name);
|
||||
const auto targets = Analyser::Static::GetTargets(arguments.file_name);
|
||||
if(targets.empty()) {
|
||||
std::cerr << "Cannot open " << arguments.file_name << "; no target machine found" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
@ -459,6 +485,8 @@ int main(int argc, char *argv[]) {
|
||||
400, 300,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
|
||||
DynamicWindowTitler window_titler(window);
|
||||
|
||||
SDL_GLContext gl_context = nullptr;
|
||||
if(window) {
|
||||
gl_context = SDL_GL_CreateContext(window);
|
||||
@ -628,6 +656,7 @@ int main(int argc, char *argv[]) {
|
||||
// Use ctrl+escape to release the mouse (if captured).
|
||||
if(event.key.keysym.sym == SDLK_ESCAPE && (SDL_GetModState()&KMOD_CTRL)) {
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
window_titler.set_mouse_is_captured(false);
|
||||
}
|
||||
|
||||
// Capture ctrl+shift+d as a take-a-screenshot command.
|
||||
@ -734,6 +763,7 @@ int main(int argc, char *argv[]) {
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if(uses_mouse && !SDL_GetRelativeMouseMode()) {
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
window_titler.set_mouse_is_captured(true);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
|
Loading…
Reference in New Issue
Block a user