Events & Hooks

This is the API that scripts use to participate in WirePlumber's event-driven session management logic. Almost all of the policy that ships with WirePlumber is implemented as hooks registered through this API.

For a conceptual introduction to events and hooks, read Events and Hooks first. The underlying C API is documented in Events, Event Dispatcher and Event Hooks.

Overview

An event is something that happened, pushed onto the event stack. Events carry a type (for example node-added), a priority, a set of properties, a source and a subject.

A hook is a piece of code that runs when a matching event is dispatched. A hook declares:

  • a unique name, which other hooks use to order themselves against it

  • one or more interests, which decide whether the hook runs for a given event

  • before / after constraints, which order it relative to other hooks running on the same event

  • the code to run, either as a single execute function (SimpleEventHook()) or as a state machine (AsyncEventHook())

Hooks do not run until they are registered with EventHook.register(). A minimal hook therefore looks like this:

SimpleEventHook {
  name = "example/log-new-nodes",
  interests = {
    EventInterest {
      Constraint { "event.type", "=", "node-added" },
    },
  },
  execute = function (event)
    local node = event:get_subject ()
    log:info (node, "node added: " .. tostring (node.properties ["node.name"]))
  end
}:register ()

Event

The event object is passed to the hook's execute function.

Event.get_properties(self)

Binds wp_event_get_properties()

Returns the properties of the event. These always include event.type, and typically include properties copied from the subject, which is what makes it possible to match on them in an EventInterest().

Returns:

the properties of the event

Return type:

Properties, see Properties

Event.get_source(self)

Binds wp_event_get_source()

Returns the object that pushed this event. For events pushed by the standard event source, this is the standard-event-source plugin, which is also the object that provides the schedule-rescan and get-object-manager actions used by the shipped scripts.

Returns:

the source of the event

Return type:

GObject

Event.get_subject(self)

Binds wp_event_get_subject()

Returns the object that this event is about; for a node-added event this is the Node that was added.

Returns:

the subject of the event

Return type:

GObject

Event.stop_processing(self)

Binds wp_event_stop_processing()

Stops the event from being processed any further. No hooks that would run after this one will run for this event.

Event.set_data(self, key, value)

Binds wp_event_set_data()

Stores an arbitrary value on the event, so that later hooks running on the same event can read it with Event.get_data(). This is the standard way for a chain of hooks to communicate; the select-target chain, for instance, uses it to pass the selected target from the find-* hooks to linking/link-target.

Passing no value (or nil) removes the data.

Parameters:
  • key (string) -- the key to store the value under

  • value -- the value to store; booleans, numbers, strings, tables (stored as Properties) and GValue userdata are supported

Event.get_data(self, key)

Binds wp_event_get_data()

Returns the value previously stored with Event.set_data(), or nil.

Parameters:

key (string) -- the key to look up

Returns:

the stored value, or nil

EventInterest

EventInterest(decl)

Creates an ObjectInterest that matches Event objects. It is a shorthand for Interest { type = "event", ... }.

The constraints are matched against the event's properties, so event.type is almost always the first constraint. See Object Interest for the full constraint syntax.

EventInterest {
  Constraint { "event.type", "=", "node-added" },
  Constraint { "media.class", "#", "Stream/*" },
}

When an event is created, the properties of its subject - both the info properties and the global properties - are merged into the event's own properties, which is why the media.class constraint above works without any further qualification. Consequently, the constraint type is irrelevant for "pw" and "pw-global" constraints on an event interest; both look up the same merged set of properties. A "gobject" constraint, on the other hand, is still matched against the GObject properties of the event's subject.

A hook may declare several interests; the hook runs if any of them matches.

Returns:

the new event interest

Return type:

ObjectInterest

EventHook

Both SimpleEventHook() and AsyncEventHook() return an object with the following methods.

EventHook.register(self)

Binds wp_event_dispatcher_register_hook()

Registers the hook with the event dispatcher, so that it starts running for matching events. A hook has no effect until this is called.

EventHook.remove(self)

Binds wp_event_dispatcher_unregister_hook()

Unregisters the hook. This is how scripts enable and disable hooks at runtime in response to a setting change; see the example below.

SimpleEventHook

SimpleEventHook(decl)

Binds wp_simple_event_hook_new()

Constructs a hook whose action is a single synchronous function.

The constructor takes a single table with the following fields:

Field

Contains

name

(required) a string, the unique name of the hook

execute

(required) the function to run; it takes the Event as its only argument and returns nothing

interests

a list of EventInterest() objects; the hook runs if any of them matches

before

a hook name, or a list of hook names, that must run after this hook

after

a hook name, or a list of hook names, that must run before this hook

SimpleEventHook {
  name = "linking/find-default-target",
  after = { "linking/find-defined-target",
            "linking/find-filter-target" },
  before = "linking/prepare-link",
  interests = {
    EventInterest {
      Constraint { "event.type", "=", "select-target" },
    },
  },
  execute = function (event)
    -- ...
  end
}:register ()
Returns:

the new hook

Return type:

EventHook

AsyncEventHook

AsyncEventHook(decl)

Binds wp_async_event_hook_new()

Constructs a hook whose action is a state machine, for actions that cannot complete synchronously — typically because they need to wait for an object to activate or for a PipeWire round trip.

The constructor takes the same name, interests, before and after fields as SimpleEventHook(), but instead of execute it takes a steps table.

Each entry in steps is named by a string and contains:

Field

Contains

next

(required) the name of the step to run next, or the string "none" to finish

execute

(required) the function to run for this step; it takes the Event and a Transition

Execution always starts at the step named start and follows the next links until a step whose next is "none" has run.

Each step's execute function is responsible for advancing the transition, either immediately with transition:advance () or later from an asynchronous callback. Calling transition:return_error (message) aborts the hook.

A step named error is optional; if present, it runs when the transition fails.

AsyncEventHook {
  name = "node/create-item",
  interests = {
    EventInterest {
      Constraint { "event.type", "=", "node-added" },
    },
  },
  steps = {
    start = {
      next = "register",
      execute = function (event, transition)
        local node = event:get_subject ()
        -- ... create and configure the item ...
        transition:advance ()
      end
    },
    register = {
      next = "none",
      execute = function (event, transition)
        -- ... register the item ...
        transition:advance ()
      end
    },
  },
}:register ()
Returns:

the new hook

Return type:

EventHook

EventDispatcher

EventDispatcher.push_event(event)

Binds wp_event_dispatcher_push_event()

Pushes an event onto the event stack, where it will be picked up and dispatched to all the hooks that are interested in it.

The argument is either an existing Event, or a table describing a new event to construct and push:

Field

Contains

type

(required) a string, the event type

priority

(required) a number; higher priority events are dispatched before lower priority ones

properties

a table or Properties with additional properties for the event

source

the object pushing the event

subject

the object the event is about

Properties of the subject are automatically copied into the event's properties, so interests can match on them.

In practice, the shipped scripts rarely build the table themselves. They ask the standard-event-source plugin to create the event instead, which assigns the priority that is configured for that event type, then attach any extra data to it and push it:

source = source or Plugin.find ("standard-event-source")

local e = source:call ("create-event", "create-v4l2-device", parent, nil)
e:set_data ("device-properties", properties)
e:set_data ("factory", factory)

EventDispatcher.push_event (e)

The create-event action takes the event type, the subject and an optional properties table.

Returns:

the event that was pushed

Return type:

Event

Enabling and disabling hooks at runtime

Because EventHook.register() and EventHook.remove() can be called at any time, a script can react to a setting changing by adding or removing a hook, rather than by checking the setting on every event. This is the pattern used by, for example, linking/rescan-trigger-on-target-metadata-changed:

local hook = nil

local function updateEnabled (enable)
  if enable and not hook then
    hook = SimpleEventHook {
      name = "example/my-hook",
      interests = { EventInterest { Constraint { "event.type", "=", "node-added" } } },
      execute = function (event) --[[ ... ]] end
    }
    hook:register ()
  elseif hook and not enable then
    hook:remove ()
    hook = nil
  end
end

Settings.subscribe ("example.my-setting", function ()
  updateEnabled (Settings.get_boolean ("example.my-setting"))
end)
updateEnabled (Settings.get_boolean ("example.my-setting"))

See also Settings and Custom Scripts.