AppVolumeControl class
The session's app volumes: the boxes in the room that remote 3D applications render into.
Reached through Session.AppVolumes.
Namespace: Quaternar.Qaros · Assembly: Quaternar.Qaros.dll · Source: AppVolumeControl.cs
public sealed class AppVolumeControl
Remarks
App volume state is shared session state and eventually consistent. A change method
returns success once it has published the change; the change reaches the shared state,
and every peer's snapshots, later, and may still be refused there. A read right after a
write can return the old value: observe the result through
AppVolumeControl.SubscribeUpdates
instead of reading back once.
A change to a volume this session does not know yet (its id was never seen, or its creation has not arrived) fails rather than waiting for it.
Spaces: AppVolumeControl.ChangePose places the cuboid centre in room space, in metres.
AppVolumeControl.ChangeAppPose and AppVolumeControl.ChangeAppScale place the application's own
world (app content space) inside the box. See AppVolume.
Every method throws ObjectDisposedException once the session is disposed.
Methods
| Member | Description |
|---|---|
| GetOrCreate(AppVolumeOptions) | Returns the volume identified by AppVolumeOptions.CommonName, creating it from options if it does not exist yet. |
| ChangeDisplayName(AppVolumeId, string) | Changes the label users see. Any UTF-8 text, emoji included; the common name, and with it the id, never changes. |
| ChangeSize(AppVolumeId, AppVolumeSize) | Resizes the box around its centre, in metres. A fast-path change: AppVolumeControl.GetLatestSize returns it at once. |
| Close(AppVolumeId) | Closes the volume for every peer: its AppVolume.LifetimeStatus becomes AppVolumeLifetimeStatus.Closed. |
| UpdateUsedByPeers(AppVolumeId, PeerId[], PeerId[]) | Adds and removes peers in AppVolume.UsedByPeers. Null arrays mean none. |
| StartEditing(AppVolumeId) | Marks the volume's box as being moved or resized by this session's own peer, which every peer reads in AppVolume.EditingStatus, for example to show who is moving it. |
| StopEditing(AppVolumeId) | Clears the editing status, whichever peer set it. |
| ChangeAppPose(AppVolumeId, Pose) | Sets the app pose: the application's origin in app volume space, in volume metres. |
| ChangeAppScale(AppVolumeId, float) | Sets the app scale: room metres per app metre, so 0.01 shows a 100 m model as 1 m. Must be greater than 0, otherwise the call fails with StatusCode.ArgumentNotSupported. |
| ChangeAppWorldAnchor(AppVolumeId, GeoAnchorFrame) | Pins the volume to an Earth-fixed frame (ECEF WGS84, metres). Cleared with AppVolumeControl.ClearAppWorldAnchor. |
| ClearAppWorldAnchor(AppVolumeId) | |
| ChangePose(AppVolumeId, Pose) | Moves the volume. pose is the cuboid centre in room space. |
| GetLatestAppPose(AppVolumeId) | Latest locally known app pose, in app volume space. |
| GetLatestAppScale(AppVolumeId) | Latest locally known app scale; see AppVolumeControl.GetLatestAppPose. |
| GetLatestPose(AppVolumeId) | Latest locally known volume pose (the cuboid centre), in room space; see AppVolumeControl.GetLatestAppPose. |
| GetLatestSize(AppVolumeId) | Latest locally known size; see AppVolumeControl.GetLatestAppPose. |
| ChangeGestureConfiguration(AppVolumeId, AppVolumeGestureConfiguration) | Replaces the rules of gesture mode AppVolumeGestureState.DefaultMode and the allowed peers (AppVolumeGestureConfiguration.AllowedPeers). Other modes are kept. Fails with StatusCode.AppVolumeGestureConfigurationInvalid for a rule that could never work; the volume then keeps its previous configuration. |
| ChangeGestureMode(AppVolumeId, uint, AppVolumeGestureConfiguration) | Defines gesture mode modeId, or replaces the mode with that id. Other modes and the allowed peers are kept; AppVolumeGestureConfiguration.AllowedPeers must be empty. |
| RemoveGestureMode(AppVolumeId, uint) | Removes a gesture mode; peers in it fall back to AppVolumeGestureState.DefaultMode. The default mode cannot be removed (StatusCode.AppVolumeGestureConfigurationInvalid); an unknown one fails with StatusCode.AppVolumeGestureModeUnknown. |
| SelectGestureMode(AppVolumeId, PeerId?, uint) | Switches peer to a gesture mode, or, with a null peer, makes it the default every peer without its own mode follows. A running gesture keeps the mode it began in. |
| ClearPeerGestureMode(AppVolumeId, PeerId) | Drops one peer's own gesture mode, so it follows the default again. |
| ClearAllPeerGestureModes(AppVolumeId) | Drops every peer's own gesture mode, so all follow the default. |
| SubscribeAllGestureUpdates(AppVolumeId, EventHandler<AppVolumeGestureEventArgs>, CancellationToken) | Subscribe to every gesture kind on one app volume through one handler. Prefer this when taking more than one kind: everything arrives in the order the producer published it. Disposing the result stops delivery. |
| GetAll() | Snapshot of every app volume the session's shared storage holds. Blocks for a network round trip of about 2 s on every call. |
| SubscribeUpdates() | Starts delivering app volume updates through the AppVolumeControl.Updated event until the result is disposed. |
| SubscribeUpdates(CancellationToken) | Starts delivering app volume updates through the AppVolumeControl.Updated event until the result is disposed. |
| SubscribeUpdates(EventHandler<AppVolumeUpdatedEventArgs>) | Calls handler once for every app volume already stored, then again for every change to any volume, until the result is disposed. |
| SubscribeUpdates(EventHandler<AppVolumeUpdatedEventArgs>, CancellationToken) | Calls handler once for every app volume already stored, then again for every change to any volume, until the result is disposed. |
| WatchUpdatesAsync(CancellationToken) | The snapshots of AppVolumeControl.SubscribeUpdates as an async stream: every stored volume first, then every change. |
| SubscribeGestureUpdates(AppVolumeId, GestureKind, Action<AppVolumeGestureEvent>) | Calls handler for every gesture of one kind that a peer performs against one app volume, until the result is disposed. |
| SubscribeGestureUpdates(AppVolumeId, GestureKind, Action<AppVolumeGestureEvent>, CancellationToken) | Calls handler for every gesture of one kind that a peer performs against one app volume, until the result is disposed. |
| SubscribeGestureUpdates(AppVolumeId, GestureKind, EventHandler<AppVolumeGestureEventArgs>) | Calls handler for every gesture of one kind that a peer performs against one app volume, until the result is disposed. |
| SubscribeGestureUpdates(AppVolumeId, GestureKind, EventHandler<AppVolumeGestureEventArgs>, CancellationToken) | Calls handler for every gesture of one kind that a peer performs against one app volume, until the result is disposed. |
| WatchGestureUpdatesAsync(AppVolumeId, GestureKind, CancellationToken) | The gestures of AppVolumeControl.SubscribeGestureUpdates as an async stream. |
GetOrCreate
Returns the volume identified by AppVolumeOptions.CommonName,
creating it from options if it does not exist yet.
public Result<AppVolumeId> GetOrCreate(AppVolumeOptions options)
Parameters
| Name | Type | Description |
|---|---|---|
options | AppVolumeOptions |
Returns: Result<AppVolumeId>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | options is null. |
| ArgumentException | AppVolumeOptions.CommonName is null. |
Remarks
The id is derived from the common name alone, so the same common name gives the
same volume in every session, from every peer and after a restart: call this on every
start instead of persisting the id. The common name is the identity key and must be
ASCII; put user-facing text, which may be any UTF-8, in
AppVolumeOptions.DisplayName.
When an open volume of that name exists, the rest of options is
ignored and the existing volume keeps its pose, size, app transform and gesture
configuration; change them with the Change* methods. A closed volume
(AppVolumeControl.Close) is reopened with options.
A gesture configuration whose rule could never work fails with
StatusCode.AppVolumeGestureConfigurationInvalid.
ChangeDisplayName
Changes the label users see. Any UTF-8 text, emoji included; the common name, and with it the id, never changes.
public Result<Unit> ChangeDisplayName(AppVolumeId id, string displayName)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
displayName | string |
ChangeSize
Resizes the box around its centre, in metres. A fast-path change:
AppVolumeControl.GetLatestSize returns it at once.
public Result<Unit> ChangeSize(AppVolumeId id, AppVolumeSize size)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
size | AppVolumeSize |
Close
Closes the volume for every peer: its AppVolume.LifetimeStatus becomes
AppVolumeLifetimeStatus.Closed.
public Result<Unit> Close(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
Remarks
The volume is not forgotten: AppVolumeControl.GetOrCreate with the same common name
reopens it with new options. A render sender bound to a closed volume fails to begin
frames with StatusCode.RenderingProducerAppVolumeUnresolved.
UpdateUsedByPeers
Adds and removes peers in AppVolume.UsedByPeers. Null arrays mean none.
public Result<Unit> UpdateUsedByPeers(AppVolumeId id, PeerId[] additions, PeerId[] removals)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
additions | PeerId[] | |
removals | PeerId[] |
Remarks
Each peer is a separate change, additions first. The first failure stops the call and the changes before it stay published.
StartEditing
Marks the volume's box as being moved or resized by this session's own peer, which
every peer reads in AppVolume.EditingStatus, for example to show who is
moving it.
public Result<Unit> StartEditing(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
Remarks
It replaces whatever editor was set before. A peer manipulating the app with a gesture
is not an editor; see AppVolumeGestureState.Holder.
StopEditing
Clears the editing status, whichever peer set it.
public Result<Unit> StopEditing(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
ChangeAppPose
Sets the app pose: the application's origin in app volume space, in volume metres.
public Result<Unit> ChangeAppPose(AppVolumeId id, Pose appPose)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
appPose | Pose |
Remarks
Pans the content inside the box without the renderer changing anything: the render
sender's view poses follow. Not scaled by the app scale. A fast-path change:
AppVolumeControl.GetLatestAppPose returns it at once. Gestures mapped to the app
transform change it too.
ChangeAppScale
Sets the app scale: room metres per app metre, so 0.01 shows a 100 m model as 1 m.
Must be greater than 0, otherwise the call fails with
StatusCode.ArgumentNotSupported.
public Result<Unit> ChangeAppScale(AppVolumeId id, float appScale)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
appScale | float |
Remarks
Zooms the content inside the box without the renderer changing anything. A fast-path
change: AppVolumeControl.GetLatestAppScale returns it at once. Gestures mapped to the app
transform change it too.
ChangeAppWorldAnchor
Pins the volume to an Earth-fixed frame (ECEF WGS84, metres). Cleared with
AppVolumeControl.ClearAppWorldAnchor.
public Result<Unit> ChangeAppWorldAnchor(AppVolumeId id, GeoAnchorFrame anchor)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
anchor | GeoAnchorFrame |
ClearAppWorldAnchor
public Result<Unit> ClearAppWorldAnchor(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
ChangePose
Moves the volume. pose is the cuboid centre in room space.
public Result<Unit> ChangePose(AppVolumeId id, Pose pose)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
pose | Pose |
Remarks
The centre, not a corner or a point on the floor. A fast-path change:
AppVolumeControl.GetLatestPose returns it at once.
GetLatestAppPose
Latest locally known app pose, in app volume space.
public Result<Pose> GetLatestAppPose(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
Remarks
The GetLatest* reads answer from this session's local view without a network
round trip: the latest fast-path value from this or any other peer, else the stored
state. They can be newer than the last AppVolume snapshot delivered, and
fail while this session does not know the volume yet.
GetLatestAppScale
Latest locally known app scale; see AppVolumeControl.GetLatestAppPose.
public Result<float> GetLatestAppScale(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
GetLatestPose
Latest locally known volume pose (the cuboid centre), in room space; see
AppVolumeControl.GetLatestAppPose.
public Result<Pose> GetLatestPose(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
GetLatestSize
Latest locally known size; see AppVolumeControl.GetLatestAppPose.
public Result<AppVolumeSize> GetLatestSize(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
Returns: Result<AppVolumeSize>
ChangeGestureConfiguration
Replaces the rules of gesture mode AppVolumeGestureState.DefaultMode
and the allowed peers (AppVolumeGestureConfiguration.AllowedPeers).
Other modes are kept. Fails with
StatusCode.AppVolumeGestureConfigurationInvalid for a rule
that could never work; the volume then keeps its previous configuration.
public Result<Unit> ChangeGestureConfiguration(AppVolumeId id, AppVolumeGestureConfiguration configuration)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
configuration | AppVolumeGestureConfiguration |
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | configuration is null. |
Remarks
Rule order is priority: an earlier rule can suppress a later one when both are active
for the same volume and source peer. The call reads the volume back from the shared
storage before writing, so it takes about as long as AppVolumeControl.GetAll.
ChangeGestureMode
Defines gesture mode modeId, or replaces the mode with
that id. Other modes and the allowed peers are kept;
AppVolumeGestureConfiguration.AllowedPeers must be empty.
public Result<Unit> ChangeGestureMode(AppVolumeId id, uint modeId, AppVolumeGestureConfiguration configuration)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
modeId | uint | |
configuration | AppVolumeGestureConfiguration |
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | configuration is null. |
Remarks
A mode is a set of mapping rules peers are switched to with
AppVolumeControl.SelectGestureMode, so a user's gestures and rays change with one small
call. Any id other than AppVolumeGestureState.DefaultMode is the
application's to choose. Non-empty allowed peers fail with
StatusCode.ArgumentNotSupported, a rule that could never work with
StatusCode.AppVolumeGestureConfigurationInvalid.
RemoveGestureMode
Removes a gesture mode; peers in it fall back to
AppVolumeGestureState.DefaultMode. The default mode cannot be
removed (StatusCode.AppVolumeGestureConfigurationInvalid); an
unknown one fails with StatusCode.AppVolumeGestureModeUnknown.
public Result<Unit> RemoveGestureMode(AppVolumeId id, uint modeId)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
modeId | uint |
Remarks
A gesture already running in the removed mode finishes in it.
SelectGestureMode
Switches peer to a gesture mode, or, with a null peer,
makes it the default every peer without its own mode follows. A running
gesture keeps the mode it began in.
public Result<Unit> SelectGestureMode(AppVolumeId id, PeerId? peer, uint modeId)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
peer | PeerId? | |
modeId | uint |
Remarks
Changing the default leaves peers switched individually in their own mode; clear them
with AppVolumeControl.ClearPeerGestureMode or AppVolumeControl.ClearAllPeerGestureModes. A
mode the volume does not define fails with
StatusCode.AppVolumeGestureModeUnknown.
ClearPeerGestureMode
Drops one peer's own gesture mode, so it follows the default again.
public Result<Unit> ClearPeerGestureMode(AppVolumeId id, PeerId peer)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
peer | PeerId |
ClearAllPeerGestureModes
Drops every peer's own gesture mode, so all follow the default.
public Result<Unit> ClearAllPeerGestureModes(AppVolumeId id)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId |
SubscribeAllGestureUpdates
Subscribe to every gesture kind on one app volume through one handler. Prefer this when taking more than one kind: everything arrives in the order the producer published it. Disposing the result stops delivery.
public Result<IDisposable> SubscribeAllGestureUpdates(AppVolumeId id, EventHandler<AppVolumeGestureEventArgs> handler, CancellationToken cancellationToken = default)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
handler | EventHandler<AppVolumeGestureEventArgs> | |
cancellationToken | CancellationToken |
Returns: Result<IDisposable>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | handler is null. |
Remarks
Dispatch on AppVolumeGestureEvent.GestureKind. The volume's mapping
rules still decide what is published at all; only the kind filter is gone. Everything
else - mapping, several producers, threading and disposal - is as for
AppVolumeControl.SubscribeGestureUpdates.
GetAll
Snapshot of every app volume the session's shared storage holds. Blocks for a network round trip of about 2 s on every call.
public Result<AppVolume[]> GetAll()
Remarks
Use it for a one-off listing. To track volumes, subscribe with
AppVolumeControl.SubscribeUpdates,
which delivers the same snapshot first; for one volume's live transform, use the
GetLatest* reads.
SubscribeUpdates()
Starts delivering app volume updates through the AppVolumeControl.Updated event until the
result is disposed.
public Result<IDisposable> SubscribeUpdates()
Returns: Result<IDisposable>
Remarks
Same delivery and disposal rules as
AppVolumeControl.SubscribeUpdates.
SubscribeUpdates(CancellationToken)
Starts delivering app volume updates through the AppVolumeControl.Updated event until the
result is disposed.
public Result<IDisposable> SubscribeUpdates(CancellationToken cancellationToken)
Parameters
| Name | Type | Description |
|---|---|---|
cancellationToken | CancellationToken |
Returns: Result<IDisposable>
Remarks
Same delivery and disposal rules as
AppVolumeControl.SubscribeUpdates.
SubscribeUpdates(EventHandler<AppVolumeUpdatedEventArgs>)
Calls handler once for every app volume already stored, then again
for every change to any volume, until the result is disposed.
public Result<IDisposable> SubscribeUpdates(EventHandler<AppVolumeUpdatedEventArgs> handler)
Parameters
| Name | Type | Description |
|---|---|---|
handler | EventHandler<AppVolumeUpdatedEventArgs> |
Returns: Result<IDisposable>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | handler is null. |
Remarks
Each call carries an immutable AppVolume snapshot that is safe to keep.
Its poses are in the publishing peer's room space.
The handler runs on a library thread: do not block in it, do not call blocking QAROS methods from it, and marshal to your own thread before touching single-threaded state. An exception it throws is caught and dropped.
Disposing the result ends delivery: when Dispose returns, a handler call
running on another thread has finished and no further one starts. Called from inside
the handler, Dispose returns at once and no call starts after the current one.
Never dispose while holding a lock the handler takes: it deadlocks. Cancelling
cancellationToken ends delivery the same way, and
CancellationTokenSource.Cancel waits the same way.
SubscribeUpdates(EventHandler<AppVolumeUpdatedEventArgs>, CancellationToken)
Calls handler once for every app volume already stored, then again
for every change to any volume, until the result is disposed.
public Result<IDisposable> SubscribeUpdates(EventHandler<AppVolumeUpdatedEventArgs> handler, CancellationToken cancellationToken)
Parameters
| Name | Type | Description |
|---|---|---|
handler | EventHandler<AppVolumeUpdatedEventArgs> | |
cancellationToken | CancellationToken |
Returns: Result<IDisposable>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | handler is null. |
Remarks
Each call carries an immutable AppVolume snapshot that is safe to keep.
Its poses are in the publishing peer's room space.
The handler runs on a library thread: do not block in it, do not call blocking QAROS methods from it, and marshal to your own thread before touching single-threaded state. An exception it throws is caught and dropped.
Disposing the result ends delivery: when Dispose returns, a handler call
running on another thread has finished and no further one starts. Called from inside
the handler, Dispose returns at once and no call starts after the current one.
Never dispose while holding a lock the handler takes: it deadlocks. Cancelling
cancellationToken ends delivery the same way, and
CancellationTokenSource.Cancel waits the same way.
WatchUpdatesAsync
The snapshots of
AppVolumeControl.SubscribeUpdates
as an async stream: every stored volume first, then every change.
public IAsyncEnumerable<AppVolume> WatchUpdatesAsync(CancellationToken cancellationToken = default)
Parameters
| Name | Type | Description |
|---|---|---|
cancellationToken | CancellationToken |
Returns: IAsyncEnumerable<AppVolume>
Exceptions
| Exception | Condition |
|---|---|
| NativeCallException | Thrown on the first MoveNextAsync when the subscription cannot be started. |
Remarks
The subscription starts with the enumeration and ends when the enumerator is disposed. Updates are queued without bound until read, so a consumer that stops reading without disposing keeps accumulating them.
SubscribeGestureUpdates(AppVolumeId, GestureKind, Action<AppVolumeGestureEvent>)
Calls handler for every gesture of one kind that a peer performs
against one app volume, until the result is disposed.
public Result<IDisposable> SubscribeGestureUpdates(AppVolumeId id, GestureKind gestureKind, Action<AppVolumeGestureEvent> handler)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
gestureKind | GestureKind | |
handler | Action<AppVolumeGestureEvent> |
Returns: Result<IDisposable>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | handler is null. |
Remarks
Each gesture is already mapped through the volume's gesture configuration: filtered
by its rules, its
points in app content space, its deltas in app volume space. When
AppVolumeGestureEvent.WasMappedToAppTransform is true the library
already applied the delta to the app pose or scale; applying it again moves the
content twice.
Several producers publish, one per user who can reach the volume. Order holds within
one producer, not across producers, and a source peer never seen before is a new user.
Keep per-user state per AppVolumeGestureEvent.SourcePeerId. Separate
per-kind subscriptions have no order relative to each other; to take more than one
kind, prefer AppVolumeControl.SubscribeAllGestureUpdates.
Threading, exceptions and disposal follow
AppVolumeControl.SubscribeUpdates:
the handler runs on a library thread, and Dispose waits for a running call.
SubscribeGestureUpdates(AppVolumeId, GestureKind, Action<AppVolumeGestureEvent>, CancellationToken)
Calls handler for every gesture of one kind that a peer performs
against one app volume, until the result is disposed.
public Result<IDisposable> SubscribeGestureUpdates(AppVolumeId id, GestureKind gestureKind, Action<AppVolumeGestureEvent> handler, CancellationToken cancellationToken)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
gestureKind | GestureKind | |
handler | Action<AppVolumeGestureEvent> | |
cancellationToken | CancellationToken |
Returns: Result<IDisposable>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | handler is null. |
Remarks
Each gesture is already mapped through the volume's gesture configuration: filtered
by its rules, its
points in app content space, its deltas in app volume space. When
AppVolumeGestureEvent.WasMappedToAppTransform is true the library
already applied the delta to the app pose or scale; applying it again moves the
content twice.
Several producers publish, one per user who can reach the volume. Order holds within
one producer, not across producers, and a source peer never seen before is a new user.
Keep per-user state per AppVolumeGestureEvent.SourcePeerId. Separate
per-kind subscriptions have no order relative to each other; to take more than one
kind, prefer AppVolumeControl.SubscribeAllGestureUpdates.
Threading, exceptions and disposal follow
AppVolumeControl.SubscribeUpdates:
the handler runs on a library thread, and Dispose waits for a running call.
SubscribeGestureUpdates(AppVolumeId, GestureKind, EventHandler<AppVolumeGestureEventArgs>)
Calls handler for every gesture of one kind that a peer performs
against one app volume, until the result is disposed.
public Result<IDisposable> SubscribeGestureUpdates(AppVolumeId id, GestureKind gestureKind, EventHandler<AppVolumeGestureEventArgs> handler)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
gestureKind | GestureKind | |
handler | EventHandler<AppVolumeGestureEventArgs> |
Returns: Result<IDisposable>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | handler is null. |
Remarks
Each gesture is already mapped through the volume's gesture configuration: filtered
by its rules, its
points in app content space, its deltas in app volume space. When
AppVolumeGestureEvent.WasMappedToAppTransform is true the library
already applied the delta to the app pose or scale; applying it again moves the
content twice.
Several producers publish, one per user who can reach the volume. Order holds within
one producer, not across producers, and a source peer never seen before is a new user.
Keep per-user state per AppVolumeGestureEvent.SourcePeerId. Separate
per-kind subscriptions have no order relative to each other; to take more than one
kind, prefer AppVolumeControl.SubscribeAllGestureUpdates.
Threading, exceptions and disposal follow
AppVolumeControl.SubscribeUpdates:
the handler runs on a library thread, and Dispose waits for a running call.
SubscribeGestureUpdates(AppVolumeId, GestureKind, EventHandler<AppVolumeGestureEventArgs>, CancellationToken)
Calls handler for every gesture of one kind that a peer performs
against one app volume, until the result is disposed.
public Result<IDisposable> SubscribeGestureUpdates(AppVolumeId id, GestureKind gestureKind, EventHandler<AppVolumeGestureEventArgs> handler, CancellationToken cancellationToken)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
gestureKind | GestureKind | |
handler | EventHandler<AppVolumeGestureEventArgs> | |
cancellationToken | CancellationToken |
Returns: Result<IDisposable>
Exceptions
| Exception | Condition |
|---|---|
| ArgumentNullException | handler is null. |
Remarks
Each gesture is already mapped through the volume's gesture configuration: filtered
by its rules, its
points in app content space, its deltas in app volume space. When
AppVolumeGestureEvent.WasMappedToAppTransform is true the library
already applied the delta to the app pose or scale; applying it again moves the
content twice.
Several producers publish, one per user who can reach the volume. Order holds within
one producer, not across producers, and a source peer never seen before is a new user.
Keep per-user state per AppVolumeGestureEvent.SourcePeerId. Separate
per-kind subscriptions have no order relative to each other; to take more than one
kind, prefer AppVolumeControl.SubscribeAllGestureUpdates.
Threading, exceptions and disposal follow
AppVolumeControl.SubscribeUpdates:
the handler runs on a library thread, and Dispose waits for a running call.
WatchGestureUpdatesAsync
The gestures of
AppVolumeControl.SubscribeGestureUpdates
as an async stream.
public IAsyncEnumerable<AppVolumeGestureEvent> WatchGestureUpdatesAsync(AppVolumeId id, GestureKind gestureKind, CancellationToken cancellationToken = default)
Parameters
| Name | Type | Description |
|---|---|---|
id | AppVolumeId | |
gestureKind | GestureKind | |
cancellationToken | CancellationToken |
Returns: IAsyncEnumerable<AppVolumeGestureEvent>
Exceptions
| Exception | Condition |
|---|---|
| NativeCallException | Thrown on the first MoveNextAsync when the subscription cannot be started. |
Remarks
The subscription starts with the enumeration and ends when the enumerator is disposed. Gestures are queued without bound until read.
Events
Updated
Raised for every app volume update, but only while a subscription started by
AppVolumeControl.SubscribeUpdates or
AppVolumeControl.SubscribeUpdates is alive.
public event EventHandler<AppVolumeUpdatedEventArgs>? Updated
Event type: EventHandler<AppVolumeUpdatedEventArgs>
Remarks
Raised on a library thread with this control as the sender; see
AppVolumeControl.SubscribeUpdates
for what is delivered and the threading rules. Each such subscription raises the event
once per update, so two live subscriptions raise it twice.