Skip to content

Architecture Overview

Samera2022 edited this page Jan 30, 2026 · 1 revision

Architecture Overview

Relevant source files

Purpose and Scope

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:


Layered Architecture

MouseMacros follows a layered architecture pattern with clear separation of concerns. The system is organized into five primary layers, each with distinct responsibilities.

System Layer Diagram

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
Loading

Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L1-L18, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L1-L246

Layer Responsibilities

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


Core Component Relationships

The following diagram shows the primary components and their interactions at the class level, using actual code entity names.

Component Collaboration Diagram

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
Loading

Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L28-L165, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java


Application Initialization Flow

The application follows a specific initialization sequence to ensure proper setup of native libraries, configuration loading, and UI construction.

Initialization Sequence

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)
Loading

Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L10-L17, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L44-L165


Design Patterns

MouseMacros employs several design patterns to achieve modularity, maintainability, and extensibility.

Singleton Pattern

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

Manager Pattern

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

Adapter Pattern

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
Loading

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

Strategy Pattern

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


Configuration Architecture

The configuration system maintains two separate persistence layers with distinct purposes.

Configuration vs. Cache

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
Loading

Configuration Loading Priority

The system resolves settings using a priority chain:

  1. System Settings (if followSystemSettings = true) * OS language detection via SystemUtil.getSystemLang() * OS dark mode detection via SystemUtil.isSystemDarkMode()
  2. User Configuration (if followSystemSettings = false) * Explicit values from config.cfg
  3. Cached State * Recent directories from cache.json * Window sizes from cache.json
  4. Defaults * Hardcoded fallback values

Sources: src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L45-L49, README.md L66-L90


Event Capture Architecture

The event capture system integrates with the operating system through JNativeHook to monitor global input events.

Event Flow Architecture

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
Loading

Sources: src/io/github/samera2022/mouse_macros/MouseMacro.java L11-L15, src/io/github/samera2022/mouse_macros/ui/frame/MainFrame.java L133-L141

Native Library Configuration

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


UI Theme and Localization Architecture

The UI layer supports dynamic theming and multi-language localization through a coordinated system.

Theme Application Flow

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
Loading

Localization Flow

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
Loading

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


File Persistence Strategy

The application persists data to platform-specific directories using a cross-platform abstraction layer.

Storage Location Resolution

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.

Persistent File Types

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
Loading

Macro File Format Evolution

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


Version Management

The application uses an enum-based system to track version history and display update information.

UpdateInfo Enum Structure

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
Loading

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


Summary

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.

Clone this wiki locally