diff --git a/Components/DiskII/IWM.cpp b/Components/DiskII/IWM.cpp index 93a513511..d8accbc71 100644 --- a/Components/DiskII/IWM.cpp +++ b/Components/DiskII/IWM.cpp @@ -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); +} diff --git a/Components/DiskII/IWM.hpp b/Components/DiskII/IWM.hpp index dd49b2563..04c1d5739 100644 --- a/Components/DiskII/IWM.hpp +++ b/Components/DiskII/IWM.hpp @@ -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 @@ -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; diff --git a/Components/DiskII/MacintoshDoubleDensityDrive.cpp b/Components/DiskII/MacintoshDoubleDensityDrive.cpp index 9f5a04b32..a45221eb3 100644 --- a/Components/DiskII/MacintoshDoubleDensityDrive.cpp +++ b/Components/DiskII/MacintoshDoubleDensityDrive.cpp @@ -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) { diff --git a/Inputs/Keyboard.hpp b/Inputs/Keyboard.hpp index 7b2481dab..ee9fe7d72 100644 --- a/Inputs/Keyboard.hpp +++ b/Inputs/Keyboard.hpp @@ -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, diff --git a/Machines/AmstradCPC/Keyboard.cpp b/Machines/AmstradCPC/Keyboard.cpp index e756fb106..c21f624b3 100644 --- a/Machines/AmstradCPC/Keyboard.cpp +++ b/Machines/AmstradCPC/Keyboard.cpp @@ -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); diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index e39dec9da..25898655f 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -795,7 +795,7 @@ template 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; } } diff --git a/Machines/Apple/Macintosh/Keyboard.cpp b/Machines/Apple/Macintosh/Keyboard.cpp new file mode 100644 index 000000000..4be2dcffe --- /dev/null +++ b/Machines/Apple/Macintosh/Keyboard.cpp @@ -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 + } +} diff --git a/Machines/Apple/Macintosh/Keyboard.hpp b/Machines/Apple/Macintosh/Keyboard.hpp index 7bce67f8e..4489eb249 100644 --- a/Machines/Apple/Macintosh/Keyboard.hpp +++ b/Machines/Apple/Macintosh/Keyboard.hpp @@ -10,6 +10,7 @@ #define Apple_Macintosh_Keyboard_hpp #include "../../KeyboardMachine.hpp" +#include "../../../ClockReceiver/ClockReceiver.hpp" #include #include @@ -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 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; }; } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 0146d13a2..ad54d73b6 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -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 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 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()); diff --git a/Machines/Commodore/Vic-20/Keyboard.cpp b/Machines/Commodore/Vic-20/Keyboard.cpp index 30bda5b20..e868c9772 100644 --- a/Machines/Commodore/Vic-20/Keyboard.cpp +++ b/Machines/Commodore/Vic-20/Keyboard.cpp @@ -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); diff --git a/Machines/Electron/Keyboard.cpp b/Machines/Electron/Keyboard.cpp index 5830906b6..e7dd11f60 100644 --- a/Machines/Electron/Keyboard.cpp +++ b/Machines/Electron/Keyboard.cpp @@ -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); diff --git a/Machines/MSX/Keyboard.cpp b/Machines/MSX/Keyboard.cpp index 177e6f60e..bdce3bcd1 100644 --- a/Machines/MSX/Keyboard.cpp +++ b/Machines/MSX/Keyboard.cpp @@ -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; } diff --git a/Machines/Oric/Keyboard.cpp b/Machines/Oric/Keyboard.cpp index 58b25179c..1a5a192a4 100644 --- a/Machines/Oric/Keyboard.cpp +++ b/Machines/Oric/Keyboard.cpp @@ -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); diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index c5ea334a6..b6f9f5f5b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -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 = ""; }; 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = ""; }; 4B643F3E1D77B88000D431D6 /* DocumentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentController.swift; sourceTree = ""; }; + 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = ""; }; 4B698D1A1FE768A100696C91 /* SampleSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SampleSource.hpp; sourceTree = ""; }; 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tape.cpp; sourceTree = ""; }; 4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Tape.hpp; sourceTree = ""; }; @@ -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 */, diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 9e60e057a..c849095fb 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -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 diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 28e755a8e..9f9280e12 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -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); diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h index 4bf010a1e..8437bb999 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h @@ -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 diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 919fb03ca..697aa138c 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -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. diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index ffa356b30..02405664c 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -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: {