Skip to main content
Version: 0.9.0

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​

MemberDescription
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

NameTypeDescription
optionsAppVolumeOptions

Returns: Result<AppVolumeId>

Exceptions

ExceptionCondition
ArgumentNullExceptionoptions is null.
ArgumentExceptionAppVolumeOptions.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

NameTypeDescription
idAppVolumeId
displayNamestring

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
sizeAppVolumeSize

Returns: Result<Unit>

Close​

Closes the volume for every peer: its AppVolume.LifetimeStatus becomes AppVolumeLifetimeStatus.Closed.

public Result<Unit> Close(AppVolumeId id)

Parameters

NameTypeDescription
idAppVolumeId

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
additionsPeerId[]
removalsPeerId[]

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
appPosePose

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
appScalefloat

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
anchorGeoAnchorFrame

Returns: Result<Unit>

ClearAppWorldAnchor​

public Result<Unit> ClearAppWorldAnchor(AppVolumeId id)

Parameters

NameTypeDescription
idAppVolumeId

Returns: Result<Unit>

ChangePose​

Moves the volume. pose is the cuboid centre in room space.

public Result<Unit> ChangePose(AppVolumeId id, Pose pose)

Parameters

NameTypeDescription
idAppVolumeId
posePose

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId

Returns: Result<Pose>

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

NameTypeDescription
idAppVolumeId

Returns: Result<float>

GetLatestPose​

Latest locally known volume pose (the cuboid centre), in room space; see AppVolumeControl.GetLatestAppPose.

public Result<Pose> GetLatestPose(AppVolumeId id)

Parameters

NameTypeDescription
idAppVolumeId

Returns: Result<Pose>

GetLatestSize​

Latest locally known size; see AppVolumeControl.GetLatestAppPose.

public Result<AppVolumeSize> GetLatestSize(AppVolumeId id)

Parameters

NameTypeDescription
idAppVolumeId

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

NameTypeDescription
idAppVolumeId
configurationAppVolumeGestureConfiguration

Returns: Result<Unit>

Exceptions

ExceptionCondition
ArgumentNullExceptionconfiguration 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

NameTypeDescription
idAppVolumeId
modeIduint
configurationAppVolumeGestureConfiguration

Returns: Result<Unit>

Exceptions

ExceptionCondition
ArgumentNullExceptionconfiguration 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

NameTypeDescription
idAppVolumeId
modeIduint

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
peerPeerId?
modeIduint

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
peerPeerId

Returns: Result<Unit>

ClearAllPeerGestureModes​

Drops every peer's own gesture mode, so all follow the default.

public Result<Unit> ClearAllPeerGestureModes(AppVolumeId id)

Parameters

NameTypeDescription
idAppVolumeId

Returns: Result<Unit>

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

NameTypeDescription
idAppVolumeId
handlerEventHandler<AppVolumeGestureEventArgs>
cancellationTokenCancellationToken

Returns: Result<IDisposable>

Exceptions

ExceptionCondition
ArgumentNullExceptionhandler 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()

Returns: Result<AppVolume[]>

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

NameTypeDescription
cancellationTokenCancellationToken

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

NameTypeDescription
handlerEventHandler<AppVolumeUpdatedEventArgs>

Returns: Result<IDisposable>

Exceptions

ExceptionCondition
ArgumentNullExceptionhandler 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

NameTypeDescription
handlerEventHandler<AppVolumeUpdatedEventArgs>
cancellationTokenCancellationToken

Returns: Result<IDisposable>

Exceptions

ExceptionCondition
ArgumentNullExceptionhandler 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

NameTypeDescription
cancellationTokenCancellationToken

Returns: IAsyncEnumerable<AppVolume>

Exceptions

ExceptionCondition
NativeCallExceptionThrown 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

NameTypeDescription
idAppVolumeId
gestureKindGestureKind
handlerAction<AppVolumeGestureEvent>

Returns: Result<IDisposable>

Exceptions

ExceptionCondition
ArgumentNullExceptionhandler 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

NameTypeDescription
idAppVolumeId
gestureKindGestureKind
handlerAction<AppVolumeGestureEvent>
cancellationTokenCancellationToken

Returns: Result<IDisposable>

Exceptions

ExceptionCondition
ArgumentNullExceptionhandler 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

NameTypeDescription
idAppVolumeId
gestureKindGestureKind
handlerEventHandler<AppVolumeGestureEventArgs>

Returns: Result<IDisposable>

Exceptions

ExceptionCondition
ArgumentNullExceptionhandler 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

NameTypeDescription
idAppVolumeId
gestureKindGestureKind
handlerEventHandler<AppVolumeGestureEventArgs>
cancellationTokenCancellationToken

Returns: Result<IDisposable>

Exceptions

ExceptionCondition
ArgumentNullExceptionhandler 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

NameTypeDescription
idAppVolumeId
gestureKindGestureKind
cancellationTokenCancellationToken

Returns: IAsyncEnumerable<AppVolumeGestureEvent>

Exceptions

ExceptionCondition
NativeCallExceptionThrown 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.