Skip to content

Conversation

@jhenstridge
Copy link
Contributor

What's new?

This implements cursor the cursor session parts of the ext_image_copy_capture_v1 protocol. At present, it sends a dummy cursor image, which will be replaced with the real cursor in a follow-up PR.

Cursor position tracking is done via the CursorObserverMultiplexer, with a source-dependent backend class responsible for transforming the output space cursor coordinates to those of the corresponding image capture, and indicating whether the cursor is within bounds.

This can be tested like so:

  1. Run miral with the following command:
    ./bin/miral-app --add-wayland-extensions ext_output_image_capture_source_manager_v1:ext_image_copy_capture_manager_v1:zwlr_virtual_pointer_manager_v1:zwp_virtual_keyboard_manager_v1
    
  2. Run wayvnc with WAYLAND_DISPLAY=wayland-1 wayvnc
  3. Connect using a VNC client that supports client side cursors. Remina works here, but GNOME Connections does not.
  4. See that the placeholder white triangle cursor is displayed.

Checklist

  • Tests added and pass
  • Adequate documentation added
  • (optional) Added Screenshots or videos

The session uses the CursorObserverMultiplexer to track the cursor
position, and a source-dependent backend class to transform the output
space coordinates to match those of the captured image.

Currently it sends a white triangle as the cursor image rather than the
actual cursor image.
@jhenstridge jhenstridge requested a review from a team as a code owner January 14, 2026 01:28
@github-actions
Copy link

TICS Quality Gate

❌ Failed

mir

Coding Standards: ❌ 2 Blocking Issues

❌ Condition “No new Coding Standard Violations for level 1, 2, 3 with respect to Previous analysis” failed 1 time.
FileIssues
🪲 Total❌ Blocking

src/server/frontend_wayland/ext_image_capture_v1.cpp

2+2

See the results in the TICS Viewer

The following files have been checked for this project
  • src/server/frontend_wayland/ext_image_capture_v1.cpp
  • src/server/frontend_wayland/ext_image_capture_v1.h
  • src/server/frontend_wayland/wayland_connector.cpp
  • src/server/frontend_wayland/wayland_connector.h
  • src/server/frontend_wayland/wayland_default_configuration.cpp

TICS / TICS / Run TICS analysis

@AlanGriffiths AlanGriffiths requested a review from Copilot January 14, 2026 11:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements cursor session support for the ext_image_copy_capture_v1 Wayland protocol. The implementation adds cursor position tracking via CursorObserverMultiplexer and a backend architecture for transforming output-space coordinates to capture-space coordinates.

Changes:

  • Extended the image copy capture manager to accept cursor observer and clock dependencies
  • Implemented cursor session class with position tracking and visibility management
  • Added backend abstraction for coordinate mapping between different capture sources

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
wayland_default_configuration.cpp Passes cursor observer multiplexer and clock to image copy capture manager factory
wayland_connector.h Adds cursor observer multiplexer and clock to WaylandExtensions context
wayland_connector.cpp Wires cursor observer multiplexer through connector constructor
ext_image_capture_v1.h Updates function signatures to accept new dependencies
ext_image_capture_v1.cpp Implements cursor session tracking, coordinate mapping, and placeholder cursor rendering

auto data = mapping->data();
auto size = mapping->size();
auto stride = mapping->stride();
printf("Buffer length is %d (%dx%d, stride %d)\n", (int)mapping->len(), size.width.as_int(), size.height.as_int(), stride.as_int());
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print statements should be removed before merging. Use proper logging infrastructure if diagnostic output is needed.

Copilot uses AI. Check for mistakes.
}
}
// TODO: get a real timestamp from the clock
printf("Sending cursor image\n");
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print statements should be removed before merging. Use proper logging infrastructure if diagnostic output is needed.

Copilot uses AI. Check for mistakes.
public:
ImageCopyBackend(ExtImageCopyCaptureSessionV1 *session,
std::shared_ptr<time::Clock> const& clock);
//~ImageCopyBackend();
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out destructor declaration should be removed if not needed.

Suggested change
//~ImageCopyBackend();

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@AlanGriffiths AlanGriffiths left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some tidy-up that can be done now and not wait for a follow-up

Comment on lines +608 to +611
return geom::Point{
(abs_x - output_space_area.left().as_value()) * x_scale,
(abs_y - output_space_area.top().as_value()) * y_scale
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid unwrapping the values and gain type safety and clarity:

Suggested change
return geom::Point{
(abs_x - output_space_area.left().as_value()) * x_scale,
(abs_y - output_space_area.top().as_value()) * y_scale
};
auto const raw_offset = abs_pos - output_space_area.top_left;
decltype(raw_offset) const scaled_offset{x_scale * raw_offset.dx, y_scale * raw_offset.dy};
return as_point(scaled_offset);

public:
ImageCopyBackend(ExtImageCopyCaptureSessionV1 *session,
std::shared_ptr<time::Clock> const& clock);
//~ImageCopyBackend();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//~ImageCopyBackend();

auto data = mapping->data();
auto size = mapping->size();
auto stride = mapping->stride();
printf("Buffer length is %d (%dx%d, stride %d)\n", (int)mapping->len(), size.width.as_int(), size.height.as_int(), stride.as_int());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debugging?

Comment on lines +962 to +983
for (int y = 0; y < size.height.as_int(); y++)
{
for (int x = 0; x < size.width.as_int(); x++)
{
auto pos = y * stride.as_int() + x * 4;

if (x <= y)
{
data[pos] = std::byte{0xff};
data[pos+1] = std::byte{0xff};
data[pos+2] = std::byte{0xff};
data[pos+3] = std::byte{0xff};
}
else
{
data[pos] = std::byte{0x00};
data[pos+1] = std::byte{0x00};
data[pos+2] = std::byte{0x00};
data[pos+3] = std::byte{0x00};
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this temporary faking for image capture? I won't whine about the C-style loops

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants