[feat] add HarmonyOS desktop and mobile clients,issue #117

This commit is contained in:
kunkundi
2026-10-16 00:42:55 +08:00
parent 43a0789c1e
commit 45bf6ec4ee
149 changed files with 11587 additions and 93 deletions
+413
View File
@@ -0,0 +1,413 @@
#include "bridge.h"
#include "capture.h"
#include "input.h"
#include "keyboard_hook.h"
#include "host_access.h"
#include "keyboard_hook_mock.h"
#include "platform/harmony/input/system_shortcuts.h"
#include "screen_capturer_factory.h"
#include "device_controller_factory.h"
#include "runtime/cursor_state_provider.h"
#include "runtime/cursor_presentation.h"
#include <multimodalinput/oh_input_manager.h>
#include <hilog/log.h>
#include <cassert>
#include <memory>
#include <tuple>
#include <limits>
// Exercise the production desktop adapter and SDK declarations without
// recording the workstation or requesting real input-injection permissions.
using crossdesk::harmony::desktop::Json;
Json environment;
crossdesk::harmony::Capture::Frame frame_callback;
crossdesk::harmony::Capture::State state_callback;
std::tuple<int, int, int, int> capture_parameters;
int starts = 0, stops = 0, fail_display = -1, input_display = -1, releases = 0;
bool release_ok = true;
bool persistent_capture = false;
bool capture_show_cursor = true;
crossdesk::harmony::CaptureCursorMode captured_cursor_mode = crossdesk::harmony::CaptureCursorMode::separate;
struct Input_CursorInfo {};
bool pointer_visible = true;
Input_PointerStyle pointer_style = DEFAULT;
Input_Result cursor_result = INPUT_SUCCESS, location_result = INPUT_SUCCESS, style_result = INPUT_SUCCESS;
int32_t pointer_display = 0;
double pointer_x = 960, pointer_y = 270;
struct Input_MouseEvent { int32_t action, display, x, y; };
Input_MouseEventCallback mouse_monitor = nullptr;
Input_Result monitor_add_result = INPUT_PERMISSION_DENIED, monitor_remove_result = INPUT_SUCCESS;
int monitor_adds = 0, monitor_removes = 0;
const Input_MouseEvent* initial_monitor_event = nullptr;
int injection_requests = 0;
Input_InjectionStatus authorization = UNAUTHORIZED;
namespace crossdesk::harmony::desktop {
Json Environment() { return environment; }
void Emit(Json) {}
}
namespace crossdesk::harmony {
void CaptureSurface::Stop() {}
bool Capture::Start(int width, int height, uint64_t display, Frame frame, State state, Audio, int fps, bool persistent_permission, bool show_cursor) {
++starts;
persistent_capture = persistent_permission;
capture_show_cursor = show_cursor;
if (int(display) == fail_display) return false;
capture_parameters = {width, height, int(display), fps};
frame_callback = std::move(frame);
state_callback = state;
cursor_mode_ = show_cursor ? CaptureCursorMode::embedded : captured_cursor_mode;
state(OH_SCREEN_CAPTURE_STATE_STARTED);
return true;
}
void Capture::Stop() { ++stops; frame_callback = {}; }
bool Input::Configure(int, int, int display) { if (!Release()) return false; input_display = display; return true; }
bool Input::Release() { ++releases; return release_ok; }
bool Input::Apply(const RemoteAction&) { return true; }
bool Input::LockWorkstation() { return true; }
}
extern "C" {
int OH_LOG_Print(LogType, LogLevel, unsigned int, const char*, const char*, ...) { return 0; }
Input_Result OH_Input_QueryAuthorizedStatus(Input_InjectionStatus* result) { *result = authorization; return INPUT_SUCCESS; }
Input_Result OH_Input_RequestInjection(Input_InjectAuthorizeCallback callback) {
++injection_requests;
authorization = AUTHORIZED; callback(authorization); return INPUT_SUCCESS;
}
void OH_Input_CancelInjection() { authorization = UNAUTHORIZED; }
Input_CursorInfo* OH_Input_CursorInfo_Create() { return new Input_CursorInfo; }
void OH_Input_CursorInfo_Destroy(Input_CursorInfo** info) { delete *info; *info = nullptr; }
Input_Result OH_Input_GetCursorInfo(Input_CursorInfo*, OH_PixelmapNative** pixels) {
assert(pixels == nullptr); return cursor_result;
}
Input_Result OH_Input_CursorInfo_IsVisible(Input_CursorInfo*, bool* visible) {
*visible = pointer_visible; return INPUT_SUCCESS;
}
Input_Result OH_Input_CursorInfo_GetStyle(Input_CursorInfo*, Input_PointerStyle* style) {
assert(pointer_visible); *style = pointer_style; return style_result;
}
Input_Result OH_Input_GetPointerLocation(int32_t* display, double* x, double* y) {
*display = pointer_display; *x = pointer_x; *y = pointer_y; return location_result;
}
Input_Result OH_Input_AddMouseEventMonitor(Input_MouseEventCallback callback) {
++monitor_adds;
assert(!mouse_monitor);
if (monitor_add_result == INPUT_SUCCESS) {
mouse_monitor = callback;
if (initial_monitor_event) callback(initial_monitor_event);
}
return monitor_add_result;
}
Input_Result OH_Input_RemoveMouseEventMonitor(Input_MouseEventCallback callback) {
++monitor_removes;
assert(mouse_monitor == callback || (!mouse_monitor && monitor_remove_result == INPUT_PARAMETER_ERROR));
if (monitor_remove_result == INPUT_SUCCESS || monitor_remove_result == INPUT_PARAMETER_ERROR) mouse_monitor = nullptr;
return monitor_remove_result;
}
int32_t OH_Input_GetMouseEventAction(const Input_MouseEvent* event) { return event->action; }
int32_t OH_Input_GetMouseEventDisplayId(const Input_MouseEvent* event) { return event->display; }
int32_t OH_Input_GetMouseEventDisplayX(const Input_MouseEvent* event) { return event->x; }
int32_t OH_Input_GetMouseEventDisplayY(const Input_MouseEvent* event) { return event->y; }
}
int main() {
using namespace crossdesk;
const auto primary = Json::parse(R"({"id":0,"primary":true,"width":1920,"height":1080})");
const auto secondary = Json::parse(R"({"id":42,"x":-2560,"width":2560,"height":1440})");
environment["displays"] = Json::array({secondary, primary});
ScreenCapturerFactory factory;
std::unique_ptr<ScreenCapturer> screen(factory.Create());
std::string stream;
screen->Init(30, [&](unsigned char*, int, int, int, const char* id, const MiniRtcNativeVideoFrame*) { stream = id; });
assert(screen->GetDisplayInfoList().size() == 2);
assert(screen->Start(false) == 0);
assert((capture_parameters == std::tuple{1920, 1080, 0, 30}));
MiniRtcVideoFrame frame{}; frame_callback(frame); assert(stream == "Display1");
CursorStateProvider cursor;
CursorState cursor_state{};
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(!capture_show_cursor && cursor_state.render_mode == CursorRenderMode::separate);
assert(cursor_state.visible && cursor_state.shape == RemoteCursorShape::default_cursor);
assert(cursor_state.position_update && cursor_state.position_valid && cursor_state.display_id == 0);
assert(cursor_state.x == 0.5f && cursor_state.y == 0.25f);
const std::pair<Input_PointerStyle, RemoteCursorShape> shapes[] = {
{TEXT_CURSOR, RemoteCursorShape::text}, {HORIZONTAL_TEXT_CURSOR, RemoteCursorShape::text},
{HAND_POINTING, RemoteCursorShape::pointer}, {HAND_OPEN, RemoteCursorShape::grab},
{HAND_GRABBING, RemoteCursorShape::grabbing}, {CURSOR_FORBID, RemoteCursorShape::not_allowed},
{NORTH_WEST_SOUTH_EAST, RemoteCursorShape::nwse_resize}, {NORTH_EAST_SOUTH_WEST, RemoteCursorShape::nesw_resize},
{WEST_EAST, RemoteCursorShape::ew_resize}, {NORTH_SOUTH, RemoteCursorShape::ns_resize},
{RESIZE_LEFT_RIGHT, RemoteCursorShape::col_resize}, {RESIZE_UP_DOWN, RemoteCursorShape::row_resize},
{NORTH, RemoteCursorShape::n_resize}, {SOUTH, RemoteCursorShape::s_resize},
{EAST, RemoteCursorShape::e_resize}, {WEST, RemoteCursorShape::w_resize},
{NORTH_EAST, RemoteCursorShape::ne_resize}, {NORTH_WEST, RemoteCursorShape::nw_resize},
{SOUTH_EAST, RemoteCursorShape::se_resize}, {SOUTH_WEST, RemoteCursorShape::sw_resize},
{LOADING, RemoteCursorShape::wait}, {RUNNING, RemoteCursorShape::progress},
{HELP, RemoteCursorShape::help}, {MOVE, RemoteCursorShape::move},
{CROSS, RemoteCursorShape::crosshair}, {CURSOR_COPY, RemoteCursorShape::copy},
{DEVELOPER_DEFINED_ICON, RemoteCursorShape::default_cursor},
{static_cast<Input_PointerStyle>(999), RemoteCursorShape::default_cursor}
};
for (const auto& [native, remote] : shapes) {
pointer_style = native;
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.shape == remote);
RemoteAction outgoing{}, incoming{};
outgoing.type = ControlType::cursor_state; outgoing.cs = cursor_state;
assert(incoming.from_json(outgoing.to_json()));
assert(incoming.type == ControlType::cursor_state && incoming.cs.position_valid);
assert(incoming.cs.display_id == 0 && incoming.cs.x == 0.5f && incoming.cs.y == 0.25f);
// Mac and Windows both use this presentation policy for the Harmony host.
CursorPresentation presentation;
presentation.Observe(incoming.cs, 100);
assert(presentation.Resolve(101, HostPlatform::HarmonyOS) == remote);
}
pointer_visible = false;
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(!cursor_state.visible && cursor_state.shape == RemoteCursorShape::none);
assert(cursor_state.render_mode == CursorRenderMode::hidden && cursor_state.hidden_reason == CursorHiddenReason::system_hidden);
location_result = INPUT_DEVICE_NO_POINTER;
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(!cursor_state.position_valid && cursor_state.hidden_reason == CursorHiddenReason::no_pointing_device);
pointer_visible = true; pointer_style = TEXT_CURSOR;
location_result = INPUT_APP_NOT_FOCUSED;
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.visible && cursor_state.shape == RemoteCursorShape::text && !cursor_state.position_valid);
cursor_result = INPUT_SERVICE_EXCEPTION;
assert(!cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
cursor_result = INPUT_SUCCESS; style_result = INPUT_SERVICE_EXCEPTION;
assert(!cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
style_result = INPUT_SUCCESS; location_result = INPUT_SUCCESS;
pointer_display = 42; pointer_x = 1280; pointer_y = 720;
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.position_valid && cursor_state.display_id == 1 && cursor_state.x == 0.5f && cursor_state.y == 0.5f);
auto overlapping = screen->GetDisplayInfoList();
overlapping[1].left = overlapping[1].top = 0;
overlapping[1].right = 2560; overlapping[1].bottom = 1440;
assert(cursor.Sample(overlapping, 0, &cursor_state) && cursor_state.display_id == 1);
for (double invalid : {-1., 2560., std::numeric_limits<double>::infinity(), std::numeric_limits<double>::quiet_NaN()}) {
pointer_x = invalid;
assert(cursor.Sample(overlapping, 0, &cursor_state) && !cursor_state.position_valid);
}
pointer_x = 1280; pointer_display = 99;
assert(cursor.Sample(overlapping, 0, &cursor_state) && !cursor_state.position_valid);
pointer_display = 42;
overlapping[1].width = 0;
assert(cursor.Sample(overlapping, 0, &cursor_state) && !cursor_state.position_valid);
assert(screen->SwitchTo(1) == 0 && screen->GetCurrentMonitorIndex() == 1);
assert(!capture_show_cursor); // Separate cursor policy survives a physical display switch.
assert((capture_parameters == std::tuple{2560, 1440, 42, 30}));
frame_callback(frame); assert(stream == "Display2");
DeviceControllerFactory input_factory;
std::unique_ptr<DeviceController> device(input_factory.Create(DeviceControllerFactory::Mouse));
auto* mouse = static_cast<MouseController*>(device.get());
mouse->Init(screen->GetDisplayInfoList());
std::unique_ptr<DeviceController> keyboard_device(input_factory.Create(DeviceControllerFactory::Keyboard));
auto* keyboard = static_cast<KeyboardCapturer*>(keyboard_device.get());
const int before_unhook = releases;
assert(keyboard->Unhook() == 0 && releases == before_unhook);
harmony::desktop::SetKeyboardFocus(2, true);
harmony::desktop::SetKeyboardHookPermission(false);
hook_add_result = INPUT_PERMISSION_DENIED;
assert(keyboard->Hook([](int, bool, uint32_t, bool, void*) {}, nullptr) == INPUT_PERMISSION_DENIED);
assert(!keyboard->ShouldRetryHook()); // Do not interrupt a held fallback chord every second.
harmony::desktop::SetKeyboardHookPermission(true);
assert(keyboard->ShouldRetryHook());
hook_add_result = INPUT_SUCCESS;
RemoteAction action{}; action.type = ControlType::mouse;
release_ok = false;
assert(mouse->SendMouseCommand(action, 1) != 0 && input_display == 0);
release_ok = true;
const int before_input_switch = releases;
assert(mouse->SendMouseCommand(action, 1) == 0 && input_display == 42);
assert(releases == before_input_switch + 1);
assert(mouse->SendMouseCommand(action, 1) == 0 && releases == before_input_switch + 1);
assert(mouse->SendMouseCommand(action, -1) != 0 && mouse->SendMouseCommand(action, 2) != 0);
state_callback(OH_SCREEN_CAPTURE_STATE_MIC_MUTED_BY_USER);
assert(mouse->SendMouseCommand(action, 1) == 0);
state_callback(OH_SCREEN_CAPTURE_STATE_ENTER_PRIVATE_SCENE);
assert(mouse->SendMouseCommand(action, 1) != 0);
assert(!cursor.Sample(screen->GetDisplayInfoList(), 1, &cursor_state));
state_callback(OH_SCREEN_CAPTURE_STATE_EXIT_PRIVATE_SCENE);
assert(mouse->SendMouseCommand(action, 1) == 0);
authorization = UNAUTHORIZED;
assert(mouse->SendMouseCommand(action, 1) != 0);
authorization = AUTHORIZED;
assert(SendHarmonySystemShortcut(ServiceCommandFlag::lock_workstation));
assert(!SendHarmonySystemShortcut(ServiceCommandFlag::send_sas));
fail_display = 0;
assert(screen->SwitchTo(0) != 0 && screen->GetCurrentMonitorIndex() == 1);
frame_callback(frame); assert(stream == "Display2"); // Failed switch rolls back.
fail_display = -1;
environment["displays"] = Json::array({secondary});
const int before_removal = starts;
auto displays = screen->GetDisplayInfoList();
assert(displays.size() == 2 && displays[0].width == 0 && displays[1].width == 2560);
assert(screen->GetCurrentMonitorIndex() == 1 && starts == before_removal);
mouse->UpdateDisplayInfoList(displays);
assert(mouse->SendMouseCommand(action, 0) != 0);
assert(screen->SwitchTo(0) != 0);
environment["displays"] = Json::array({primary});
screen->GetDisplayInfoList();
assert(screen->GetCurrentMonitorIndex() == 0);
frame_callback(frame); assert(stream == "Display1");
environment["displays"].push_back(Json::parse(R"({"id":91,"width":1280,"height":720})"));
assert(screen->GetDisplayInfoList().size() == 2); // Unnegotiated streams must not be advertised as usable.
environment["displays"] = Json::array();
screen->GetDisplayInfoList();
assert(screen->GetCurrentMonitorIndex() == -1 && !frame_callback);
const int before_repeat = stops;
screen->GetDisplayInfoList(); assert(stops == before_repeat);
environment["displays"] = Json::array({secondary});
screen->GetDisplayInfoList();
assert(screen->GetCurrentMonitorIndex() == 1 && frame_callback);
frame_callback(frame); assert(stream == "Display2");
assert(screen->Pause(1) == 0 && !frame_callback);
assert(screen->Resume(1) == 0 && frame_callback);
assert(screen->Stop() == 0);
assert(mouse->SendMouseCommand(action, 1) != 0); // Authorization alone is insufficient after capture ends.
assert(!SendHarmonySystemShortcut(ServiceCommandFlag::lock_workstation));
// A persistent grant is independent of dialog authorization and survives
// disconnect/reconnect. Capture must still be active before accepting input.
harmony::desktop::SetHostPermissions(true, true);
const int before_persistent = injection_requests;
assert(authorization == UNAUTHORIZED);
assert(screen->Resume(1) == 0 && persistent_capture);
assert(injection_requests == before_persistent && authorization == UNAUTHORIZED);
mouse->UpdateDisplayInfoList(screen->GetDisplayInfoList());
assert(mouse->SendMouseCommand(action, 1) == 0);
assert(keyboard->SendKeyboardCommand(65, true) == 0);
assert(SendHarmonySystemShortcut(ServiceCommandFlag::lock_workstation));
state_callback(OH_SCREEN_CAPTURE_STATE_ENTER_PRIVATE_SCENE);
assert(mouse->SendMouseCommand(action, 1) != 0);
assert(keyboard->SendKeyboardCommand(65, false) != 0);
state_callback(OH_SCREEN_CAPTURE_STATE_EXIT_PRIVATE_SCENE);
assert(injection_requests == before_persistent);
assert(screen->Stop() == 0 && mouse->SendMouseCommand(action, 1) != 0);
assert(screen->Resume(1) == 0 && persistent_capture && injection_requests == before_persistent);
const int before_revoke = releases;
harmony::desktop::SetHostPermissions(false, false);
assert(releases == before_revoke + 1);
assert(mouse->SendMouseCommand(action, 1) != 0);
assert(keyboard->SendKeyboardCommand(65, true) != 0);
assert(!SendHarmonySystemShortcut(ServiceCommandFlag::lock_workstation));
assert(injection_requests == before_persistent); // Revocation never triggers a prompt loop.
assert(screen->Stop() == 0 && screen->Resume(1) == 0);
assert(!persistent_capture && injection_requests == before_persistent + 1);
assert(mouse->SendMouseCommand(action, 1) == 0); // Ordinary consent remains usable.
screen->Stop();
assert(!cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(screen->Start(true) == 0 && capture_show_cursor);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.render_mode == CursorRenderMode::embedded && !cursor_state.visible);
assert(cursor_state.shape == RemoteCursorShape::none && !cursor_state.position_valid);
CursorPresentation embedded;
embedded.Observe(cursor_state, 100);
assert(embedded.Resolve(101, HostPlatform::HarmonyOS) == RemoteCursorShape::none);
assert(screen->Start(false) == 0 && !capture_show_cursor);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.render_mode == CursorRenderMode::separate);
screen->Stop();
captured_cursor_mode = harmony::CaptureCursorMode::embedded; // Recorder refused to remove its cursor.
assert(screen->Start(false) == 0);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.render_mode == CursorRenderMode::embedded);
screen->Stop();
captured_cursor_mode = harmony::CaptureCursorMode::unknown;
assert(screen->Start(false) == 0);
assert(!cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
screen->Stop();
// INPUT_MONITORING supplies screen-relative positions while the direct
// query is rejected in the background. No INPUT_DEVICE_CONFIGURATOR grant.
environment["displays"] = Json::array({primary, secondary});
captured_cursor_mode = harmony::CaptureCursorMode::separate;
location_result = INPUT_APP_NOT_FOCUSED;
monitor_add_result = INPUT_SUCCESS;
assert(screen->Start(false) == 0 && mouse_monitor);
assert(cursor.Sample(screen->GetDisplayInfoList(), 1, &cursor_state) && !cursor_state.position_valid);
Input_MouseEvent motion{MOUSE_ACTION_MOVE, 42, 640, 720};
mouse_monitor(&motion);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.position_valid && cursor_state.display_id == 1);
assert(cursor_state.x == 0.25f && cursor_state.y == 0.5f && cursor_state.shape == RemoteCursorShape::text);
RemoteAction monitored_out{}, monitored_in{};
monitored_out.type = ControlType::cursor_state; monitored_out.cs = cursor_state;
assert(monitored_in.from_json(monitored_out.to_json()));
assert(monitored_in.cs.position_valid && monitored_in.cs.display_id == 1 && monitored_in.cs.x == 0.25f);
const int before_notification = monitor_adds;
state_callback(OH_SCREEN_CAPTURE_STATE_MIC_MUTED_BY_USER);
assert(monitor_adds == before_notification);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && cursor_state.position_valid);
const auto queued_monitor = mouse_monitor;
state_callback(OH_SCREEN_CAPTURE_STATE_ENTER_PRIVATE_SCENE);
assert(!mouse_monitor && !cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
queued_monitor(&motion); // Late events cannot repopulate a stopped session.
state_callback(OH_SCREEN_CAPTURE_STATE_EXIT_PRIVATE_SCENE);
assert(mouse_monitor && cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(!cursor_state.position_valid);
mouse_monitor(&motion);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && cursor_state.position_valid);
motion.action = MOUSE_ACTION_CANCEL;
mouse_monitor(&motion);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && !cursor_state.position_valid);
motion.action = MOUSE_ACTION_MOVE; motion.x = -1;
mouse_monitor(&motion);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && !cursor_state.position_valid);
motion.x = 640;
mouse_monitor(&motion);
assert(screen->SwitchTo(0) == 0 && mouse_monitor);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && !cursor_state.position_valid);
motion.display = 0; motion.x = 480; motion.y = 540;
mouse_monitor(&motion);
assert(cursor.Sample(screen->GetDisplayInfoList(), 1, &cursor_state));
assert(cursor_state.position_valid && cursor_state.display_id == 0 && cursor_state.x == 0.25f && cursor_state.y == 0.5f);
location_result = INPUT_DEVICE_NO_POINTER; pointer_visible = false;
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(!cursor_state.position_valid && cursor_state.hidden_reason == CursorHiddenReason::no_pointing_device);
location_result = INPUT_APP_NOT_FOCUSED; pointer_visible = true;
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && !cursor_state.position_valid);
monitor_remove_result = INPUT_SERVICE_EXCEPTION;
screen->Stop();
assert(mouse_monitor);
mouse_monitor(&motion); // Failed removal stays inert.
const int before_failed_restart = monitor_adds;
assert(screen->Start(false) == 0);
assert(monitor_adds == before_failed_restart); // Never install a duplicate callback.
mouse_monitor(&motion);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && !cursor_state.position_valid);
monitor_remove_result = INPUT_SUCCESS;
screen->Stop();
assert(!mouse_monitor);
assert(screen->Start(false) == 0 && mouse_monitor);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && !cursor_state.position_valid);
mouse_monitor(&motion);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && cursor_state.position_valid);
// Capture can remove the monitor itself; "not registered" is clean removal.
mouse_monitor = nullptr;
monitor_remove_result = INPUT_PARAMETER_ERROR;
state_callback(OH_SCREEN_CAPTURE_STATE_PAUSED_BY_USER);
monitor_remove_result = INPUT_SUCCESS;
initial_monitor_event = &motion;
state_callback(OH_SCREEN_CAPTURE_STATE_RESUMED_BY_USER);
initial_monitor_event = nullptr;
assert(mouse_monitor);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state) && cursor_state.position_valid);
screen->Stop();
assert(!mouse_monitor);
monitor_add_result = INPUT_PERMISSION_DENIED;
assert(screen->Start(false) == 0 && !mouse_monitor);
const int before_denied_notification = monitor_adds;
state_callback(OH_SCREEN_CAPTURE_STATE_MIC_MUTED_BY_USER);
assert(monitor_adds == before_denied_notification);
assert(cursor.Sample(screen->GetDisplayInfoList(), 0, &cursor_state));
assert(cursor_state.visible && cursor_state.shape == RemoteCursorShape::text && !cursor_state.position_valid);
screen->Stop();
const int before_embedded = monitor_adds;
assert(screen->Start(true) == 0 && !mouse_monitor && monitor_adds == before_embedded);
screen->Stop();
}