Plugin IPC System
The Noctalia plugin system allows plugins to register their own IPC handlers, making them accessible via the shell’s IPC interface. This enables external scripts, keybindings, and other tools to interact with your plugin programmatically.
Overview
Section titled “Overview”IPC (Inter-Process Communication) handlers allow your plugin to respond to commands from external sources. This is useful for:
- Triggering plugin actions from keybindings
- Integrating with external scripts and tools
- Automating plugin behavior
- Creating command-line interfaces for your plugin
When a plugin is loaded, its IPC handlers are automatically registered and can be called using the standard Noctalia IPC command format.
How It Works
Section titled “How It Works”Plugins register IPC handlers in their Main.qml file using the IpcHandler component from Quickshell. Each handler function becomes a callable command that can be invoked from the command line or other IPC clients.
The IPC system routes commands to the appropriate plugin handler based on the target prefix plugin:your-plugin-id.
Creating IPC Handlers
Section titled “Creating IPC Handlers”Basic Structure
Section titled “Basic Structure”In your plugin’s Main.qml file, register IPC handlers using the IpcHandler component:
import QtQuickimport Quickshell.Ioimport qs.Services.UI
Item { property var pluginApi: null
IpcHandler { target: "plugin:your-plugin-id" function yourCommand() { // Your handler logic here } }}Target Naming
Section titled “Target Naming”The target property must follow this format:
- Prefix: Always use
"plugin:"as the prefix - Plugin ID: Follow with your plugin’s ID from
manifest.json - Example: If your plugin ID is
"hello-world", use"plugin:hello-world"
Plugin API Access
Section titled “Plugin API Access”The pluginApi property is automatically injected by the plugin system and provides access to:
- Settings:
pluginApi.pluginSettings- Read and write plugin settings - Persistence:
pluginApi.saveSettings()- Save settings to disk - Panels:
pluginApi.openPanel(screen)/pluginApi.closePanel(screen)- Control plugin panels - Translations:
pluginApi.tr(key, interpolations)- Translate text - Metadata:
pluginApi.manifest- Access plugin manifest data
For complete API documentation, see the Plugin API Reference.
Examples
Section titled “Examples”Simple Command
Section titled “Simple Command”A basic handler that updates a plugin setting:
import QtQuickimport Quickshell.Ioimport qs.Services.UI
Item { property var pluginApi: null
IpcHandler { target: "plugin:hello-world" function setMessage(message: string) { if (pluginApi && message) { pluginApi.pluginSettings.message = message; pluginApi.saveSettings(); ToastService.showNotice("Message updated to: " + message); } } }}This handler can be called from the command line:
qs -c noctalia-shell ipc call plugin:hello-world setMessage "Hello from IPC!"Multiple Commands
Section titled “Multiple Commands”You can define multiple functions within a single IpcHandler:
IpcHandler { target: "plugin:my-plugin"
function toggle() { if (!pluginApi) return; pluginApi.pluginSettings.enabled = !pluginApi.pluginSettings.enabled; pluginApi.saveSettings(); }
function enable() { if (!pluginApi) return; pluginApi.pluginSettings.enabled = true; pluginApi.saveSettings(); }
function disable() { if (!pluginApi) return; pluginApi.pluginSettings.enabled = false; pluginApi.saveSettings(); }
function setValue(value: string) { if (!pluginApi) return; var numValue = parseFloat(value); if (!Number.isNaN(numValue)) { pluginApi.pluginSettings.value = numValue; pluginApi.saveSettings(); } }}Function Parameters
Section titled “Function Parameters”IPC handler functions can accept parameters. When called via IPC, all arguments are passed as strings and should be parsed as needed:
IpcHandler { target: "plugin:my-plugin"
function setValue(value: string) { // Parse string to number var numValue = parseFloat(value); if (Number.isNaN(numValue)) { Logger.w("MyPlugin", "Invalid value:", value); return; }
pluginApi.pluginSettings.value = numValue; pluginApi.saveSettings(); }
function setMultiple(param1: string, param2: string) { // Handle multiple parameters pluginApi.pluginSettings.param1 = param1; pluginApi.pluginSettings.param2 = param2; pluginApi.saveSettings(); }}Calling IPC Commands
Section titled “Calling IPC Commands”Once your plugin is loaded, you can call its IPC handlers using the standard Noctalia IPC command format:
qs -c noctalia-shell ipc call plugin:your-plugin-id commandName [arguments]Command Examples
Section titled “Command Examples”# Call a command with a string argumentqs -c noctalia-shell ipc call plugin:hello-world setMessage "Hello from IPC!"
# Call a command with no argumentsqs -c noctalia-shell ipc call plugin:my-plugin toggle
# Call a command with a numeric argument (passed as string)qs -c noctalia-shell ipc call plugin:my-plugin setValue "42"Integration with Keybindings
Section titled “Integration with Keybindings”IPC commands can be bound to keyboard shortcuts in your Noctalia configuration:
{ "keybinds": { "Super+Shift+P": "qs -c noctalia-shell ipc call plugin:my-plugin toggle" }}Best Practices
Section titled “Best Practices”Input Validation
Section titled “Input Validation”Always validate and parse input parameters before use:
function setValue(value: string) { if (!pluginApi) { Logger.e("MyPlugin", "Plugin API not available"); return; }
var numValue = parseFloat(value); if (Number.isNaN(numValue)) { Logger.w("MyPlugin", "Invalid numeric value:", value); ToastService.showError("Invalid value: " + value); return; }
pluginApi.pluginSettings.value = numValue; pluginApi.saveSettings();}Error Handling
Section titled “Error Handling”Check for null/undefined values and handle errors gracefully:
function updateSetting(value: string) { if (!pluginApi) { Logger.e("MyPlugin", "Plugin API not available"); return; }
if (!value || value.trim() === "") { Logger.w("MyPlugin", "Value is required"); return; }
// Proceed with update... pluginApi.pluginSettings.setting = value; pluginApi.saveSettings();}User Feedback
Section titled “User Feedback”Use ToastService to provide feedback for important operations:
import qs.Services.UI
function updateSettings() { // ... update logic ...
ToastService.showNotice("Settings updated successfully"); // or ToastService.showError("Failed to update settings");}Logging
Section titled “Logging”Use the Logger for debugging and error tracking:
import qs.Commons
function handleCommand(value: string) { Logger.d("MyPlugin", "Command called with value:", value); Logger.i("MyPlugin", "Settings updated"); Logger.w("MyPlugin", "Warning: invalid input"); Logger.e("MyPlugin", "Error occurred:", error);}Using Plugin API
Section titled “Using Plugin API”Leverage the pluginApi for common operations:
// Save settingspluginApi.saveSettings();
// Open/close panelsif (Quickshell.screens.length > 0) { var screen = Quickshell.screens[0]; pluginApi.openPanel(screen); pluginApi.closePanel(screen);}
// Access translationsvar text = pluginApi.tr("settings.enabled");Complete Example
Section titled “Complete Example”Here’s a complete example showing a plugin with multiple IPC handlers:
import QtQuickimport Quickshellimport Quickshell.Ioimport qs.Commonsimport qs.Services.UI
Item { property var pluginApi: null
IpcHandler { target: "plugin:example-plugin"
// Toggle plugin state function toggle() { if (!pluginApi) return;
pluginApi.pluginSettings.enabled = !pluginApi.pluginSettings.enabled; pluginApi.saveSettings();
var status = pluginApi.pluginSettings.enabled ? "enabled" : "disabled"; ToastService.showNotice("Plugin " + status); Logger.i("ExamplePlugin", "Toggled to:", status); }
// Enable plugin function enable() { if (!pluginApi) return;
pluginApi.pluginSettings.enabled = true; pluginApi.saveSettings(); ToastService.showNotice("Plugin enabled"); }
// Disable plugin function disable() { if (!pluginApi) return;
pluginApi.pluginSettings.enabled = false; pluginApi.saveSettings(); ToastService.showNotice("Plugin disabled"); }
// Set a numeric value function setValue(value: string) { if (!pluginApi) return;
var numValue = parseFloat(value); if (Number.isNaN(numValue)) { Logger.w("ExamplePlugin", "Invalid value:", value); ToastService.showError("Invalid value: " + value); return; }
pluginApi.pluginSettings.value = numValue; pluginApi.saveSettings(); ToastService.showNotice("Value set to: " + numValue); }
// Open plugin panel function openPanel() { if (!pluginApi || Quickshell.screens.length === 0) return;
var screen = Quickshell.screens[0]; pluginApi.openPanel(screen); } }}Testing
Section titled “Testing”After creating your IPC handlers, follow these steps to test them:
- Reload your plugin: Disable and re-enable your plugin in Noctalia settings to ensure the handlers are registered
- Test via command line: Use the IPC command format to call your handlers
Terminal window qs -c noctalia-shell ipc call plugin:your-plugin-id yourCommand - Check logs: Monitor Quickshell logs for any errors or debug messages
- Verify behavior: Ensure your plugin responds correctly to IPC calls
Troubleshooting
Section titled “Troubleshooting”Handler Not Working
Section titled “Handler Not Working”If your IPC handler isn’t responding:
- Verify target name: Ensure it’s exactly
"plugin:your-plugin-id"(with theplugin:prefix) - Check plugin status: Verify that your plugin is enabled and loaded
- Function name: Function names are case-sensitive - ensure exact matches
- Review logs: Check Quickshell/Noctalia logs for registration errors
Parameters Not Received
Section titled “Parameters Not Received”If parameters aren’t being received correctly:
- String conversion: Remember that all IPC arguments are strings - parse them as needed
- Parameter count: Ensure the number of parameters matches your function signature
- Type validation: Always validate parameter types before use
Plugin API Not Available
Section titled “Plugin API Not Available”If pluginApi is null or undefined:
- Timing: The
pluginApiis injected after component creation - add null checks - Null checks: Always verify
pluginApiis available before using it - Component lifecycle: IPC handlers remain active as long as the plugin is loaded
See Also
Section titled “See Also”- Getting Started - Create your first plugin
- Main Component - Overview of Main.qml
- Plugin API Reference - Complete API documentation
- Manifest Reference - Plugin configuration