-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
Relevant source files
This document describes the high-level architecture of MouseMacros, including the organization of major system components, their relationships, and the design patterns that structure the codebase. It provides a conceptual model of how the application is structured and how data flows between subsystems.
For details on specific subsystems:
- Application startup and initialization: see Application Entry Point and Initialization
- Main window implementation: see Main Window (MainFrame))
- Macro recording and playback mechanics: see Macro Recording and Playback
- Configuration system details: see Configuration and Persistence
- Localization implementation: see Internationalization and Localization
MouseMacros follows a layered architecture pattern with clear separation of concerns. The system is organized into five primary layers, each with distinct responsibilities.
flowchart TD
MouseMacro["MouseMacro<br>(main class)"]
MainFrame["MainFrame"]
SettingsDialog["SettingsDialog"]
MacroSettingsDialog["MacroSettingsDialog"]
ExitDialog["ExitDialog"]
UpdateInfoDialog["UpdateInfoDialog"]
AboutDialog["AboutDialog"]
HotkeyDialog["HotkeyDialog"]
MacroManager["MacroManager"]
GlobalMouseListener["GlobalMouseListener"]
MouseAction["MouseAction"]
ConfigManager["ConfigManager"]
Config["Config"]
CacheManager["CacheManager"]
Cache["Cache"]
Localizer["Localizer"]
ComponentUtil["ComponentUtil"]
FileUtil["FileUtil"]
SystemUtil["SystemUtil"]
ScreenUtil["ScreenUtil"]
LogManager["LogManager"]
JNativeHook["JNativeHook<br>(native library)"]
MouseMacro --> MainFrame
MouseMacro --> JNativeHook
MainFrame --> MacroManager
MainFrame --> GlobalMouseListener
MainFrame --> ConfigManager
MainFrame --> CacheManager
MainFrame --> Localizer
MainFrame --> ComponentUtil
SettingsDialog --> ConfigManager
SettingsDialog --> Localizer
MacroSettingsDialog --> ConfigManager
ExitDialog --> CacheManager
MacroManager --> FileUtil
GlobalMouseListener --> JNativeHook
ComponentUtil --> ConfigManager
subgraph subGraph5 ["External Integration"]
JNativeHook
end
subgraph subGraph4 ["Utility Layer"]
Localizer
ComponentUtil
FileUtil
SystemUtil
ScreenUtil
LogManager
Localizer --> SystemUtil
end
subgraph subGraph3 ["Configuration Layer"]
ConfigManager
Config
CacheManager
Cache
ConfigManager --> Config
CacheManager --> Cache
end
subgraph subGraph2 ["Business Logic Layer"]
MacroManager
GlobalMouseListener
MouseAction
MacroManager --> MouseAction
GlobalMouseListener --> MacroManager
end
subgraph subGraph1 ["UI Layer"]
MainFrame
SettingsDialog
MacroSettingsDialog
ExitDialog
UpdateInfoDialog
AboutDialog
HotkeyDialog
end
subgraph subGraph0 ["Entry Layer"]
MouseMacro
end
Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L1-L18, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L1-L246
| Layer | Purpose | Key Components |
|---|---|---|
| Entry Layer | Application bootstrap and native library initialization | MouseMacro |
| UI Layer | User interface windows and dialogs |
MainFrame, SettingsDialog, MacroSettingsDialog, ExitDialog, AboutDialog, UpdateInfoDialog, HotkeyDialog
|
| Business Logic Layer | Core macro recording, playback, and event capture |
MacroManager, GlobalMouseListener, MouseAction
|
| Configuration Layer | Settings management and state persistence |
ConfigManager, Config, CacheManager, Cache
|
| Utility Layer | Cross-cutting concerns and helper functions |
Localizer, ComponentUtil, FileUtil, SystemUtil, ScreenUtil, LogManager
|
Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L1-L18, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L28-L165
The following diagram shows the primary components and their interactions at the class level, using actual code entity names.
flowchart TD
CacheManager_class["CacheManager"]
cache["Cache cache"]
MainFrame_class["MainFrame"]
logArea["JTextArea logArea"]
GML["GlobalMouseListener GML"]
MacroManager_class["MacroManager"]
actions["List<MouseAction> actions"]
recording["boolean recording"]
playing["boolean playing"]
GML_impl["GlobalMouseListener"]
NativeKeyListener["NativeKeyListener"]
NativeMouseListener["NativeMouseInputListener"]
NativeWheelListener["NativeMouseWheelListener"]
ConfigManager_class["ConfigManager"]
config["Config config"]
CONFIG_DIR["CONFIG_DIR"]
Localizer_class["Localizer"]
translations["Map translations"]
load["load(lang)"]
get["get(key)"]
MainFrame_class --> MacroManager_class
MainFrame_class --> ConfigManager_class
GML_impl --> MacroManager_class
MacroManager_class --> ConfigManager_class
MainFrame_class --> Localizer_class
subgraph Localizer.java ["Localizer.java"]
Localizer_class
translations
load
get
Localizer_class --> translations
end
subgraph ConfigManager.java ["ConfigManager.java"]
ConfigManager_class
config
CONFIG_DIR
ConfigManager_class --> config
end
subgraph GlobalMouseListener.java ["GlobalMouseListener.java"]
GML_impl
NativeKeyListener
NativeMouseListener
NativeWheelListener
GML_impl --> NativeKeyListener
GML_impl --> NativeMouseListener
GML_impl --> NativeWheelListener
end
subgraph MacroManager.java ["MacroManager.java"]
MacroManager_class
actions
recording
playing
MacroManager_class --> actions
MacroManager_class --> recording
MacroManager_class --> playing
end
subgraph MainFrame.java ["MainFrame.java"]
MainFrame_class
logArea
GML
MainFrame_class --> GML
MainFrame_class --> logArea
end
subgraph CacheManager.java ["CacheManager.java"]
CacheManager_class
cache
CacheManager_class --> cache
end
Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L28-L165, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java
The application follows a specific initialization sequence to ensure proper setup of native libraries, configuration loading, and UI construction.
sequenceDiagram
participant MouseMacro.main()
participant System Properties
participant ConfigManager
participant Localizer
participant MainFrame
participant JNativeHook Library
participant GlobalMouseListener
MouseMacro.main()->>System Properties: setProperty("jnativehook.lib.path")
note over MouseMacro.main(),System Properties: Points to CONFIG_DIR/libs/
MouseMacro.main()->>MainFrame: new MainFrame()
MainFrame->>ConfigManager: read config.followSystemSettings
loop [followSystemSettings == true]
MainFrame->>ConfigManager: getAvailableLangs()
MainFrame->>MainFrame: SystemUtil.getSystemLang()
MainFrame->>MainFrame: SystemUtil.isSystemDarkMode()
end
MainFrame->>Localizer: load(config.lang)
Localizer->>Localizer: Load JSON language file
MainFrame->>MainFrame: Initialize UI components
MainFrame->>MainFrame: Load keyMap from config
MainFrame->>JNativeHook Library: GlobalScreen.registerNativeHook()
MainFrame->>GlobalMouseListener: new GlobalMouseListener()
MainFrame->>JNativeHook Library: addNativeKeyListener(GML)
MainFrame->>JNativeHook Library: addNativeMouseListener(GML)
MainFrame->>JNativeHook Library: addNativeMouseWheelListener(GML)
MainFrame->>JNativeHook Library: addNativeMouseMotionListener(GML)
MainFrame->>MainFrame: ComponentUtil.adjustFrameWithCache()
MouseMacro.main()->>MainFrame: setVisible(true)
Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L10-L17, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L44-L165
MouseMacros employs several design patterns to achieve modularity, maintainability, and extensibility.
The application uses the Singleton pattern for key components that should have only one instance:
| Component | Location | Purpose |
|---|---|---|
MAIN_FRAME |
MainFrame.java L39 | Single instance of the main window |
GML (GlobalMouseListener) |
MainFrame.java L37 | Single global event listener |
config |
ConfigManager | Single configuration object |
cache |
CacheManager | Single cache object |
Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L37-L39
The codebase uses Manager classes to encapsulate domain-specific logic:
| Manager | Responsibility |
|---|---|
MacroManager |
Recording, playback, and file operations for macros |
ConfigManager |
Loading, saving, and providing access to configuration |
CacheManager |
Managing runtime state and window dimensions |
LogManager |
Centralized logging to the UI |
Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L10-L11
Window lifecycle management uses adapters:
flowchart TD
WindowClosingAdapter["WindowClosingAdapter<br>(extends WindowAdapter)"]
MainFrame["MainFrame"]
WindowAdapter["WindowAdapter"]
WindowListener["WindowListener<br>(interface)"]
WindowClosingAdapter --> WindowAdapter
WindowAdapter --> WindowListener
MainFrame --> WindowClosingAdapter
The WindowClosingAdapter implements only the windowClosing event handler, avoiding the need to implement all methods of WindowListener.
Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L148
The application uses strategy-like patterns for configurable behavior:
| Configuration Key | Strategy | Possible Values |
|---|---|---|
readjustFrameMode |
Window resizing strategy |
MIXED, STANDARDIZED, MEMORIZED
|
defaultCloseOperation |
Exit behavior strategy |
UNKNOWN, EXIT_ON_CLOSE, MINIMIZE_TO_TRAY
|
followSystemSettings |
Settings source strategy | System settings vs. user-specified settings |
Sources: README.md L84, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L152-L162
The configuration system maintains two separate persistence layers with distinct purposes.
flowchart TD
ConfigFile["config.cfg<br>(JSON)"]
ConfigCategories["Configuration Categories:<br>- followSystemSettings<br>- lang<br>- enableDarkMode<br>- enableDefaultStorage<br>- defaultMmcStoragePath<br>- enableQuickMode<br>- enableCustomMacroSettings<br>- repeatTime<br>- repeatDelay<br>- allowLongStr<br>- readjustFrameMode<br>- keyMap"]
CacheFile["cache.json<br>(JSON)"]
CacheData["Cache Data:<br>- lastSaveDirectory<br>- lastLoadDirectory<br>- windowSizeMap<br>- defaultCloseOperation"]
SettingsDialog["SettingsDialog"]
MacroSettingsDialog["MacroSettingsDialog"]
ExitDialog["ExitDialog"]
MainFrame["MainFrame"]
FileChooser["JFileChooser"]
ConfigManager["ConfigManager"]
CacheManager["CacheManager"]
ConfigFile --> ConfigManager
ConfigManager --> ConfigCategories
SettingsDialog --> ConfigManager
MacroSettingsDialog --> ConfigManager
MainFrame --> ConfigManager
CacheFile --> CacheManager
CacheManager --> CacheData
ExitDialog --> CacheManager
MainFrame --> CacheManager
FileChooser --> CacheManager
subgraph subGraph2 ["Application Usage"]
SettingsDialog
MacroSettingsDialog
ExitDialog
MainFrame
FileChooser
end
subgraph subGraph1 ["Runtime State (Ephemeral)"]
CacheFile
CacheData
end
subgraph subGraph0 ["User Preferences (Durable)"]
ConfigFile
ConfigCategories
end
The system resolves settings using a priority chain:
-
System Settings (if
followSystemSettings = true) * OS language detection viaSystemUtil.getSystemLang()* OS dark mode detection viaSystemUtil.isSystemDarkMode() -
User Configuration (if
followSystemSettings = false) * Explicit values fromconfig.cfg -
Cached State * Recent directories from
cache.json* Window sizes fromcache.json - Defaults * Hardcoded fallback values
Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L45-L49, README.md L66-L90
The event capture system integrates with the operating system through JNativeHook to monitor global input events.
flowchart TD
OS["Operating System<br>Input Events"]
JNativeHook["JNativeHook<br>GlobalScreen"]
LibPath["jnativehook.lib.path"]
NativeDLL["JNativeHook.x86_64.dll"]
GML["GlobalMouseListener"]
nativeKeyPressed["nativeKeyPressed()"]
nativeMousePressed["nativeMousePressed()"]
nativeMouseMoved["nativeMouseMoved()"]
nativeMouseWheelMoved["nativeMouseWheelMoved()"]
HotkeyCheck["Check if<br>hotkey matches<br>F2/F3/F4/F5"]
RecordingCheck["Check if<br>recording == true"]
DialogCheck["Check if<br>inHotKeyDialog == false"]
ScreenUtil["ScreenUtil<br>normalizeCoordinates()"]
MouseAction["new MouseAction()"]
MacroManager["MacroManager<br>recordAction()"]
OS --> JNativeHook
JNativeHook --> GML
nativeKeyPressed --> HotkeyCheck
nativeMousePressed --> DialogCheck
nativeMouseMoved --> DialogCheck
HotkeyCheck --> MacroManager
HotkeyCheck --> MacroManager
HotkeyCheck --> MacroManager
HotkeyCheck --> MacroManager
RecordingCheck --> ScreenUtil
subgraph subGraph3 ["Action Recording"]
ScreenUtil
MouseAction
MacroManager
ScreenUtil --> MouseAction
MouseAction --> MacroManager
end
subgraph subGraph2 ["Event Processing"]
HotkeyCheck
RecordingCheck
DialogCheck
DialogCheck --> RecordingCheck
end
subgraph subGraph1 ["Event Listener"]
GML
nativeKeyPressed
nativeMousePressed
nativeMouseMoved
nativeMouseWheelMoved
GML --> nativeKeyPressed
GML --> nativeMousePressed
GML --> nativeMouseMoved
GML --> nativeMouseWheelMoved
end
subgraph subGraph0 ["Native Integration"]
JNativeHook
LibPath
NativeDLL
LibPath --> NativeDLL
end
Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L11-L15, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L133-L141
The application configures JNativeHook's native library path during initialization:
CONFIG_DIR/libs/JNativeHook.x86_64.dll
This path is set via the system property jnativehook.lib.path before any JNativeHook operations.
Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L11-L15
The UI layer supports dynamic theming and multi-language localization through a coordinated system.
flowchart TD
followSystemSettings["config.followSystemSettings"]
enableDarkMode["config.enableDarkMode"]
SystemUtil["SystemUtil.isSystemDarkMode()"]
OSTheme["OS Theme Setting"]
ColorConsts["ColorConsts"]
DARK_MODE["DARK_MODE = 0"]
LIGHT_MODE["LIGHT_MODE = 1"]
ComponentUtil["ComponentUtil.setMode()"]
Components["All JComponents"]
followSystemSettings --> SystemUtil
OSTheme --> DARK_MODE
OSTheme --> LIGHT_MODE
enableDarkMode --> DARK_MODE
enableDarkMode --> LIGHT_MODE
DARK_MODE --> ComponentUtil
LIGHT_MODE --> ComponentUtil
subgraph Application ["Application"]
ComponentUtil
Components
ComponentUtil --> Components
end
subgraph subGraph2 ["Theme Constants"]
ColorConsts
DARK_MODE
LIGHT_MODE
ColorConsts --> DARK_MODE
ColorConsts --> LIGHT_MODE
end
subgraph Detection ["Detection"]
SystemUtil
OSTheme
SystemUtil --> OSTheme
end
subgraph Configuration ["Configuration"]
followSystemSettings
enableDarkMode
followSystemSettings --> enableDarkMode
end
flowchart TD
followSystemSettings["config.followSystemSettings"]
SystemLang["SystemUtil.getSystemLang()"]
ConfigLang["config.lang"]
Localizer["Localizer.load(lang)"]
LangFile["lang/{lang}.json"]
TranslationMap["Map<String, String><br>translations"]
MainFrame["MainFrame"]
Dialogs["SettingsDialog<br>MacroSettingsDialog<br>etc."]
Buttons["JButton components"]
SystemLang --> Localizer
ConfigLang --> Localizer
MainFrame --> TranslationMap
Dialogs --> TranslationMap
Buttons --> TranslationMap
subgraph subGraph2 ["UI Components"]
MainFrame
Dialogs
Buttons
end
subgraph subGraph1 ["Language Loading"]
Localizer
LangFile
TranslationMap
Localizer --> LangFile
LangFile --> TranslationMap
end
subgraph subGraph0 ["Language Detection"]
followSystemSettings
SystemLang
ConfigLang
followSystemSettings --> SystemLang
followSystemSettings --> ConfigLang
end
Supported languages are detected via ConfigManager.getAvailableLangs(), which scans for available JSON files in the lang/ directory.
Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L45-L53, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L142, src/io/github/samera2022/mouse_macros/constant/OtherConsts.java L4-L5
The application persists data to platform-specific directories using a cross-platform abstraction layer.
| Platform | Path |
|---|---|
| Windows | %LOCALAPPDATA%/MouseMacros/ |
| macOS | ~/Library/Application Support/MouseMacros/ |
| Linux | ~/.local/share/MouseMacros/ |
This resolution is handled by FileUtil.getLocalStoragePath(), which detects the operating system and returns the appropriate path.
flowchart TD
ConfigCfg["config.cfg<br>(JSON)<br>User preferences"]
CacheJson["cache.json<br>(JSON)<br>Runtime state"]
LibsDir["libs/<br>JNativeHook.x86_64.dll"]
MMCFiles["*.mmc files<br>(CSV)<br>Macro data"]
ConfigManager["ConfigManager"]
CacheManager["CacheManager"]
MouseMacro["MouseMacro"]
MacroManager["MacroManager"]
ConfigManager --> ConfigCfg
CacheManager --> CacheJson
MouseMacro --> LibsDir
MacroManager --> MMCFiles
subgraph subGraph1 ["User-Specified Locations"]
MMCFiles
end
subgraph CONFIG_DIR ["CONFIG_DIR"]
ConfigCfg
CacheJson
LibsDir
end
The .mmc file format uses CSV with versioning based on field count:
| Version | Field Count | Fields |
|---|---|---|
| v4 | 8 | x, y, type, button, delay, wheelAmount, keyCode, awtKeyCode |
| v3 | 7 | x, y, type, button, delay, wheelAmount, keyCode |
| v2 | 6 | x, y, type, button, delay, wheelAmount |
| v1 | 5 | x, y, type, button, delay |
The system maintains backward compatibility by detecting field count during load operations and defaulting missing fields.
Sources: README.md L66-L73, src/io/github/samera2022/mouse_macros/MouseMacro.java L12-L13
The application uses an enum-based system to track version history and display update information.
flowchart TD
UpdateInfo["UpdateInfo enum"]
VERSION_0_0_1["VERSION_0_0_1"]
VERSION_1_0_0["VERSION_1_0_0"]
VERSION_1_2_2["VERSION_1_2_2"]
More["..."]
Version["version: String"]
ReleaseDate["releaseDate: String"]
Description["description: String"]
UpdateInfo --> VERSION_0_0_1
UpdateInfo --> VERSION_1_0_0
UpdateInfo --> VERSION_1_2_2
UpdateInfo --> More
VERSION_0_0_1 --> Version
VERSION_0_0_1 --> ReleaseDate
VERSION_0_0_1 --> Description
subgraph subGraph1 ["Each Constant Contains"]
Version
ReleaseDate
Description
end
subgraph subGraph0 ["Enum Constants"]
VERSION_0_0_1
VERSION_1_0_0
VERSION_1_2_2
More
end
The UpdateInfo enum provides methods:
-
getFormattedLog()- Returns formatted changelog text -
findByVersion(String)- Looks up version by string -
getAllVersions()- Returns array of all version strings -
getAllDisplayNames()- Returns array of formatted display names
This enum is used by UpdateInfoDialog to display version history.
Sources: src/io/github/samera2022/mouse_macros/UpdateInfo.java L5-L156
MouseMacros employs a clean, layered architecture with:
- Clear separation of concerns across Entry, UI, Business Logic, Configuration, and Utility layers
-
Singleton pattern for global components (
MAIN_FRAME,GML,config,cache) - Manager pattern for domain logic encapsulation
- Dual persistence layers distinguishing user preferences (config) from runtime state (cache)
-
Platform abstraction via utility classes (
FileUtil,SystemUtil) - Native integration through JNativeHook for global event capture
- Dynamic UI with theme switching and multi-language support
- Versioned file format maintaining backward compatibility for macro files
This architecture enables extensibility while maintaining code organization and testability.