Skip to content

Cursor

Breaking change: set_position now holds the cursor

set_position no longer performs a one-shot warp. It holds the in-engine cursor at the requested position until release() is called (CursorState.held reports the hold). The hold never grabs the OS mouse pointer, but while held, human mouse movement does not reach the in-engine cursor and clicks fire at the held position. Switching world focus deactivates the hold (held becomes False).

Runnable example

python/examples/cursor_move.pyget_position → center → move → restore the desktop cursor.

resoio.cursor.CursorClient

CursorClient(socket_path: str | None = None)

Bases: _BaseClient[CursorStub]

Async client for the Resonite IO Cursor service over a UDS.

Use as an async context manager so the gRPC channel is closed deterministically. Socket resolution mirrors :class:resoio.ConnectionClient.

Source code in src/resoio/_client.py
def __init__(self, socket_path: str | None = None) -> None:
    # Defer resolution to __aenter__ so env vars patched between
    # construction and connection are honoured, and so resolution
    # errors surface at the connect site.
    self._explicit_path: str | None = socket_path
    self._channel: Channel | None = None
    self._stub: TStub | None = None
    self._resolved_path: str | None = None

set_position async

set_position(x: float, y: float) -> CursorState

Move the cursor to normalized (x, y) and hold it there.

The cursor is held at the requested position until :meth:release is called; the returned state has held=True. Calling again while held updates the held position. The hold acts on the in-engine cursor only and never grabs the OS mouse pointer. Side effect to be aware of: while held, human mouse movement does not reach the in-engine cursor, but clicks fire at the held position. Switching world focus deactivates the hold on the engine side and held becomes False (it is not re-applied automatically).

x and y must be in [0, 1] (center is (0.5, 0.5)); out-of-range values surface as a :class:grpclib.exceptions.GRPCError (INVALID_ARGUMENT).

Source code in src/resoio/cursor.py
async def set_position(self, x: float, y: float) -> CursorState:
    """Move the cursor to normalized ``(x, y)`` and hold it there.

    The cursor is held at the requested position until
    :meth:`release` is called; the returned state has
    ``held=True``. Calling again while held updates the held
    position. The hold acts on the in-engine cursor only and never
    grabs the OS mouse pointer. Side effect to be aware of: while
    held, human mouse movement does not reach the in-engine cursor,
    but clicks fire at the held position. Switching world focus
    deactivates the hold on the engine side and ``held`` becomes
    ``False`` (it is not re-applied automatically).

    ``x`` and ``y`` must be in ``[0, 1]`` (center is ``(0.5, 0.5)``);
    out-of-range values surface as a
    :class:`grpclib.exceptions.GRPCError` (``INVALID_ARGUMENT``).
    """
    stub = self._require_stub()
    request = CursorSetPositionRequest(x=x, y=y)
    return _state_from_proto(await stub.set_position(request))

get_position async

get_position() -> CursorState

Return the current cursor position and hold state (no side effects).

Source code in src/resoio/cursor.py
async def get_position(self) -> CursorState:
    """Return the current cursor position and hold state (no side
    effects)."""
    stub = self._require_stub()
    return _state_from_proto(await stub.get_position(CursorGetPositionRequest()))

release async

release() -> CursorState

Release a :meth:set_position hold and return the new state.

Idempotent: succeeds even when nothing is held. The returned state has held=False. gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/cursor.py
async def release(self) -> CursorState:
    """Release a :meth:`set_position` hold and return the new state.

    Idempotent: succeeds even when nothing is held. The returned
    state has ``held=False``. gRPC failures surface as
    :class:`grpclib.exceptions.GRPCError`.
    """
    stub = self._require_stub()
    return _state_from_proto(await stub.release(CursorReleaseRequest()))

resoio.cursor.CursorState dataclass

CursorState(x: float, y: float, window_width: int, window_height: int, held: bool)

Snapshot of the desktop cursor position.

x / y are normalized window coordinates in [0, 1] (center is (0.5, 0.5)). window_width / window_height are the window resolution in pixels, the basis for normalized <-> pixel. held reports whether a set_position hold is in effect at the time of this snapshot.