daemon

package
v1.43.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: GPL-2.0 Imports: 18 Imported by: 0

Documentation

Overview

ABOUTME: Client library for communicating with the notification daemon. ABOUTME: Provides functions to check daemon status, start on-demand, and send notifications.

ABOUTME: Window focus methods for Linux desktop environments. ABOUTME: Implements a fallback chain to focus windows on GNOME, KDE, Sway, and other compositors.

ABOUTME: IPC protocol types for communication between daemon client and server. ABOUTME: Uses JSON-over-Unix-socket for simple, reliable inter-process communication.

ABOUTME: Daemon server that maintains persistent D-Bus connection for click-to-focus notifications. ABOUTME: Listens on Unix socket for IPC requests and handles notification action callbacks.

Index

Constants

View Source
const ProtocolVersion = "1.0"

Protocol version for compatibility checking

Variables

View Source
var (
	ErrDaemonNotAvailable = errors.New("daemon not available")
	ErrDaemonNotRunning   = errors.New("daemon not running")
)

Common errors

Functions

func DetectFocusTools

func DetectFocusTools() map[string]bool

DetectFocusTools returns a map of available focus tools.

func GetAppID

func GetAppID(terminalName string) string

GetAppID returns the .desktop app ID for a terminal name.

func GetDaemonPID

func GetDaemonPID() int

GetDaemonPID returns the PID of the running daemon, or 0 if not running

func GetDesktopEntryID

func GetDesktopEntryID(terminalName string) string

GetDesktopEntryID returns the desktop entry ID (without .desktop suffix) for a terminal. This is the value expected by the freedesktop "desktop-entry" notification hint.

func GetExactWindowTitle

func GetExactWindowTitle(terminalName string) string

GetExactWindowTitle returns an exact top-level window title for terminals that expose a reliable per-terminal identifier. Currently Terminator can provide this via TERMINATOR_UUID + remotinator.

func GetGnomeWmClass

func GetGnomeWmClass(terminalName string) string

GetGnomeWmClass returns the WM_CLASS used by the activate-window-by-title GNOME Shell extension for activateByWmClass calls. For Wayland-native apps this is the app_id in reverse-domain format.

func GetKdotoolClass

func GetKdotoolClass(terminalName string) string

GetKdotoolClass returns the window class for kdotool search.

func GetNotificationDesktopEntryID

func GetNotificationDesktopEntryID(terminalName string) string

GetNotificationDesktopEntryID returns the desktop-entry hint value to use for notifications. GNOME on Wayland shows a long-running loading cursor when the clicked notification advertises the terminal/editor desktop entry and the app never consumes the generated activation token. A dedicated hidden desktop file with StartupNotify=false avoids that spinner while preserving click handling via our daemon.

func GetPidFilePath

func GetPidFilePath() string

GetPidFilePath returns the path to the daemon's PID file.

func GetSearchTerm

func GetSearchTerm(terminalName string) string

GetSearchTerm returns a window title search term for a terminal name.

func GetSearchTermWithFolder

func GetSearchTermWithFolder(terminalName, folderName string) string

GetSearchTermWithFolder returns the window title search term, using the project folder name for VS Code when available (more specific than "Visual Studio Code").

func GetSocketPath

func GetSocketPath() string

GetSocketPath returns the Unix socket path for the daemon. Uses XDG_RUNTIME_DIR if available, falls back to /tmp with UID suffix.

func GetTerminalName

func GetTerminalName() string

GetTerminalName detects the current terminal from environment variables.

func GetWezTermFocusHints

func GetWezTermFocusHints(terminalName string) (paneID, socketPath string)

GetWezTermFocusHints returns WezTerm-specific focus hints only when the detected focus target is WezTerm. WEZTERM_* variables are often inherited by GUI apps launched from WezTerm, so they must not be trusted for other targets.

func GetWezTermPaneID

func GetWezTermPaneID() string

GetWezTermPaneID returns the WezTerm pane ID from the environment.

func GetWezTermSocketPath

func GetWezTermSocketPath() string

GetWezTermSocketPath returns the WezTerm Unix socket path from the environment.

func GetWlrctlAppID

func GetWlrctlAppID(terminalName string) string

GetWlrctlAppID returns the wlroots app_id for a terminal name.

func GetX11WindowID

func GetX11WindowID() string

GetX11WindowID returns the current terminal window's X11 window ID when available. It is captured in the hook process and later used by the daemon for exact focus on X11.

func GetXdotoolClass

func GetXdotoolClass(terminalName string) string

GetXdotoolClass returns the X11 WM_CLASS for xdotool search.

func IsDaemonRunning

func IsDaemonRunning() bool

IsDaemonRunning checks if the daemon is running and responsive

func IsWezTermTerminalName

func IsWezTermTerminalName(terminalName string) bool

IsWezTermTerminalName reports whether terminalName identifies WezTerm.

func StartDaemonOnDemand

func StartDaemonOnDemand() bool

StartDaemonOnDemand starts the daemon if it's not already running. Returns true if daemon is running (either started now or was already running).

func StopDaemon

func StopDaemon() error

StopDaemon stops the running daemon

func TryActivateWindowByTitle

func TryActivateWindowByTitle(terminalName, folderName string) error

TryActivateWindowByTitle uses the activate-window-by-title GNOME extension. https://extensions.gnome.org/extension/5021/activate-window-by-title/ This method does NOT require unsafe_mode and works on GNOME 42+.

Search order:

  1. activateBySubstring with the folder-specific term, when available — ensures the correct project window is focused when multiple windows of the same app are open (e.g. two VS Code windows for different projects).
  2. activateByWmClass — reliable for Wayland-native terminals whose WM class is a reverse-domain app ID (e.g. com.mitchellh.ghostty, org.wezfurlong.wezterm) and whose window title does not contain the app name.
  3. activateBySubstring with the generic terminal name as a final fallback.

func TryFocus

func TryFocus(terminalName, folderName string) error

TryFocus attempts to focus a window using available tools. folderName is the project folder name used for title-based window search (may be empty). It tries each method in order until one succeeds.

func TryFocusWithHints

func TryFocusWithHints(terminalName, folderName, windowID, windowTitle, wezTermPaneID, wezTermSocket, warpFocusURL string) error

TryFocusWithHints attempts exact focus using hook-time hints first, then falls back to compositor-specific methods. wezTermPaneID and wezTermSocket enable tab-level focus for WezTerm. warpFocusURL is a Warp session deep link that focuses the originating window/tab/pane.

For WezTerm, window-level focus runs first, then the pane switch runs after a short delay. This ordering matters: GNOME's XDG Activation Token is processed asynchronously after the window-level call and may restore the previously active tab if the pane switch runs first. Running the pane switch last ensures it wins. If all window-level methods fail but a pane ID is available, TryWezTermPane is tried as a last resort (activate-pane also raises the window on WezTerm).

func TryFocusWithWindowID

func TryFocusWithWindowID(terminalName, folderName, windowID string) error

TryFocusWithWindowID preserves the previous API for callers that only have an exact X11 window ID.

func TryGnomeFocusApp

func TryGnomeFocusApp(terminalName, folderName string) error

TryGnomeFocusApp uses GNOME Shell's FocusApp method (available since GNOME 45).

func TryGnomeShellEval

func TryGnomeShellEval(terminalName, folderName string) error

TryGnomeShellEval uses GNOME Shell's Eval method to activate an app. Requires unsafe_mode or development-tools enabled.

func TryGnomeShellEvalByTitle

func TryGnomeShellEvalByTitle(terminalName, folderName string) error

TryGnomeShellEvalByTitle uses GNOME Shell's Eval to find and focus window by title. Requires unsafe_mode or development-tools enabled.

func TryKdotool

func TryKdotool(terminalName, folderName string) error

TryKdotool uses kdotool for KDE Plasma.

func TryWezTermPane

func TryWezTermPane(paneID, socketPath string) error

TryWezTermPane activates a specific WezTerm pane by ID using the WezTerm CLI. This switches to the exact tab/pane where Claude is running. socketPath is passed via WEZTERM_UNIX_SOCKET env var (the CLI has no --unix-socket flag).

func TryWlrctl

func TryWlrctl(terminalName, folderName string) error

TryWlrctl uses wlrctl for wlroots-based compositors (Sway, etc.).

func TryXdotool

func TryXdotool(terminalName, folderName string) error

TryXdotool uses xdotool for X11-based desktop environments (XFCE, MATE, Cinnamon, i3, bspwm, and X11 sessions of GNOME/KDE).

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client communicates with the daemon via Unix socket

func NewClient

func NewClient() (*Client, error)

NewClient creates a new daemon client

func (*Client) Ping

func (c *Client) Ping() (*PingResponse, error)

Ping checks if the daemon is responding and returns status info

func (*Client) SendNotification

func (c *Client) SendNotification(
	title,
	body,
	focusTarget,
	focusFolder,
	focusWindowID,
	focusWindowTitle,
	wezTermPaneID,
	wezTermSocket,
	warpFocusURL string,
	timeout int,
) (*NotifyResponse, error)

SendNotification sends a notification request to the daemon. focusFolder is the project folder name for window-specific focus (may be empty). focusWindowID and focusWindowTitle are optional exact window hints captured in the hook process. wezTermPaneID and wezTermSocket are WezTerm-specific hints for tab-level focus (may be empty). warpFocusURL is a Warp session deep link that focuses the originating pane (may be empty).

func (*Client) Stop

func (c *Client) Stop() error

Stop requests the daemon to shut down

type FocusMethod

type FocusMethod struct {
	Name string
	Fn   func(terminalName, folderName string) error
}

FocusMethod represents a method for focusing a window

func GetFocusMethods

func GetFocusMethods() []FocusMethod

GetFocusMethods returns the ordered list of focus methods to try

type MessageType

type MessageType string

MessageType identifies the type of IPC message

const (
	MessageTypeNotify MessageType = "notify"
	MessageTypePing   MessageType = "ping"
	MessageTypeStop   MessageType = "stop"
)

type NotifyRequest

type NotifyRequest struct {
	Title              string `json:"title"`
	Body               string `json:"body"`
	FocusTarget        string `json:"focus_target"`                    // Terminal identifier (empty = auto-detect)
	FocusFolder        string `json:"focus_folder,omitempty"`          // Project folder name for window-specific focus
	FocusWindowID      string `json:"focus_window_id,omitempty"`       // Exact X11 window ID captured in the hook process
	FocusWindowTitle   string `json:"focus_window_title,omitempty"`    // Exact window title captured in the hook process when available
	FocusWezTermPaneID string `json:"focus_wezterm_pane_id,omitempty"` // WezTerm pane ID ($WEZTERM_PANE)
	FocusWezTermSocket string `json:"focus_wezterm_socket,omitempty"`  // WezTerm unix socket ($WEZTERM_UNIX_SOCKET)
	FocusWarpURL       string `json:"focus_warp_url,omitempty"`        // Warp pane deep link ($WARP_FOCUS_URL)
	Timeout            int    `json:"timeout"`                         // Notification timeout in seconds
}

NotifyRequest contains notification details sent to the daemon

type NotifyResponse

type NotifyResponse struct {
	Success        bool   `json:"success"`
	NotificationID uint32 `json:"notification_id"`
	Error          string `json:"error,omitempty"`
}

NotifyResponse contains the result of a notification request

type PingResponse

type PingResponse struct {
	Version string `json:"version"`
	Uptime  int64  `json:"uptime"` // Seconds since daemon started
}

PingResponse contains daemon status information

type Request

type Request struct {
	Type    MessageType    `json:"type"`
	Notify  *NotifyRequest `json:"notify,omitempty"`
	Version string         `json:"version"`
}

Request is the wrapper for all IPC requests

type Response

type Response struct {
	Type   MessageType     `json:"type"`
	Notify *NotifyResponse `json:"notify,omitempty"`
	Ping   *PingResponse   `json:"ping,omitempty"`
	Error  string          `json:"error,omitempty"`
}

Response is the wrapper for all IPC responses

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is the notification daemon server

func NewServer

func NewServer(cfg ServerConfig) (*Server, error)

NewServer creates a new daemon server

func (*Server) Run

func (s *Server) Run() error

Run starts the daemon server

func (*Server) Shutdown

func (s *Server) Shutdown() error

Shutdown gracefully shuts down the server

type ServerConfig

type ServerConfig struct {
	IdleTimeout time.Duration // Auto-shutdown after this duration of inactivity (0 = disabled)
}

ServerConfig contains server configuration options

func DefaultServerConfig

func DefaultServerConfig() ServerConfig

DefaultServerConfig returns the default server configuration

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL