|
Gin
|
A small TCP server that lets an external process drive a JUCE UI. More...
#include <gin_remoteserver.h>
Inherits juce::Thread.
Classes | |
| struct | CommandResult |
| The outcome of a command. More... | |
| class | Context |
| Passed to async commands so they can hop onto the message thread and pause between steps. More... | |
| struct | Options |
Public Types | |
| using | CommandHandler = std::function< CommandResult(const juce::var &args)> |
| Handler for a command that runs on the message thread. | |
| using | AsyncCommandHandler = std::function< CommandResult(Context &, const juce::var &args)> |
| Handler for a command that runs on the server thread. | |
| using | ComponentInfoProvider = std::function< void(juce::Component &, juce::DynamicObject &info)> |
| Called for every component description so products can add their own fields. | |
Public Member Functions | |
| RemoteServer () | |
| RemoteServer (const Options &) | |
| ~RemoteServer () override | |
| bool | start () |
| Starts listening. | |
| void | stop () |
| Stops listening and disconnects any client. | |
| bool | isRunning () const noexcept |
| int | getPort () const noexcept |
| const Options & | getOptions () const noexcept |
| void | addCommand (const juce::String &name, const juce::String &description, const juce::String &argsSpec, CommandHandler handler) |
| Adds a command that runs on the message thread. | |
| void | addAsyncCommand (const juce::String &name, const juce::String &description, const juce::String &argsSpec, AsyncCommandHandler handler) |
| Adds a command that runs on the server thread. | |
| void | removeCommand (const juce::String &name) |
| void | addComponentInfoProvider (ComponentInfoProvider) |
| Registers a hook that can add fields to every component description. | |
| void | log (const juce::String &line) |
| Appends a line to the buffer returned by the log command. | |
| juce::var | describeComponent (juce::Component &, bool full=false) |
| Describes a component as a json object, including fields from the registered providers. | |
| juce::var | describeTree (juce::Component &, int depth=-1, bool visibleOnly=true, int maxNodes=5000) |
| Describes a component and its descendants. | |
Static Public Member Functions | |
| static juce::Array< juce::Component * > | findComponents (const juce::String &selector, bool visibleOnly=true, juce::Component *root=nullptr) |
| Returns every component matching a selector. | |
| static juce::Component * | findComponent (const juce::String &selector, bool visibleOnly=true, juce::Component *root=nullptr) |
| Returns the first component matching a selector, or nullptr. | |
| static juce::String | getClassName (juce::Component &) |
| Demangled class name of a component, e.g. | |
| static juce::String | getComponentPath (juce::Component &) |
| Index path of a component, e.g. | |
| static juce::String | getComponentText (juce::Component &) |
| Text shown by a component if it is a button, label, text editor or combo box. | |
| static juce::var | getComponentValue (juce::Component &) |
| Value of a component if it is a slider, toggle button, combo box, etc. | |
| static juce::String | setComponentValue (juce::Component &, const juce::var &value) |
| Sets the value of a standard component. | |
| static juce::var | getArg (const juce::var &args, const char *name, const juce::var &defaultValue={}) |
| Reads an argument with a default. | |
| static bool | injectMouse (juce::Point< float > screenPos, juce::ModifierKeys mods, juce::Component *target=nullptr) |
| Synthesises a mouse event through the component peer, so it travels the same route as a real one. | |
| static bool | injectWheel (juce::Point< float > screenPos, float deltaX, float deltaY, juce::ModifierKeys mods, juce::Component *target=nullptr) |
| static bool | injectKey (const juce::KeyPress &, juce::Component *target=nullptr) |
| Sends a key press to the focused peer, or the peer owning target. | |
| static juce::Component * | getComponentAt (juce::Point< int > screenPos) |
| The deepest component under a screen position, across all desktop windows. | |
| static juce::ComponentPeer * | getPeerAt (juce::Point< int > screenPos) |
| The peer under a screen position, if any. | |
| static juce::ComponentPeer * | getFocusedPeer () |
| The focused peer, falling back to the first one. | |
| static void | setMoveRealCursor (bool shouldMove) noexcept |
| See Options::moveRealCursor. | |
| static bool | getMoveRealCursor () noexcept |
A small TCP server that lets an external process drive a JUCE UI.
Intended for UI automation, scripted testing and AI coding agents. Clients connect over a local socket and exchange newline-delimited JSON:
Built in commands cover the component tree (tree, find, describe, at, windows), input (click, drag, mouse, wheel, key, type, focus), state (get, set, press, resize), screenshots and waiting. Products add their own with addCommand(), and can decorate every component description with addComponentInfoProvider().
Nothing happens unless you create and start a server, so it is safe to compile into a shared module. Only start it in development builds.
Components are addressed with a tiny selector language. A selector is a space separated list of steps, each step narrowing the search to the descendants of the previous matches:
#id component ID (Component::getComponentID) .ClassName class name, with or without namespace name component name, or button / label text ~text substring match of name, text or id @x,y deepest component at a screen point /0/3/1 index path: desktop window 0, child 3, child 1 any component step[n] nth match of that step
e.g. "#sidebar .TextButton[2]" or "Save" or "@120,240"
A command line client and MCP server live in tools/gin_remote.
| using RemoteServer::CommandHandler = std::function<CommandResult (const juce::var& args)> |
Handler for a command that runs on the message thread.
| using RemoteServer::AsyncCommandHandler = std::function<CommandResult (Context&, const juce::var& args)> |
Handler for a command that runs on the server thread.
Use this when a command needs to span several message loop iterations, e.g. a drag or a wait.
| using RemoteServer::ComponentInfoProvider = std::function<void (juce::Component&, juce::DynamicObject& info)> |
Called for every component description so products can add their own fields.
| RemoteServer::RemoteServer | ( | ) |
|
override |
| bool RemoteServer::start | ( | ) |
Starts listening.
Returns false if no port in the configured range could be bound.
| void RemoteServer::stop | ( | ) |
Stops listening and disconnects any client.
|
noexcept |
|
noexcept |
| void RemoteServer::addCommand | ( | const juce::String & | name, |
| const juce::String & | description, | ||
| const juce::String & | argsSpec, | ||
| CommandHandler | handler | ||
| ) |
Adds a command that runs on the message thread.
| name | command name, e.g. "loadPreset" |
| description | one line of help, shown by the commands command and used as the MCP tool description |
| argsSpec | optional json object describing the arguments, mapping name to "type: description". Types are string, int, number, bool, array, object. Add ! after the type for required args. e.g. R"({"name":"string!: preset name","layer":"int: 0 based layer, default 0"})" |
| handler | the handler |
Referenced by addAudioProcessorCommands().
| void RemoteServer::addAsyncCommand | ( | const juce::String & | name, |
| const juce::String & | description, | ||
| const juce::String & | argsSpec, | ||
| AsyncCommandHandler | handler | ||
| ) |
Adds a command that runs on the server thread.
See AsyncCommandHandler.
| void RemoteServer::addComponentInfoProvider | ( | ComponentInfoProvider | ) |
Registers a hook that can add fields to every component description.
Appends a line to the buffer returned by the log command.
Thread safe.
|
static |
Returns every component matching a selector.
root == nullptr searches all desktop windows.
|
static |
Returns the first component matching a selector, or nullptr.
Describes a component as a json object, including fields from the registered providers.
| juce::var RemoteServer::describeTree | ( | juce::Component & | , |
| int | depth = -1, |
||
| bool | visibleOnly = true, |
||
| int | maxNodes = 5000 |
||
| ) |
Describes a component and its descendants.
depth < 0 means unlimited.
|
static |
Demangled class name of a component, e.g.
"juce::TextButton".
|
static |
Index path of a component, e.g.
"/0/3/1". The first index is the desktop window.
|
static |
Text shown by a component if it is a button, label, text editor or combo box.
|
static |
Value of a component if it is a slider, toggle button, combo box, etc.
Returns void var if none.
|
static |
Sets the value of a standard component.
Returns an error message on failure.
|
static |
Reads an argument with a default.
Accepts missing args object.
Referenced by addAudioProcessorCommands().
|
static |
Synthesises a mouse event through the component peer, so it travels the same route as a real one.
pos is in screen coordinates. Pass the button in mods for a press.
|
static |
|
static |
Sends a key press to the focused peer, or the peer owning target.
The deepest component under a screen position, across all desktop windows.
Unlike Desktop::findComponentAt this ignores windows belonging to other applications, so it works when the window being driven is behind a terminal.
The peer under a screen position, if any.
|
static |
The focused peer, falling back to the first one.
Applies to every server in the process.