├── debian ├── compat ├── docs ├── source │ ├── format │ └── options ├── manpages ├── install ├── desktop │ └── appName.desktop ├── changelog ├── rules ├── control ├── copyright └── your-application.1 ├── docs ├── taskList │ ├── KnownBugs.md │ ├── FeatureWishlist.md │ ├── DesignChanges.md │ └── TODO.md ├── screenshots │ ├── basic.png │ ├── typing.png │ ├── altColors.png │ ├── controls.png │ ├── function.png │ ├── altCharSet.png │ ├── altControls.png │ └── helpScreen.png ├── templates │ ├── config │ │ ├── ExampleListener.cpp │ │ ├── example.json │ │ ├── ExampleListener.h │ │ ├── ExampleObject.cpp │ │ ├── ExampleObject.h │ │ ├── ExampleKeys.h │ │ └── ExampleFile.cpp │ ├── ModuleDoc.md │ └── TestTemplate.cpp ├── implementation │ ├── NewTests.md │ ├── NewResource.md │ ├── NewModules.md │ └── NewClasses.md ├── modules │ ├── Windows.md │ ├── Testing.md │ ├── Util.md │ ├── Output.md │ ├── Locale.md │ ├── Assets.md │ ├── Debug.md │ ├── Component.md │ ├── Theme.md │ └── Input.md ├── Libraries.md ├── Main.md ├── configuration │ ├── config.md │ └── keyBindings_alt.json ├── Implementation.md ├── BuildAndInstall.md ├── Configuration.md ├── StyleGuide.md ├── Documentation Checklist.md └── style │ └── Documentation.md ├── bitFont.png ├── bitFont.xcf ├── Source ├── Files │ ├── Config │ │ ├── Implementation │ │ │ ├── Config_AlertWindow.cpp │ │ │ ├── Config_AlertWindow.h │ │ │ └── Config_ListenerInterface.h │ │ ├── Config_DataKey.cpp │ │ ├── Config_MainKeys.h │ │ ├── Config_MainResource.cpp │ │ ├── Config_MainResource.h │ │ ├── Config_MainFile.cpp │ │ ├── Config_DataKey.h │ │ └── Config_MainFile.h │ └── Assets │ │ └── Assets_XDGDirectories.h ├── Framework │ ├── SharedResource │ │ ├── Implementation │ │ │ ├── SharedResource_ReferenceInterface.cpp │ │ │ ├── SharedResource_LockType.h │ │ │ ├── SharedResource_ReferenceInterface.h │ │ │ ├── SharedResource_LockedInstancePtr.cpp │ │ │ ├── SharedResource_Reference.h │ │ │ └── SharedResource_Reference.cpp │ │ ├── SharedResource_Resource.cpp │ │ └── SharedResource_LockedPtr.h │ ├── Locale │ │ ├── Locale.cpp │ │ ├── Locale.h │ │ └── Locale_TextUser.h │ ├── Util │ │ └── Util_Math.h │ └── Windows │ │ └── Windows_FocusControl.h ├── Development │ ├── Debug │ │ ├── Debug_Component.h │ │ ├── Debug_ScopeTimer.cpp │ │ ├── Debug_ScopeTimer.h │ │ ├── Debug_ScopeTimerRecords.h │ │ ├── Debug_AddressLog.h │ │ ├── Debug_AddressLog.cpp │ │ └── Debug_Component.cpp │ └── Testing │ │ ├── Testing_DelayUtils.cpp │ │ ├── Testing_Window.cpp │ │ ├── Testing_DelayUtils.h │ │ └── Testing_Window.h ├── GUI │ ├── Theme │ │ ├── Theme_LookAndFeel.cpp │ │ ├── Colour │ │ │ ├── Theme_Colour_Element.cpp │ │ │ ├── Theme_Colour_UICategory.h │ │ │ ├── Theme_Colour_JSONResource.h │ │ │ ├── Theme_Colour_JSONResource.cpp │ │ │ └── Theme_Colour_Element.h │ │ └── Theme_LookAndFeel.h │ ├── Text │ │ ├── CharSet │ │ │ ├── Text_CharSet_Type.h │ │ │ ├── Text_CharSet_JSONKeys.h │ │ │ └── Text_CharSet_ConfigFile.h │ │ ├── Text_CharTypes.h │ │ └── Text_BinaryFont.h │ ├── Output │ │ ├── Output_Modifiers.cpp │ │ ├── Output_Modifiers.h │ │ ├── Output_Sending.h │ │ ├── Output_Buffer.cpp │ │ └── Output_Buffer.h │ ├── Component │ │ ├── Component_CharsetDisplay.h │ │ ├── Component_HelpScreen.h │ │ ├── Component_ChordKeyDisplay.h │ │ ├── Component_InputView.h │ │ ├── Component_InputView.cpp │ │ ├── Component_ColourIds.h │ │ └── Component_ChordPreview.h │ └── Input │ │ └── Key │ │ ├── Input_Key_ConfigFile.cpp │ │ └── Input_Key_JSONResource.h ├── Main.cpp ├── MainWindow.h └── MainWindow.cpp ├── .gitmodules ├── .gitignore ├── clockworkpi_Keypad ├── UsbKeyboard.zip ├── USBCodes.h └── PinCodes.h ├── assets ├── configuration │ ├── config.json │ ├── colours.json │ └── keyBindings.json └── locale │ └── en_US.json ├── project-scripts ├── GdbLocal.sh ├── ColourIdElementManager.pl ├── PrefixFile.sh ├── ListFiles.pl ├── MarkdownGen.pl ├── TestCppGen.pl ├── TestClassGen.pl ├── DepClean.sh ├── OverflowSeek.sh ├── BuildLabel.sh ├── ColourID │ └── Menus │ │ ├── NamespaceListMenu.pm │ │ ├── MainMenu.pm │ │ └── KeyMenu.pm ├── Paths.pm ├── DeadLinkFinder.pl └── BitFontGen.pl ├── JuceLibraryCode ├── include_juce_core.mm ├── include_juce_core.cpp ├── include_juce_events.cpp ├── include_juce_events.mm ├── include_juce_graphics.cpp ├── include_juce_graphics.mm ├── include_juce_gui_basics.cpp ├── include_juce_gui_basics.mm ├── include_juce_data_structures.cpp ├── include_juce_data_structures.mm ├── ReadMe.txt ├── Makefile ├── JuceHeader.h └── BinaryData.h ├── pack-debian └── DEBIAN │ ├── postinst │ └── control ├── .clang-format ├── Tests └── Files │ ├── Config │ ├── Config_Test_Listener.cpp │ ├── Config_Test_ObjectData.cpp │ ├── Config_Test_Listener.h │ ├── Config_Test_ObjectData.h │ └── Config_Test_JSONKeys.h │ └── Assets │ └── Assets_XpmTest.cpp ├── makefiles ├── Testing.mk ├── Locale.mk ├── Util.mk ├── Windows.mk ├── Output.mk ├── Assets.mk ├── Debug.mk ├── Theme.mk ├── Input.mk ├── Text.mk ├── Component.mk ├── SharedResource.mk └── Config.mk └── README.md /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | docs/* 3 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/manpages: -------------------------------------------------------------------------------- 1 | debian/your-application.1 2 | -------------------------------------------------------------------------------- /docs/taskList/KnownBugs.md: -------------------------------------------------------------------------------- 1 | ## Known Issues 2 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | diff-ignore 2 | tar-ignore 3 | -------------------------------------------------------------------------------- /docs/taskList/FeatureWishlist.md: -------------------------------------------------------------------------------- 1 | # New Features to Implement 2 | -------------------------------------------------------------------------------- /bitFont.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/bitFont.png -------------------------------------------------------------------------------- /bitFont.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/bitFont.xcf -------------------------------------------------------------------------------- /Source/Files/Config/Implementation/Config_AlertWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "Config_AlertWindow.h" 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/JUCE"] 2 | path = deps/JUCE 3 | url = https://github.com/WeAreROLI/JUCE 4 | -------------------------------------------------------------------------------- /docs/screenshots/basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/basic.png -------------------------------------------------------------------------------- /docs/screenshots/typing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/typing.png -------------------------------------------------------------------------------- /docs/screenshots/altColors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/altColors.png -------------------------------------------------------------------------------- /docs/screenshots/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/controls.png -------------------------------------------------------------------------------- /docs/screenshots/function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/function.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | pack-debian/usr/ 3 | debian/.debhelper/* 4 | debian/debhelper-build-stamp 5 | assets/testing/* 6 | -------------------------------------------------------------------------------- /docs/screenshots/altCharSet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/altCharSet.png -------------------------------------------------------------------------------- /docs/screenshots/altControls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/altControls.png -------------------------------------------------------------------------------- /docs/screenshots/helpScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/docs/screenshots/helpScreen.png -------------------------------------------------------------------------------- /clockworkpi_Keypad/UsbKeyboard.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centuryglass/KeyChord/HEAD/clockworkpi_Keypad/UsbKeyboard.zip -------------------------------------------------------------------------------- /assets/configuration/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "minimized" : false, 3 | "snapToBottom" : true, 4 | "immediateMode" : true 5 | } 6 | -------------------------------------------------------------------------------- /docs/templates/config/ExampleListener.cpp: -------------------------------------------------------------------------------- 1 | #include "ExampleListener.h" 2 | #include "ExampleResource.h" 3 | 4 | ExampleListener::ExampleListener() { } 5 | -------------------------------------------------------------------------------- /debian/install: -------------------------------------------------------------------------------- 1 | build/Release/your-app-name /usr/bin 2 | debian/desktop/* /usr/share/applications 3 | assets/* /usr/share/your-app-name 4 | debian/icon/* /usr/share/icons/hicolor 5 | -------------------------------------------------------------------------------- /docs/taskList/DesignChanges.md: -------------------------------------------------------------------------------- 1 | # Project Design Plans 2 | This document tracks all changes to the way this project is designed and structured that are currently under consideration. 3 | -------------------------------------------------------------------------------- /debian/desktop/appName.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Name=Template Application 4 | Comment=Replace with your application's information! 5 | Categories=System; 6 | Exec= 7 | Icon= 8 | Type=Application 9 | -------------------------------------------------------------------------------- /project-scripts/GdbLocal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Launch your application with gdb, using local libraries compiled with debug 3 | # flags. 4 | APPNAME= 5 | env LD_LIBRARY_PATH=/home/$USER/.local/lib:$LD_LIBRARY_PATH gdb "$APPNAME" 6 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | JUCE-Template (0.0.1) unstable; urgency=low 2 | 3 | * Replace this entry with a similar one for each update to your application. 4 | 5 | -- Anthony Brown Tue, 14 May 2019 22:23:00 +0000 6 | -------------------------------------------------------------------------------- /docs/implementation/NewTests.md: -------------------------------------------------------------------------------- 1 | # Using Test Classes (WIP) 2 | When and where to create test classes. 3 | 4 | 1. Test class paths and naming conventions. 5 | 2. Using the Test template file. 6 | 3. Where to add test support classes. 7 | 4. Running application tests. 8 | 9 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_core.mm: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_core.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/Implementation/SharedResource_ReferenceInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "SharedResource_ReferenceInterface.h" 2 | 3 | // Gets the lock used to control this reference. 4 | juce::CriticalSection& SharedResource::ReferenceInterface::getLock() 5 | { 6 | return lock; 7 | } 8 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_events.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_events.mm: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /pack-debian/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Removing the default invisible cursor" 4 | sed -i.bak s/'awful.util.spawn_with_shell("xsetroot -cursor'/'-- awful.util.spawn_with_shell("xsetroot -cursor'/g /home/chip/.config/awesome/rc.lua 5 | echo "Please REBOOT to make the changes effective" 6 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_graphics.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_graphics.mm: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_gui_basics.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_gui_basics.mm: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | DPKG_EXPORT_BUILDFLAGS = 1 4 | include /usr/share/dpkg/default.mk 5 | 6 | %: 7 | dh $@ --parallel 8 | 9 | # Everything install by install file 10 | override_dh_auto_install: 11 | true 12 | 13 | override_dh_install: 14 | dh_install --exclude=Lato-Regular.ttf 15 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_data_structures.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /JuceLibraryCode/include_juce_data_structures.mm: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | IMPORTANT! This file is auto-generated each time you save your 4 | project - if you alter its contents, your changes may be overwritten! 5 | 6 | */ 7 | 8 | #include "AppConfig.h" 9 | #include 10 | -------------------------------------------------------------------------------- /docs/taskList/TODO.md: -------------------------------------------------------------------------------- 1 | # JUCE Template Project Task List 2 | Related Pages: 3 | - [Bug Tracking](KnownBugs.md): Known bugs to fix. 4 | - [Design Changes](DesignChanges.md): Planned improvements to project design. 5 | - [Feature Wishlist](FeatureWishlist.md): New features to implement. 6 | 7 | ## Testing priorities 8 | 9 | ## Misc. fixes and improvements 10 | -------------------------------------------------------------------------------- /docs/templates/config/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "example integer": 10, 3 | "example string" : "This is a config file implementation example.", 4 | "example bool" : true, 5 | "example double" : 0.01, 6 | "example array" : [1, 2, 3], 7 | "example object" : 8 | { 9 | "example object num" : 5, 10 | "example object bool" : false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pack-debian/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Source: your-project-name 2 | Version: 0.0.1 3 | Section: x11 4 | Priority: optional 5 | Maintainer: [Your Name] <[your email]> 6 | Homepage: https://github.com/centuryglass 7 | Package: your-project-name 8 | Architecture: your-arch 9 | Depends: ${shlibs:Depends}, ${misc:Depends} 10 | Description: Brief project description. 11 | Replace this with a sentence describing your project. 12 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: your-package-name 2 | Section: x11 3 | Priority: optional 4 | Maintainer: Anthony Brown 5 | Build-Depends: debhelper (>= 9) 6 | Standards-Version: 3.9.8 7 | Homepage: https://github.com/centuryglass 8 | 9 | Package: your-package-name 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends} 12 | Description: JUCE Project Template 13 | Replace with your project's description. 14 | -------------------------------------------------------------------------------- /Source/Development/Debug/Debug_Component.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Debug_Component.h 4 | * 5 | * @brief Provides tools for debugging juce::Component objects. 6 | */ 7 | 8 | #include "JuceHeader.h" 9 | 10 | namespace Debug 11 | { 12 | namespace Component 13 | { 14 | /** 15 | * @brief Prints the structure of the component tree to debug output. 16 | */ 17 | void trace(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/modules/Windows.md: -------------------------------------------------------------------------------- 1 | # Windows Module Documentation 2 | The Windows module creates, finds, tracks, and controls open windows. 3 | 4 | #### [Windows::XInterface](../../Source/Framework/Windows/Windows_XInterface.h) 5 | XInterface objects interact with the X Window System to find and manipulate windows. 6 | 7 | #### [Windows::FocusControl](../../Source/Framework/Windows/Windows_FocusControl.h) 8 | FocusControl send signals to focus any open window, then waits until that window is actually focused. 9 | -------------------------------------------------------------------------------- /docs/templates/config/ExampleListener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file ExampleListener.h 4 | * 5 | * @brief A listener that tracks updates to the example.json file. 6 | */ 7 | 8 | #include "Config_Listener.h" 9 | 10 | class ExampleResource; 11 | 12 | /** 13 | * @brief Receives updates when relevant values change within the example 14 | * configuration file. 15 | */ 16 | class ExampleListener : public Config::Listener 17 | { 18 | public: 19 | ExampleListener(); 20 | virtual ~ExampleListener() { } 21 | }; 22 | -------------------------------------------------------------------------------- /docs/implementation/NewResource.md: -------------------------------------------------------------------------------- 1 | # Adding Shared Application Resources (WIP) 2 | Brief description of the SharedResource module here. 3 | 4 | 1. Consider if it's appropriate to implement a feature using the SharedResource module. 5 | 2. Decide what kind of resource class to create. 6 | 7 | ### Using SharedResource::Resource 8 | 9 | ### Using SharedResource::Modular::Resource 10 | 11 | ### Using SharedResource::Thread::Resource 12 | 13 | ### Using SharedResource::Handler for Resource Access 14 | 15 | ### Using SharedResource::Handler as a Resource Listener 16 | -------------------------------------------------------------------------------- /Source/Development/Debug/Debug_ScopeTimer.cpp: -------------------------------------------------------------------------------- 1 | #include "Debug_ScopeTimer.h" 2 | #include "Debug_ScopeTimerRecords.h" 3 | 4 | 5 | // Starts the timer on construction. 6 | Debug::ScopeTimer::ScopeTimer(const juce::String scopeName) : 7 | scopeName(scopeName), 8 | threadID(juce::Thread::getCurrentThreadId()) 9 | { 10 | ScopeTimerRecords::addStartRecord(scopeName, threadID); 11 | } 12 | 13 | 14 | // Saves the total amount of time this object existed before it is destroyed. 15 | Debug::ScopeTimer::~ScopeTimer() 16 | { 17 | ScopeTimerRecords::addStopRecord(scopeName, threadID); 18 | } 19 | -------------------------------------------------------------------------------- /Source/Framework/Locale/Locale.cpp: -------------------------------------------------------------------------------- 1 | #include "Locale.h" 2 | #include "JuceHeader.h" 3 | 4 | // Default locale to use if no other is found: 5 | static const constexpr char* defaultLocale = "en_US"; 6 | 7 | // Get the name of the system locale. 8 | juce::String Locale::getLocaleName() 9 | { 10 | std::locale l(""); 11 | return juce::String(l.name()).initialSectionNotContaining("."); 12 | } 13 | 14 | 15 | // Gets a default locale to use when the system locale is undefined or 16 | // unsupported. 17 | juce::String Locale::getDefaultLocale() 18 | { 19 | return defaultLocale; 20 | } 21 | -------------------------------------------------------------------------------- /docs/templates/config/ExampleObject.cpp: -------------------------------------------------------------------------------- 1 | #include "ExampleObject.h" 2 | 3 | 4 | // Constructs the example object, permanently setting its stored data. 5 | ExampleObject::ExampleObject(const int exampleNum, const bool exampleBool) : 6 | exampleNum(exampleNum), exampleBool(exampleBool) { } 7 | 8 | 9 | // Gets the custom object's stored integer value. 10 | int ExampleObject::getExampleNum() const 11 | { 12 | return exampleNum; 13 | } 14 | 15 | 16 | // Gets the custom object's stored boolean value. 17 | bool ExampleObject::getExampleBool() const 18 | { 19 | return exampleBool; 20 | } 21 | -------------------------------------------------------------------------------- /Source/GUI/Theme/Theme_LookAndFeel.cpp: -------------------------------------------------------------------------------- 1 | #include "Theme_LookAndFeel.h" 2 | #include "Theme_Colour_JSONKeys.h" 3 | #include "Theme_Colour_ConfigFile.h" 4 | 5 | Theme::LookAndFeel::LookAndFeel() 6 | { 7 | using juce::Array; 8 | using juce::Identifier; 9 | Colour::ConfigFile colourConfig; 10 | const Array& colourIds = Colour::JSONKeys::getColourIds(); 11 | for (const int& id : colourIds) 12 | { 13 | setColour(id, colourConfig.getColour(id)); 14 | } 15 | DBG("Theme::LookAndFeel::LookAndFeel: Loaded " << colourIds.size() 16 | << " colour id values."); 17 | } 18 | -------------------------------------------------------------------------------- /Source/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "JuceHeader.h" 2 | #include "Application.h" 3 | 4 | // //The macro START_JUCE_APPLICATION(Application) 5 | // //expands to this code when building for Linux: 6 | 7 | // juce::JUCEApplicationBase* juce_CreateApplication(); 8 | // juce::JUCEApplicationBase* juce_CreateApplication() { 9 | // return new Application(); 10 | // } 11 | 12 | 13 | // extern "C" int main(int argc, char* argv[]) 14 | // { 15 | // juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; 16 | // return juce::JUCEApplicationBase::main( argc, (const char**) argv ); 17 | // } 18 | 19 | START_JUCE_APPLICATION(Application) 20 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Mozilla 3 | ColumnLimit: 100 4 | IndentWidth: 2 5 | ContinuationIndentWidth: 4 6 | Standard: Cpp11 7 | AllowShortFunctionsOnASingleLine: Empty 8 | AllowShortIfStatementsOnASingleLine: true 9 | AllowShortLoopsOnASingleLine: true 10 | ConstructorInitializerIndentWidth: 0 11 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 12 | PointerAlignment: Right 13 | AlwaysBreakTemplateDeclarations: true 14 | BreakConstructorInitializersBeforeComma: false 15 | MaxEmptyLinesToKeep: 2 16 | BreakBeforeBraces: Attach 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | KeepEmptyLinesAtTheStartOfBlocks: false 19 | ... 20 | -------------------------------------------------------------------------------- /Source/GUI/Text/CharSet/Text_CharSet_Type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Text_Charset_Type.h 4 | * 5 | * @brief Defines the types of available keyboard character sets. 6 | */ 7 | 8 | namespace Text 9 | { 10 | namespace CharSet 11 | { 12 | enum class Type 13 | { 14 | main, // main set, lowercase letters by default. 15 | alt, // alternate set, number values by default. 16 | special, // special character set 17 | modifier, // modifier keys 18 | }; 19 | static const constexpr int numCharacterSets = 4; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /project-scripts/ColourIdElementManager.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | ### ColourId Element Manager ################################################### 3 | # Provides options for automatically generating, updating, and managing the 4 | # JUCE ColourId element list and the list of configurable UI colour value keys. 5 | ################################################################################ 6 | 7 | use strict; 8 | use warnings; 9 | use lib './project-scripts/ColourID'; 10 | use IOUtils; 11 | use lib './project-scripts/ColourID/Menus'; 12 | use MainMenu; 13 | 14 | my $cache = new IDCache(); 15 | IOUtils::cacheCodeFiles($cache); 16 | MainMenu::openMenu($cache); 17 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/Implementation/SharedResource_LockType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file SharedResource_LockType.h 4 | * 5 | * @brief Describes the types of locks that can be applied to a Resource. 6 | */ 7 | 8 | namespace SharedResource { enum class LockType; } 9 | 10 | enum class SharedResource::LockType 11 | { 12 | // Allow multiple threads to read from the resource, but keep them 13 | // from writing to the resource until all read locks are released. 14 | read, 15 | // Allows a single thread to read from and write to the resource, 16 | // blocking all others until the write lock is released. 17 | write 18 | }; 19 | -------------------------------------------------------------------------------- /Source/Development/Testing/Testing_DelayUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "Testing_DelayUtils.h" 2 | 3 | // Runs the juce dispatch loop until an expected condition is true. 4 | bool Testing::DelayUtils::idleUntil 5 | (const std::function waitCondition, int testFrequencyMS, int timeoutMS) 6 | { 7 | juce::int64 startTime = juce::Time::currentTimeMillis(); 8 | while (juce::Time::currentTimeMillis() < (startTime + timeoutMS)) 9 | { 10 | if (waitCondition()) 11 | { 12 | return true; 13 | } 14 | juce::MessageManager::getInstance()->runDispatchLoopUntil 15 | (testFrequencyMS); 16 | } 17 | return false; 18 | } 19 | -------------------------------------------------------------------------------- /Source/Files/Config/Config_DataKey.cpp: -------------------------------------------------------------------------------- 1 | #include "Config_DataKey.h" 2 | 3 | 4 | // Initializes a DataKey, constructing its key from a C-string. 5 | Config::DataKey::DataKey(const char* keyString, const DataType dataType) : 6 | key(juce::Identifier(keyString)), dataType(dataType) { } 7 | 8 | 9 | // Initializes a DataKey, constructing its key from an existing Identifier. 10 | Config::DataKey::DataKey 11 | (const juce::Identifier& key, const DataType dataType) : 12 | key(key), dataType(dataType) { } 13 | 14 | 15 | // Allow the DataKey to be used as if it was just its key value. 16 | Config::DataKey::operator const juce::Identifier&() const 17 | { 18 | return key; 19 | } 20 | -------------------------------------------------------------------------------- /docs/modules/Testing.md: -------------------------------------------------------------------------------- 1 | # Testing Module Documentation 2 | The testing module provides tools used for building test classes. 3 | 4 | #### [Testing\::Window](../../Source/Development/Testing/Testing_Window.h) 5 | Window quickly creates and shows a window containing a single test component. 6 | 7 | #### [Testing\::StressTest](../../Source/Development/Testing/Testing_StressTest.h) 8 | StressTest objects test execution of many simultaneous random tests running on different threads. 9 | 10 | #### [Testing\::DelayUtils](../../Source/Development/Testing/Testing_DelayUtils.h) 11 | DelayUtils provides a function that allows tests to run the JUCE event loop until some condition is met. 12 | 13 | -------------------------------------------------------------------------------- /Tests/Files/Config/Config_Test_Listener.cpp: -------------------------------------------------------------------------------- 1 | #define TEST_RESOURCE_IMPLEMENTATION 2 | #include "Config_Test_Listener.h" 3 | #include "Config_Test_Resource.h" 4 | 5 | Config::Test::Listener::Listener() { } 6 | 7 | 8 | // Gets the last updated key this listener received. 9 | juce::String Config::Test::Listener::getLastUpdated() const 10 | { 11 | if (lastUpdated == nullptr) 12 | { 13 | return "nullptr"; 14 | } 15 | return lastUpdated->toString(); 16 | } 17 | 18 | 19 | // Saves the updated key when the configuration file is updated. 20 | void Config::Test::Listener::configValueChanged 21 | (const juce::Identifier& propertyKey) 22 | { 23 | lastUpdated = &propertyKey; 24 | } 25 | -------------------------------------------------------------------------------- /docs/Libraries.md: -------------------------------------------------------------------------------- 1 | # Project Libraries 2 | This page will hold documentation links for major libraries used by this project, along with brief descriptions of their role in this project. 3 | 4 | ## [JUCE](https://docs.juce.com/master/index.html) 5 | The JUCE Library is the framework this template is built upon. This project uses the JUCE Library's GUI system, event handling, thread handling, unit test system, and innumerable other features. 6 | 7 | ## [Xlib](https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html) 8 | The Xlib library provides tools for interacting with the X Server. This application uses it to focus/activate open windows, and to select the window where keyboard input will be sent. 9 | -------------------------------------------------------------------------------- /docs/modules/Util.md: -------------------------------------------------------------------------------- 1 | # Util Module Documentation 2 | The Util module provides miscellaneous support classes and functions used to build the application. 3 | 4 | #### [Util\::Math](../../Source/Framework/Util/Util_Math.h) 5 | The Util\::Math namespace is meant to provide miscellaneous mathematical functions. This currently only contains a template function for finding the median of three values. 6 | 7 | #### [Util\::ConditionChecker](../../Source/Framework/Util/Util_ConditionChecker.h) 8 | ConditionChecker objects handle tasks that require waiting for an event that will occur after an indeterminate delay or not at all. They periodically check for a condition to be true, then run a callback function once it is. 9 | -------------------------------------------------------------------------------- /Source/GUI/Text/Text_CharTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Text_CharTypes.h 4 | * 5 | * @brief Defines types used to handle binary character data. 6 | */ 7 | 8 | #include "JuceHeader.h" 9 | 10 | namespace Text 11 | { 12 | 13 | /** 14 | * @brief Either the value of a printable ISO 8859 character value, or one 15 | * of the special values assigned in Text::Values; 16 | */ 17 | typedef unsigned int CharValue; 18 | 19 | /** 20 | * @brief A string made up of CharValues. 21 | */ 22 | typedef juce::Array CharString; 23 | 24 | /** 25 | * @brief An array of CharString lines. 26 | */ 27 | typedef juce::Array CharLineArray; 28 | } 29 | -------------------------------------------------------------------------------- /docs/Main.md: -------------------------------------------------------------------------------- 1 | # JUCE Project Template Documentation 2 | 3 | ### [Code Modules](./Modules.md) 4 | 5 | An overview of the project's code structure, with links to documentation pages for each code module. 6 | 7 | ### [Configuration Files](./Configuration.md) 8 | 9 | Documents this project's configurable JSON files and the values they define. 10 | 11 | ### [Implementation Guides](./Implementation.md) 12 | 13 | Guides for extending and updating this project. 14 | 15 | ### [Project Style Guide](./StyleGuide.md) 16 | 17 | An overview of the preferred coding and documentation conventions used by this project. 18 | 19 | ### [Library References](./Libraries.md) 20 | 21 | Lists and explains the libraries directly required by this project. 22 | -------------------------------------------------------------------------------- /Source/GUI/Output/Output_Modifiers.cpp: -------------------------------------------------------------------------------- 1 | #include "Output_Modifiers.h" 2 | #include 3 | 4 | 5 | // Gets the string representation of any combination of modifier types. 6 | juce::String Output::Modifiers::getModString(const int modifierFlags) 7 | { 8 | using juce::String; 9 | const std::map modNames = 10 | { 11 | {(int) control, "control+" }, 12 | {(int) alt, "alt+" }, 13 | {(int) shift, "shift+" }, 14 | {(int) super, "super+" }, 15 | }; 16 | juce::String modString; 17 | for (const auto& iter : modNames) 18 | { 19 | if ((modifierFlags & iter.first) != 0) 20 | { 21 | modString += iter.second; 22 | } 23 | } 24 | return modString; 25 | } 26 | -------------------------------------------------------------------------------- /Source/GUI/Theme/Colour/Theme_Colour_Element.cpp: -------------------------------------------------------------------------------- 1 | #include "Theme_Colour_Element.h" 2 | 3 | // Constructs a colour element object for a JUCE ColourID value. 4 | Theme::Colour::Element::Element 5 | (const int colourId, const UICategory category) : 6 | colourId(colourId), category(category) { } 7 | 8 | 9 | // Creates an empty colour element object. 10 | Theme::Colour::Element::Element() : 11 | colourId(0), category(UICategory::none) { } 12 | 13 | 14 | // Gets this object's JUCE ColourId value. 15 | int Theme::Colour::Element::getColourId() const 16 | { 17 | return colourId; 18 | } 19 | 20 | 21 | // Gets this object's UI element category. 22 | Theme::Colour::UICategory Theme::Colour::Element::getColourCategory() const 23 | { 24 | return category; 25 | } 26 | -------------------------------------------------------------------------------- /Source/GUI/Theme/Colour/Theme_Colour_UICategory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Theme_Colour_UICategory.h 4 | * 5 | * @brief Defines broad categories of UI element types used to assign default 6 | * colour values. 7 | */ 8 | 9 | namespace Theme { namespace Colour { enum class UICategory; } } 10 | 11 | /** 12 | * @brief All JUCE ColourId values used by this program are assigned to one of 13 | * these categories. 14 | */ 15 | enum class Theme::Colour::UICategory 16 | { 17 | windowBackground = 0, 18 | widgetBackground, 19 | widgetOn, 20 | widgetOff, 21 | menuBackground, 22 | outline, 23 | focusedOutline, 24 | text, 25 | highlightedText, 26 | textField, 27 | highlightedTextField, 28 | none 29 | }; 30 | -------------------------------------------------------------------------------- /JuceLibraryCode/ReadMe.txt: -------------------------------------------------------------------------------- 1 | May 7 2019: 2 | This project no longer uses the Projucer, so the rest of this file no longer 3 | applies. 4 | 5 | #### 6 | Important Note!! 7 | ================ 8 | 9 | The purpose of this folder is to contain files that are auto-generated by the Projucer, 10 | and ALL files in this folder will be mercilessly DELETED and completely re-written whenever 11 | the Projucer saves your project. 12 | 13 | Therefore, it's a bad idea to make any manual changes to the files in here, or to 14 | put any of your own files in here if you don't want to lose them. (Of course you may choose 15 | to add the folder's contents to your version-control system so that you can re-merge your own 16 | modifications after the Projucer has saved its changes). 17 | -------------------------------------------------------------------------------- /docs/modules/Output.md: -------------------------------------------------------------------------------- 1 | # Output Module Documentation 2 | The Output module prepares outgoing text and key events, and sends them to another application's window. 3 | 4 | #### [Output\::Modifiers](../../Source/GUI/Output/Output_Modifiers.h) 5 | The Modifier namespace defines the modifier keys that may be held down while other keys are entered. 6 | 7 | #### [Output\::Buffer](../../Source/GUI/Output/Output_Buffer.h) 8 | The Buffer object stores the list of key events waiting to be sent to the target application window, along with any modifier keys that should be held down during those key events. 9 | 10 | #### [Output\::Sending](../../Source/GUI/Output/Output_Sending.h) 11 | The Sending namespace provides functions for sending text or key events to other application windows. 12 | -------------------------------------------------------------------------------- /docs/modules/Locale.md: -------------------------------------------------------------------------------- 1 | # Locale Module Documentation 2 | The Locale module attempts to provide localized display text for the system's current locale. Supported locales must provide all display text values in a JSON file named after the locale, placed in the locale subdirectory of the application's assets folder. 3 | 4 | #### [Locale](../../Source/Framework/Locale/Locale.h) 5 | Locale provides functions for selecting an appropriate localization file. 6 | 7 | #### [Locale\::TextUser](../../Source/Framework/Locale/Locale_TextUser.h) 8 | TextUser objects load a set of localized display text strings from the selected locale file. 9 | 10 | #### [Locale\::Time](../../Source/Framework/Locale/Locale_Time.h) 11 | Time is a TextUser subclass that generates localized text representing an amount of time that has passed. 12 | -------------------------------------------------------------------------------- /makefiles/Testing.mk: -------------------------------------------------------------------------------- 1 | ############################### Testing Module ################################# 2 | TEST_DIR = Source/Development/Testing 3 | 4 | TEST_PREFIX := Testing_ 5 | TEST_OBJ := $(JUCE_OBJDIR)/$(TEST_PREFIX) 6 | 7 | OBJECTS_TEST := \ 8 | $(TEST_OBJ)StressTest.o \ 9 | $(TEST_OBJ)Window.o \ 10 | $(TEST_OBJ)DelayUtils.o 11 | 12 | 13 | ifeq ($(BUILD_TESTS), 1) 14 | DEVELOPMENT_MODULES := $(DEVELOPMENT_MODULES) testing 15 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_TEST) 16 | endif 17 | 18 | testing : $(OBJECTS_TEST) 19 | @echo " Built Testing module" 20 | 21 | $(TEST_OBJ)StressTest.o: \ 22 | $(TEST_DIR)/$(TEST_PREFIX)StressTest.cpp 23 | $(TEST_OBJ)Window.o: \ 24 | $(TEST_DIR)/$(TEST_PREFIX)Window.cpp 25 | $(TEST_OBJ)DelayUtils.o: \ 26 | $(TEST_DIR)/$(TEST_PREFIX)DelayUtils.cpp 27 | -------------------------------------------------------------------------------- /Tests/Files/Config/Config_Test_ObjectData.cpp: -------------------------------------------------------------------------------- 1 | #include "Config_Test_ObjectData.h" 2 | 3 | 4 | // Constructs the test object, permanently setting its stored data. 5 | Config::Test::ObjectData::ObjectData(const int testNum, const bool testBool) : 6 | testNum(testNum), testBool(testBool) { } 7 | 8 | 9 | // Gets the custom object's stored integer value. 10 | int Config::Test::ObjectData::getTestNum() const 11 | { 12 | return testNum; 13 | } 14 | 15 | 16 | // Gets the custom object's stored boolean value. 17 | bool Config::Test::ObjectData::getTestBool() const 18 | { 19 | return testBool; 20 | } 21 | 22 | 23 | // Compares this object with another. 24 | bool Config::Test::ObjectData::operator== (const ObjectData& rhs) const 25 | { 26 | return (testNum == rhs.testNum) && (testBool == rhs.testBool); 27 | } 28 | -------------------------------------------------------------------------------- /Source/GUI/Theme/Theme_LookAndFeel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Theme_LookAndFeel.h 4 | * 5 | * @brief Sets the default appearance and behavior of all UI component objects. 6 | */ 7 | 8 | #include "JuceHeader.h" 9 | #include "Theme_Colour_ConfigFile.h" 10 | 11 | namespace Theme { class LookAndFeel; } 12 | 13 | /** 14 | * @brief Loads configurable colour values, so that Component classes can apply 15 | * them by ID using the setColour function. 16 | * 17 | * Directly interacting with this class should not be necessary, except when 18 | * the application calls LookAndFeel::setDefaultLookAndFeel() to set a 19 | * Theme::LookAndFeel object as the default. 20 | */ 21 | class Theme::LookAndFeel : public juce::LookAndFeel_V4 22 | { 23 | public: 24 | LookAndFeel(); 25 | 26 | virtual ~LookAndFeel() { } 27 | }; 28 | -------------------------------------------------------------------------------- /Source/Framework/Locale/Locale.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Locale.h 4 | * 5 | * @brief Gets the system's selected locale. 6 | */ 7 | 8 | namespace juce { class String; } 9 | 10 | namespace Locale 11 | { 12 | /** 13 | * @brief Gets the name of the system locale. 14 | * 15 | * @return The short locale name, used for naming locale text files and 16 | * identifying locale in .desktop files. 17 | */ 18 | juce::String getLocaleName(); 19 | 20 | /** 21 | * @brief Gets a default locale to use when the system locale is undefined 22 | * or unsupported. 23 | * 24 | * @return The American English locale name, to use by default if more 25 | * appropriate localization data is unavailable. 26 | */ 27 | juce::String getDefaultLocale(); 28 | } 29 | -------------------------------------------------------------------------------- /makefiles/Locale.mk: -------------------------------------------------------------------------------- 1 | ########################## Locale Module ###################################### 2 | LOCALE_DIR = Source/Framework/Locale 3 | 4 | LOCALE_PREFIX := Locale_ 5 | LOCALE_OBJ := $(JUCE_OBJDIR)/$(LOCALE_PREFIX) 6 | 7 | OBJECTS_LOCALE := \ 8 | $(LOCALE_OBJ)TextUser.o \ 9 | $(LOCALE_OBJ)Locale.o 10 | 11 | OBJECTS_LOCALE_TEST := 12 | 13 | ifeq ($(BUILD_TESTS), 1) 14 | OBJECTS_LOCALE := $(OBJECTS_LOCALE) $(OBJECTS_LOCALE_TEST) 15 | endif 16 | 17 | FRAMEWORK_MODULES := $(FRAMEWORK_MODULES) locale 18 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_LOCALE) 19 | 20 | locale : $(OBJECTS_LOCALE) 21 | @echo " Built Locale module" 22 | 23 | $(LOCALE_OBJ)Time.o: \ 24 | $(LOCALE_DIR)/$(LOCALE_PREFIX)Time.cpp 25 | $(LOCALE_OBJ)TextUser.o: \ 26 | $(LOCALE_DIR)/$(LOCALE_PREFIX)TextUser.cpp 27 | $(LOCALE_OBJ)Locale.o: \ 28 | $(LOCALE_DIR)/Locale.cpp 29 | -------------------------------------------------------------------------------- /project-scripts/PrefixFile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Recursively adds a prefix string to the name of each file within a directory. 3 | # Use: ./prefixFile.sh /path/to/directory "PrefixString" 4 | if [[ ! -d $1 ]]; then 5 | echo "$1 is not a valid directory." 6 | exit 0 7 | fi 8 | if [[ -z $2 ]]; then 9 | echo "Failed to provide a filename prefix string." 10 | exit 0 11 | fi 12 | 13 | rootDir=$1 14 | prefix=$2 15 | count=0 16 | 17 | function recursiveRename() 18 | { 19 | local searchDir=$1 20 | for filePath in $searchDir/*; do 21 | if [[ -d $filePath ]]; then 22 | recursiveRename $filePath 23 | else 24 | fileName=$(basename $filePath) 25 | mv $filePath "$searchDir/$prefix$fileName" 26 | ((count++)) 27 | fi 28 | done 29 | } 30 | recursiveRename $rootDir 31 | echo "Renamed $count files." 32 | -------------------------------------------------------------------------------- /Source/Files/Config/Config_MainKeys.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Config_MainKeys.h 4 | * 5 | * @brief Defines the keys used to load data from the main configuration file. 6 | */ 7 | 8 | #include "Config_DataKey.h" 9 | 10 | namespace Config 11 | { 12 | namespace MainKeys 13 | { 14 | // Whether the application should start in minimized mode: 15 | static const DataKey minimized("minimized", 16 | DataKey::DataType::boolType); 17 | // Whether the application should snap to the bottom edge of the screen 18 | // instead of the top: 19 | static const DataKey snapToBottom("snapToBottom", 20 | DataKey::DataType::boolType); 21 | // Whether the application should start in immediate text entry mode: 22 | static const DataKey immediateMode("immediateMode", 23 | DataKey::DataType::boolType); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /project-scripts/ListFiles.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Finds all .cpp and .h project files, and lists them alphabetically by name, 4 | # removing file extensions and duplicates. 5 | # 6 | use strict; 7 | use warnings; 8 | use File::Find; 9 | use File::Slurp; 10 | use lib './project-scripts'; 11 | use Paths; 12 | 13 | my $projectDir = Paths::PROJECT_DIR; 14 | my $sourceDir = "$projectDir/Source"; 15 | my $testDir = "$projectDir/Tests"; 16 | my %names; 17 | 18 | my ($dirName) = ($projectDir =~ /.*\/(.*?$)/s); 19 | 20 | sub findModules 21 | { 22 | my $name = $File::Find::name; 23 | if ($name =~ /^.+\.(h|cpp)$/) 24 | { 25 | $name =~ s/\..*//; 26 | $name =~ s/.*$dirName//g; 27 | 28 | $names{$name} = 1; 29 | } 30 | } 31 | find(\&findModules, $sourceDir); 32 | find(\&findModules, $testDir); 33 | 34 | my @names = sort( keys(%names)); 35 | 36 | foreach my $name (@names) 37 | { 38 | print("$name\n"); 39 | } 40 | -------------------------------------------------------------------------------- /makefiles/Util.mk: -------------------------------------------------------------------------------- 1 | ########################## Utility Module ###################################### 2 | UTIL_DIR = Source/Framework/Util 3 | UTIL_TEST_DIR = Tests/Framework/Util 4 | 5 | UTIL_PREFIX = Util_ 6 | UTIL_OBJ := $(JUCE_OBJDIR)/$(UTIL_PREFIX) 7 | 8 | OBJECTS_UTIL := \ 9 | $(UTIL_OBJ)ConditionChecker.o 10 | 11 | UTIL_TEST_PREFIX := $(UTIL_PREFIX)Test_ 12 | UTIL_TEST_OBJ := $(UTIL_OBJ)Test_ 13 | OBJECTS_UTIL_TEST := \ 14 | $(UTIL_TEST_OBJ)ConditionTest.o 15 | 16 | ifeq ($(BUILD_TESTS), 1) 17 | OBJECTS_UTIL := $(OBJECTS_UTIL) $(OBJECTS_UTIL_TEST) 18 | endif 19 | 20 | FRAMEWORK_MODULES := $(FRAMEWORK_MODULES) util 21 | 22 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_UTIL) 23 | 24 | util : $(OBJECTS_UTIL) 25 | @echo " Built Utility module" 26 | 27 | $(UTIL_OBJ)ConditionChecker.o : \ 28 | $(UTIL_DIR)/$(UTIL_PREFIX)ConditionChecker.cpp 29 | 30 | $(UTIL_TEST_OBJ)ConditionTest.o : \ 31 | $(UTIL_TEST_DIR)/$(UTIL_TEST_PREFIX)ConditionTest.cpp 32 | -------------------------------------------------------------------------------- /makefiles/Windows.mk: -------------------------------------------------------------------------------- 1 | ############################ Windows Module ################################## 2 | WINDOW_DIR = Source/Framework/Windows 3 | WINDOW_TEST_DIR = Tests/Framework/Windows 4 | 5 | WINDOW_PREFIX = Windows_ 6 | WINDOW_OBJ := $(JUCE_OBJDIR)/$(WINDOW_PREFIX) 7 | OBJECTS_WINDOW := \ 8 | $(WINDOW_OBJ)XInterface.o \ 9 | $(WINDOW_OBJ)FocusControl.o 10 | 11 | WINDOW_TEST_PREFIX := $(WINDOW_PREFIX)Test_ 12 | WINDOW_TEST_OBJ := $(WINDOW_OBJ)Test_ 13 | OBJECTS_WINDOW_TEST := 14 | 15 | ifeq ($(BUILD_TESTS), 1) 16 | OBJECTS_WINDOW := $(OBJECTS_WINDOW) $(OBJECTS_WINDOW_TEST) 17 | endif 18 | 19 | FRAMEWORK_MODULES := $(FRAMEWORK_MODULES) window 20 | 21 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_WINDOW) 22 | 23 | window : $(OBJECTS_WINDOW) 24 | @echo " Built Window module" 25 | 26 | $(WINDOW_OBJ)XInterface.o : \ 27 | $(WINDOW_DIR)/$(WINDOW_PREFIX)XInterface.cpp 28 | $(WINDOW_OBJ)FocusControl.o : \ 29 | $(WINDOW_DIR)/$(WINDOW_PREFIX)FocusControl.cpp 30 | -------------------------------------------------------------------------------- /Source/GUI/Output/Output_Modifiers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Output_Modifiers.h 4 | * 5 | * @brief Defines all modifier key types. 6 | */ 7 | 8 | #include "JuceHeader.h" 9 | 10 | namespace Output 11 | { 12 | namespace Modifiers 13 | { 14 | enum TypeFlag 15 | { 16 | control = 0b0001, 17 | shift = 0b0010, 18 | alt = 0b0100, 19 | super = 0b1000 20 | }; 21 | 22 | /** 23 | * @brief Gets the string representation of any combination of modifier 24 | * types. 25 | * 26 | * @param modifierFlags Any number of modifier values combined with the 27 | * binary OR operator. 28 | * 29 | * @return The string representation of the given modifier 30 | * set. 31 | */ 32 | juce::String getModString(const int modifierFlags); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Files/Config/Config_MainResource.cpp: -------------------------------------------------------------------------------- 1 | #define CONFIG_MAIN_IMPLEMENTATION 2 | #include "Config_MainResource.h" 3 | #include "Config_MainKeys.h" 4 | 5 | // SharedResource object instance key: 6 | const juce::Identifier Config::MainResource::resourceKey("Config_MainResource"); 7 | 8 | // Configuration file name: 9 | static const juce::String configFilename("config.json"); 10 | 11 | 12 | Config::MainResource::MainResource() : 13 | Config::FileResource(resourceKey, configFilename) 14 | { 15 | loadJSONData(); 16 | } 17 | 18 | Config::MainResource::~MainResource() { } 19 | 20 | 21 | // Gets the set of all basic(non-array, non-object) properties tracked by this 22 | // Resource. 23 | const std::vector& Config::MainResource::getConfigKeys() const 24 | { 25 | static const std::vector keyList = 26 | { 27 | MainKeys::minimized, 28 | MainKeys::snapToBottom, 29 | MainKeys::immediateMode 30 | }; 31 | return keyList; 32 | } 33 | -------------------------------------------------------------------------------- /docs/configuration/config.md: -------------------------------------------------------------------------------- 1 | 2 | # KeyChord General Configuration 3 | The [config.json](../../assets/configuration/config.json) file stores the state the application will be in when launched. All values currently defined here may be only be set to true or false. Changing the application state using bound keys will update these values, so that they always reflect the last state of the application. 4 | 5 | If any miscellaneous configuration values are added to Keychord later, they will be defined in config.json and documented on this page. 6 | Key | Description 7 | --------------- | --- 8 | "minimized" | Whether the KeyChord window starts in minimized mode, where the window is smaller and doesn't show chord previews. 9 | "snapToBottom" | Whether the KeyChord window should be placed on the bottom edge of the display or the top. 10 | "immediateMode" | Whether KeyChord should immediately send all typed chord values to the targeted window, or buffer them until the send key is pressed. 11 | -------------------------------------------------------------------------------- /docs/modules/Assets.md: -------------------------------------------------------------------------------- 1 | # Assets Module Documentation 2 | The Assets module locates and loads application asset files. Default asset files are stored in /usr/share/KeyChord, but custom asset files may be loaded from other directories. 3 | 4 | #### [Assets](../../Source/Files/Assets/Assets.h) 5 | Assets provides functions to find and loads application asset files, preferring matches found in the default application data directory. Assets provides functions to load generic file objects, juce\::Image objects from image files, or juce\::var objects from JSON files. 6 | 7 | #### [Assets\::XDGDirectories](../../Source/Files/Assets/Assets_XDGDirectories.h) 8 | XDGDirectories follows the XDG base directory specification to determine the most appropriate directories to use when locating or saving different file types. 9 | 10 | #### [Assets\::JSONFile](../../Source/Files/Assets/Assets_JSONFile.h) 11 | JSONFile objects read from and write to a single JSON data file. All file data access is type checked. 12 | 13 | 14 | -------------------------------------------------------------------------------- /Source/GUI/Theme/Colour/Theme_Colour_JSONResource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Theme_Colour_JSONResource.h 4 | * 5 | * @brief Reads and writes UI colours defined in the configurable colours.json 6 | * file. 7 | */ 8 | 9 | #include "Config_FileResource.h" 10 | #include "Config_Listener.h" 11 | 12 | namespace Theme { namespace Colour { class JSONResource; } } 13 | 14 | class Theme::Colour::JSONResource : public Config::FileResource 15 | { 16 | public: 17 | // SharedResource object key: 18 | static const juce::Identifier resourceKey; 19 | 20 | JSONResource(); 21 | 22 | virtual ~JSONResource() { } 23 | 24 | private: 25 | /** 26 | * @brief Gets the set of all basic(non-array, non-object) properties 27 | * tracked by this JSON resource. 28 | * 29 | * @return The keys to all variables tracked in this config file. 30 | */ 31 | virtual const std::vector& getConfigKeys() 32 | const final override; 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /docs/implementation/NewModules.md: -------------------------------------------------------------------------------- 1 | ## Module Implementation Guide 2 | Follow these steps when adding new modules to the project: 3 | 4 | 1. Figure out where the module fits. What module category does it belong in? Would it make more sense as a submodule of an existing module? 5 | 2. Choose a short, descriptive name for the new module. 6 | 3. Create module directories in `[ProjectDir]/Source` and `[ProjectDir]/Tests`. 7 | 4. Create a makefile for the module at `[ProjectDir]/makefiles/[moduleName].mk`,using the [module makefile template](../templates/ModuleMakefile.mk) as a guide. 8 | 5. Create a documentation file for the module at `[ProjectDir]/docs/modules/[ModuleName].md`, using the [module documentation template](../templates/ModuleDoc.md) as a guide. 9 | 6. Add a brief description of the module in the [main module documentation](../Modules.md) under the appropriate category, linking to the module's guide document. 10 | 7. Add classes/enums/etc. to the module following the [new module class guide](./NewClasses.md). 11 | -------------------------------------------------------------------------------- /Source/Files/Config/Config_MainResource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef CONFIG_MAIN_IMPLEMENTATION 3 | #error Config_MainResource.h included outside of implementation files! 4 | #endif 5 | /** 6 | * @file Config_MainResource.h 7 | * 8 | * @brief Reads from and writes to the main configuration file. 9 | */ 10 | 11 | #include "Config_FileResource.h" 12 | #include "JuceHeader.h" 13 | 14 | namespace Config { class MainResource; } 15 | 16 | class Config::MainResource : public Config::FileResource 17 | { 18 | public: 19 | // SharedResource object instance key: 20 | static const juce::Identifier resourceKey; 21 | 22 | MainResource(); 23 | 24 | virtual ~MainResource(); 25 | 26 | private: 27 | /** 28 | * @brief Gets the set of all basic(non-array, non-object) properties 29 | * tracked by this Resource. 30 | * 31 | * @return All specific string values stored by this resouve 32 | */ 33 | const std::vector& getConfigKeys() const final override; 34 | }; 35 | -------------------------------------------------------------------------------- /makefiles/Output.mk: -------------------------------------------------------------------------------- 1 | ############################ Output Module #################################### 2 | OUTPUT_DIR = Source/GUI/Output 3 | OUTPUT_TEST_DIR = Tests/GUI/Output 4 | 5 | OUTPUT_PREFIX = Output_ 6 | OUTPUT_OBJ := $(JUCE_OBJDIR)/$(OUTPUT_PREFIX) 7 | 8 | OBJECTS_OUTPUT := \ 9 | $(OUTPUT_OBJ)Buffer.o \ 10 | $(OUTPUT_OBJ)Modifiers.o \ 11 | $(OUTPUT_OBJ)Sending.o 12 | 13 | OUTPUT_TEST_PREFIX := $(OUTPUT_PREFIX)Test_ 14 | OUTPUT_TEST_OBJ := $(OUTPUT_OBJ)Test_ 15 | OBJECTS_OUTPUT_TEST := 16 | 17 | ifeq ($(BUILD_TESTS), 1) 18 | OBJECTS_OUTPUT := $(OBJECTS_OUTPUT) $(OBJECTS_OUTPUT_TEST) 19 | endif 20 | 21 | output : $(OBJECTS_OUTPUT) 22 | @echo " Built Output module" 23 | 24 | GUI_MODULES := $(GUI_MODULES) output 25 | 26 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_OUTPUT) 27 | 28 | $(OUTPUT_OBJ)Buffer.o: \ 29 | $(OUTPUT_DIR)/$(OUTPUT_PREFIX)Buffer.cpp 30 | $(OUTPUT_OBJ)Modifiers.o: \ 31 | $(OUTPUT_DIR)/$(OUTPUT_PREFIX)Modifiers.cpp 32 | $(OUTPUT_OBJ)Sending.o: \ 33 | $(OUTPUT_DIR)/$(OUTPUT_PREFIX)Sending.cpp 34 | -------------------------------------------------------------------------------- /JuceLibraryCode/Makefile: -------------------------------------------------------------------------------- 1 | ########################## Juce Library Modules ################################ 2 | JUCE_PREFIX := $(JUCE_OBJDIR)/juce_ 3 | JUCE_ROOT = JuceLibraryCode 4 | 5 | OBJECTS_JUCE := \ 6 | $(JUCE_PREFIX)core.o \ 7 | $(JUCE_PREFIX)data_structures.o \ 8 | $(JUCE_PREFIX)events.o \ 9 | $(JUCE_PREFIX)graphics.o \ 10 | $(JUCE_PREFIX)gui_basics.o \ 11 | $(JUCE_PREFIX)BinaryData.o 12 | 13 | MODULES := juce $(MODULES) 14 | 15 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_JUCE) 16 | 17 | juce : $(OBJECTS_JUCE) 18 | @echo "Built JUCE library modules" 19 | 20 | $(JUCE_PREFIX)BinaryData.o: \ 21 | $(JUCE_ROOT)/BinaryData.cpp 22 | $(JUCE_PREFIX)core.o: \ 23 | $(JUCE_ROOT)/include_juce_core.cpp 24 | $(JUCE_PREFIX)data_structures.o: \ 25 | $(JUCE_ROOT)/include_juce_data_structures.cpp 26 | $(JUCE_PREFIX)events.o: \ 27 | $(JUCE_ROOT)/include_juce_events.cpp 28 | $(JUCE_PREFIX)graphics.o: \ 29 | $(JUCE_ROOT)/include_juce_graphics.cpp 30 | $(JUCE_PREFIX)gui_basics.o: \ 31 | $(JUCE_ROOT)/include_juce_gui_basics.cpp 32 | -------------------------------------------------------------------------------- /Source/Development/Testing/Testing_Window.cpp: -------------------------------------------------------------------------------- 1 | #include "Testing_Window.h" 2 | #include "JuceHeader.h" 3 | 4 | // Creates and shows a window holding a test component. 5 | Testing::Window::Window(const juce::String testName, 6 | juce::Component* const testComponent, 7 | const int xPos, 8 | const int yPos, 9 | const int width, 10 | const int height) : 11 | juce::DocumentWindow(testName, juce::Colours::white, 12 | juce::DocumentWindow::allButtons) 13 | { 14 | setBounds(xPos, yPos, width, height); 15 | setLookAndFeel(&juce::LookAndFeel::getDefaultLookAndFeel()); 16 | setUsingNativeTitleBar(true); 17 | setResizable(true, false); 18 | setVisible(true); 19 | if (testComponent != nullptr) 20 | { 21 | if (testComponent->getBounds().isEmpty()) 22 | { 23 | testComponent->setBounds(0, 0, width, height); 24 | } 25 | testComponent->setVisible(true); 26 | DocumentWindow::setContentOwned(testComponent, false); 27 | } 28 | addToDesktop(); 29 | } 30 | -------------------------------------------------------------------------------- /docs/templates/ModuleDoc.md: -------------------------------------------------------------------------------- 1 | # Example Module Documentation 2 | Describe the intent and purpose of your module here. This should be the main place used to document the module as a whole. If the module is relatively straightforward, a few sentences here is enough, but feel free to go into greater depth if necessary. 3 | 4 | #### [Example\::Object](../../Source/ModuleCategory/Example/Example_Object.h) 5 | Provide a brief description of the Example::Object class/namespace/enum/etc. This can be directly copied from the @brief tag in the header file's documentation block. Include an entry like this for each header file in the module. Save more in-depth documentation for inside the actual header file. 6 | 7 | ## Example Submodule 8 | Describe the intent and purpose of this inner category module within the main module. This should be the main place the submodule is documented, and may be as short or as long as is necessary. Entries for header files or inner submodules within this submodule should be placed below this section. All submodules in the module should be documented with a section like this. 9 | -------------------------------------------------------------------------------- /docs/Implementation.md: -------------------------------------------------------------------------------- 1 | # Implementation Guides 2 | 3 | #### [Creating Application Modules](./implementation/NewModules.md): 4 | How to add and document new project code modules. 5 | 6 | #### [Creating Module Classes](./implementation/NewClasses.md): 7 | How to add new classes to application modules. 8 | 9 | #### [Creating Test Classes](./implementation/NewTests.md): 10 | How to extend juce::UnitTest to create application test classes. 11 | 12 | #### [Declaring Component ColourId values](./implementation/AddColourIds.md): 13 | How to add configurable colours to UI component classes. 14 | 15 | #### [Creating Classes with Localized Text](./implementation/Localization.md): 16 | How to extend Locale::TextUser to use localized display text. 17 | 18 | #### [Adding Configuration Files](./implementation/NewConfig.md): 19 | How to add shared application configuration files using the Config module. 20 | 21 | #### [Creating SharedResource Classes (Incomplete)](./implementation/NewResource.md): 22 | How to use the SharedResource module to create and use access-controlled, reference counted singleton classes. 23 | -------------------------------------------------------------------------------- /docs/modules/Debug.md: -------------------------------------------------------------------------------- 1 | # Debug Module Documentation 2 | The Debug module provides tools for testing application performance and locating errors. 3 | 4 | #### [Debug\::ScopeTimer](../../Source/Development/Debug/Debug_ScopeTimer.h) 5 | ScopeTimer saves the time of its creation and the time it goes out of scope. This allows scope execution times to be recorded by declaring a ScopeTimer at the top of the scope. 6 | 7 | #### [Debug\::ScopeTimerRecords](../../Source/Development/Debug/Debug_ScopeTimerRecords.h) 8 | ScopeTimerRecords stores the records saved by all ScopeTimer objects, and prints them out when the application finishes executing. 9 | 10 | #### [Debug\::Component](../../Source/Development/Debug/Debug_Component.h) 11 | The Component namespace provides functions for inspecting and debugging juce\::Component objects. Currently, it includes a single function to print the tree of all visible components. 12 | 13 | #### [Debug\::AddressLog](../../Source/Development/Debug/Debug_AddressLog.h) 14 | The AddressLog keeps and prints logs of events associated with particular memory addresses. 15 | 16 | 17 | -------------------------------------------------------------------------------- /makefiles/Assets.mk: -------------------------------------------------------------------------------- 1 | ############################ Assets Module #################################### 2 | ASSETS_DIR = Source/Files/Assets 3 | ASSETS_TEST_DIR = Tests/Files/Assets 4 | 5 | ASSETS_PREFIX = Assets_ 6 | ASSETS_OBJ := $(JUCE_OBJDIR)/$(ASSETS_PREFIX) 7 | 8 | OBJECTS_ASSETS := \ 9 | $(ASSETS_OBJ)Assets.o \ 10 | $(ASSETS_OBJ)JSONFile.o \ 11 | $(ASSETS_OBJ)XDGDirectories.o 12 | 13 | ASSETS_TEST_PREFIX := $(ASSETS_PREFIX)Test_ 14 | ASSETS_TEST_OBJ := $(ASSETS_OBJ)Test_ 15 | 16 | ifeq ($(BUILD_TESTS), 1) 17 | OBJECTS_ASSETS := $(OBJECTS_ASSETS) $(OBJECTS_ASSETS_TEST) 18 | endif 19 | 20 | FILE_MODULES := $(FILE_MODULES) assets 21 | 22 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_ASSETS) 23 | 24 | assets : $(OBJECTS_ASSETS) 25 | @echo " Built Assets module" 26 | 27 | $(ASSETS_OBJ)Assets.o : \ 28 | $(ASSETS_DIR)/Assets.cpp 29 | $(ASSETS_OBJ)JSONFile.o : \ 30 | $(ASSETS_DIR)/$(ASSETS_PREFIX)JSONFile.cpp 31 | $(ASSETS_OBJ)XDGDirectories.o : \ 32 | $(ASSETS_DIR)/$(ASSETS_PREFIX)XDGDirectories.cpp 33 | $(ASSETS_OBJ)XPMLoader.o : \ 34 | $(ASSETS_DIR)/$(ASSETS_PREFIX)XPMLoader.cpp 35 | 36 | -------------------------------------------------------------------------------- /Source/Development/Debug/Debug_ScopeTimer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Debug_ScopeTimer.h 4 | * 5 | * @brief Measures execution time of some scope within this program. 6 | */ 7 | 8 | #include "JuceHeader.h" 9 | 10 | namespace Debug { class ScopeTimer; } 11 | 12 | /** 13 | * @brief Measures execution time by logging the time between its creation and 14 | * destruction. 15 | */ 16 | class Debug::ScopeTimer 17 | { 18 | public: 19 | /** 20 | * @brief Starts the timer on construction. 21 | * 22 | * @param scopeName A name identifying the portion of the program the 23 | * timer is tracking. 24 | */ 25 | ScopeTimer(const juce::String scopeName); 26 | 27 | /** 28 | * @brief Saves the total amount of time this object existed before it is 29 | * destroyed. 30 | */ 31 | virtual ~ScopeTimer(); 32 | 33 | private: 34 | // The timer name saved on construction: 35 | const juce::String scopeName; 36 | 37 | // The ID of the thread where this timer was created: 38 | const juce::Thread::ThreadID threadID; 39 | }; 40 | -------------------------------------------------------------------------------- /project-scripts/MarkdownGen.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Generates markdown files with links to all header files within a directory. 4 | # 5 | use strict; 6 | use warnings; 7 | use File::Find; 8 | use File::Slurp; 9 | use lib './project-scripts'; 10 | use Paths; 11 | 12 | my $projectDir = Paths::PROJECT_DIR; 13 | 14 | if ( (scalar @ARGV) < 2) 15 | { 16 | die("Usage: ./MarkdownGen.pl \"Path/To/Module/Dir\"" 17 | ." \"MarkdownOutput.md\"\n"); 18 | } 19 | my $moduleDir = $ARGV[0]; 20 | my $markdownFile = $ARGV[1]; 21 | my $moduleName = substr($moduleDir, rindex($moduleDir, '/') + 1); 22 | 23 | my $markdownText = "# $moduleName Module Documentation\n\n"; 24 | 25 | sub findFiles 26 | { 27 | my $fullPath = $File::Find::name; 28 | if ($fullPath =~ /^.+\.h$/) 29 | { 30 | my $relativePath = "../../".$fullPath; 31 | my $name = $_; 32 | $name = substr($name, 0, length($name) - 2); 33 | $name =~ s/_/::/g; 34 | $markdownText = $markdownText."#### [$name]($relativePath)\n\n"; 35 | } 36 | } 37 | find(\&findFiles, $moduleDir); 38 | write_file($markdownFile, $markdownText); 39 | -------------------------------------------------------------------------------- /project-scripts/TestCppGen.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Make .cpp test files for each header file in the project within a test 4 | # directory to test the CppGen.pl script. 5 | # 6 | use strict; 7 | use warnings; 8 | use File::Find; 9 | use File::Slurp; 10 | use lib './project-scripts'; 11 | use Paths; 12 | 13 | my $projectDir = Paths::PROJECT_DIR; 14 | my $sourceDir = "$projectDir/Source"; 15 | my $testDir = "$projectDir/project-scripts/cppTest"; 16 | 17 | my @commands; 18 | 19 | sub findModules 20 | { 21 | my $header = substr($File::Find::name, length($projectDir) + 1); 22 | if ($header =~ /^.+\.h$/) 23 | { 24 | my $cppFile = substr($File::Find::name, length($sourceDir)); 25 | $cppFile =~ s/\..*/.cpp/; 26 | $cppFile =~ s/\//-/g; 27 | $cppFile = $testDir.'/'.$cppFile; 28 | my $command = "./project-scripts/MakeCpp.pl $header $cppFile"; 29 | push(@commands, $command); 30 | } 31 | } 32 | find(\&findModules, $sourceDir); 33 | 34 | system("rm -r $testDir"); 35 | system("mkdir $testDir"); 36 | foreach my $command (@commands) 37 | { 38 | system($command); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/Implementation/SharedResource_ReferenceInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file SharedResource_ReferenceInterface.h 4 | * 5 | * @brief The interface shared by Reference and Instance objects to control 6 | * Reference access. 7 | */ 8 | #include "JuceHeader.h" 9 | 10 | namespace SharedResource { class ReferenceInterface; } 11 | 12 | /** 13 | * @brief The interface extended by the Reference class and used by Instance 14 | * objects to manage their connected Reference objects. 15 | */ 16 | class SharedResource::ReferenceInterface 17 | { 18 | public: 19 | virtual ~ReferenceInterface() { } 20 | 21 | protected: 22 | // Only the Reference and its Instance may access the lock. 23 | friend class Instance; 24 | 25 | /** 26 | * @brief Gets the lock used to control this Reference. 27 | * 28 | * This lock prevents the Reference from being destroyed while its 29 | * resource Instance is accessing it. 30 | * 31 | * @return The reference lock. 32 | */ 33 | juce::CriticalSection& getLock(); 34 | 35 | private: 36 | juce::CriticalSection lock; 37 | }; 38 | -------------------------------------------------------------------------------- /JuceLibraryCode/JuceHeader.h: -------------------------------------------------------------------------------- 1 | // This is the header file that your files should include in order to get all 2 | // the JUCE library headers. You should avoid including the JUCE headers 3 | // directly in your own source files, because that wouldn't pick up the correct 4 | // configuration options for your app. 5 | 6 | #pragma once 7 | 8 | #include "AppConfig.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "BinaryData.h" 17 | 18 | #if ! DONT_SET_USING_JUCE_NAMESPACE 19 | // Currently disabled by setting DONT_SET_USING_JUCE_NAMESPACE in build. 20 | // Otherwise juce::Window conflicts with X11 Window values. Besides, explicit 21 | // namespaces 22 | using namespace juce; 23 | #endif 24 | 25 | #if ! JUCE_DONT_DECLARE_PROJECTINFO 26 | namespace ProjectInfo 27 | { 28 | const char* const projectName = "KeyChord"; 29 | const char* const versionString = "0.0.1"; 30 | const int versionNumber = 0x1; 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /makefiles/Debug.mk: -------------------------------------------------------------------------------- 1 | ############################ Debug Module #################################### 2 | DEBUG_DIR = Source/Development/Debug 3 | DEBUG_TEST_DIR = Tests/Development/Debug 4 | 5 | DEBUG_PREFIX = Debug_ 6 | DEBUG_OBJ := $(JUCE_OBJDIR)/$(DEBUG_PREFIX) 7 | OBJECTS_DEBUG := \ 8 | $(DEBUG_OBJ)AddressLog.o \ 9 | $(DEBUG_OBJ)Component.o \ 10 | $(DEBUG_OBJ)ScopeTimer.o \ 11 | $(DEBUG_OBJ)ScopeTimerRecords.o 12 | 13 | DEBUG_DEBUG_PREFIX := $(DEBUG_PREFIX)Test_ 14 | DEBUG_DEBUG_OBJ := $(DEBUG_OBJ)Test_ 15 | OBJECTS_DEBUG_TEST := 16 | 17 | ifeq ($(BUILD_TESTS), 1) 18 | OBJECTS_DEBUG := $(OBJECTS_DEBUG) $(OBJECTS_DEBUG_TEST) 19 | endif 20 | 21 | DEVELOPMENT_MODULES := $(DEVELOPMENT_MODULES) debugging 22 | 23 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_DEBUG) 24 | 25 | debugging : $(OBJECTS_DEBUG) 26 | @echo " Built Debug module" 27 | 28 | $(DEBUG_OBJ)AddressLog.o: \ 29 | $(DEBUG_DIR)/$(DEBUG_PREFIX)AddressLog.cpp 30 | $(DEBUG_OBJ)Component.o: \ 31 | $(DEBUG_DIR)/$(DEBUG_PREFIX)Component.cpp 32 | $(DEBUG_OBJ)ScopeTimer.o: \ 33 | $(DEBUG_DIR)/$(DEBUG_PREFIX)ScopeTimer.cpp 34 | $(DEBUG_OBJ)ScopeTimerRecords.o: \ 35 | $(DEBUG_DIR)/$(DEBUG_PREFIX)ScopeTimerRecords.cpp 36 | -------------------------------------------------------------------------------- /Tests/Files/Config/Config_Test_Listener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Config_Test_Listener.h 4 | * 5 | * @brief A listener that tracks updates to the configTest.json file. 6 | */ 7 | 8 | #include "Config_Listener.h" 9 | 10 | namespace Config 11 | { 12 | namespace Test 13 | { 14 | class Resource; 15 | class Listener; 16 | } 17 | } 18 | 19 | /** 20 | * @brief Receives updates when relevant values change within the test 21 | * configuration file. 22 | */ 23 | class Config::Test::Listener : public Config::Listener 24 | { 25 | public: 26 | Listener(); 27 | virtual ~Listener() { } 28 | 29 | /** 30 | * @brief Gets the last updated key this listener received. 31 | * 32 | * @return The string value of the last updated key value the listener 33 | * received. 34 | */ 35 | juce::String getLastUpdated() const; 36 | 37 | private: 38 | /** 39 | * @brief Saves the updated key when the configuration file is updated. 40 | * 41 | * @param propertyKey The key to the updated property value. 42 | */ 43 | void configValueChanged(const juce::Identifier& propertyKey) override; 44 | 45 | const juce::Identifier* lastUpdated = nullptr; 46 | }; 47 | -------------------------------------------------------------------------------- /docs/templates/config/ExampleObject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file ExampleObject.h 4 | * 5 | * @brief An example of a custom object data structure to load from a 6 | * configuration file. 7 | */ 8 | 9 | class ExampleObject 10 | { 11 | public: 12 | /** 13 | * @brief Constructs the example object, permanently setting its stored 14 | * data. Custom config objects don't need to be immutable, but it 15 | * simplifies things if they are. 16 | * 17 | * @param exampleNum An arbitrary stored int value used as an example. 18 | * 19 | * @param exampleBool An arbitrary stored boolean value used as an example. 20 | */ 21 | ExampleObject(const int exampleNum, const bool exampleBool); 22 | 23 | virtual ~ExampleObject() { } 24 | 25 | /** 26 | * @brief Gets the custom object's stored integer value. 27 | * 28 | * @return The object's integer value. 29 | */ 30 | int getExampleNum() const; 31 | 32 | /** 33 | * @brief Gets the custom object's stored boolean value. 34 | * 35 | * @return The object's boolean value. 36 | */ 37 | bool getExampleBool() const; 38 | 39 | private: 40 | const int exampleNum; 41 | const bool exampleBool; 42 | }; 43 | -------------------------------------------------------------------------------- /clockworkpi_Keypad/USBCodes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file USBCodes.h 3 | * 4 | * @brief Defines additional USB keyboard codes. 5 | */ 6 | 7 | #define KEY_ENTER 0x28 // Keyboard Return (ENTER) 8 | #define KEY_ESCAPE 0x29 // Keyboard Escape 9 | #define KEY_BACKSPACE 0x2A // Keyboard Backspace 10 | #define KEY_TAB 0x2B // Keyboard Tab 11 | #define KEY_SPACE 0x2C // Keyboard Space 12 | #define KEY_DASH 0x2D // Keyboard - and _ 13 | #define KEY_EQUAL 0x2E // Keyboard = and + 14 | #define KEY_HOME 0x4A // Keyboard Home 15 | #define KEY_PAGE_UP 0x4B // Keyboard Page Up 16 | #define KEY_END 0x4D // Keyboard End 17 | #define KEY_PAGE_DOWN 0x4E // Keyboard Page Down 18 | #define KEY_RIGHT_ARROW 0x4F // Keyboard Right Arrow 19 | #define KEY_LEFT_ARROW 0x50 // Keyboard Left Arrow 20 | #define KEY_DOWN_ARROW 0x51 // Keyboard Down Arrow 21 | #define KEY_UP_ARROW 0x52 // Keyboard Up Arrow 22 | #define KEY_NUM_DASH 0x56 // Keyboard Num Pad - 23 | #define KEY_NUM_PLUS 0x57 // Keyboard Num Pad + 24 | #define KEY_CTRL_LEFT 0xE0 // Keyboard Left Control 25 | #define KEY_ALT_LEFT 0xE2 // Keyboard Left ALT 26 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: your-package-name 3 | Source: 4 | 5 | Files: * 6 | Copyright: 2019 Anthony Brown 7 | License: GPL-3.0+ 8 | 9 | Files: debian/* 10 | Copyright: 2019 Anthony Brown 11 | 2016 Andrew Hay Kurtz 12 | License: GPL-3.0+ 13 | 14 | License: GPL-3.0+ 15 | This program is free software: you can redistribute it and/or modify 16 | it under the terms of the GNU General Public License as published by 17 | the Free Software Foundation, either version 3 of the License, or 18 | (at your option) any later version. 19 | . 20 | This package is distributed in the hope that it will be useful, 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | GNU General Public License for more details. 24 | . 25 | You should have received a copy of the GNU General Public License 26 | along with this program. If not, see . 27 | . 28 | On Debian systems, the complete text of the GNU General 29 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 30 | -------------------------------------------------------------------------------- /Source/GUI/Theme/Colour/Theme_Colour_JSONResource.cpp: -------------------------------------------------------------------------------- 1 | #include "Theme_Colour_JSONResource.h" 2 | #include "Theme_Colour_JSONKeys.h" 3 | 4 | #ifdef JUCE_DEBUG 5 | // Print the full class name before all debug output: 6 | static const constexpr char* dbgPrefix = "Theme::Colour::JSONResource::"; 7 | #endif 8 | 9 | // SharedResource object key: 10 | const juce::Identifier Theme::Colour::JSONResource::resourceKey 11 | = "Theme::Colour::JSONResource"; 12 | 13 | // Filename of the JSON configuration file: 14 | static const constexpr char * configFilename = "colours.json"; 15 | 16 | Theme::Colour::JSONResource::JSONResource() : 17 | Config::FileResource(resourceKey, configFilename) 18 | { 19 | loadJSONData(); 20 | } 21 | 22 | 23 | // Gets the set of all basic(non-array, non-object) properties tracked by this 24 | // JSON resource. 25 | const std::vector& 26 | Theme::Colour::JSONResource::getConfigKeys() const 27 | { 28 | using juce::Identifier; 29 | using Config::DataKey; 30 | static std::vector keys; 31 | if (keys.empty()) 32 | { 33 | for (const Identifier* key : JSONKeys::getColourKeys()) 34 | { 35 | keys.push_back(DataKey(*key, DataKey::stringType)); 36 | } 37 | } 38 | return keys; 39 | } 40 | -------------------------------------------------------------------------------- /Source/Development/Testing/Testing_DelayUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "JuceHeader.h" 3 | 4 | /** 5 | * @file Testing_DelayUtils.h 6 | * 7 | * @brief Handles tests that require waiting until some particular event is 8 | * true. 9 | */ 10 | namespace Testing { class DelayUtils; } 11 | 12 | /** 13 | * @brief Provides the static idleUntil function used to run the JUCE event 14 | * loop until some condition is met or times out. 15 | */ 16 | class Testing::DelayUtils 17 | { 18 | public: 19 | /** 20 | * @brief Runs the juce dispatch loop until an expected condition is true. 21 | * 22 | * @param waitCondition Tests if the expected condition is true yet. 23 | * 24 | * @param testFrequencyMS Milliseconds to wait between tests of the 25 | * expected condition. 26 | * 27 | * @param timeoutMS Milliseconds to wait before assuming the 28 | * condition will never be met. 29 | * 30 | * @return Whether waitCondition returned true before the 31 | * timeout period ended. 32 | */ 33 | static bool idleUntil(const std::function waitCondition, 34 | int testFrequencyMS, int timeoutMS); 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /Source/GUI/Text/CharSet/Text_CharSet_JSONKeys.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Text_CharSet_JSONKeys.h 4 | * 5 | * @brief Defines keys used to load configurable character sets. 6 | */ 7 | 8 | #include "Config_DataKey.h" 9 | 10 | namespace Text { namespace CharSet { namespace JSONKeys 11 | { 12 | // ####### String value keys: ####### 13 | // Stores the names used to identify character sets on the help screen: 14 | const Config::DataKey mainSetName("main character set name", 15 | Config::DataKey::DataType::stringType); 16 | const Config::DataKey altSetName("alternate character set name", 17 | Config::DataKey::DataType::stringType); 18 | const Config::DataKey specialSetName("special character set name", 19 | Config::DataKey::DataType::stringType); 20 | 21 | // ####### Character set keys: ####### 22 | // Each stores a set of character values in display order, paired with the 23 | // characters that replace them when the shift modifier is active, and the 24 | // priority they should take when assigning chord values. 25 | const juce::Identifier mainCharSet("main character set"); 26 | const juce::Identifier altCharSet("alternate character set"); 27 | const juce::Identifier specialCharSet("special character set"); 28 | } } } 29 | -------------------------------------------------------------------------------- /project-scripts/TestClassGen.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Generates empty unit test classes for project source files. 4 | # 5 | # Use: ./TestGen.pl "path/in/Source/dir/to/file.cpp" "Test Category Name" 6 | 7 | use strict; 8 | use warnings; 9 | use File::Slurp; 10 | use lib './project-scripts'; 11 | use Paths; 12 | 13 | my $projectRoot = Paths::PROJECT_DIR; 14 | 15 | my $path = $ARGV[0] or die "Missing source file path!\n"; 16 | my $category = $ARGV[1] or die "Missing test category!\n"; 17 | my $className; 18 | if ($path =~ /\/(.+)\.cpp$/) 19 | { 20 | $className = $1; 21 | } 22 | else 23 | { 24 | die "Failed to parse classname from file path $path\n"; 25 | } 26 | my $testClassName = $className."Test"; 27 | my $header = $path; 28 | $header =~ s/cpp/h/; 29 | 30 | my $testFile = qq 31 | [#include "JuceHeader.h" 32 | #include "$header" 33 | 34 | class $testClassName : public juce::UnitTest 35 | { 36 | public: 37 | $testClassName() : juce::UnitTest("$category\::$className Testing", 38 | "$category") {} 39 | 40 | void runTest() override 41 | { 42 | } 43 | }; 44 | 45 | static $testClassName test; 46 | ]; 47 | 48 | my $subDir = $path; 49 | $subDir =~ s/^.*Source\///; 50 | my $testPath = "$projectRoot/Tests/$subDir"; 51 | write_file($testPath, $testFile) or die "Failed to write to $testPath\n"; 52 | -------------------------------------------------------------------------------- /Source/GUI/Output/Output_Sending.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Output_Sending.h 4 | * 5 | * @brief Creates artificial key events to send to another window. 6 | */ 7 | 8 | #include "Output_Buffer.h" 9 | 10 | namespace Output 11 | { 12 | namespace Sending 13 | { 14 | /** 15 | * @brief Sends a single key press event to a window. 16 | * 17 | * @param keyValue A key value to type. 18 | * 19 | * @param modifierFlags Modifier flags to apply to the key, as defined 20 | * in Input::Modifiers. 21 | * 22 | * @param targetWindow The ID of the window where the key event should 23 | * be sent. 24 | */ 25 | void sendKey(const Text::CharValue keyValue, const int modifierFlags, 26 | const int targetWindow); 27 | 28 | /** 29 | * @brief Take all text from a buffer object, and sends it to a window. 30 | * 31 | * @param outputBuffer The buffer object. 32 | * 33 | * @param targetWindow The ID of the window where the text will be 34 | * sent. 35 | */ 36 | void sendBufferedOutput(Buffer& outputBuffer, 37 | const int targetWindow); 38 | 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Source/Framework/Util/Util_Math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Util_Math.h 4 | * 5 | * @brief Provides miscellaneous mathematical functions. 6 | */ 7 | 8 | namespace Util 9 | { 10 | namespace Math 11 | { 12 | /** 13 | * @brief Finds the median of three values. 14 | * 15 | * @param a The first of the values to compare. 16 | * 17 | * @param b The second of the values to compare. 18 | * 19 | * @param c The third of the values to compare. 20 | * 21 | * @tparam T The compared value type. 22 | * 23 | * @return The median of a, b, and c. 24 | */ 25 | template 26 | T median(const T a, const T b, const T c) 27 | { 28 | if (a > b) 29 | { 30 | if (b > c) 31 | { 32 | return b; 33 | } 34 | if (a < c) 35 | { 36 | return a; 37 | } 38 | return c; 39 | } 40 | if (a > c) 41 | { 42 | return a; 43 | } 44 | if (b < c) 45 | { 46 | return b; 47 | } 48 | return c; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/GUI/Component/Component_CharsetDisplay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Component_CharsetDisplay.h 4 | * 5 | * @brief Draws all characters in the active character set, outlining the 6 | * selected character, and highlighting all characters that could be 7 | * selected by holding down additional chord keys. 8 | */ 9 | 10 | namespace Component { class CharsetDisplay; } 11 | 12 | #include "Component_KeyGrid.h" 13 | 14 | class Component::CharsetDisplay : public Component::KeyGrid 15 | { 16 | public: 17 | CharsetDisplay() { } 18 | virtual ~CharsetDisplay() { } 19 | 20 | /** 21 | * @brief Gets the number of character columns the KeyGrid contains. 22 | * 23 | * @return One for each character in the current active character set, plus 24 | * another for each wide character in the current set. 25 | */ 26 | int getColumnCount() const override; 27 | 28 | /** 29 | * @brief Gets the number of character rows the KeyGrid contains. 30 | * 31 | * @return One, the component only contains a single row. 32 | */ 33 | int getRowCount() const override; 34 | 35 | private: 36 | /** 37 | * @brief Draws all characters in the current active set. 38 | * 39 | * @param g The JUCE graphics context. 40 | */ 41 | void paint(juce::Graphics& g) override; 42 | }; 43 | -------------------------------------------------------------------------------- /Source/Development/Debug/Debug_ScopeTimerRecords.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Debug_ScopeTimerRecords.h 4 | * 5 | * @brief Records and organizes times recorded by all ScopeTimer objects. 6 | */ 7 | 8 | #include "JuceHeader.h" 9 | 10 | namespace Debug 11 | { 12 | namespace ScopeTimerRecords 13 | { 14 | /** 15 | * @brief Records the start of a ScopeTimer's measurement period. 16 | * 17 | * @param scopeName A name identifying the measured scope. 18 | * 19 | * @param threadID The thread where the timer was created and 20 | * destroyed. 21 | */ 22 | void addStartRecord(const juce::String scopeName, 23 | const juce::Thread::ThreadID threadID); 24 | 25 | /** 26 | * @brief Records the end of a ScopeTimer's measurement period. 27 | * 28 | * @param scopeName A name identifying the measured scope. 29 | * 30 | * @param threadID The thread where the timer was created and 31 | * destroyed. 32 | */ 33 | void addStopRecord(const juce::String scopeName, 34 | const juce::Thread::ThreadID threadID); 35 | 36 | /** 37 | * @brief Organizes and prints all records chronologically. 38 | */ 39 | void printRecords(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Source/Files/Config/Implementation/Config_AlertWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Config_AlertWindow.h 4 | * 5 | * @brief Displays pop-up error messages when something goes wrong with reading 6 | * or writing JSON configuration files. 7 | */ 8 | 9 | // TODO: Localized error messages for: 10 | // -Bad permissions on config files 11 | // -Inability to create new files 12 | // -Inappropriate data types in config files 13 | // -Missing default config files 14 | 15 | #include "Locale_TextUser.h" 16 | #include "JuceHeader.h" 17 | 18 | namespace Config { class AlertWindow; } 19 | 20 | /** 21 | * @brief Alerts the user to a problem with reading or writing configuration 22 | * files. 23 | */ 24 | class Config::AlertWindow : private Locale::TextUser 25 | { 26 | public: 27 | AlertWindow() : Locale::TextUser("ConfigAlertWindows") { } 28 | 29 | virtual ~AlertWindow() { } 30 | 31 | /** 32 | * @brief Shows a simple alert window with error text. 33 | * 34 | * This should be replaced when ConfigErrorMessage is properly implemented. 35 | */ 36 | void showPlaceholderError(juce::String errorMessage) 37 | { 38 | juce::AlertWindow::showMessageBoxAsync( 39 | juce::AlertWindow::AlertIconType::WarningIcon, 40 | "ConfigFile Error", 41 | errorMessage); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /assets/locale/en_US.json: -------------------------------------------------------------------------------- 1 | { 2 | "Component_HelpScreen": 3 | { 4 | "helpTitle" : "KeyChord Controls:", 5 | "chordKeys" : "Chord input keys" 6 | }, 7 | "Input_Key_Binding": 8 | { 9 | "Chord1" : "Chord Key 1", 10 | "Chord2" : "Chord Key 2", 11 | "Chord3" : "Chord Key 3", 12 | "Chord4" : "Chord Key 4", 13 | "Chord5" : "Chord Key 5", 14 | "Select charSet pre" : "Select ", 15 | "Select charSet post" : " character set", 16 | "Select next character set" : "Select next character set", 17 | "Select modifier set" : "Show modifier character set", 18 | "Toggle shifted characters" : "Toggle shifted characters", 19 | "Backspace" : "Delete last character", 20 | "Clear all" : "Clear all", 21 | "Send text" : "Send text", 22 | "Close and send" : "Close and send", 23 | "Close" : "Close", 24 | "Toggle immediate mode" : "Toggle immediate input sending", 25 | "Show help" : "Show help screen", 26 | "Toggle window edge" : "Toggle window edge", 27 | "Toggle minimize" : "Toggle minimize" 28 | }, 29 | "Input_Controller": 30 | { 31 | "immediateMode" : "(Immediate input mode)" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Development/Testing/Testing_Window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Testing_Window.h 4 | * 5 | * @brief Creates a basic window for testing a single component. 6 | */ 7 | 8 | #include "JuceHeader.h" 9 | 10 | namespace Testing { class Window; } 11 | 12 | /** 13 | * @file Testing_Window.h 14 | * 15 | * @brief A simple window object that owns a single component. 16 | */ 17 | class Testing::Window : public juce::DocumentWindow 18 | { 19 | public: 20 | /** 21 | * @brief Creates and shows a window holding a test component. 22 | * 23 | * @param testName The name of the tested component, to use as the 24 | * window title. 25 | * 26 | * @param testComponent The component that the window will display. 27 | * 28 | * @param xPos The initial x-coordinate position of the window. 29 | * 30 | * @param yPos The initial y-coordinate position of the window. 31 | * 32 | * @param width The initial width of the window. 33 | * 34 | * @param height The initial height of the window. 35 | */ 36 | Window(const juce::String testName, 37 | juce::Component* const testComponent, 38 | const int xPos, 39 | const int yPos, 40 | const int width, 41 | const int height); 42 | 43 | virtual ~Window() { } 44 | }; 45 | -------------------------------------------------------------------------------- /project-scripts/DepClean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # DepClean.sh 3 | # Removes build files that depend on removed/renamed files 4 | 5 | buildDir=$1 6 | projectDir=$(cd $(dirname $0)/.. && pwd) 7 | 8 | 9 | if [[ ! -d $buildDir ]]; then 10 | echo "$buildDir is not a valid directory!" 11 | exit -1 12 | fi 13 | 14 | if [[ ! $(ls -A $buildDir | grep "d\$") ]]; then 15 | echo "No dependency files to check." 16 | exit 0 17 | fi 18 | 19 | 20 | echo "Removing build files with missing dependencies:" 21 | 22 | numScanned=0 23 | numRemoved=0 24 | for fileName in $buildDir/*.d; do 25 | numDeps=0 26 | for path in $(<$fileName); do 27 | numDeps=$(($numDeps + 1)) 28 | echo -en "\r\033[KChecking $numScanned, dependency $numDeps" 29 | if [[ $path == '\' ]]; then 30 | continue 31 | fi 32 | if [[ ${path: -1} == ':' ]]; then 33 | strlen=${#path} 34 | path=${path:0:$(($strlen - 1))} 35 | fi 36 | if [[ -f $path ]]; then 37 | continue 38 | fi 39 | fullPath="$projectDir/$path" 40 | if [[ ! -f $fullPath ]]; then 41 | oldFile=$(echo $fileName | sed 's/\(.*\)\..*/\1/') 42 | rm "$oldFile.d" 43 | rm "$oldFile.o" 44 | numRemoved=$(($numRemoved + 1)) 45 | break 46 | fi 47 | done 48 | numScanned=$(($numScanned + 1)) 49 | done 50 | echo -e "\r\033[K$numRemoved/$numScanned build files were removed." 51 | 52 | -------------------------------------------------------------------------------- /Source/Files/Config/Implementation/Config_ListenerInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Config_ListenerInterface.h 4 | * 5 | * @brief The interface used by Config::FileResource objects to send 6 | * notifications to Config::Listener objects when one of their tracked 7 | * values are updated. 8 | */ 9 | 10 | #include "JuceHeader.h" 11 | 12 | namespace Config { class ListenerInterface; } 13 | 14 | /** 15 | * @brief The interface used to update Listeners that track any number of 16 | * values in a JSON configuration file. 17 | */ 18 | class Config::ListenerInterface 19 | { 20 | public: 21 | // Only FileResource may send updates to Listeners. 22 | friend class FileResource; 23 | 24 | ListenerInterface() { } 25 | 26 | virtual ~ListenerInterface() { } 27 | 28 | protected: 29 | /** 30 | * @brief Checks if a particular value is tracked by this Listener. 31 | * 32 | * @param key The key to a value stored in the Listener's file resource. 33 | * 34 | * @return Whether the value with the given key is tracked by the 35 | * Listener. 36 | */ 37 | virtual bool isKeyTracked(const juce::Identifier& key) const = 0; 38 | 39 | /** 40 | * @brief Notifies the Listener that a value it tracks has been updated. 41 | * 42 | * @param updatedValue The key to the updated value. 43 | */ 44 | virtual void configValueChanged(const juce::Identifier& updatedValue) = 0; 45 | }; 46 | 47 | -------------------------------------------------------------------------------- /makefiles/Theme.mk: -------------------------------------------------------------------------------- 1 | ############################ Theme Module ###################################### 2 | THEME_DIR = Source/GUI/Theme 3 | THEME_COLOUR_DIR := $(THEME_DIR)/Colour 4 | THEME_IMAGE_DIR := $(THEME_DIR)/Image 5 | 6 | THEME_PREFIX = Theme_ 7 | THEME_OBJ := $(JUCE_OBJDIR)/$(THEME_PREFIX) 8 | 9 | THEME_COLOUR_PREFIX := $(THEME_PREFIX)Colour_ 10 | THEME_COLOUR_OBJ := $(THEME_OBJ)Colour_ 11 | OBJECTS_THEME_COLOUR := \ 12 | $(THEME_COLOUR_OBJ)Element.o \ 13 | $(THEME_COLOUR_OBJ)JSONKeys.o \ 14 | $(THEME_COLOUR_OBJ)JSONResource.o \ 15 | $(THEME_COLOUR_OBJ)ConfigFile.o 16 | 17 | OBJECTS_THEME := \ 18 | $(OBJECTS_THEME_COLOUR) \ 19 | $(THEME_OBJ)LookAndFeel.o 20 | 21 | OBJECTS_THEME_TEST := 22 | 23 | ifeq ($(BUILD_TESTS), 1) 24 | OBJECTS_THEME := $(OBJECTS_THEME) $(OBJECTS_THEME_TEST) 25 | endif 26 | 27 | GUI_MODULES := $(GUI_MODULES) theme 28 | 29 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_THEME) 30 | 31 | theme : $(OBJECTS_THEME) 32 | @echo " Built Theme module" 33 | 34 | $(THEME_COLOUR_OBJ)Element.o : \ 35 | $(THEME_COLOUR_DIR)/$(THEME_COLOUR_PREFIX)Element.cpp 36 | $(THEME_COLOUR_OBJ)JSONKeys.o : \ 37 | $(THEME_COLOUR_DIR)/$(THEME_COLOUR_PREFIX)JSONKeys.cpp 38 | $(THEME_COLOUR_OBJ)JSONResource.o : \ 39 | $(THEME_COLOUR_DIR)/$(THEME_COLOUR_PREFIX)JSONResource.cpp 40 | $(THEME_COLOUR_OBJ)ConfigFile.o : \ 41 | $(THEME_COLOUR_DIR)/$(THEME_COLOUR_PREFIX)ConfigFile.cpp 42 | 43 | $(THEME_OBJ)LookAndFeel.o : \ 44 | $(THEME_DIR)/Theme_LookAndFeel.cpp 45 | -------------------------------------------------------------------------------- /Tests/Files/Config/Config_Test_ObjectData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Config_Test_ObjectData.h 4 | * 5 | * @brief An example of a custom object data structure to load from a 6 | * configuration file. 7 | */ 8 | 9 | namespace Config { namespace Test { class ObjectData; } } 10 | 11 | class Config::Test::ObjectData 12 | { 13 | public: 14 | /** 15 | * @brief Constructs the test object, permanently setting its stored 16 | * data. 17 | * 18 | * @param testNum An arbitrary stored int value used for testing. 19 | * 20 | * @param testBool An arbitrary stored boolean value used for testing. 21 | */ 22 | ObjectData(const int testNum, const bool testBool); 23 | 24 | virtual ~ObjectData() { } 25 | 26 | /** 27 | * @brief Gets the custom object's stored integer value. 28 | * 29 | * @return The object's integer value. 30 | */ 31 | int getTestNum() const; 32 | 33 | /** 34 | * @brief Gets the custom object's stored boolean value. 35 | * 36 | * @return The object's boolean value. 37 | */ 38 | bool getTestBool() const; 39 | 40 | /** 41 | * @brief Compares this object with another. 42 | * 43 | * @param rhs The compared object. 44 | * 45 | * @return Whether both objects have the same stored number and bool. 46 | */ 47 | bool operator== (const ObjectData& rhs) const; 48 | 49 | private: 50 | const int testNum; 51 | const bool testBool; 52 | }; 53 | -------------------------------------------------------------------------------- /Source/GUI/Component/Component_HelpScreen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Component_HelpScreen.h 4 | * 5 | * @brief Displays localized key binding descriptions to the user. 6 | */ 7 | 8 | #include "Input_Key_ConfigFile.h" 9 | #include "Locale_TextUser.h" 10 | #include "Text_CharTypes.h" 11 | #include "JuceHeader.h" 12 | 13 | namespace Component { class HelpScreen; } 14 | 15 | class Component::HelpScreen : public juce::Component, 16 | public Locale::TextUser 17 | { 18 | public: 19 | /** 20 | * @brief Loads help text on construction. 21 | */ 22 | HelpScreen(); 23 | 24 | virtual ~HelpScreen() { } 25 | 26 | private: 27 | /** 28 | * @brief Prints all help text. 29 | * 30 | * @param g JUCE graphics context object. 31 | */ 32 | void paint(juce::Graphics& g) override; 33 | 34 | // Ensures key bindings are available. 35 | Input::Key::ConfigFile keyConfig; 36 | 37 | // Store Chord key info on its own, as it is handled differently: 38 | // Chord key display symbols: 39 | Text::CharString chordChars; 40 | // Chord key names: 41 | Text::CharString chordNames; 42 | // Chord key description: 43 | Text::CharString chordDescription; 44 | 45 | // Single symbols used to represent key commands: 46 | Text::CharString symbolChars; 47 | // All key names, divided into lines. 48 | Text::CharLineArray keyNames; 49 | // All key action descriptions, divided into lines. 50 | Text::CharLineArray descriptions; 51 | 52 | }; 53 | -------------------------------------------------------------------------------- /Source/Files/Config/Config_MainFile.cpp: -------------------------------------------------------------------------------- 1 | #include "Config_MainFile.h" 2 | #define CONFIG_MAIN_IMPLEMENTATION 3 | #include "Config_MainResource.h" 4 | #include "Config_MainKeys.h" 5 | 6 | Config::MainFile::MainFile() { } 7 | 8 | 9 | // Checks if the application should be displayed in minimized mode. 10 | bool Config::MainFile::getMinimized() const 11 | { 12 | return getConfigValue(MainKeys::minimized); 13 | } 14 | 15 | 16 | // Gets whether the application is currently configured to snap to the bottom 17 | // edge of the display. 18 | bool Config::MainFile::getSnapToBottom() const 19 | { 20 | return getConfigValue(MainKeys::snapToBottom); 21 | } 22 | 23 | 24 | // Gets whether the application is in immediate input mode. 25 | bool Config::MainFile::getImmediateMode() const 26 | { 27 | return getConfigValue(MainKeys::immediateMode); 28 | } 29 | 30 | 31 | // Sets whether the application should start up in minimized mode. 32 | void Config::MainFile::setMinimised(const bool minimized) 33 | { 34 | setConfigValue(MainKeys::minimized, minimized); 35 | } 36 | 37 | 38 | // Sets which edge of the display the window should snap to on construction. 39 | void Config::MainFile::setSnapToBottom(const bool snapToBottom) 40 | { 41 | setConfigValue(MainKeys::snapToBottom, snapToBottom); 42 | } 43 | 44 | 45 | // Sets whether the application should start in immediate mode. 46 | void Config::MainFile::setImmediateMode(const bool immediateMode) 47 | { 48 | setConfigValue(MainKeys::immediateMode, immediateMode); 49 | } 50 | -------------------------------------------------------------------------------- /Source/Framework/Windows/Windows_FocusControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Windows_FocusControl.h 4 | * 5 | * @brief Simplifies the process of changing window focus and waiting until 6 | * that focus has been gained. 7 | */ 8 | 9 | #include "Util_ConditionChecker.h" 10 | 11 | namespace Windows { class FocusControl; } 12 | 13 | class Windows::FocusControl 14 | { 15 | public: 16 | FocusControl(); 17 | 18 | /** 19 | * @brief Focuses the window with the given window ID, waiting until 20 | * focus is gained successfully before doing anything else. 21 | * 22 | * @param windowID The X11 window ID of the window that should be focused. 23 | * 24 | * @param onFailure An optional failure action to perform if the window 25 | * no longer exists or fails to focus before the timeout 26 | * period finishes. 27 | */ 28 | void focusWindow(const int windowID, 29 | std::function onFailure = [](){}); 30 | 31 | /** 32 | * @brief Refocuses this application's window, and ensures the main 33 | * component has keyboard focus. 34 | * 35 | * @param mainComponent A pointer to the component object that should be 36 | * given keyboard focus. 37 | */ 38 | void takeFocus(juce::Component* mainComponent); 39 | 40 | private: 41 | // Handles the process of regularly checking for window focus conditions to 42 | // be met: 43 | Util::ConditionChecker focusChecker; 44 | }; 45 | -------------------------------------------------------------------------------- /JuceLibraryCode/BinaryData.h: -------------------------------------------------------------------------------- 1 | /* ========================================================================================= 2 | 3 | This is an auto-generated file: Any edits you make may be overwritten! 4 | 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace BinaryData 10 | { 11 | extern const char* bluetooth_json; 12 | const int bluetooth_jsonSize = 1119; 13 | 14 | extern const char* LatoRegular_ttf; 15 | const int LatoRegular_ttfSize = 120196; 16 | 17 | extern const char* wifi_json; 18 | const int wifi_jsonSize = 1167; 19 | 20 | // Number of elements in the namedResourceList and originalFileNames arrays. 21 | const int namedResourceListSize = 3; 22 | 23 | // Points to the start of a list of resource names. 24 | extern const char* namedResourceList[]; 25 | 26 | // Points to the start of a list of resource filenames. 27 | extern const char* originalFilenames[]; 28 | 29 | // If you provide the name of one of the binary resource variables above, this function will 30 | // return the corresponding data and its size (or a null pointer if the name isn't found). 31 | const char* getNamedResource (const char* resourceNameUTF8, int& dataSizeInBytes); 32 | 33 | // If you provide the name of one of the binary resource variables above, this function will 34 | // return the corresponding original, non-mangled filename (or a null pointer if the name isn't found). 35 | const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8); 36 | } 37 | -------------------------------------------------------------------------------- /makefiles/Input.mk: -------------------------------------------------------------------------------- 1 | ############################ Input Module #################################### 2 | INPUT_DIR = Source/GUI/Input 3 | INPUT_TEST_DIR = Tests/GUI/Input 4 | 5 | INPUT_KEY_DIR := $(INPUT_DIR)/Key 6 | 7 | INPUT_PREFIX = Input_ 8 | INPUT_OBJ := $(JUCE_OBJDIR)/$(INPUT_PREFIX) 9 | 10 | INPUT_KEY_PREFIX := $(INPUT_PREFIX)Key_ 11 | INPUT_KEY_OBJ := $(INPUT_OBJ)Key_ 12 | OBJECTS_INPUT_KEY := \ 13 | $(INPUT_KEY_OBJ)Binding.o \ 14 | $(INPUT_KEY_OBJ)JSONResource.o \ 15 | $(INPUT_KEY_OBJ)ConfigFile.o 16 | 17 | OBJECTS_INPUT := \ 18 | $(INPUT_OBJ)Chord.o \ 19 | $(INPUT_OBJ)ChordReader.o \ 20 | $(INPUT_OBJ)Controller.o \ 21 | $(OBJECTS_INPUT_KEY) 22 | 23 | INPUT_TEST_PREFIX := $(INPUT_PREFIX)Test_ 24 | INPUT_TEST_OBJ := $(INPUT_OBJ)Test_ 25 | OBJECTS_INPUT_TEST := 26 | 27 | ifeq ($(BUILD_TESTS), 1) 28 | OBJECTS_INPUT := $(OBJECTS_INPUT) $(OBJECTS_INPUT_TEST) 29 | endif 30 | 31 | input : $(OBJECTS_INPUT) 32 | @echo " Built Input module" 33 | 34 | GUI_MODULES := $(GUI_MODULES) input 35 | 36 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_INPUT) 37 | 38 | $(INPUT_KEY_OBJ)Binding.o: \ 39 | $(INPUT_KEY_DIR)/$(INPUT_KEY_PREFIX)Binding.cpp 40 | $(INPUT_KEY_OBJ)JSONResource.o: \ 41 | $(INPUT_KEY_DIR)/$(INPUT_KEY_PREFIX)JSONResource.cpp 42 | $(INPUT_KEY_OBJ)ConfigFile.o: \ 43 | $(INPUT_KEY_DIR)/$(INPUT_KEY_PREFIX)ConfigFile.cpp 44 | 45 | $(INPUT_OBJ)Chord.o: \ 46 | $(INPUT_DIR)/$(INPUT_PREFIX)Chord.cpp 47 | $(INPUT_OBJ)ChordReader.o: \ 48 | $(INPUT_DIR)/$(INPUT_PREFIX)ChordReader.cpp 49 | $(INPUT_OBJ)Controller.o: \ 50 | $(INPUT_DIR)/$(INPUT_PREFIX)Controller.cpp 51 | -------------------------------------------------------------------------------- /Source/Framework/Locale/Locale_TextUser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Locale_TextUser.h 4 | * 5 | * @brief Provides access to all localized text strings by class. 6 | */ 7 | 8 | #include "Locale/Locale.h" 9 | #include "JuceHeader.h" 10 | #include 11 | 12 | namespace Locale { class TextUser; } 13 | 14 | /** 15 | * @brief Loads a set of localized strings from an appropriate locale file. 16 | */ 17 | class Locale::TextUser 18 | { 19 | public: 20 | /** 21 | * @brief Creates a TextUser, loading locale data if necessary. 22 | * 23 | * @param className Identifies the text values for a particular class in 24 | * the localization file. Only text values under this 25 | * name will be accessed. 26 | */ 27 | TextUser(const juce::Identifier& className); 28 | 29 | virtual ~TextUser() { } 30 | 31 | protected: 32 | /** 33 | * @brief Looks up a localized text string associated with this class. 34 | * 35 | * @param key One of this object's text keys. 36 | * 37 | * @return The localized text string, or the empty string if text 38 | * wasn't found. 39 | */ 40 | juce::String localeText(const juce::Identifier& key) const; 41 | 42 | private: 43 | // The key to all localized strings that belong to this class: 44 | const juce::Identifier& className; 45 | 46 | // All localized text data loaded from the JSON locale file: 47 | static std::map> localeData; 49 | }; 50 | -------------------------------------------------------------------------------- /makefiles/Text.mk: -------------------------------------------------------------------------------- 1 | ############################## Text Module ##################################### 2 | TEXT_DIR = Source/GUI/Text 3 | TEXT_TEST_DIR = Tests/GUI/Text 4 | 5 | TEXT_CHARSET_DIR = $(TEXT_DIR)/CharSet 6 | 7 | TEXT_PREFIX = Text_ 8 | TEXT_OBJ := $(JUCE_OBJDIR)/$(TEXT_PREFIX) 9 | 10 | TEXT_CHARSET_PREFIX = $(TEXT_PREFIX)CharSet_ 11 | TEXT_CHARSET_OBJ = $(TEXT_OBJ)CharSet_ 12 | OBJECTS_TEXT_CHARSET := \ 13 | $(TEXT_CHARSET_OBJ)Cache.o \ 14 | $(TEXT_CHARSET_OBJ)JSONResource.o \ 15 | $(TEXT_CHARSET_OBJ)ConfigFile.o 16 | 17 | 18 | OBJECTS_TEXT := \ 19 | $(TEXT_OBJ)BinaryFont.o \ 20 | $(TEXT_OBJ)Painter.o \ 21 | $(TEXT_OBJ)Values.o \ 22 | $(OBJECTS_TEXT_CHARSET) 23 | 24 | TEXT_TEST_PREFIX := $(TEXT_PREFIX)Test_ 25 | TEXT_TEST_OBJ := $(TEXT_OBJ)Test_ 26 | OBJECTS_TEXT_TEST := 27 | 28 | ifeq ($(BUILD_TESTS), 1) 29 | OBJECTS_TEXT := $(OBJECTS_TEXT) $(OBJECTS_TEXT_TEST) 30 | endif 31 | 32 | text : $(OBJECTS_TEXT) 33 | @echo " Built Text module" 34 | 35 | GUI_MODULES := $(GUI_MODULES) text 36 | 37 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_TEXT) 38 | 39 | $(TEXT_CHARSET_OBJ)Cache.o: \ 40 | $(TEXT_CHARSET_DIR)/$(TEXT_CHARSET_PREFIX)Cache.cpp 41 | $(TEXT_CHARSET_OBJ)JSONResource.o: \ 42 | $(TEXT_CHARSET_DIR)/$(TEXT_CHARSET_PREFIX)JSONResource.cpp 43 | $(TEXT_CHARSET_OBJ)ConfigFile.o: \ 44 | $(TEXT_CHARSET_DIR)/$(TEXT_CHARSET_PREFIX)ConfigFile.cpp 45 | 46 | $(TEXT_OBJ)BinaryFont.o: \ 47 | $(TEXT_DIR)/$(TEXT_PREFIX)BinaryFont.cpp 48 | $(TEXT_OBJ)Painter.o: \ 49 | $(TEXT_DIR)/$(TEXT_PREFIX)Painter.cpp 50 | $(TEXT_OBJ)Values.o: \ 51 | $(TEXT_DIR)/$(TEXT_PREFIX)Values.cpp 52 | -------------------------------------------------------------------------------- /Source/MainWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file MainWindow.h 4 | * 5 | * @brief Creates the main application window, and makes sure the application 6 | * is closed if the window is blocked. 7 | */ 8 | 9 | #include "JuceHeader.h" 10 | 11 | /** 12 | * @brief The sole application window object and the root component in the 13 | * component display tree. 14 | */ 15 | class MainWindow : public juce::DocumentWindow 16 | { 17 | public: 18 | /** 19 | * @brief Creates and shows the main application window. 20 | * 21 | * @param windowName Sets the text of the window title bar. 22 | */ 23 | MainWindow(juce::String windowName); 24 | 25 | virtual ~MainWindow() { } 26 | 27 | private: 28 | /** 29 | * @brief Closes the application normally when the window closes. 30 | */ 31 | void closeButtonPressed() override; 32 | 33 | /** 34 | * @brief Resizes page content to match the window size. 35 | */ 36 | virtual void resized() override; 37 | 38 | /** 39 | * @brief Starts the exit timer when the window loses focus, and stops 40 | * if if the window gains focus again. 41 | */ 42 | void activeWindowStatusChanged() override; 43 | 44 | /** 45 | * @brief Shuts down the application if the window loses focus and doesn't 46 | * regain it. 47 | */ 48 | class ExitTimer : public juce::Timer 49 | { 50 | private: 51 | virtual void timerCallback() override; 52 | }; 53 | ExitTimer exitTimer; 54 | 55 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow) 56 | }; 57 | -------------------------------------------------------------------------------- /Source/GUI/Component/Component_ChordKeyDisplay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Component_ChordKeyDisplay.h 4 | * 5 | * @brief Draws a vertical indicator of the state and bindings of the chord 6 | * input keys. 7 | */ 8 | 9 | #include "Component_KeyGrid.h" 10 | #include "Text_CharTypes.h" 11 | #include "JuceHeader.h" 12 | 13 | namespace Component { class ChordKeyDisplay; } 14 | 15 | /** 16 | * @brief Draws key images representing each chord key, highlighting each key 17 | * if it is currently held down. 18 | * 19 | * Chord keys are drawn in a single vertical column. 20 | */ 21 | class Component::ChordKeyDisplay : public Component::KeyGrid 22 | { 23 | public: 24 | /** 25 | * @brief Loads and saves chord key bindings on construction. 26 | */ 27 | ChordKeyDisplay(); 28 | 29 | virtual ~ChordKeyDisplay() { } 30 | 31 | /** 32 | * @brief Gets the number of character columns the KeyGrid contains. 33 | * 34 | * @return One, as the ChordKeyDisplay arranges all chord keys in one 35 | * column. 36 | */ 37 | int getColumnCount() const override; 38 | 39 | /** 40 | * @brief Gets the number of character rows the KeyGrid contains. 41 | * 42 | * @return The number of chord input keys. 43 | */ 44 | int getRowCount() const override; 45 | 46 | private: 47 | /** 48 | * @brief Paints the chord keys. 49 | * 50 | * @param g JUCE graphics context. 51 | */ 52 | void paint(juce::Graphics& g) override; 53 | 54 | // Stores the chord key characters: 55 | Text::CharString chordKeys; 56 | }; 57 | -------------------------------------------------------------------------------- /Source/GUI/Output/Output_Buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Output_Buffer.h" 2 | #include "Util_ConditionChecker.h" 3 | #include "Text_Values.h" 4 | #include "MainWindow.h" 5 | 6 | #ifdef JUCE_DEBUG 7 | // Print the full class name before all debug output: 8 | static const constexpr char* dbgPrefix = "Output::Buffer::"; 9 | #endif 10 | 11 | 12 | // Gets the cached output string, not including modifiers. 13 | Text::CharString Output::Buffer::getBufferedText() const 14 | { 15 | return bufferedText; 16 | } 17 | 18 | 19 | // Gets the modifier key flags that will be applied to the output. 20 | int Output::Buffer::getModifierFlags() const 21 | { 22 | return keyModifiers; 23 | } 24 | 25 | 26 | // Adds a character to the end of the cached output string. 27 | void Output::Buffer::appendCharacter(const Text::CharValue outputChar) 28 | { 29 | bufferedText.add(outputChar); 30 | } 31 | 32 | 33 | // Sets the modifier keys that will be applied to the buffered text. 34 | void Output::Buffer::setModifiers(const int modifierFlags) 35 | { 36 | keyModifiers = modifierFlags; 37 | } 38 | 39 | 40 | // Removes the last character from the end of the output string. 41 | void Output::Buffer::deleteLastChar() 42 | { 43 | bufferedText.removeLast(); 44 | } 45 | 46 | 47 | // Removes all bufferedText. 48 | void Output::Buffer::clear(const bool clearModifiers) 49 | { 50 | bufferedText.clear(); 51 | if (clearModifiers) 52 | { 53 | keyModifiers = 0; 54 | } 55 | } 56 | 57 | 58 | // Checks if the buffer currently contains any text or key values. 59 | bool Output::Buffer::isEmpty() const 60 | { 61 | return bufferedText.isEmpty(); 62 | } 63 | -------------------------------------------------------------------------------- /debian/your-application.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" (C) Copyright 2017 Stephen Paul Weber , 3 | .\" 4 | .\" First parameter, NAME, should be all caps 5 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 6 | .\" other parameters are allowed: see man(7), man(1) 7 | .TH YOUR-APPLICATION 1 "May 14, 2019" 8 | .\" Please adjust this date whenever revising the manpage. 9 | .\" 10 | .\" Some roff macros, for reference: 11 | .\" .nh disable hyphenation 12 | .\" .hy enable hyphenation 13 | .\" .ad l left justify 14 | .\" .ad b justify to both left and right margins 15 | .\" .nf disable filling 16 | .\" .fi enable filling 17 | .\" .br insert line break 18 | .\" .sp insert n+1 empty lines 19 | .\" for manpage-specific macros, see man(7) 20 | .SH NAME 21 | Your-Application \- Replace with brief description. 22 | .SH SYNOPSIS 23 | .B Your-Application 24 | .RI [ options ] 25 | .SH DESCRIPTION 26 | Replace this page with documentation for 27 | .B your application. 28 | . 29 | .PP 30 | .\" TeX users may be more comfortable with the \fB\fP and 31 | .\" \fI\fP escape sequences to invode bold face and italics, 32 | .\" respectively. 33 | Avoid releasing projects without updating, replacing, or removing this file. 34 | .SH OPTIONS 35 | .TP 36 | .B \-\-help 37 | Show summary of options. 38 | .TP 39 | .B \-\-test 40 | Run application tests, if built with testing enabled. 41 | .TP 42 | .B \-v 43 | Print verbose output when running tests. 44 | .TP 45 | .B \-categories [category names] 46 | Only run tests within the listed categories. 47 | -------------------------------------------------------------------------------- /project-scripts/OverflowSeek.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### DeadLinkF.sh ############################################################ 3 | # Opens Vim editing the first detected line in the project that exceeds the 80 4 | # character line length limit. 5 | #--- Usage: --- 6 | # ./project-scripts/OverflowSeek.sh 7 | ################################################################################ 8 | 9 | 10 | #==============================================================================# 11 | #--- findMatch: --- 12 | # Recursively searches within a directory until a line longer than 80 characters 13 | # is found, or all files have been searched. The $match variable will be set to 14 | # a string contaiing the match file and line, or the empty string if no match is 15 | # found. 16 | #--- Parameters: --- 17 | # $searchDir: A directory path to recursively search for files with lines 18 | # exceeding 80 characters. 19 | #==============================================================================# 20 | function findMatch() 21 | { 22 | local searchDir=$1 23 | match=`grep -rn '.\{81\}' $searchDir | head -1` 24 | } 25 | 26 | # In order, scan the main source code directory, the test class directory, and 27 | # the project script directory. 28 | findMatch './Source' 29 | if [ -z "$match" ]; then 30 | findMatch './Tests' 31 | fi 32 | if [ -z "$match" ]; then 33 | findMatch './project-scripts' 34 | fi 35 | if [ -z "$match" ]; then 36 | echo "No lines found over 80 characters." 37 | exit 0; 38 | fi 39 | file=`echo "$match" | cut -d: -f1` 40 | line=`echo "$match" | cut -d: -f2` 41 | 42 | echo "Fixing line $line of file $file" 43 | 44 | vim +$line $file 45 | -------------------------------------------------------------------------------- /Source/GUI/Input/Key/Input_Key_ConfigFile.cpp: -------------------------------------------------------------------------------- 1 | #include "Input_Key_ConfigFile.h" 2 | #define INPUT_KEY_CONFIG_IMPLEMENTATION 3 | 4 | #include "Input_Key_JSONResource.h" 5 | #include "Input_Key_Binding.h" 6 | 7 | Input::Key::ConfigFile::ConfigFile() { } 8 | 9 | 10 | // Gets the JUCE KeyPress bound to a particular action. 11 | juce::KeyPress Input::Key::ConfigFile::getBoundKey 12 | (const juce::Identifier& bindingID) const 13 | { 14 | return getKeyBinding(bindingID)->getKeyPress(); 15 | } 16 | 17 | 18 | // Gets a localized description of this key's action. 19 | juce::String Input::Key::ConfigFile::getActionDescription 20 | (const juce::Identifier& bindingID) 21 | const 22 | { 23 | return getKeyBinding(bindingID)->getActionDescription(); 24 | } 25 | 26 | 27 | // Gets the name that should be used to describe the bound key. 28 | juce::String Input::Key::ConfigFile::getKeyName 29 | (const juce::Identifier& bindingID) 30 | const 31 | { 32 | return getKeyBinding(bindingID)->getKeyName(); 33 | } 34 | 35 | 36 | // Gets a single key character that may be used to represent the bound key. 37 | Text::CharValue Input::Key::ConfigFile::getKeyChar 38 | (const juce::Identifier& bindingID) 39 | const 40 | { 41 | return getKeyBinding(bindingID)->getCharName(); 42 | } 43 | 44 | 45 | // Loads all key binding information for an action from the configuration file. 46 | const Input::Key::Binding* Input::Key::ConfigFile::getKeyBinding 47 | (const juce::Identifier& bindingID) const 48 | { 49 | SharedResource::LockedPtr jsonResource 50 | = getReadLockedResource(); 51 | return jsonResource->getKeyBinding(bindingID); 52 | } 53 | -------------------------------------------------------------------------------- /makefiles/Component.mk: -------------------------------------------------------------------------------- 1 | ########################## Component Module #################################### 2 | COMPONENT_DIR = Source/GUI/Component 3 | COMPONENT_TEST_DIR = Tests/GUI/Component 4 | 5 | COMPONENT_PREFIX = Component_ 6 | COMPONENT_OBJ := $(JUCE_OBJDIR)/$(COMPONENT_PREFIX) 7 | 8 | OBJECTS_COMPONENT := \ 9 | $(COMPONENT_OBJ)MainView.o \ 10 | $(COMPONENT_OBJ)KeyGrid.o \ 11 | $(COMPONENT_OBJ)ChordKeyDisplay.o \ 12 | $(COMPONENT_OBJ)CharsetDisplay.o \ 13 | $(COMPONENT_OBJ)ChordPreview.o \ 14 | $(COMPONENT_OBJ)InputView.o \ 15 | $(COMPONENT_OBJ)HelpScreen.o 16 | 17 | COMPONENT_TEST_PREFIX := $(COMPONENT_PREFIX)Test_ 18 | COMPONENT_TEST_OBJ := $(COMPONENT_OBJ)Test_ 19 | OBJECTS_COMPONENT_TEST := 20 | 21 | ifeq ($(BUILD_TESTS), 1) 22 | OBJECTS_COMPONENT := $(OBJECTS_COMPONENT) $(OBJECTS_COMPONENT_TEST) 23 | endif 24 | 25 | component : $(OBJECTS_COMPONENT) 26 | @echo " Built Component module" 27 | 28 | GUI_MODULES := $(GUI_MODULES) component 29 | 30 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_COMPONENT) 31 | 32 | $(COMPONENT_OBJ)MainView.o: \ 33 | $(COMPONENT_DIR)/$(COMPONENT_PREFIX)MainView.cpp 34 | $(COMPONENT_OBJ)KeyGrid.o: \ 35 | $(COMPONENT_DIR)/$(COMPONENT_PREFIX)KeyGrid.cpp 36 | $(COMPONENT_OBJ)ChordKeyDisplay.o: \ 37 | $(COMPONENT_DIR)/$(COMPONENT_PREFIX)ChordKeyDisplay.cpp 38 | $(COMPONENT_OBJ)CharsetDisplay.o: \ 39 | $(COMPONENT_DIR)/$(COMPONENT_PREFIX)CharsetDisplay.cpp 40 | $(COMPONENT_OBJ)ChordPreview.o: \ 41 | $(COMPONENT_DIR)/$(COMPONENT_PREFIX)ChordPreview.cpp 42 | $(COMPONENT_OBJ)InputView.o: \ 43 | $(COMPONENT_DIR)/$(COMPONENT_PREFIX)InputView.cpp 44 | $(COMPONENT_OBJ)HelpScreen.o: \ 45 | $(COMPONENT_DIR)/$(COMPONENT_PREFIX)HelpScreen.cpp 46 | -------------------------------------------------------------------------------- /project-scripts/BuildLabel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### BuildLabel.sh ############################################################## 3 | # Generates an appropriate build label to apply while compiling. 4 | ################################################################################ 5 | 6 | # Removes quotes from around a string parameter and prints the result. 7 | unquote() 8 | { 9 | unquoted="${1%\"}" 10 | unquoted="${unquoted#\"}" 11 | echo $unquoted 12 | } 13 | 14 | # Attempts to generate and print a build label using the contents of a release 15 | # file, printing an appropriate build name if the file contains expected data. 16 | readReleaseFile() 17 | { 18 | filePath=$1 19 | buildName=`grep -oP '(?<=^PRETTY_NAME=).+' $filePath` 20 | if [ -z "$buildName" ]; then 21 | buildName=`grep -oP '(?<=^NAME=).+' $filePath` 22 | buildName=`unquote "$buildName"` 23 | version=`grep -oP '(?<=^VERSION=").+' $filePath` 24 | if [ -n "$version" ]; then 25 | version=`unquote "$version"` 26 | buildName="$buildName $version" 27 | fi 28 | fi 29 | buildName=`unquote "$buildName"` 30 | 31 | if [ -z "$buildName" ]; then 32 | echo "Unknown" 33 | else 34 | echo $buildName 35 | fi 36 | } 37 | 38 | # Standard and alternate paths to the Linux OS release files: 39 | releasePath="/etc/os-release" 40 | altReleasePath="/usr/lib/os-release" 41 | 42 | # Attempt to read build information from either release file: 43 | if [ -f $releasePath ]; then 44 | readReleaseFile $releasePath 45 | elif [ -f $altReleasePath ]; then 46 | readReleaseFile $altReleasePath 47 | else 48 | echo "Unknown" 49 | fi 50 | -------------------------------------------------------------------------------- /Source/GUI/Theme/Colour/Theme_Colour_Element.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Theme_Colour_Element.h 4 | * 5 | * @brief Stores information about a configurable UI colour element. 6 | */ 7 | 8 | #include "Theme_Colour_UICategory.h" 9 | #include "JuceHeader.h" 10 | 11 | namespace Theme { namespace Colour { class Element; } } 12 | 13 | /** 14 | * @brief Stores the JUCE ColourId value, and general colour category of a UI 15 | * colour element. 16 | */ 17 | class Theme::Colour::Element 18 | { 19 | public: 20 | /** 21 | * @brief Constructs a colour element object for a JUCE ColourID value. 22 | * 23 | * @param colourId The element's JUCE ColourID value. 24 | * 25 | * @param category The UI element category that best matches this element. 26 | */ 27 | Element(const int colourId, 28 | const UICategory category = UICategory::none); 29 | 30 | /** 31 | * @brief Creates an empty colour element object. 32 | */ 33 | Element(); 34 | 35 | virtual ~Element() { } 36 | 37 | /** 38 | * @brief Gets this object's JUCE ColourId value. 39 | * 40 | * @return The object's ID, or zero if the object is empty. 41 | */ 42 | int getColourId() const; 43 | 44 | /** 45 | * @brief Gets this object's UI element category. 46 | * 47 | * @return The UI element category that best matches this object, or 48 | * UICategory::none if the object is empty. 49 | */ 50 | UICategory getColourCategory() const; 51 | 52 | private: 53 | // The element's JUCE ColourId value: 54 | int colourId; 55 | // The element's general UI element category 56 | UICategory category; 57 | }; 58 | -------------------------------------------------------------------------------- /docs/implementation/NewClasses.md: -------------------------------------------------------------------------------- 1 | # Adding New Module Classes 2 | 1. Use the [module documentation](../Modules.md) to select an appropriate place to put the new class. Prefer simple, descriptive class names, using module and submodule namespaces to distinguish different classes with similar uses. Include the class name and full namespace within the header file name. For example, if class "Example" is in module "Guide", submodule "Docs", name it Guide_Docs_Example.h. 3 | 2. Follow the [code documentation guide](../style/Documentation.md) when writing header files. 4 | 3. A source file with empty function definitions can be automatically generated with appropriate formatting using the [CppGen.pl script](../../project-scripts/CppGen.pl). From the project's root directory, if your header file is at `Source/Guide/Docs/Example`, run `./project-scripts/CppGen.pl Source/Guide/Docs/Example/Guide_Docs_Example.h Source/Guide/Docs/Example/Guide_Docs_Example.cpp`. You'll need to have Perl 5 and the File::Slurp perl module installed to use this script. 5 | 4. Add the new source file to the module's makefile. In our example, the makefile would be at `makefiles/Guide.mk` within the project's root directory. Add a new object file target to the build rule for the lowest submodule level containing the class, and add a new rule to build the object file from your class source file. In our example, you would add `$(GUIDE_DOC_OBJ)Example.o` as a new target for `OBJECTS_GUIDE_DOCS`, and add the rule `$(GUIDE_DOC_OBJ)Example.o: $(GUIDE_DOC_DIR)/$(GUIDE_DOC_PREFIX)Example.cpp`. 6 | 5. Within the module's markdown documentation file in `[ProjectDir]/docs/modules`, add section linking to your class header file, along with a brief description of the class. 7 | -------------------------------------------------------------------------------- /project-scripts/ColourID/Menus/NamespaceListMenu.pm: -------------------------------------------------------------------------------- 1 | ##### NamespaceListMenu.pm ##################################################### 2 | # Constructs the namespace management menu. 3 | ################################################################################ 4 | 5 | #==============================================================================# 6 | #--- openMenu: --- 7 | # Displays the namespace list menu, repeatedly accepting input and running the 8 | # menu action with the selected option parameter until the user enters 'q'. 9 | #--- Parameters: --- 10 | # $cache: an IDCache object to use for menu options. 11 | #==============================================================================# 12 | 13 | use strict; 14 | use warnings; 15 | 16 | package NamespaceListMenu; 17 | use lib './project-scripts/ColourID/Menus'; 18 | use InputMenu; 19 | use NamespaceMenu; 20 | 21 | # Displays the namespace list menu, repeatedly accepting input and running the 22 | # menu action with the selected option parameter until the user enters 'q'. 23 | sub openMenu 24 | { 25 | my $cache = shift; 26 | my $menu = new InputMenu( 27 | "Edit ColourId namespaces:", 28 | "Return to main menu", 29 | \&NamespaceMenu::openMenu, 30 | $cache); 31 | my $refreshAction = sub 32 | { 33 | print("Running refresh...\n"); 34 | my $menu = shift; 35 | $menu->clearOptions(); 36 | my @namespaceNames = $cache->getNamespaceNames(); 37 | foreach my $name (@namespaceNames) 38 | { 39 | $menu->addOption($name, $name); 40 | } 41 | }; 42 | $menu->setRefreshAction($refreshAction); 43 | $menu->openMenu(); 44 | } 45 | 1; 46 | -------------------------------------------------------------------------------- /docs/BuildAndInstall.md: -------------------------------------------------------------------------------- 1 | # Building and Installing KeyChord for ClockworkPi GameShell 2 | These instructions will be improved shortly. 3 | 4 | ## 1. Remapping Light Keys: 5 | 6 | To properly use the light keys for chorded keyboard entry, the key module needs to be remapped so that LK3 is no longer assigned as shift. Follow [these instructions](https://forum.clockworkpi.com/t/tutorial-how-to-compile-and-upload-code-to-the-keypad/1065) to get everything set up to update the keypad. I've included my alternate Arduino keypad code in the clockworkpi_Keypad directory. This will remap LK3 to 'z', and shift + LK3 to f12. 7 | 8 | All steps from this point on should be done on the Gameshell over SSH. 9 | 10 | ## 2. Building KeyChord from Source: 11 | 12 | #### - Install Required Packages 13 | sudo apt-get install \ 14 | git \ 15 | build-essential \ 16 | libx11-dev \ 17 | libxrandr-dev \ 18 | libxcursor-dev \ 19 | libxft-dev \ 20 | libxinerama-dev 21 | 22 | #### - Clone, Build, and Install 23 | git clone --recursive https://www.github.com/centuryglass/KeyChord 24 | cd KeyChord 25 | make 26 | make install 27 | 28 | ## 3. Configuring KeyChord to launch with shift + LK3 29 | - Install xbindkeys with `sudo apt-get install xbindkeys` 30 | - Create or modify the file at /home/cpi/.xbindkeysrc to launch KeyChord when F12 is pressed. 31 | * You can set that up by running `echo -e "\"KeyChord\"\n F12" >> /home/cpi/.xbindkeysrc` 32 | - Setup xbindkeys to run automatically on launch. 33 | * Edit /home/cpi/launcher/.xinitrc. 34 | * Within that file, you'll see two lines that start with "feh --bg-center". 35 | * Under each of those lines, add `xbindkeys` as a new line. 36 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/SharedResource_Resource.cpp: -------------------------------------------------------------------------------- 1 | #define SHARED_RESOURCE_IMPLEMENTATION 2 | #include "SharedResource_Resource.h" 3 | #include "SharedResource_Holder.h" 4 | 5 | // Creates the single resource object instance. 6 | SharedResource::Resource::Resource(const juce::Identifier& resourceKey) : 7 | Instance(resourceKey) { } 8 | 9 | 10 | // Packages an asynchronous action so that it will check if the SharedResource 11 | // instance that created it still valid, and if so, ensure it remains valid 12 | // while the action is executed. 13 | std::function SharedResource::Resource::buildAsyncFunction( 14 | SharedResource::LockType lockType, 15 | std::function action, 16 | std::function ifDestroyed) 17 | { 18 | const juce::Identifier& resKey = getResourceKey(); 19 | return [this, lockType, resKey, action, ifDestroyed]() 20 | { 21 | Holder* resourceHolder = Holder::getHolderInstance(); 22 | if (lockType == LockType::read) 23 | { 24 | juce::ScopedReadLock(resourceHolder->getResourceLock(resKey)); 25 | if (this == resourceHolder->getResource(resKey)) 26 | { 27 | action(); 28 | return; 29 | } 30 | } 31 | else 32 | { 33 | juce::ScopedWriteLock(resourceHolder->getResourceLock(resKey)); 34 | if (this == resourceHolder->getResource(resKey)) 35 | { 36 | action(); 37 | return; 38 | } 39 | } 40 | DBG("SharedResource::Resource: Cancelled async function, resource " 41 | << resKey.toString() << " was destroyed."); 42 | ifDestroyed(); 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /docs/modules/Component.md: -------------------------------------------------------------------------------- 1 | # Component Module Documentation 2 | The Component module provides classes that create all interface elements displayed within the application. 3 | 4 | #### [Component\::MainView](../../Source/GUI/Component/Component_MainView.h) 5 | MainView holds and arranges all other Component objects within the application's window. 6 | 7 | #### [Component\::KeyGrid](../../Source/GUI/Component/Component_KeyGrid.h) 8 | KeyGrid provides a basis for Component classes that react to user input by drawing text. KeyGrid uses the [Text](./Text.md) module to store and render text. 9 | 10 | #### [Component\::CharsetDisplay](../../Source/GUI/Component/Component_CharsetDisplay.h) 11 | CharsetDisplay is a KeyGrid class that displays all characters in the active character set, highlighting the selected character. 12 | 13 | #### [Component\::ChordKeyDisplay](../../Source/GUI/Component/Component_ChordKeyDisplay.h) 14 | ChordKeyDisplay is a KeyGrid class that represents the application's [Chord](../../Source/GUI/Input/Input_Chord.h) input keys, highlighting the ones that are currently held down. 15 | 16 | #### [Component\::ChordPreview](../../Source/GUI/Component/Component_ChordPreview.h) 17 | ChordPreview is a KeyGrid class that displays the Chord key combination for each character in the active character set, highlighting selected or partially selected chords. 18 | 19 | #### [Component\::InputView](../../Source/GUI/Component/Component_InputView.h) 20 | InputView is a KeyGrid class that displays all input recorded by KeyChord that is waiting to be sent to the target window. 21 | 22 | #### [Component\::HelpScreen](../../Source/GUI/Component/Component_HelpScreen.h) 23 | HelpScreen is a KeyGrid class that displays the key bindings used to control KeyChord. 24 | -------------------------------------------------------------------------------- /clockworkpi_Keypad/PinCodes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file PinCodes.h 3 | * 4 | * @brief Defines KeyPad button pin codes 5 | */ 6 | 7 | // Value read when a button is released: 8 | #define KEY_OFF 1 9 | // Value read when a button is pressed: 10 | #define KEY_ON 0 11 | // Threshold to register an analog button press: 12 | #define ADC_BOUNDARY 500 13 | // Number of digital key pins: 14 | #define DIGITAL_KEY_NUM 9 15 | // Number of analog key pins: 16 | #define ANALOG_KEY_NUM 7 17 | // Total pin count: 18 | #define KEY_NUM (DIGITAL_KEY_NUM + ANALOG_KEY_NUM) 19 | 20 | // ### All keypad pins: ### 21 | // Digital Pins: 22 | #define KEYPAD_PIN_UP 3 23 | #define KEYPAD_PIN_LEFT 6 24 | #define KEYPAD_PIN_DOWN 7 25 | #define KEYPAD_PIN_RIGHT 8 26 | #define KEYPAD_PIN_Y 9 27 | #define KEYPAD_PIN_X 10 28 | #define KEYPAD_PIN_A 11 29 | #define KEYPAD_PIN_B 12 30 | #define KEYPAD_PIN_START 13 31 | // Analog Pins: 32 | #define KEYPAD_PIN_MENU 0 33 | #define KEYPAD_PIN_SELECT 2 34 | #define KEYPAD_PIN_LIGHT1 3 35 | #define KEYPAD_PIN_LIGHT2 4 36 | #define KEYPAD_PIN_LIGHT3 5 37 | #define KEYPAD_PIN_LIGHT4 6 38 | #define KEYPAD_PIN_LIGHT5 7 39 | // Shift Modifier Pin (Analog): 40 | #define KEYPAD_PIN_SHIFT 1 41 | 42 | // ### Pin indices: ### 43 | // The main loop checks pins in this order. 44 | enum 45 | { 46 | INDEX_PIN_UP = 0, 47 | INDEX_PIN_LEFT, 48 | INDEX_PIN_DOWN, 49 | INDEX_PIN_RIGHT, 50 | INDEX_PIN_Y, 51 | INDEX_PIN_X, 52 | INDEX_PIN_A, 53 | INDEX_PIN_B, 54 | INDEX_PIN_START, 55 | INDEX_PIN_MENU, 56 | INDEX_PIN_SELECT, 57 | INDEX_PIN_LIGHT1, 58 | INDEX_PIN_LIGHT2, 59 | INDEX_PIN_LIGHT3, 60 | INDEX_PIN_LIGHT4, 61 | INDEX_PIN_LIGHT5 62 | }; 63 | -------------------------------------------------------------------------------- /project-scripts/Paths.pm: -------------------------------------------------------------------------------- 1 | ##### Paths.pm: ################################################################ 2 | # Defines project colour file paths 3 | ################################################################################ 4 | use strict; 5 | use warnings; 6 | 7 | package Paths; 8 | use FindBin; 9 | use Cwd; 10 | 11 | # Project script directory: 12 | use constant SCRIPT_DIR 13 | => $FindBin::Bin; 14 | 15 | # Main project directory: 16 | use constant PROJECT_DIR 17 | => Cwd::abs_path(SCRIPT_DIR.'/..'); 18 | 19 | # Main source code directory: 20 | use constant SOURCE_DIR 21 | => PROJECT_DIR."/Source"; 22 | 23 | # Test code directory: 24 | use constant TEST_DIR 25 | => PROJECT_DIR."/Tests"; 26 | 27 | # Documentation directory: 28 | use constant DOC_DIR 29 | => PROJECT_DIR."/docs"; 30 | 31 | # Theme::Colour directory: 32 | use constant COLOUR_DIR 33 | => SOURCE_DIR."/GUI/Theme/Colour"; 34 | 35 | # ColourId header file: 36 | use constant COLOUR_ID_HEADER 37 | => COLOUR_DIR."/Theme_Colour_ColourIds.h"; 38 | 39 | # JSON colour key source file: 40 | use constant COLOUR_KEY_SOURCE 41 | => COLOUR_DIR."/Theme_Colour_JSONKeys.cpp"; 42 | 43 | # Default colour configuration file: 44 | use constant DEFAULT_COLOUR_CONFIG 45 | => PROJECT_DIR."/assets/configuration/colours.json"; 46 | 47 | # Make sure the script isn't running from somewhere strange, as that will 48 | # result in incorrect path values; 49 | if(SCRIPT_DIR ne PROJECT_DIR.'/project-scripts') 50 | { 51 | die("Paths.pm: Unexpected directories found, make sure your script is " 52 | ."running within the main project script directory.\n" 53 | ."Actual script directory: ".SCRIPT_DIR."\n" 54 | ."This script will now exit.\n"); 55 | } 56 | 1; 57 | -------------------------------------------------------------------------------- /Source/Development/Debug/Debug_AddressLog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Debug_AddressLog.h 4 | * 5 | * @brief Keeps and prints logs of events associated with specific memory 6 | * address values. 7 | */ 8 | 9 | #include "JuceHeader.h" 10 | 11 | namespace Debug 12 | { 13 | namespace AddressLog 14 | { 15 | /** 16 | * @brief Gets a unique, fixed ID value to represent a particular 17 | * memory address. Nullptr will always have ID 0. 18 | * 19 | * @param address Any stored memory address. 20 | * 21 | * @return An ID number unique to the given pointer. 22 | */ 23 | int getID(const void* address); 24 | 25 | /** 26 | * @brief Appends a line of text to the log of events occurring to a 27 | * specific address. 28 | * 29 | * @param address Any stored memory address. 30 | * 31 | * @param event A description of some event that occurred involving 32 | * the address. 33 | * 34 | * @param address2 An optional second pointer involved in the event. 35 | * If non-null, its ID will be appended onto the end 36 | * of the event log. 37 | * 38 | * @return A reference to the full event log for the address. 39 | */ 40 | const juce::String& addEvent(const void* address, 41 | const juce::String event, const void* address2 = nullptr); 42 | 43 | /** 44 | * @brief Prints all logged events for a specific memory address 45 | * 46 | * @param addressID The address being debugged. 47 | */ 48 | void printLog(int addressID); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/GUI/Text/Text_BinaryFont.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Text_BinaryFont.h 3 | * 4 | * @brief Loads low-res character image data encoded in binary. 5 | */ 6 | 7 | #include "Text_CharTypes.h" 8 | #include "JuceHeader.h" 9 | 10 | namespace Text 11 | { 12 | namespace BinaryFont 13 | { 14 | // The width and height of each character: 15 | static const constexpr int charSize = 10; 16 | 17 | /** 18 | * @brief Gets one row of image data for a specific character. 19 | * 20 | * @param character Either a standard printable character within the 21 | * ISO-8859 character set, or one of the nonstandard 22 | * character values defined in this namespace 23 | * 24 | * @param row The index of a row of pixels in the character 25 | * image, ordered from top to bottom. 26 | * 27 | * @return A bitmap storing black and white image data for 28 | * that row. 29 | */ 30 | juce::uint16 getCharacterRow(const CharValue character, 31 | const int row); 32 | 33 | /** 34 | * @brief Gets one row of image data for a double-wide character. 35 | * 36 | * @param character One of the double-wide character values defined 37 | * in this namespace. 38 | * 39 | * @param row The index of a row of pixels in the character 40 | * image, ordered from top to bottom. 41 | * 42 | * @return A bitmap storing black and white image data for 43 | * that row. 44 | */ 45 | juce::uint32 getDoubleCharRow(const CharValue character, 46 | const int row); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/SharedResource_LockedPtr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file SharedResource_LockedPtr.h 4 | * 5 | * @brief Provides access to a specific SharedResource::Resource, keeping it 6 | * locked for as long as the LockedPtr exists. 7 | */ 8 | 9 | #include "SharedResource_LockedInstancePtr.h" 10 | #include "SharedResource_LockType.h" 11 | 12 | namespace SharedResource { template class LockedPtr; } 13 | 14 | /** 15 | * @brief A scoped lock object that can be used to access the resource object 16 | * it locks. 17 | * 18 | * @tparam ResourceType The SharedResource::Resource subclass this LockedPtr 19 | * accesses. 20 | */ 21 | template 22 | class SharedResource::LockedPtr : public LockedInstancePtr 23 | { 24 | private: 25 | // LockedPtr objects should only be created and used internally by 26 | // Handler objects. 27 | template friend class Handler; 28 | 29 | /** 30 | * @brief Locks the ResourceType resource for as long as the LockedPtr 31 | * exists. 32 | * 33 | * @param resourceKey The unique key string identifying the ResourceType 34 | * Resource class. 35 | * 36 | * @param lockType The type of lock used to secure the resource. 37 | */ 38 | LockedPtr(const juce::Identifier& resourceKey, const LockType lockType) : 39 | LockedInstancePtr(resourceKey, lockType) { } 40 | 41 | public: 42 | virtual ~LockedPtr() { } 43 | 44 | /** 45 | * @brief Accesses the locked resource's functions or data. 46 | * 47 | * @return The address of the locked resource object instance. 48 | */ 49 | ResourceType* operator->() const 50 | { 51 | return static_cast(getInstance()); 52 | } 53 | }; 54 | 55 | -------------------------------------------------------------------------------- /docs/Configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration Guide 2 | This page links to guides to all configuration files used by this application. These files store all configurable options as JSON data. These files are all stored in the `~/XDG_CONFIG_HOME/KeyChord` directory, or `~/.config/KeyChord` if the XDG_CONFIG_HOME environment variable is not defined. 3 | 4 | #### [KeyChord General Configuration](./configuration/config.md): 5 | Setting general configuration options using config.json. 6 | 7 | #### [Character Set Configuration](./configuration/charSets.md): 8 | Configuring displayed keyboard character sets using charSets.json. 9 | 10 | #### [Keyboard Binding Configuration](./configuration/charSets.md): 11 | Configuring application control keys using charSets.json. 12 | 13 | #### [Colour Configuration](./configuration/colours.md): 14 | Configuring application UI colors using colours.json. 15 | 16 | ## Default Configuration Files 17 | Default versions of each configuration file are located in `KeyChord/assets/configuration`. On installation, these files are copied to `/usr/share/KeyChord/configuration`. Any values not defined in user configuration files will be copied in from these default files. 18 | 19 | ## Alternate Configuration Files 20 | Several alternate configuration file defaults are provided as examples: 21 | 22 | #### [Alternate Character Set Configuration](./configuration/charSets_alt.md): 23 | This example extends the default letter character set from the Latin alphabet to the Dano-Norwegian alphabet. 24 | 25 | #### [Alternate GameShell Key Configuration](./configuration/keyBindings_alt.md): 26 | This example provides an alternate set of keybindings for the GameShell that do not use the light key module. 27 | 28 | #### [PC Key Configuration](./configuration/keyBindings_pc.md): 29 | This example provides a set of keybindings useful for testing KeyChord on a standard PC keyboard. 30 | -------------------------------------------------------------------------------- /Tests/Files/Assets/Assets_XpmTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Assets_XPMLoader.h" 2 | #include "Assets.h" 3 | #include "Testing_Window.h" 4 | #include "Widgets_DrawableImage.h" 5 | #include "JuceHeader.h" 6 | #include 7 | 8 | // Test window bounds: 9 | static const constexpr int winX = 5; 10 | static const constexpr int winY = 5; 11 | static const constexpr int winWidth = 480; 12 | static const constexpr int winHeight = 272; 13 | 14 | // Test file paths: 15 | static const juce::StringArray testFiles = 16 | { 17 | "testing/XPM/test1.xpm", 18 | "testing/XPM/test2.xpm", 19 | "testing/XPM/test3.xpm" 20 | }; 21 | 22 | namespace Assets { class XpmTest; } 23 | 24 | /** 25 | * @brief Tests loading images with the .xpm image format using 26 | * Assets::xpmImage. 27 | */ 28 | class Assets::XpmTest : public juce::UnitTest 29 | { 30 | public: 31 | XpmTest() : juce::UnitTest("xpm Image Testing", 32 | "Assets") {} 33 | 34 | 35 | void runTest() override 36 | { 37 | beginTest("Displaying xpm images"); 38 | 39 | int testIndex = 0; 40 | for (const juce::String& assetPath : testFiles) 41 | { 42 | juce::File assetFile(findAssetFile(assetPath, false)); 43 | expect(assetFile.existsAsFile(), assetPath + " not found!"); 44 | juce::Image xpmImage = Assets::XPMLoader::loadXPMImage(assetFile); 45 | expect(xpmImage.isValid(), assetPath 46 | + " not loaded as a valid image."); 47 | Widgets::DrawableImage* drawnImage 48 | = new Widgets::DrawableImage(xpmImage); 49 | Testing::Window imageWindow("XPM Testing", drawnImage, winX, winY, 50 | winWidth, winHeight); 51 | juce::MessageManager::getInstance()->runDispatchLoopUntil(5000); 52 | } 53 | 54 | } 55 | }; 56 | 57 | static Assets::XpmTest test; 58 | -------------------------------------------------------------------------------- /project-scripts/DeadLinkFinder.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Finds and prints dead links within all markdown documentation files. 3 | use strict; 4 | use warnings; 5 | use File::Find; 6 | use File::Slurp; 7 | use lib './project-scripts'; 8 | use Paths; 9 | 10 | sub findMatches 11 | { 12 | if ($File::Find::name =~ /md$/) 13 | { 14 | my @lines = read_file($File::Find::name); 15 | my @badLinks; 16 | for (my $i = 0; $i < scalar @lines; $i++) 17 | { 18 | my $line = $lines[$i]; 19 | my @links = ($line =~ / 20 | (? /dev/null 2>&1") == 0) 29 | { 30 | next; 31 | } 32 | else 33 | { 34 | die ("\"$link\" was not a valid webpage.\n"); 35 | } 36 | } 37 | elsif (-f $link) 38 | { 39 | next; 40 | } 41 | push(@badLinks, { _link => $link, _line => $i }); 42 | } 43 | } 44 | 45 | my $badLinkCount = @badLinks; 46 | if ($badLinkCount > 0) 47 | { 48 | print("\nFound $badLinkCount link(s) in $File::Find::name:\n"); 49 | foreach my $link (@badLinks) 50 | { 51 | print("\tLine $link->{_line}: $link->{_link}\n"); 52 | } 53 | } 54 | } 55 | } 56 | find(\&findMatches, Paths::DOC_DIR); 57 | -------------------------------------------------------------------------------- /Source/GUI/Component/Component_InputView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Component_InputView.h 4 | * 5 | * @brief Shows a preview of buffered input text. 6 | */ 7 | 8 | #include "Text_CharTypes.h" 9 | #include "JuceHeader.h" 10 | 11 | namespace Component { class InputView; } 12 | 13 | /** 14 | * @brief Shows a preview of buffered input text. 15 | * 16 | * User entered text that has not yet been sent to the target window is 17 | * displayed in the InputView. This includes active modifiers, always printed 18 | * before all other text. If immediate mode is enabled and all normal keys 19 | * are sent immediately to the target window, InputView will instead show any 20 | * active modifiers, followed by "(Immediate input mode)", or the localized 21 | * equivalent. 22 | */ 23 | class Component::InputView : public juce::Component 24 | { 25 | public: 26 | InputView() { } 27 | virtual ~InputView() { } 28 | 29 | /** 30 | * @brief Custom input preview colors: 31 | */ 32 | enum ColourIds 33 | { 34 | // Color used to draw text: 35 | text = 0x1900200, 36 | // Component background: 37 | background = 0x1900201, 38 | // Component outline: 39 | outline = 0x1900202, 40 | // Color used to highlight/underline entered text: 41 | inputHighlight = 0x1900203 42 | }; 43 | 44 | /** 45 | * @brief Updates the array of character indices the InputView will draw. 46 | * 47 | * @param updatedInput The buffered input text string. 48 | */ 49 | void updateInputText(const Text::CharString updatedInput); 50 | 51 | private: 52 | /** 53 | * @brief Draws the buffered input text. 54 | * 55 | * @param g The JUCE graphics context. 56 | */ 57 | void paint(juce::Graphics& g) override; 58 | 59 | // Cached input text: 60 | Text::CharString inputText; 61 | }; 62 | -------------------------------------------------------------------------------- /Source/GUI/Input/Key/Input_Key_JSONResource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef INPUT_KEY_CONFIG_IMPLEMENTATION 3 | #error "Private JSONResource included outside of Key::ConfigFile!" 4 | #endif 5 | /** 6 | * @file Input_Key_JSONResource.h 7 | * 8 | * @brief Controls access to the keyBindings.json configuration file. 9 | */ 10 | 11 | #include "Input_Key_Binding.h" 12 | #include "Config_FileResource.h" 13 | #include "JuceHeader.h" 14 | 15 | namespace Input { namespace Key { class JSONResource; } } 16 | 17 | class Input::Key::JSONResource : public Config::FileResource 18 | { 19 | public: 20 | // SharedResource object key: 21 | static const juce::Identifier resourceKey; 22 | 23 | /** 24 | * @brief Loads the list of key bindings on construction. 25 | */ 26 | JSONResource(); 27 | 28 | virtual ~JSONResource() { } 29 | 30 | /** 31 | * @brief Gets an object representing a particular key binding. 32 | * 33 | * @param keyID An identifier selecting a specific keyboard action. 34 | * 35 | * @return Stored data about that action and its bound key. 36 | */ 37 | const Binding* getKeyBinding(const juce::Identifier& keyID) const; 38 | 39 | private: 40 | /** 41 | * @brief Gets the set of all basic(non-array, non-object) properties 42 | * tracked by this Resource. 43 | * 44 | * @return An empty list, as all stored values are custom Binding objects. 45 | */ 46 | const std::vector& getConfigKeys() const final override; 47 | 48 | /** 49 | * @brief Checks if a key string is valid for this FileResource. 50 | * 51 | * @param key A key string value to check. 52 | * 53 | * @return Whether the key is a valid action ID. 54 | */ 55 | bool isValidKey(const juce::Identifier& key) const override; 56 | 57 | // Map each binding ID to its stored values: 58 | juce::OwnedArray keyBindings; 59 | }; 60 | -------------------------------------------------------------------------------- /Source/GUI/Component/Component_InputView.cpp: -------------------------------------------------------------------------------- 1 | #include "Component_InputView.h" 2 | #include "Text_Values.h" 3 | #include "Text_Painter.h" 4 | 5 | // Layout constants: 6 | 7 | // Component outline, as a fraction of height: 8 | static const constexpr float outlineFraction = 0.05; 9 | // Minimum outline size in pixels: 10 | static const constexpr int minimumOutline = 1; 11 | // Maximum character size, as a fraction of height: 12 | static const constexpr float maxCharSize = 0.8; 13 | // Maximum number of text rows allowed: 14 | static const constexpr int maxRows = 1; 15 | 16 | 17 | // Updates the array of character indices the InputView will draw. 18 | void Component::InputView::updateInputText 19 | (const juce::Array updatedInput) 20 | { 21 | inputText = updatedInput; 22 | repaint(); 23 | } 24 | 25 | 26 | // Draws the buffered input text. 27 | void Component::InputView::paint(juce::Graphics& g) 28 | { 29 | juce::Rectangle bounds = getLocalBounds(); 30 | 31 | // draw background: 32 | g.setColour(findColour(background)); 33 | g.fillRect(bounds); 34 | 35 | // draw outline: 36 | const int outlineSize = std::max(getHeight() * outlineFraction, 37 | minimumOutline); 38 | g.setColour(findColour(outline)); 39 | g.drawRect(bounds, outlineSize); 40 | bounds.reduce(outlineSize * 2, outlineSize * 2); 41 | 42 | g.setColour(findColour(inputHighlight)); 43 | const int rightEdge = Text::Painter::paintString(g, inputText, 44 | bounds.getX(), bounds.getY(), 45 | bounds.getWidth(), bounds.getHeight(), 46 | bounds.getHeight() * maxCharSize); 47 | g.fillRect(bounds.getX(), bounds.getY(), 48 | rightEdge - bounds.getX(), bounds.getHeight()); 49 | 50 | g.setColour(findColour(text)); 51 | Text::Painter::paintString(g, inputText, bounds.getX(), bounds.getY(), 52 | bounds.getWidth(), bounds.getHeight(), 53 | bounds.getHeight() * maxCharSize); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /makefiles/SharedResource.mk: -------------------------------------------------------------------------------- 1 | ###################### SharedResource Module ################################### 2 | SHARED_DIR = Source/Framework/SharedResource 3 | SHARED_THREAD_DIR = $(SHARED_DIR)/Thread 4 | SHARED_IMPL_DIR = $(SHARED_DIR)/Implementation 5 | SHARED_TEST_DIR = Tests/Framework/SharedResource 6 | 7 | SHARED_PREFIX = SharedResource_ 8 | SHARED_OBJ := $(JUCE_OBJDIR)/$(SHARED_PREFIX) 9 | 10 | OBJECTS_SHARED_IMPL := \ 11 | $(SHARED_OBJ)Holder.o \ 12 | $(SHARED_OBJ)ReferenceInterface.o \ 13 | $(SHARED_OBJ)Instance.o \ 14 | $(SHARED_OBJ)Reference.o \ 15 | $(SHARED_OBJ)LockedInstancePtr.o 16 | 17 | OBJECTS_SHARED_RESOURCE := \ 18 | $(OBJECTS_SHARED_IMPL) \ 19 | $(OBJECTS_SHARED_THREAD) \ 20 | $(SHARED_OBJ)Resource.o 21 | 22 | SHARED_TEST_PREFIX := $(SHARED_PREFIX)Test_ 23 | SHARED_TEST_OBJ := $(SHARED_OBJ)Test_ 24 | OBJECTS_SHARED_TEST := 25 | 26 | ifeq ($(BUILD_TESTS), 1) 27 | OBJECTS_SHARED_RESOURCE := $(OBJECTS_SHARED_RESOURCE) \ 28 | $(OBJECTS_SHARED_TEST) 29 | endif 30 | 31 | FRAMEWORK_MODULES := $(FRAMEWORK_MODULES) sharedResource 32 | 33 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_SHARED_RESOURCE) 34 | 35 | sharedResource : $(OBJECTS_SHARED_RESOURCE) 36 | @echo " Built SharedResource module" 37 | 38 | $(SHARED_OBJ)Holder.o : \ 39 | $(SHARED_IMPL_DIR)/$(SHARED_PREFIX)Holder.cpp 40 | $(SHARED_OBJ)ReferenceInterface.o : \ 41 | $(SHARED_IMPL_DIR)/$(SHARED_PREFIX)ReferenceInterface.cpp 42 | $(SHARED_OBJ)Instance.o : \ 43 | $(SHARED_IMPL_DIR)/$(SHARED_PREFIX)Instance.cpp 44 | $(SHARED_OBJ)Reference.o : \ 45 | $(SHARED_IMPL_DIR)/$(SHARED_PREFIX)Reference.cpp 46 | $(SHARED_OBJ)LockedInstancePtr.o : \ 47 | $(SHARED_IMPL_DIR)/$(SHARED_PREFIX)LockedInstancePtr.cpp 48 | $(SHARED_OBJ)Resource.o : \ 49 | $(SHARED_DIR)/$(SHARED_PREFIX)Resource.cpp 50 | 51 | $(SHARED_TEST_OBJ)ModuleTest.o : \ 52 | $(SHARED_TEST_DIR)/$(SHARED_TEST_PREFIX)ModuleTest.cpp 53 | $(SHARED_TEST_OBJ)ModuleTestClasses.o : \ 54 | $(SHARED_TEST_DIR)/$(SHARED_TEST_PREFIX)ModuleTestClasses.cpp 55 | -------------------------------------------------------------------------------- /Source/GUI/Component/Component_ColourIds.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Component_ColourIds.h 4 | * 5 | * @brief Defines the shared JUCE ColourId codes used when selecting keyboard 6 | * colors. 7 | */ 8 | 9 | namespace Component 10 | { 11 | /** 12 | * @brief The set of shared colors used by Component::KeyGrid classes. 13 | * 14 | */ 15 | enum ColourIds 16 | { 17 | // Color used to draw text: 18 | text = 0x1900100, 19 | // Color used to draw text for characters that conflict with the current 20 | // selection: 21 | inactiveText = 0x1900101, 22 | // Colors used to draw held chord keys on the selected character: 23 | chord1Selected = 0x1900102, 24 | chord2Selected = 0x1900103, 25 | chord3Selected = 0x1900104, 26 | chord4Selected = 0x1900105, 27 | chord5Selected = 0x1900106, 28 | emptySelected = 0x1900107, // Empty = key should not be held down. 29 | // Colors used to draw held chord keys in chords that don't conflict 30 | // with the current selection: 31 | chord1Active = 0x1900108, 32 | chord2Active = 0x1900109, 33 | chord3Active = 0x190010a, 34 | chord4Active = 0x190010b, 35 | chord5Active = 0x190010c, 36 | emptyActive = 0x190010d, 37 | // Colors used to draw chord keys in chords that aren't held down and 38 | // don't conflict with the current selection: 39 | chord1Open = 0x190010e, 40 | chord2Open = 0x190010f, 41 | chord3Open = 0x1900110, 42 | chord4Open = 0x1900111, 43 | chord5Open = 0x1900112, 44 | emptyOpen = 0x1900113, 45 | // Colors used to draw inactive chord keys that conflict with the 46 | // current selection: 47 | chord1Blocked = 0x1900114, 48 | chord2Blocked = 0x1900115, 49 | chord3Blocked = 0x1900116, 50 | chord4Blocked = 0x1900117, 51 | chord5Blocked = 0x1900118, 52 | emptyBlocked = 0x1900119, 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /Source/Files/Config/Config_DataKey.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "JuceHeader.h" 3 | /** 4 | * @file Config_DataKey.h 5 | * 6 | * @brief Holds the key string and data type of a value in a JSON 7 | * configuration file. 8 | */ 9 | 10 | namespace Config { struct DataKey; } 11 | 12 | /** 13 | * @brief Provides the key and expected value type of a value stored in a 14 | * Config::FileResource's JSON file. 15 | * 16 | * DataKeys are immutable objects, as the keys and types of basic data values 17 | * in configuration files should not change. DataKeys may represent string, 18 | * integer, boolean, or double values. 19 | */ 20 | struct Config::DataKey 21 | { 22 | /** 23 | * @brief All valid key data types. 24 | */ 25 | enum DataType 26 | { 27 | stringType, 28 | intType, 29 | boolType, 30 | doubleType 31 | }; 32 | 33 | /** 34 | * @brief Initializes a DataKey, constructing its key from a C-string. 35 | * 36 | * @param keyString The key string identifying a value stored in a 37 | * configuration file. 38 | * 39 | * @param dataType The data type of that stored value. 40 | */ 41 | DataKey(const char* keyString, const DataType dataType); 42 | 43 | /** 44 | * @brief Initializes a DataKey, constructing its key from an existing 45 | * Identifier. 46 | * 47 | * @param key The key identifying a value stored in a configuration 48 | * file. 49 | * 50 | * @param dataType The data type of that stored value. 51 | */ 52 | DataKey(const juce::Identifier& keyString, const DataType dataType); 53 | 54 | // The value's JSON key: 55 | const juce::Identifier key; 56 | 57 | // The value's data type: 58 | const DataType dataType; 59 | 60 | /** 61 | * @brief Allows the DataKey to be used as if it was just its key value. 62 | * 63 | * @return A reference to the DataKey's Identifier key. 64 | */ 65 | operator const juce::Identifier&() const; 66 | }; 67 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/Implementation/SharedResource_LockedInstancePtr.cpp: -------------------------------------------------------------------------------- 1 | #define SHARED_RESOURCE_IMPLEMENTATION 2 | #include "SharedResource_LockedInstancePtr.h" 3 | #include "SharedResource_Holder.h" 4 | 5 | // Initializes the resource pointer, locking the resource. 6 | SharedResource::LockedInstancePtr::LockedInstancePtr 7 | (const juce::Identifier& resourceKey, const LockType lockType) : 8 | resourceKey(resourceKey), 9 | lockType(lockType) 10 | { 11 | const juce::ReadWriteLock& resourceLock 12 | = Holder::getHolderInstance()->getResourceLock(resourceKey); 13 | if (lockType == LockType::read) 14 | { 15 | resourceLock.enterRead(); 16 | } 17 | else 18 | { 19 | resourceLock.enterWrite(); 20 | } 21 | locked = true; 22 | } 23 | 24 | 25 | // Unlocks the resource when the LockedInstancePtr is destroyed. 26 | SharedResource::LockedInstancePtr::~LockedInstancePtr() 27 | { 28 | unlock(); 29 | } 30 | 31 | 32 | // Unlocks the resource. Once the resource is unlocked, the LockedInstancePtr 33 | // may no longer access the resource Instance or lock. 34 | void SharedResource::LockedInstancePtr::unlock() 35 | { 36 | if (locked) 37 | { 38 | const juce::ReadWriteLock& resourceLock 39 | = Holder::getHolderInstance()->getResourceLock(resourceKey); 40 | if (lockType == LockType::read) 41 | { 42 | resourceLock.exitRead(); 43 | } 44 | else 45 | { 46 | resourceLock.exitWrite(); 47 | } 48 | locked = false; 49 | } 50 | } 51 | 52 | 53 | // Checks if the resource is still locked by this pointer. 54 | bool SharedResource::LockedInstancePtr::isLocked() const 55 | { 56 | return locked; 57 | } 58 | 59 | 60 | // Accesses the resource Instance's functions or data. 61 | SharedResource::Instance* 62 | SharedResource::LockedInstancePtr::getInstance() const 63 | { 64 | if (locked) 65 | { 66 | return Holder::getHolderInstance()->getResource(resourceKey); 67 | } 68 | else 69 | { 70 | return nullptr; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/modules/Theme.md: -------------------------------------------------------------------------------- 1 | # Theme Module Documentation 2 | The Theme module handles tasks related to the general appearance of the application. Its primary responsibility is loading UI images and colours from JSON configuration files. 3 | 4 | #### [Theme\::LookAndFeel](../../Source/GUI/Theme/Theme_LookAndFeel.h) 5 | LookAndFeel controls how the JUCE library draws UI components. This sets the application font and mouse cursor, and defines custom drawing routines for several UI components. LookAndFeel also loads and applies configurable UI colour values. 6 | 7 | ## Theme Colours 8 | The Colour submodule loads and sets UI component colour values from the colours.json configuration file. 9 | 10 | #### [Theme\::Colour\::JSONResource](../../Source/GUI/Theme/Colour/Theme_Colour_JSONResource.h) 11 | JSONResource defines the [SharedResource](./SharedResource.md) class instance used to manage the colours.json file. 12 | 13 | #### [Theme\::Colour\::ColourIds](../../Source/GUI/Theme/Colour/Theme_Colour_ColourIds.h) 14 | The JUCE library assigns a unique ID to each UI component colour value. The ColourIds namespace provides all of these IDs in a single file. 15 | 16 | #### [Theme\::Colour\::UICategory](../../Source/GUI/Theme/Colour/Theme_Colour_UICategory.h) 17 | UICategory defines a set of broad UI component colour categories. Each JUCE ColourId value is assigned to one of these categories, and the JSONResource stores colour values for each category. If no specific colour is provided by the JSONResource for a given ColourId, it uses the colour provided by its UICategory. 18 | 19 | #### [Theme\::Colour\::JSONKeys](../../Source/GUI/Theme/Colour/Theme_Colour_JSONKeys.h) 20 | JSONKeys provides the list of all colour keys that should be present in the colours.json configuration file. It also converts between ColourId and JSON key values, and looks up the generic colour category assigned to any ColourId or JSON key. 21 | 22 | #### [Theme\::Colour\::ConfigFile](../../Source/GUI/Theme/Colour/Theme_Colour_ConfigFile.h) 23 | ConfigFile objects connect to the JSONResource to lookup or change colour values by ColourId value or JSON key. 24 | -------------------------------------------------------------------------------- /Source/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.h" 2 | 3 | #ifdef JUCE_DEBUG 4 | // Print the full class name before all debug output: 5 | static const constexpr char* dbgPrefix = "MainWindow::"; 6 | #endif 7 | 8 | // Milliseconds to wait before closing the application after window focus is 9 | // lost. 10 | static const constexpr int windowTimeout = 2000; 11 | 12 | // Creates and shows the main application window. 13 | MainWindow::MainWindow(juce::String windowName) : 14 | juce::DocumentWindow(windowName, juce::Colours::darkgrey, 15 | juce::DocumentWindow::allButtons) 16 | { 17 | setUsingNativeTitleBar(false); 18 | setResizable(false, false); 19 | setLookAndFeel(&juce::LookAndFeel::getDefaultLookAndFeel()); 20 | setVisible(true); 21 | setWantsKeyboardFocus(false); 22 | setColour(juce::DocumentWindow::backgroundColourId, 23 | juce::LookAndFeel::getDefaultLookAndFeel() 24 | .findColour(backgroundColourId)); 25 | } 26 | 27 | 28 | // Closes the application normally when the window closes. 29 | void MainWindow::closeButtonPressed() 30 | { 31 | juce::JUCEApplication::getInstance()->systemRequestedQuit(); 32 | } 33 | 34 | 35 | // Resizes page content to match the window size. 36 | void MainWindow::resized() 37 | { 38 | const juce::Rectangle bounds = getLocalBounds(); 39 | for (juce::Component* contentComponent : getChildren()) 40 | { 41 | if (contentComponent != nullptr) 42 | { 43 | contentComponent->setBounds(bounds); 44 | } 45 | } 46 | } 47 | 48 | 49 | // Starts the exit timer when the window loses focus, and stops if if the window 50 | // gains focus again. 51 | void MainWindow::activeWindowStatusChanged() 52 | { 53 | if (isActiveWindow()) 54 | { 55 | exitTimer.stopTimer(); 56 | } 57 | else 58 | { 59 | exitTimer.startTimer(windowTimeout); 60 | } 61 | } 62 | 63 | void MainWindow::ExitTimer::timerCallback() 64 | { 65 | DBG(dbgPrefix << __func__ << ": Window focus lost, closing application."); 66 | juce::JUCEApplication::getInstance()->systemRequestedQuit(); 67 | } 68 | -------------------------------------------------------------------------------- /makefiles/Config.mk: -------------------------------------------------------------------------------- 1 | ############################## Config Module ################################### 2 | CONFIG_DIR = Source/Files/Config 3 | CONFIG_IMPL_DIR := $(CONFIG_DIR)/Implementation 4 | CONFIG_TEST_DIR = Tests/Files/Config 5 | 6 | CONFIG_PREFIX = Config_ 7 | CONFIG_OBJ := $(JUCE_OBJDIR)/$(CONFIG_PREFIX) 8 | 9 | OBJECTS_CONFIG_IMPL := \ 10 | $(CONFIG_OBJ)AlertWindow.o 11 | 12 | OBJECTS_CONFIG := \ 13 | $(OBJECTS_CONFIG_IMPL) \ 14 | $(CONFIG_OBJ)FileResource.o \ 15 | $(CONFIG_OBJ)DataKey.o \ 16 | $(CONFIG_OBJ)MainResource.o \ 17 | $(CONFIG_OBJ)MainFile.o 18 | 19 | CONFIG_TEST_PREFIX := $(CONFIG_PREFIX)Test_ 20 | CONFIG_TEST_OBJ := $(CONFIG_OBJ)Test_ 21 | OBJECTS_CONFIG_TEST := \ 22 | $(CONFIG_TEST_OBJ)Resource.o \ 23 | $(CONFIG_TEST_OBJ)FileHandler.o \ 24 | $(CONFIG_TEST_OBJ)Listener.o \ 25 | $(CONFIG_TEST_OBJ)ObjectData.o \ 26 | $(CONFIG_TEST_OBJ)FileTest.o \ 27 | 28 | 29 | ifeq ($(BUILD_TESTS), 1) 30 | OBJECTS_CONFIG := $(OBJECTS_CONFIG) $(OBJECTS_CONFIG_TEST) 31 | endif 32 | 33 | FILE_MODULES := $(FILE_MODULES) config 34 | 35 | OBJECTS_APP := $(OBJECTS_APP) $(OBJECTS_CONFIG) 36 | 37 | config : $(OBJECTS_CONFIG) 38 | @echo " Built Config module" 39 | 40 | $(CONFIG_OBJ)AlertWindow.o: \ 41 | $(CONFIG_IMPL_DIR)/$(CONFIG_PREFIX)AlertWindow.cpp 42 | $(CONFIG_OBJ)FileResource.o: \ 43 | $(CONFIG_DIR)/$(CONFIG_PREFIX)FileResource.cpp 44 | $(CONFIG_OBJ)DataKey.o: \ 45 | $(CONFIG_DIR)/$(CONFIG_PREFIX)DataKey.cpp 46 | $(CONFIG_OBJ)MainResource.o: \ 47 | $(CONFIG_DIR)/$(CONFIG_PREFIX)MainResource.cpp 48 | $(CONFIG_OBJ)MainFile.o: \ 49 | $(CONFIG_DIR)/$(CONFIG_PREFIX)MainFile.cpp 50 | 51 | $(CONFIG_TEST_OBJ)Resource.o: \ 52 | $(CONFIG_TEST_DIR)/$(CONFIG_TEST_PREFIX)Resource.cpp 53 | $(CONFIG_TEST_OBJ)FileHandler.o: \ 54 | $(CONFIG_TEST_DIR)/$(CONFIG_TEST_PREFIX)FileHandler.cpp 55 | $(CONFIG_TEST_OBJ)Listener.o: \ 56 | $(CONFIG_TEST_DIR)/$(CONFIG_TEST_PREFIX)Listener.cpp 57 | $(CONFIG_TEST_OBJ)ObjectData.o: \ 58 | $(CONFIG_TEST_DIR)/$(CONFIG_TEST_PREFIX)ObjectData.cpp 59 | $(CONFIG_TEST_OBJ)FileTest.o: \ 60 | $(CONFIG_TEST_DIR)/$(CONFIG_TEST_PREFIX)FileTest.cpp 61 | -------------------------------------------------------------------------------- /project-scripts/BitFontGen.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Reads in a pixel font from a black and white image file, saving its data to 3 | # Text_BinaryFont.cpp. 4 | 5 | use strict; 6 | use warnings; 7 | use Image::Magick; 8 | use File::Slurp; 9 | use File::Basename; 10 | 11 | use constant IMG_WIDTH => 160; 12 | use constant IMG_HEIGHT => 160; 13 | use constant RELATIVE_OUTPATH => '../Source/GUI/Text/Text_BinaryFont.cpp'; 14 | 15 | my $mapVarName = 'const juce::uint32 fontMap'; 16 | 17 | my $path = $ARGV[0]; 18 | 19 | my $dirname = dirname(__FILE__); 20 | my $outPath = $dirname."/".RELATIVE_OUTPATH; 21 | 22 | if (! -f $path) 23 | { 24 | die("No valid input path!\n"); 25 | } 26 | if (! -f $outPath) 27 | { 28 | die("No valid output path!\n Path tried: \"$outPath\""); 29 | } 30 | 31 | my $image = new Image::Magick; 32 | $image->Read($path); 33 | 34 | my $output = "$mapVarName [".( (IMG_WIDTH / 32) * IMG_HEIGHT)."] = \n{"; 35 | 36 | my ($width, $height) = $image->Get('width', 'height'); 37 | if ($width != IMG_WIDTH || $height != IMG_HEIGHT) 38 | { 39 | die("Invalid image dimensions $width x $height, expected " 40 | .IMG_WIDTH." x ".IMG_HEIGHT."\n"); 41 | } 42 | 43 | foreach my $row (0 ... (IMG_HEIGHT - 1)) 44 | { 45 | my $rowText = " "; 46 | foreach my $column (0 ... (IMG_WIDTH - 1)) 47 | { 48 | if (($column % 32) == 0) 49 | { 50 | if ($column > 0) 51 | { 52 | $rowText = "$rowText, "; 53 | } 54 | $rowText = $rowText."0b"; 55 | } 56 | my @pVal = $image->Get("Pixel[$column, $row]"); 57 | if (substr($pVal[0], 0, 1) eq "0") 58 | { 59 | $rowText = $rowText."1"; 60 | } 61 | else 62 | { 63 | $rowText = $rowText."0"; 64 | } 65 | } 66 | if ($row == 0) 67 | { 68 | $output = "$output\n$rowText"; 69 | } 70 | else 71 | { 72 | $output = "$output,\n$rowText"; 73 | } 74 | } 75 | $output = "$output\n};\n"; 76 | 77 | my $updatedFile = read_file($outPath); 78 | $updatedFile =~ s/\v$mapVarName.*?;/\n$output/s; 79 | write_file($outPath, $updatedFile); 80 | -------------------------------------------------------------------------------- /assets/configuration/colours.json: -------------------------------------------------------------------------------- 1 | { 2 | "Window background" : "0xff000000", 3 | "Widget background" : "0xff000000", 4 | "Widget" : "0xffffffff", 5 | "Widget (off)" : "0xff5c5c5c", 6 | "Menu background" : "0xff000000", 7 | "Outline" : "0xff5c5c5c", 8 | "Focused outline" : "0xffffffff", 9 | "Text" : "0xffffffff", 10 | "Highlighted text" : "0xffffffff", 11 | "Text field" : "0xff000000", 12 | "Highlighted text field" : "0xff252525", 13 | "none" : "", 14 | 15 | "Main Window Background" : "", 16 | "Input preview text" : "", 17 | "Input preview background" : "", 18 | "Input preview outline" : "", 19 | "Input preview highlight" : "", 20 | "Active chord key text" : "0xffffffff", 21 | "Inactive Chord Text" : "0xff9c9c9c", 22 | 23 | "Chord1 (Selected)" : "0xff00ff33", 24 | "Chord2 (Selected)" : "0xffff0000", 25 | "Chord3 (Selected)" : "0xffeeff00", 26 | "Chord4 (Selected)" : "0xff00d3ff", 27 | "Chord5 (Selected)" : "0xffffbf00", 28 | "Unused (Selected)" : "0xffffffff", 29 | "Chord1 (Active)" : "0xff00c427", 30 | "Chord2 (Active)" : "0xffb30000", 31 | "Chord3 (Active)" : "0xffb2bf00", 32 | "Chord4 (Active)" : "0xff03a6c8", 33 | "Chord5 (Active)" : "0xffcfa014", 34 | "Unused (Active)" : "0xffc0c0c0", 35 | "Chord1 (Open)" : "0xff008f1d", 36 | "Chord2 (Open)" : "0xff8a1919", 37 | "Chord3 (Open)" : "0xff8b940a", 38 | "Chord4 (Open)" : "0xff03849f", 39 | "Chord5 (Open)" : "0xff997508", 40 | "Unused (Open)" : "0xff929292", 41 | "Chord1 (Blocked)" : "0xff012a07", 42 | "Chord2 (Blocked)" : "0xff360404", 43 | "Chord3 (Blocked)" : "0xff303301", 44 | "Chord4 (Blocked)" : "0xff002b34", 45 | "Chord5 (Blocked)" : "0xff332602", 46 | "Unused (Blocked)" : "0xff444444" 47 | } 48 | -------------------------------------------------------------------------------- /Source/GUI/Text/CharSet/Text_CharSet_ConfigFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Text_CharSet_ConfigFile.h 4 | * 5 | * @brief Controls access to the character set configuration resource. 6 | */ 7 | 8 | #include "Config_FileHandler.h" 9 | #include "Text_CharSet_Type.h" 10 | #include "Text_CharSet_Cache.h" 11 | 12 | namespace Text 13 | { 14 | namespace CharSet 15 | { 16 | class ConfigFile; 17 | class JSONResource; 18 | } 19 | } 20 | 21 | class Text::CharSet::ConfigFile : public Config::FileHandler 22 | { 23 | public: 24 | ConfigFile(); 25 | 26 | virtual ~ConfigFile() { } 27 | 28 | /** 29 | * @brief Gets cached data for the active character set. 30 | * 31 | * @return All cached data for the active character set. 32 | */ 33 | const Cache& getActiveSet() const; 34 | 35 | /** 36 | * @brief Gets a character set's displayed name. 37 | * 38 | * @param type The character set being named. 39 | * 40 | * @return The configurable character set display name, used on the 41 | * help screen. 42 | */ 43 | juce::String getSetName(const Type type) const; 44 | 45 | /** 46 | * @brief Gets the character set type that is currently selected. 47 | * 48 | * @return The character set currently being used. 49 | */ 50 | const Type getActiveType() const; 51 | 52 | /** 53 | * @brief Updates the active character set type 54 | * 55 | * @param newActiveType The type that should replace the former active 56 | * type. 57 | */ 58 | void setActiveType(const Type newActiveType); 59 | 60 | /** 61 | * @brief Checks if shifted character sets are currently in use. 62 | * 63 | * @return Whether shifted sets are currently enabled. 64 | */ 65 | bool getShifted() const; 66 | 67 | /** 68 | * @brief Sets whether the shifted versions of character sets will be used. 69 | * 70 | * @param shifted True to use shifted versions, false to use the default 71 | * versions. 72 | */ 73 | void setShifted(const bool shifted); 74 | }; 75 | -------------------------------------------------------------------------------- /docs/StyleGuide.md: -------------------------------------------------------------------------------- 1 | ## Project Style Guide 2 | To keep this project organized and legible, this style guide provides set of rules to follow when documenting and formatting project files. New project files don't need to immediately follow all of these rules, but should apply them before they are included in any major release. 3 | 4 | ### Project Modules: 5 | Classes that have similar tasks or that work together to accomplish some larger goal should be grouped together into modules. Modules should be sorted into one of the module categories described in the module documentation page. Module source and header files should all be placed in `[project directory]/Source/[category]/[module name]`, except for test files, which should go in `[project directory]/Tests/[category]/[module name]`. Larger modules should divide their classes into submodules, each with its own directory in the main module directory. Submodules may be further divided recursively into inner submodules up to any depth, as long as doing so makes it easier to keep track of the project's structure and find specific files. See the [module implementation guide](./implementation/NewModules.md) for more information about creating project modules. 6 | 7 | ### Spacing, Indentation, and Line Breaks 8 | To keep code consistent and legible, this project provides an extensive set of rules for how whitespace, indentation, and line breaks should be used. Although these rules are very specific about exactly how to format documentation and code, treat them as more of a guideline than a strict requirement. A functional, relatively organized class that ignores some of these rules is better than an incomplete class that follows them exactly. If breaking these rules would produce more readable code, always choose readable code over strict adherence to the rules. The project's [CodeFormat script](../project-scripts/CodeFormat.pl) will attempt to automate the process of applying these rules as much as is possible. See the [spacing style guide](./style/Spacing.md) for the complete list of these rules. 9 | 10 | ### Documentation: 11 | See the [C++ file documentation guide](./style/Documentation.md) for information on documentation within source and header files. 12 | -------------------------------------------------------------------------------- /Source/GUI/Component/Component_ChordPreview.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Component_ChordPreview.h 4 | * 5 | * @brief Shows a preview of available chord options, and of which chord the 6 | * user is selecting. 7 | */ 8 | 9 | namespace Component { class ChordPreview; } 10 | 11 | 12 | #include "Component_KeyGrid.h" 13 | #include "Component_ChordKeyDisplay.h" 14 | #include "Input_Chord.h" 15 | #include "Text_CharSet_Cache.h" 16 | #include "Input_Key_ConfigFile.h" 17 | #include "JuceHeader.h" 18 | 19 | /** 20 | * @brief Shows a preview of available chord options, and of which chord the 21 | * user is selecting. 22 | * 23 | * ChordPreview is meant to be displayed below Component::CharSetDisplay, and 24 | * beside Component::ChordKeyDisplay. Each column represents an index in the 25 | * active character set, and each row represents a chord input key. 26 | * 27 | * Each index in ChordPreview's grid is drawn as either a filled square, if its 28 | * chord key is used with its character index, or an empty square if it is not. 29 | * The color used to draw the square is a specific configurable color, selected 30 | * by chord key, whether the square's key is held down, and whether the square's 31 | * character is selected or could be selected by holding down more keys. 32 | */ 33 | class Component::ChordPreview : public KeyGrid 34 | { 35 | public: 36 | ChordPreview() { } 37 | 38 | virtual ~ChordPreview() { } 39 | 40 | /** 41 | * @brief Gets the number of character columns the KeyGrid contains. 42 | * 43 | * @return One for each character in the current active character set, plus 44 | * another for each wide character in the current set. 45 | */ 46 | int getColumnCount() const override; 47 | 48 | /** 49 | * @brief Gets the number of character rows the KeyGrid contains. 50 | * 51 | * @return The number of chord input keys. 52 | */ 53 | int getRowCount() const override; 54 | 55 | private: 56 | /** 57 | * @brief Draws all chord mappings within the current character set. 58 | * 59 | * @param g The JUCE graphics context. 60 | */ 61 | void paint(juce::Graphics& g) override; 62 | }; 63 | -------------------------------------------------------------------------------- /Source/Development/Debug/Debug_AddressLog.cpp: -------------------------------------------------------------------------------- 1 | #include "Debug_AddressLog.h" 2 | #include 3 | 4 | /** 5 | * @brief Gets the map used to link each logged address value to its unique ID 6 | * number. 7 | * 8 | * @return The address ID map. 9 | */ 10 | static std::map& getAddressMap() 11 | { 12 | static std::map ids; 13 | return ids; 14 | } 15 | 16 | 17 | // Gets a unique, fixed ID value to represent a particular memory address. 18 | int Debug::AddressLog::getID(const void* address) 19 | { 20 | static int nextID = 0; 21 | std::map& ids = getAddressMap(); 22 | if (nextID == 0) 23 | { 24 | ids[0] = 0; 25 | nextID++; 26 | } 27 | if (ids.count(address) == 0) 28 | { 29 | ids[address] = nextID; 30 | nextID++; 31 | } 32 | return ids[address]; 33 | } 34 | 35 | 36 | // Appends a line of text to the log of events occurring to a specific address. 37 | const juce::String& Debug::AddressLog::addEvent 38 | (const void* address, const juce::String event, const void* address2) 39 | { 40 | static juce::CriticalSection logSection; 41 | static std::map eventLog; 42 | const juce::ScopedLock eventLock(logSection); 43 | if (address == nullptr) 44 | { 45 | return eventLog[0]; 46 | } 47 | int id = getID(address); 48 | juce::String& log = eventLog[id]; 49 | log += "\n"; 50 | log += event; 51 | if (address2 != nullptr) 52 | { 53 | log += getID(address2); 54 | } 55 | return log; 56 | } 57 | 58 | 59 | // Prints all logged events for a specific memory address 60 | void Debug::AddressLog::printLog(int addressID) 61 | { 62 | const void* address = nullptr; 63 | std::map& ids = getAddressMap(); 64 | for (auto iter = ids.begin(); iter != ids.end(); iter++) 65 | { 66 | if (iter->second == addressID) 67 | { 68 | address = iter->first; 69 | break; 70 | } 71 | } 72 | const juce::String& log = addEvent(address, "printed log"); 73 | std::cout << "\n\n\tPrinting log for " << addressID << ":\n" 74 | << log << "\n\n"; 75 | } 76 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/Implementation/SharedResource_Reference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file SharedResource_Reference.h 4 | * 5 | * @brief Provides access to a Resource object's Instance and lock, and 6 | * ensures the resource is destroyed if and only if all of its 7 | * References have been destroyed. 8 | */ 9 | 10 | #include "JuceHeader.h" 11 | #include "SharedResource_ReferenceInterface.h" 12 | 13 | namespace SharedResource 14 | { 15 | class Reference; 16 | class Instance; 17 | } 18 | 19 | /** 20 | * @brief An object that serves as a reference to a specific resource Instance. 21 | */ 22 | class SharedResource::Reference : public ReferenceInterface 23 | { 24 | private: 25 | /** 26 | * @brief Connects this new Reference to its resource Instance, creating 27 | * the Instance object if necessary. 28 | * 29 | * @param resourceKey The key identifying this object's resource. 30 | * 31 | * @param createResource A function that can be used to create the 32 | * Instance if necessary. 33 | */ 34 | Reference(const juce::Identifier& resourceKey, 35 | const std::function createResource); 36 | public: 37 | /** 38 | * @brief Removes this Reference from its resource Instance object, 39 | * destroying the resource if no references to it remain. 40 | */ 41 | virtual ~Reference(); 42 | 43 | // Reference objects may only be created as the parent class of a 44 | // Handler object. 45 | template friend class Handler; 46 | 47 | protected: 48 | /** 49 | * @brief Gets the lock used to control access to the referenced resource. 50 | * 51 | * @return The resource's shared lock. 52 | */ 53 | const juce::ReadWriteLock& getResourceLock() const; 54 | 55 | private: 56 | /** 57 | * @brief Gets a pointer to this reference's resource object Instance. 58 | * 59 | * @return The resource's unique object instance. 60 | */ 61 | Instance* getResourceInstance() const; 62 | 63 | // The resource's unique key identifier. 64 | const juce::Identifier& resourceKey; 65 | }; 66 | -------------------------------------------------------------------------------- /Tests/Files/Config/Config_Test_JSONKeys.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Config_DataKey.h" 3 | /** 4 | * @file Config_Test_JSONKeys.h 5 | * 6 | * @brief Keys used in the test configuration file. 7 | */ 8 | 9 | namespace Config { namespace Test { namespace JSONKeys 10 | { 11 | //###################### Integer value keys: ############################### 12 | // A value provided to use as a test integer value. 13 | static const Config::DataKey testInt 14 | ("test integer", Config::DataKey::intType); 15 | 16 | //####################### String value keys: ############################### 17 | // A value provided to use as a test string value. 18 | static const Config::DataKey testString 19 | ("test string", Config::DataKey::stringType); 20 | 21 | //######################## Boolean value keys: ############################# 22 | // A value provided to use as a test boolean value. 23 | static const Config::DataKey testBool 24 | ("test boolean", Config::DataKey::boolType); 25 | 26 | //######################## Double value keys: ############################## 27 | // A value provided to use as a test double value. 28 | static const Config::DataKey testDouble 29 | ("test double", Config::DataKey::doubleType); 30 | 31 | // All basic data keys tracked in the Test configuration file: 32 | static const std::vector allKeys 33 | { 34 | testInt, 35 | testString, 36 | testBool, 37 | testDouble 38 | }; 39 | 40 | //#################### Custom data type keys: ############################## 41 | // A value provided to use as a test array value. 42 | static const juce::Identifier testArray("test array"); 43 | 44 | // A value provided to use as a test object value. 45 | static const juce::Identifier testObject("test object"); 46 | 47 | //##################### Test object keys: ############################### 48 | // An arbitrary integer value stored within test objects. 49 | static const juce::Identifier testObjectNum("test object num"); 50 | // An arbitrary boolean value stored within test objects. 51 | static const juce::Identifier testObjectBool("test object bool"); 52 | } } } 53 | -------------------------------------------------------------------------------- /docs/templates/config/ExampleKeys.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Config_DataKey.h" 3 | /** 4 | * @file ExampleKeys.h 5 | * 6 | * @brief An example config key file. 7 | */ 8 | 9 | namespace ExampleKeys 10 | { 11 | //###################### Integer value keys: ############################### 12 | // A value provided to use as an example integer value. 13 | static const Config::DataKey exampleInt 14 | ("example integer", Config::DataKey::intType); 15 | 16 | //####################### String value keys: ############################### 17 | // A value provided to use as an example string value. 18 | static const Config::DataKey exampleString 19 | ("example string", Config::DataKey::stringType); 20 | 21 | //######################## Boolean value keys: ############################# 22 | // A value provided to use as an example boolean value. 23 | static const Config::DataKey exampleBool 24 | ("example boolean", Config::DataKey::boolType); 25 | 26 | //######################## Double value keys: ############################## 27 | // A value provided to use as an example double value. 28 | static const Config::DataKey exampleDouble 29 | ("example double", Config::DataKey::doubleType); 30 | 31 | // All basic data keys tracked in the Example configuration file: 32 | static const std::vector allKeys 33 | { 34 | exampleInt, 35 | exampleString, 36 | exampleBool, 37 | exampleDouble 38 | }; 39 | 40 | //#################### Custom data type keys: ############################## 41 | // A value provided to use as an example array value. 42 | static const juce::Identifier exampleArray("example array"); 43 | 44 | // A value provided to use as an example object value. 45 | static const juce::Identifier exampleObject("example object"); 46 | 47 | //##################### Example object keys: ############################### 48 | // An arbitrary integer value stored within example objects. 49 | static const juce::Identifier exampleObjectNum("example object num"); 50 | // An arbitrary boolean value stored within example objects. 51 | static const juce::Identifier exampleObjectBool("example object bool"); 52 | } 53 | -------------------------------------------------------------------------------- /Source/Framework/SharedResource/Implementation/SharedResource_Reference.cpp: -------------------------------------------------------------------------------- 1 | #define SHARED_RESOURCE_IMPLEMENTATION 2 | #include "SharedResource_Reference.h" 3 | #include "SharedResource_Holder.h" 4 | #include "SharedResource_Instance.h" 5 | 6 | // Removes this Reference from its resource Instance object, destroying the 7 | // resource if no references to it remain. 8 | SharedResource::Reference::~Reference() 9 | { 10 | { 11 | const juce::ScopedWriteLock resourceLock(getResourceLock()); 12 | const juce::ScopedLock referenceLock(getLock()); 13 | Instance* resourceInstance = getResourceInstance(); 14 | jassert(resourceInstance != nullptr); 15 | resourceInstance->references.removeFirstMatchingValue(this); 16 | if (resourceInstance->references.isEmpty()) 17 | { 18 | Holder::setResource(resourceKey, nullptr); 19 | delete resourceInstance; 20 | resourceInstance = nullptr; 21 | } 22 | } 23 | Holder::clearIfEmpty(); 24 | } 25 | 26 | 27 | // Connects this new Reference to its resource Instance, creating the Instance 28 | // object if necessary. 29 | SharedResource::Reference::Reference(const juce::Identifier& resourceKey, 30 | const std::function createResource) : 31 | resourceKey(resourceKey) 32 | { 33 | const juce::ScopedWriteLock initLock(getResourceLock()); 34 | Instance* resourceInstance = getResourceInstance(); 35 | if (resourceInstance == nullptr) 36 | { 37 | resourceInstance = createResource(); 38 | jassert(getResourceInstance() != nullptr); 39 | jassert(resourceInstance->references[0] == nullptr); 40 | resourceInstance->references.set(0, this); 41 | } 42 | else 43 | { 44 | resourceInstance->references.add(this); 45 | } 46 | } 47 | 48 | 49 | // Gets the lock used to control access to the referenced resource. 50 | const juce::ReadWriteLock& SharedResource::Reference::getResourceLock() const 51 | { 52 | return Holder::getResourceLock(resourceKey); 53 | } 54 | 55 | 56 | // Gets a pointer to this reference's resource object Instance. 57 | SharedResource::Instance* 58 | SharedResource::Reference::getResourceInstance() const 59 | { 60 | return Holder::getResource(resourceKey); 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KeyChord 2 | A chorded software keyboard for the ClockworkPi GameShell and similar devices. It allows quick and easy typing using the GameShell's light keys and face buttons. Controls can be easily reconfigured to work with alternate key bindings. 3 | 4 | 5 | ![Keyboard Preview](./docs/screenshots/typing.png?raw=true "typing with KeyChord") 6 | 7 | [Installation instructions](./docs/BuildAndInstall) 8 | 9 | ## How it Works 10 | After installation, use a program like XBindKeys to setup a shortcut key to launch KeyChord (The installation instructions explain one way to do this). Whenever you need to enter text into a window, use the shortcut key to launch KeyChord, and it will automatically target whatever window was last active. All text entered into KeyChord will be forwarded to that window, either when you press the "send text" key, or immediately, if you use the "immediate input" key to turn on immediate input mode. 11 | 12 | 13 | With a chorded keyboard, rather than mapping each key to a different character, you have a small number of chord keys you use to type all characters. Each character uses a different combination of chord keys, and you type that character by holding down its chord key combination and releasing them all at once. 14 | 15 | 16 | The KeyChord keyboard displays all the chord mappings on screen. As you hold down chord keys, it marks which character would be entered if you released the keys, and highlights the characters that you could select by holding down additional keys. Other button mappings handle switching between character sets, moving the keyboard window, and sending input from the keyboard to the target window. 17 | 18 | 19 | | ![Default controls](./docs/screenshots/controls.png?raw=true "default controls") | 20 | | :---: | 21 | | Default keyboard controls. | 22 | 23 | KeyChord is highly configurable. Some supported customizations: 24 | 25 | | [![Custom controls](./docs/screenshots/altControls.png?raw=true "alternate control example")](./assets/configuration/keyBindings_alt.json) | [![Alternate character sets](./docs/screenshots/altCharSet.png?raw=true "Danish/Norwegian alphabet example")](./assets/configuration/charSets_alt.json) | ![Custom colors](./docs/screenshots/altColors.png?raw=true "Custom color example") | 26 | |:---:|:---:|:---:| 27 | | Custom controls| Alternate character sets | Custom colors | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Source/Development/Debug/Debug_Component.cpp: -------------------------------------------------------------------------------- 1 | #include "Debug_Component.h" 2 | 3 | #ifdef JUCE_DEBUG 4 | // Print the full namespace name before all debug output: 5 | static const constexpr char* dbgPrefix = "Debug::Component::"; 6 | #endif 7 | 8 | 9 | // Prints the structure of the component tree to debug output. 10 | void Debug::Component::trace() 11 | { 12 | using namespace juce; 13 | static DrawableRectangle highlightFocus; 14 | highlightFocus.setFill(FillType(Colour(0x0))); 15 | highlightFocus.setStrokeFill(FillType(Colour(0xff00ff00))); 16 | highlightFocus.setStrokeType(PathStrokeType(4)); 17 | std::function recursiveInfo; 18 | recursiveInfo = [&recursiveInfo](juce::Component* component, int depth) 19 | { 20 | String indent = ":"; 21 | for (int i = 0; i < depth; i++) 22 | { 23 | indent += "\t"; 24 | } 25 | DBG(dbgPrefix << __func__ << indent << "Component:" 26 | << component->getName()); 27 | indent += " "; 28 | DBG(dbgPrefix << __func__ << indent << "Position: (" 29 | << component->getX() << "," << component->getY() << ")"); 30 | DBG(dbgPrefix << __func__ << indent << "Size: " 31 | << component->getWidth() << "x" << component->getHeight()); 32 | String properties; 33 | if (component->getWantsKeyboardFocus()) 34 | { 35 | properties += "wantsKeyFocus,"; 36 | } 37 | if (component->hasKeyboardFocus(false)) 38 | { 39 | properties += "hasKeyFocus,"; 40 | highlightFocus.setBounds(component->getScreenBounds()); 41 | } 42 | properties += component->isShowing() ? "showing" : "not showing"; 43 | DBG(indent + properties); 44 | int numChildren = component->getNumChildComponents(); 45 | if (numChildren > 0) 46 | { 47 | DBG(dbgPrefix << __func__ << indent << "Children:" << numChildren); 48 | } 49 | for (int i = 0; i < numChildren; i++) 50 | { 51 | recursiveInfo(component->getChildComponent(i), depth + 1); 52 | } 53 | }; 54 | juce::Component * rootComponent = Desktop::getInstance().getComponent(0); 55 | recursiveInfo(rootComponent, 0); 56 | rootComponent->addAndMakeVisible(highlightFocus); 57 | } 58 | -------------------------------------------------------------------------------- /project-scripts/ColourID/Menus/MainMenu.pm: -------------------------------------------------------------------------------- 1 | ##### MainMenu.pm ############################################################## 2 | # Constructs the main ColourId management menu. # 3 | ################################################################################ 4 | 5 | #==============================================================================# 6 | #--- openMenu: --- 7 | # Displays the main menu, repeatedly accepting input and running the menu action 8 | # with the selected option parameter until the user enters 'q'. 9 | #--- Parameters: --- 10 | # $cache: an IDCache object to use for menu options. 11 | #==============================================================================# 12 | 13 | use strict; 14 | use warnings; 15 | 16 | package MainMenu; 17 | use lib './project-scripts/ColourID/Menus'; 18 | use InputMenu; 19 | use EnumMenu; 20 | use NamespaceListMenu; 21 | use KeyMenu; 22 | use CategoryMenu; 23 | use ExportMenu; 24 | use UpdateMenu; 25 | 26 | 27 | # Displays the main menu, repeatedly accepting input and running the menu action 28 | # with the selected option parameter until the user enters 'q'. 29 | sub openMenu 30 | { 31 | my $cache = shift; 32 | my $menu = new InputMenu( 33 | "ColourId management tool options:", 34 | "Exit program:"); 35 | $menu->addOption("Scan project for new ColourId enums", 36 | sub { EnumMenu::openMenu($cache); }); 37 | $menu->addOption("Find next open ID value", 38 | sub 39 | { 40 | my @elements = $cache->getElements(); 41 | my $lastID = hex($elements[-1]->getID()) + 0x100; 42 | $lastID = sprintf("0x%x", $lastID); 43 | print("Start the next new ColourId enum at $lastID\n"); 44 | }); 45 | $menu->addOption("Edit Element namespaces", 46 | sub { NamespaceListMenu::openMenu($cache); }); 47 | $menu->addOption("Edit custom colour JSON keys", 48 | sub { KeyMenu::openMenu($cache); }); 49 | $menu->addOption("Assign UI categories", 50 | sub { CategoryMenu::openMenu($cache); }); 51 | $menu->addOption("Export cached data", 52 | sub { ExportMenu::openMenu($cache); }); 53 | $menu->addOption("Update project files", 54 | sub { UpdateMenu::openMenu($cache); }); 55 | $menu->openMenu(); 56 | } 57 | 1; 58 | -------------------------------------------------------------------------------- /Source/Files/Assets/Assets_XDGDirectories.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Assets_XDGDirectories.h 4 | * 5 | * @brief Finds and prioritizes directory paths following the XDG Base 6 | * Directory Specification. 7 | * 8 | * @see https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 9 | */ 10 | 11 | #include "JuceHeader.h" 12 | 13 | namespace Assets 14 | { 15 | namespace XDGDirectories 16 | { 17 | /** 18 | * @brief Gets the path of the single base directory where 19 | * user-specific data files should be written. 20 | * 21 | * @return The path to the user's data directory. 22 | */ 23 | juce::String getUserDataPath(); 24 | 25 | /** 26 | * @brief Gets the path of the single base directory where 27 | * user-specific configuration files should be written. 28 | * 29 | * @return The path to the user's configuration directory. 30 | */ 31 | juce::String getUserConfigPath(); 32 | 33 | /** 34 | * @brief Gets the path of the single base directory where 35 | * user-specific cache files should be written. 36 | * 37 | * @return The path to the user's cache directory. 38 | */ 39 | juce::String getUserCachePath(); 40 | 41 | /** 42 | * @brief Gets the path of the single base directory where 43 | * user-specific runtime files should be written. 44 | * 45 | * @return The path to the user's runtime file directory. 46 | */ 47 | juce::String getUserRuntimePath(); 48 | 49 | /** 50 | * @brief Gets the ordered list of directories to search for user data 51 | * files. 52 | * 53 | * @return The list of directories to search for data files, from 54 | * highest priority to lowest. 55 | */ 56 | juce::StringArray getDataSearchPaths(); 57 | 58 | /** 59 | * @brief Gets the ordered list of directories to search for user 60 | * configuration files. 61 | * 62 | * @return The list of directories to search for configuration files, 63 | * from highest priority to lowest. 64 | */ 65 | juce::StringArray getConfigSearchPaths(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /project-scripts/ColourID/Menus/KeyMenu.pm: -------------------------------------------------------------------------------- 1 | ##### KeyMenu.pm ############################################################### 2 | # Constructs the JSON colour key editing menu. # 3 | ################################################################################ 4 | 5 | #==============================================================================# 6 | #--- openMenu: --- 7 | # Displays the key menu, repeatedly accepting input and running the menu 8 | # action with the selected option parameter until the user enters 'q'. 9 | #--- Parameters: --- 10 | # $cache: The cache object used to read and write JSON keys 11 | #==============================================================================# 12 | 13 | use strict; 14 | use warnings; 15 | 16 | package KeyMenu; 17 | use lib './project-scripts/ColourID'; 18 | use UserInput; 19 | use lib './project-scripts/ColourID/Menus'; 20 | use InputMenu; 21 | 22 | # Displays the category menu, repeatedly accepting input and running the menu 23 | # action with the selected option parameter until the user enters 'q'. 24 | sub openMenu 25 | { 26 | my $cache = shift; 27 | my $menu = new InputMenu( 28 | "JSON configuration key management\nSelect a key to edit:", 29 | "Return to main menu:", 30 | \&editKey, 31 | $cache); 32 | my $refreshAction = sub 33 | { 34 | my $menu = shift; 35 | $menu->clearOptions(); 36 | my @keys = $cache->getElementKeys(); 37 | foreach my $key (@keys) 38 | { 39 | my $element = $cache->findElement($key); 40 | $menu->addOption($key, $element); 41 | } 42 | }; 43 | $menu->setRefreshAction($refreshAction); 44 | $menu->openMenu(); 45 | } 46 | 47 | sub editKey 48 | { 49 | my $element = shift; 50 | my $cache = shift; 51 | print("1. Edit key\n2. Delete key\nq. Return to key menu:"); 52 | my $selection = UserInput::checkInput('1', '2', 'q'); 53 | if ($selection == 1) 54 | { 55 | print("Enter new key value:"); 56 | my $newKey = UserInput::inputText('.+'); 57 | $cache->assignKey($element->getFullName(), $newKey); 58 | } 59 | elsif ($selection == 2) 60 | { 61 | my $replacement = $element->withKey(""); 62 | $cache->removeElement($element); 63 | $cache->addElement($replacement); 64 | } 65 | } 66 | 1; 67 | -------------------------------------------------------------------------------- /docs/Documentation Checklist.md: -------------------------------------------------------------------------------- 1 | ## Documenting Header Files 2 | # At the top of the file: 3 | - List the file name under the @file tag. 4 | - Describe the file's purpose in one sentence under the @brief tag. 5 | 6 | # Above the file's main class: 7 | - Describe the class in one sentence under the @brief tag. 8 | - Explain everything the class can be used for. 9 | - List the most significant functions. 10 | - Describe any significant conditions or limitations of those functions. 11 | - Expand on the class's purpose, if necessary. 12 | - What is its purpose within the greater project? 13 | - If other options exist, what advantages does it provide over those? 14 | - Are there notable alternate reasons to use it? 15 | - Are there any circumstances where it might seem useful, but definitely should not be used? 16 | - Explain how it is typically used. 17 | - Are there any alternate ways it might be useful? 18 | - Are there any easy mistakes that should be avoided when using it? 19 | - Describe if and how any classes should be extended. 20 | - When (if ever) should sub-classes be used? 21 | - What requirements do subclasses need to meet? 22 | - Briefly describe the implementation approach. 23 | - Are there any alternative strategies that should be considered? 24 | - Are there any parts of this module that could be split into other modules? 25 | - List any closely related files or resources under the @see tag 26 | 27 | ## Documenting Functions 28 | - What is the purpose of the function? 29 | - Are there any requirements that should be met before the function is called? 30 | - Are there any side effects that occur when the function is called? 31 | - List each parameter under a @param tag, and each template parameter under a @tparam tag. 32 | - What is the purpose of the parameter? 33 | - Are there any values that cannot be passed to this parameter? 34 | - If the parameter is a reference or pointer, will the function alter it? 35 | - List any return value under a @return tag. 36 | - What is the purpose of this value? 37 | - Are there circumstances where this value could be invalid? 38 | - Are there any return values that have special connotations? 39 | - List each possible exception under a @throws tag. 40 | - When will the function throw that exception? 41 | -------------------------------------------------------------------------------- /docs/style/Documentation.md: -------------------------------------------------------------------------------- 1 | # Documenting and Commenting within C++ Files 2 | All .cpp and .h files should have complete Javadoc-style Doxygen documentation. When uncertain, it's always better to have too much documentation than to have too little. It's easy to ignore a javadoc tag that just states the obvious, but difficult to track down problems when people have different ideas of what is obvious and documentation isn't provide. 3 | 4 | ## Documentation blocks: 5 | - Documentation comment blocks should be formatted as follows, indenting the block so that the opening '/' character aligns with the start of the documented section. 6 | ``` 7 | /** 8 | * @brief Place two spaces after a documentation tag. When tag content uses 9 | * multiple lines, keep them aligned. 10 | * 11 | * Additional details may be placed after any tag. align them with the 12 | * documentation tags, indenting the first line by one space if multiple lines 13 | * are needed. 14 | * 15 | * @tagExample nextExample For tags that list a property/parameter name/ 16 | * exception type /etc, place one space after the tag 17 | * and two after the property name. 18 | * 19 | * @tagExample last Align all tag contents after the @brief tag. Keep 20 | * one line of empty space between tags. 21 | */ 22 | ``` 23 | - Documentation blocks should always be placed in the following locations: 24 | * At the top of any header file. 25 | * Before all non-empty function declarations. 26 | * Before all class definitions. 27 | 28 | - Include the following tags within documentation blocks: 29 | * **@file**: At the start of a header file's documentation block. 30 | * **@brief**: Within all documentation blocks. 31 | * **@param**: For each parameter in a function. 32 | * **@tparam**: For each template parameter in a class or function template. 33 | * **@throws**: For each exception a function may throw. 34 | * **@return**: For any function that returns a value. 35 | 36 | When defining functions that were declared separately, place the contents of the function's @brief tag before the definition within regular line comments. 37 | 38 | ## Additional comments: 39 | - Place at least one space after starting a comment line with '//'. 40 | - Outside of documentation blocks, always use line comments instead of block comments. This makes it easier to temporarily disable sections of code using block comments. 41 | -------------------------------------------------------------------------------- /docs/templates/TestTemplate.cpp: -------------------------------------------------------------------------------- 1 | #include "JuceHeader.h" 2 | /** 3 | * @file TestTemplate.cpp 4 | * 5 | * @brief Use this file as a template when creating new test classes. 6 | */ 7 | #include "ExampleClass.h" 8 | 9 | namespace ModuleName { namespace Test { class ExampleClassTest; } } 10 | 11 | class ModuleName::Test::ExampleClassTest : public juce::UnitTest 12 | { 13 | public: 14 | ExampleClassTest() : juce::UnitTest("ExampleClass Testing", 15 | "ModuleName") {} 16 | 17 | virtual void initialise() override 18 | { 19 | // Add optional initialization here 20 | } 21 | 22 | virtual void shutdown() override 23 | { 24 | // Add optional cleanup code here 25 | } 26 | 27 | void runTest() override 28 | { 29 | beginTest("First test name:"); 30 | 31 | bool testCondition = true; 32 | expect(testCondition, "Test condition was not true"); 33 | 34 | int testValue1 = 1; 35 | int testValue2 = 1; 36 | expectEquals(testValue1, testValue2, 37 | "Test values were not equal!"); 38 | 39 | int testValue3 = 2; 40 | expectNotEquals(testValue1, testValue3, 41 | "Test values were equal!"); 42 | 43 | 44 | beginTest("Second test name:"); 45 | 46 | int low = 0; 47 | int high = 9; 48 | 49 | expectLessThan(low, high, "Low was not less than high!"); 50 | expectLessOrEqual(low, high, "Low was not less than or equal to high!"); 51 | expectGreaterThan(high, low, "High was not greater than low!"); 52 | expectGreaterOrEqual(high, low, 53 | "High was not greater than or equal to low!"); 54 | 55 | logMessage("Some test message to print."); 56 | 57 | beginTest("Third test name:"); 58 | 59 | juce::Random rng = getRandom(); 60 | int random = rng.nextInt(); 61 | expectWithinAbsoluteError(random, 0, 1000, 62 | "Random value not between -1000 and 1000!"); 63 | 64 | // Passes, since this won't throw an exception 65 | expectDoesNotThrow(rng.getSystemRandom()); 66 | // Writes an error, since this won't throw an exception. 67 | expectThrows(rng.getSystemRandom()); 68 | 69 | 70 | // Writes an error, since this won't throw an out of range exception 71 | expectThrowsType(rng.getSystemRandom(), std::out_of_range); 72 | } 73 | }; 74 | 75 | static ModuleName::Test::ExampleClassTest test; 76 | -------------------------------------------------------------------------------- /Source/GUI/Output/Output_Buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Output_Buffer.h 4 | * 5 | * @brief Stores chorded keyboard input and sends it to the target application. 6 | */ 7 | 8 | #include "Text_CharTypes.h" 9 | #include "JuceHeader.h" 10 | 11 | namespace Output { class Buffer; } 12 | 13 | /** 14 | * @brief Unless immediate mode is enabled, all keyboard input is cached within 15 | * this object until the user chooses to forward the input to the target 16 | * window. 17 | */ 18 | class Output::Buffer 19 | { 20 | public: 21 | Buffer() { } 22 | 23 | virtual ~Buffer() { } 24 | 25 | /** 26 | * @brief Gets the cached output string, not including modifiers. 27 | * 28 | * @return All text waiting to be sent to the target window. 29 | */ 30 | Text::CharString getBufferedText() const; 31 | 32 | /** 33 | * @brief Gets the modifier key flags that will be applied to the output. 34 | * 35 | * @return All combined modifier flags, as defined in Output::Modifiers. 36 | */ 37 | int getModifierFlags() const; 38 | 39 | /** 40 | * @brief Adds a character to the end of the cached output string. 41 | * 42 | * @param outputChar The character that should be added to the end of the 43 | * output string. 44 | */ 45 | void appendCharacter(const Text::CharValue outputChar); 46 | 47 | /** 48 | * @brief Removes the last character from the end of the output string. 49 | */ 50 | void deleteLastChar(); 51 | 52 | /** 53 | * @brief Sets the modifier keys that will be applied to the buffered 54 | * text. 55 | * 56 | * @param modifierFlags All combined modifier flags, as defined in 57 | * Output::Modifiers. 58 | */ 59 | void setModifiers(const int modifierFlags); 60 | 61 | /** 62 | * @brief Removes all buffered text. 63 | * 64 | * @param clearModifiers Whether the modifier flags should also be cleared. 65 | */ 66 | void clear(const bool clearModifiers = true); 67 | 68 | /** 69 | * @brief Checks if the buffer currently contains any text or key values. 70 | * 71 | * @return Whether the buffer string is empty. 72 | */ 73 | bool isEmpty() const; 74 | 75 | private: 76 | // Buffered text/keys: 77 | Text::CharString bufferedText; 78 | // Combined key modifier flags, as defined in Output::Modifiers. 79 | int keyModifiers = 0; 80 | }; 81 | -------------------------------------------------------------------------------- /docs/configuration/keyBindings_alt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Chord1": { 3 | "key": "cursor right", 4 | "name": "", 5 | "charName": "right" 6 | }, 7 | "Chord2": { 8 | "key": "U", 9 | "name": "", 10 | "charName": "Y" 11 | }, 12 | "Chord3": { 13 | "key": "I", 14 | "name": "", 15 | "charName": "X" 16 | }, 17 | "Chord4": { 18 | "key": "K", 19 | "name": "", 20 | "charName": "A" 21 | }, 22 | "Chord5": { 23 | "key": "J", 24 | "name": "", 25 | "charName": "B" 26 | }, 27 | "Select next character set": { 28 | "key": "spacebar", 29 | "name": "Select", 30 | "charName": "S" 31 | }, 32 | "Toggle shifted characters": { 33 | "key": "-", 34 | "name": "Shift + Select", 35 | "charName": "-" 36 | }, 37 | "Select main character set": { 38 | "key": "O", 39 | "name": "Shift + X", 40 | "charName": "X" 41 | }, 42 | "Select alternate character set": { 43 | "key": "U", 44 | "name": "Shift + Y", 45 | "charName": "Y" 46 | }, 47 | "Select special character set": { 48 | "key": "K", 49 | "name": "Shift + A", 50 | "charName": "A" 51 | }, 52 | "Select modifier set": { 53 | "key": "J", 54 | "name": "Shift + B", 55 | "charName": "B" 56 | }, 57 | "Backspace": { 58 | "key": "cursor left", 59 | "name": "DPad left", 60 | "charName": "left" 61 | }, 62 | "Clear all": { 63 | "key": "cursor down", 64 | "name": "DPad down", 65 | "charName": "down" 66 | }, 67 | "Send text": { 68 | "key": "return", 69 | "name": "Start", 70 | "charName": "enter" 71 | }, 72 | "Close and send": { 73 | "key": "+", 74 | "name": "Shift + Start", 75 | "charName": "+" 76 | }, 77 | "Close": { 78 | "key": "escape", 79 | "name": "Menu", 80 | "charName": "M" 81 | }, 82 | "Toggle immediate mode": { 83 | "key": "cursor up", 84 | "name": "DPad up", 85 | "charName": "right" 86 | }, 87 | "Show help": { 88 | "key": "backspace", 89 | "name": "Shift + Menu", 90 | "charName": "backspace" 91 | }, 92 | "Toggle window edge": 93 | { 94 | "key" : "", 95 | "name" : "", 96 | "charName" : "" }, 97 | "Toggle minimize": 98 | { 99 | "key" : "", 100 | "name" : "", 101 | "charName" : "" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Source/Files/Config/Config_MainFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file Config_MainFile.h 4 | * 5 | * @brief Provides access to the main configuration file resource. 6 | */ 7 | 8 | #include "Config_FileHandler.h" 9 | 10 | namespace Config 11 | { 12 | class MainFile; 13 | class MainResource; 14 | } 15 | 16 | class Config::MainFile : public Config::FileHandler 17 | { 18 | public: 19 | MainFile(); 20 | 21 | virtual ~MainFile() { } 22 | 23 | /** 24 | * @brief Checks if the application should be displayed in minimized mode. 25 | * 26 | * @return Whether mimimized mode is in use, hiding 27 | * Component::ChordKeyDisplay and Component::ChordPreview. 28 | */ 29 | bool getMinimized() const; 30 | 31 | /** 32 | * @brief Gets whether the application is currently configured to snap to 33 | * the bottom edge of the display. 34 | * 35 | * @return True if the window belongs on the bottom edge, false if it 36 | * belongs on the top edge. 37 | */ 38 | bool getSnapToBottom() const; 39 | 40 | /** 41 | * @brief Gets whether the application is in immediate input mode. 42 | * 43 | * @return Whether all key input should be sent directly to the target 44 | * window, without buffering. 45 | */ 46 | bool getImmediateMode() const; 47 | 48 | /** 49 | * @brief Sets whether the application should start up in minimized mode. 50 | * 51 | * @param minimized Whether the application window will be reduced in size, 52 | * hiding the Component::ChordKeyDisplay and the 53 | * Component::ChordPreview. 54 | */ 55 | void setMinimised(const bool minimized); 56 | 57 | /** 58 | * @brief Sets which edge of the display the window should snap to on 59 | * construction. 60 | * 61 | * @param snapToBottom True if the window should use the bottom edge, false 62 | * if it should use the top edge. 63 | */ 64 | void setSnapToBottom(const bool snapToBottom); 65 | 66 | /** 67 | * @brief Sets whether the application should start in immediate mode. 68 | * 69 | * @param immediateMode Whether key input should be forwarded immediately 70 | * to the target window, or cached until the user 71 | * decides to send it. 72 | */ 73 | void setImmediateMode(const bool immediateMode); 74 | }; 75 | -------------------------------------------------------------------------------- /assets/configuration/keyBindings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Chord1": { 3 | "key": "H", 4 | "name": "LK1", 5 | "charName": "1" 6 | }, 7 | "Chord2": { 8 | "key": "Y", 9 | "name": "LK2", 10 | "charName": "2" 11 | }, 12 | "Chord3": { 13 | "key": "Z", 14 | "name": "LK3", 15 | "charName": "3" 16 | }, 17 | "Chord4": { 18 | "key": "O", 19 | "name": "LK4", 20 | "charName": "4" 21 | }, 22 | "Chord5": { 23 | "key": "L", 24 | "name": "LK5", 25 | "charName": "5" 26 | }, 27 | "Select next character set": { 28 | "key": "I", 29 | "name": "", 30 | "charName": "X" 31 | }, 32 | "Toggle shifted characters": { 33 | "key": "U", 34 | "name": "", 35 | "charName": "Y" 36 | }, 37 | "Select main character set": { 38 | "key": "home", 39 | "name": "Shift + LK1", 40 | "charName": "f1" 41 | }, 42 | "Select alternate character set": { 43 | "key": "page up", 44 | "name": "Shift + LK2", 45 | "charName": "f2" 46 | }, 47 | "Select special character set": { 48 | "key": "page down", 49 | "name": "Shift + LK4", 50 | "charName": "f4" 51 | }, 52 | "Select modifier set": { 53 | "key": "end", 54 | "name": "Shift + LK5", 55 | "charName": "f5" 56 | }, 57 | "Backspace": { 58 | "key": "J", 59 | "name": "B", 60 | "charName": "B" 61 | }, 62 | "Clear all": { 63 | "key": "backspace", 64 | "name": "Shift + Menu", 65 | "charName": "backspace" 66 | }, 67 | "Send text": { 68 | "key": "K", 69 | "name": "A", 70 | "charName": "A" 71 | }, 72 | "Close and send": { 73 | "key": "return", 74 | "name": "Start", 75 | "charName": "+" 76 | }, 77 | "Close": { 78 | "key": "escape", 79 | "name": "Menu", 80 | "charName": "M" 81 | }, 82 | "Toggle immediate mode": { 83 | "key": "cursor right", 84 | "name": "DPad right", 85 | "charName": "right" 86 | }, 87 | "Show help": { 88 | "key": "spacebar", 89 | "name": "Select", 90 | "charName": "-" 91 | }, 92 | "Toggle window edge": 93 | { 94 | "key" : "cursor up", 95 | "name" : "DPad up", 96 | "charName" : "up" 97 | }, 98 | "Toggle minimize": 99 | { 100 | "key" : "cursor down", 101 | "name" : "DPad down", 102 | "charName" : "down" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /docs/modules/Input.md: -------------------------------------------------------------------------------- 1 | # Input Module Documentation 2 | The Input module processes all user input, controlling how it is interpreted by the application or forwarded to the target window. 3 | 4 | #### [Input\::Chord](../../Source/GUI/Input/Input_Chord.h) 5 | Each Chord object represents a single input command created by holding down a combination of chord input keys. 6 | 7 | #### [Input\::ChordReader](../../Source/GUI/Input/Input_ChordReader.h) 8 | ChordReader processes all chord input events, tracking when chord entry keys are held down, and notifying registered ChordReader::Listener objects when the set of held chord keys changes, or a chord value is selected. ChordReader also passes on any key events unrelated to chord entry to its listeners without modification. 9 | 10 | #### [Input\::Controller](../../Source/GUI/Input/Input_Controller.h) 11 | Controller is a ChordReader\::Listener that determines how all keyboard input events should be used to control the application. It interacts with Component module objects to update the displayed input state, with the Output module to buffer and send text to the targeted application, and with the main Application object to move the window or close the application. 12 | 13 | ## Key Submodule 14 | The Key submodule defines the set of keyboard keys used to control KeyChord. All key bindings are defined within the keyBindings.json configuration file. 15 | 16 | #### [Input\::Key\::Binding](../../Source/GUI/Input/Key/Input_Key_Binding.h) 17 | Binding objects represent a single association between a key input code and an assigned action. Each binding stores the juce::Identifier of its associated action, the JUCE key description used to detect the key input event, a name describing the key, and a single [Text\::CharValue](../../Source/GUI/Text/Text_Values.h) character used to represent the key. 18 | 19 | #### [Input\::Key\::JSONKeys](../../Source/GUI/Input/Key/Input_Key_JSONKeys.h) 20 | The JSONKeys namespace defines JUCE Identifier objects representing all application action types that may be mapped to key events. 21 | 22 | #### [Input\::Key\::ConfigFile](../../Source/GUI/Input/Key/Input_Key_ConfigFile.h) 23 | ConfigFile objects access the configuration file resource to load Binding objects for any of the actions defined in Input\::Key\::JSONKeys. 24 | 25 | #### [Input\::Key\::JSONResource](../../Source/GUI/Input/Key/Input_Key_JSONResource.h) 26 | JSONResource handles all direct access to the key binding configuration file, and ensures bindings are cached and available as long as they are needed. 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/templates/config/ExampleFile.cpp: -------------------------------------------------------------------------------- 1 | #define EXAMPLE_FILE_IMPLEMENTATION 2 | 3 | #include "ExampleFile.h" 4 | #include "ExampleResource.h" 5 | #include "ExampleKeys.h" 6 | 7 | ExampleFile::ExampleFile() { } 8 | 9 | 10 | // Gets the file's example integer value. 11 | int ExampleFile::getExampleInt() const 12 | { 13 | return getConfigValue(ExampleKeys::exampleInt); 14 | } 15 | 16 | 17 | // Gets the file's example string value. 18 | juce::String ExampleFile::getExampleString() const 19 | { 20 | return getConfigValue(ExampleKeys::exampleString); 21 | } 22 | 23 | 24 | // Gets the file's example bool value. 25 | bool ExampleFile::getExampleBool() const 26 | { 27 | return getConfigValue(ExampleKeys::exampleBool); 28 | } 29 | 30 | 31 | // Gets the file's example double value. 32 | double ExampleFile::getExampleDouble() const 33 | { 34 | return getConfigValue(ExampleKeys::exampleDouble); 35 | } 36 | 37 | 38 | // Gets the file's example array value. 39 | juce::Array ExampleFile::getExampleArray() const 40 | { 41 | SharedResource::LockedPtr resource 42 | = getReadLockedResource(); 43 | return resource->getExampleArray(); 44 | } 45 | 46 | 47 | // Gets the file's example object value. 48 | ExampleObject ExampleFile::getExampleObject() const 49 | { 50 | SharedResource::LockedPtr resource 51 | = getReadLockedResource(); 52 | return resource->getExampleObject(); 53 | } 54 | 55 | 56 | // Sets the file's example integer value. 57 | void ExampleFile::setExampleInt(const int newInt) 58 | { 59 | setConfigValue(ExampleKeys::exampleInt, newInt); 60 | } 61 | 62 | 63 | // Sets the file's example string value. 64 | void ExampleFile::setExampleString(const juce::String newString) 65 | { 66 | setConfigValue(ExampleKeys::exampleString, newString); 67 | } 68 | 69 | 70 | // Sets the file's example bool value. 71 | void ExampleFile::setExampleBool(const bool newBool) 72 | { 73 | setConfigValue(ExampleKeys::exampleBool, newBool); 74 | } 75 | 76 | 77 | // Sets the file's example double value. 78 | void ExampleFile::setExampleDouble(const double newDouble) 79 | { 80 | setConfigValue(ExampleKeys::exampleDouble, newDouble); 81 | } 82 | 83 | 84 | // Sets the file's example object value. 85 | void ExampleFile::setExampleObject(const ExampleObject newObject) 86 | { 87 | SharedResource::LockedPtr resource 88 | = getWriteLockedResource(); 89 | resource->setExampleObject(newObject); 90 | } 91 | --------------------------------------------------------------------------------